読者です 読者をやめる 読者になる 読者になる

きゅうり。

主にCTF関係のことを書いていく気がします

House of Force をやってみる

過去の問題を元にHouse of Forceの練習をしてみたので、そのwriteupのような覚書です。

how2heapにも載っている、Boston Key Party 2016 の cookbook という問題を解いてみました。

 

House of Forceとは

以下のような状況を想定。

  • main_arena.top のアドレスがわかる
  • main_arena.top を書き換え可能
  • 任意サイズでmallocすることができる

このとき、main_arena.topを0xffffffffなどの大きな値で書き換える。その後適当な大きさでmallocをすることで、次回のmallocで任意のアドレスを返すことができるというもの。

ここで適当な大きさとは、(確保したいアドレス-8)-現在のtop-8となる。

前者のマイナス8は、malloc_chunk構造体によるもの(prev_size, sizeを考慮)である。

後者のマイナス8は、topからchunkを切り出すか判断する際のmallocの仕様によるものであり、細かくは気にしなくてよい(と思われる)。

 

通常はmallocで確保した領域に対して書き込みが可能なアプリケーションがほとんであるため、任意のアドレスを確保できるようになることはすなわち、任意のアドレスを上書き可能となったに等しい。

 

cookbook writeup

名前の通り料理系のアプリ。素材の登録・削除、レシピの登録・削除、cookbook名の登録・削除ができる。

それぞれのデータはヒープで管理されている。

gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

解析パートは省くが、様々な脆弱性がある(heap oveflow, double free, use-after-free)

このうち、use-after-freeとheap oveflowを使って解く。

 

main_arena.top 、ヒープのアドレスリーク

まずは、House of Forceの条件の1つ目である、現在のmain_arena.topのアドレスリークを行う。これは、レシピ登録にあるuse-after-freeを用いる。具体的には、一旦レシピに適当な素材を適当数登録した後discardすると、素材の個数へのポインタが入っていたアドレスにbkメンバが入る。discard後もprint current recipeすることができ、素材の個数の欄にbkの情報が漏洩する。

特にfree listがない状態でこれを行うと、bkはmain_arena.topを指すため、main_arena.topのアドレスをリークすることができる。

また、main_arena.topのアドレスを元にヒープのベースアドレスも計算することができる。

 

main_arena.top overwrite

次に、2つ目の条件であるmain_arena.topの書き換えを行う。これは、レシピ登録にあるheap oveflowを用いる。具体的には、レシピ名でoverflowが可能なので、main_arena.topのある位置まで書き込み、0xffffffffで上書きをする。

 

任意サイズでのmalloc

これは脆弱性を使うことなく、cookbook名の登録機能で実現できる。16進数で入力した値をそのまま引数にmallocしてくれるため、任意のサイズ(負数も可)でmallocを呼べる。

 

さて、以上よりHouse of Forceの条件がすべて整った。

あとは書き換えたいアドレスから確保すべきサイズを計算し実際に確保するだけである。

 

この問題はFULL RELROではないので、GOT overwriteをしてsystem(/bin/sh)を呼ぶのがおそらく一番簡単である。入力をそのまま引数にとるatoiがあるので、これのGOTを書き換える。が、systemのアドレスを求めるためにlibcのベースアドレスを漏洩させる必要がある。したがって、atoiのgotを一旦printf@pltに書き換えることでFSBを発生させてlibcのアドレスを計算する。ちなみにatoiはadd ingredientのpriceやcal設定で呼ばれる。

なお、ユーザの入力をそのまま引数にとるという点ではmallocもそうだが、このプログラムはいたる所でmalloc/freeを呼ぶため、libc計算のためにmallocを壊すとうまく動かなくなるためatoiを使った。

 

以上のことをすべてまとめると以下の手順になる。

  1. create_recipeのuse-after-freeによりmain_arena.topのアドレスをleak
  2. create_recipeのoveflowによりmain_arena.topを0xffffffffでoverwrite
  3. House of ForceによりmallocでatoiのGOT付近を返す
  4. GOT overwriteによりatoiをprintfにする
  5. FSBによりlibcのアドレスをleak
  6. 再びHouse of ForceによりatoiのGOTをsystemにoverwrite

 

以下ソルバ

 

工夫点(苦戦点)

  • 一番はじめにingredient用chunkを確保

House of Force後にやると変なところに確保されるのでは?という気がしたため。ただしこの後の工夫によって杞憂となったと思う。

  • 1回目のGOT overwriteをしつつtopをheap領域に戻すようなsizeを指定

GOTの破壊が進行してしまうのを防ぐため。ここに適当なsizeを指定していたときは、GOT内のアドレスをfreeに渡してエラー終了していたが、工夫することで正常に動くようになった。

  • GOT overwriteの際、4バイト手前から書き込んでいる点

これは、mallocの8バイトアラインメントによりピッタリのアドレスを返せないことが原因だと思われる。はじめは丁度のアドレスを返そうと試していたが、入力sizeを4バイトずらすと確保される領域は8バイトずれてしまう。

 

まとめ

というわけでHouse of Forceをやってみました。m1z0r3内の勉強会でcookbookをやっていたときはヒープがよくわからないのでスルーしていたものの、unlink attackで解いてたような気がするので時間があったらそっちもやってみます。

次はHouse of Einherjarをやってみたいです。

おわり。

 

参考

House of Forceの概要と実践(題材:BCTF2016 bcloud) - Pwn De Ring

glibc malloc exploit techniques - ももいろテクノロジー