0CTF 2017 writeup その3
その他writeup
0CTF 2017 writeup その2 - きゅうり。 (char)
0CTF 2017 writeup - きゅうり。 (integrity, oneTimePad)
残り解く予定(oneTimePad2、BabyHeap2017、diethard)
今回は、EasiestPrintfのwriteupです。と言っても、自力では解けないので他の人のwriteupを参考に勉強した記録です。
tl;dr
今回得られた知見
- printf内でのmalloc/free呼び出し
- hook関数
- one-gadget RCE
writeup
動作としては、任意のアドレスを1つだけ教えてくれた後、FSBのあるleave関数が呼び出されて即exitという流れ。
NX、Full RELROで制約は厳しい。
まず、任意のアドレスをリークできる部分で適当にlibcのアドレスを特定。
本来ならばここからデストラクタを書き換えたりGOT overwriteしたりしたいところだが、Full RELROなので思いつくことが諸々できない。
現状ではEIPを奪えていないので、まずはEIPを奪うことを考える。
結論から述べると、printf内で呼ばれるmalloc/freeを利用する。printfを追って行くとvfprintf.cなるものが呼ばれることがわかるが、ソースコードを読んでみると、widthという値がWORK_BUFFER_SIZE-32以上のときmallocが呼ばれる様子がわかる。(1475行目前後)
要するに、printfで表示する文字列がある程度長いときには一旦mallocで確保した領域に文字列を置き、表示後にfreeしているものと考えられる。
ではこのmalloc/freeをどう利用するかだが、各種hook関数(__malloc_hook, __free_hook)に注目する。hook関数を用いるとmallocなどの関数を独自関数に置き換えることができ、通常はデバッグ用などに使われる。ユーザが定義するので当然と言えば当然だが、この各種hook関数はlibcのうちwritableな領域にある。(画像一番下)
未定義の時は該当するアドレスには0が入っているため、おそらく最初にhook関数のアドレスが0かどうかを確認し、0でない時はそのアドレス先が呼ばれると思われる。
なので、FSBで__free_hookをoverwriteしつつ、一定量表示させるようにすることでEIPを奪うことができる。
最後にどうやってシェルを取るかだが、one-gadget RCEを用いた。いわゆる一発でシェルをとれるgadgetである。なおこのようなgadgetがlibcのどこにあるかは外部によくまとまっているブログがあったのでそこを参考にした。どうやら本番環境のlibcにも同様のgadgetはあるようなので問題ない、と思う。
EIPを奪えているので、スタックのアドレスをリークしつつmainに再び飛ばしても解けるとは思うが、せっかくなので新たなものを使ってみた。