これはCTF Advent Calenderの記事でもなんでもありません。
なんか最近CTFから退いていたので、色々とglibcのheap allocator周りのpwnテクに発展があったりしたかなあと思い眺めてたんですけど、面白いものはほとんどなかったと共に人の書き方があまりにも辛い感じになっていて、ボケが入っていて良く過去の記事を参照してexploitを書く僕からすると当てにならなくて辛かったので自分用に少しメモっておく。後皆雑にExploitationテクに名前つけすぎ。
House of *系
前々からあったテクだしよく使いはするんだけど他人のWriteup見てて名前が出てきてもどのテクがどれを指してるのかよく分からなくなるのでメモっとく
House of Mind
arena forgeして任意のアドレスをheapのアドレスで書き換えてるだけ。
sploitfunのブログ書き方が悪いから一目で何が本質なのかマジで分からんし勘弁して欲しい。GOTとかに制限している意味もないし、もっと言うと実はHouse of Mindはまだ弱い。
結構内部で複雑な動きするから条件は書くのめんどい。自分の過去のコードパクるといい。
House of Force
topのサイズを無限大に飛ばすことで任意のアドレスをtopからのoffsetで表して返せるようにするやつ。
House of Lore
Shellphishのgitlabに改良版?っぽいの上がってるけど、smallbinに入るようなfreed chunkの、bkを書き換えるという自明なことをしているだけだし、当然assert抜けるのにheapのアドレス知らないといけないのでクソ弱い。まあアドレス知ってたら大体のbinで任意のアドレス登録出来るよってだけ。当然fastbinsの方が強い。
よく分からないのはfastbinsの方はHouse of Loreと呼ぶの?ということで、とりあえず皆さんに言いたいのは、テクニックに名前付けたりする時は、ちゃんと責任持って、何をどうした時にそう呼ぶのかをちゃんと明確に記述して欲しい。smallbin限定なのかどうかとか(しかも仮にsmallbin限定なら弱すぎるでしょという話で、それはそれで嫌)。後下にtopあっちゃダメとか、topあろうがなかろうがいけるとか、全部書けと。heapはコード量多いからどこでどう引っかかるかはコード読みながらやらないと分からないし、テクニックとしてある程度脊髄反射的に使わせたいのであれば、それぐらいはすべきでしょう。
House of Spirit
sploitfunのコードはいちいち理解に不必要な要素が多くて読みづらいな
fastbinsはアドレスの整合性チェックしてないのでbssとかのアドレスも余裕で登録できるというだけ。というかここら辺のテクニックは全部大体同じ性質から来る手法の亜種なのに、わざわざ別に扱ってるのが理解できない。fastbinsはn重にfree出来る話とかも全部本質は同じなんだけど、こいつら理解せずに使ってるんじゃないかという気持ちになる。
pzipの時に、本質的にこれと同じことを使って、chunk内にfastbinsのchunkを作って入れ子にすることで、外側のデータをいじる時にfastbinsのchunkをいじれるというものを使うんだけど、これは逆に中々に汎用性が高くかつ忘れがちなテクニックなので名前付けたほうが良いんじゃないのという感じがする(pzipはこれ使っても他に色々あってしんどかったと思う、解いた問題いちいち覚えてないので忘れた)。
Unsafe Unlink
shellphishの所に一応コードはあるが、これも正直マジで理解しづらい。どうやったらこんなに分かりづらく書けるのか本当に疑問。多分printfがうざいのも一員。別に実行時に確認したい情報じゃないんだから説明はコメントで良いんだよ。
見るならinazさんのコードが良いと思う。
既知のアドレスAを持つメモリ内にheapのアドレスBが入っていれば、A+定数をBの中に入れておくことで、B周りは矛盾なくunlinkが出来るので昔懐かしのunlink attackでA周りの値をA周りのアドレスに書き換えられるというだけ(基本的にexploitationにおいて大事なのは、どの領域をcontrol出来るか、つまり各アドレスはどの領域に属するかということなので、定数を無視すれば、概ね*A=Bであったとき、*A=Aとなるような操作が起こると思えばよい、条件式と起こる操作の式を考えるとまあそうなるよねということが分かる)。
shellphishのコードとinazさんのコードはかなりテクいことをしているけど、本質はunlinkなので、サイズとprev_inuseを消さないと発生しないということはないが、あのやり方はかなり汎用的でコードを深く潜って起こる操作ではないので、基本あれでいいと思う。HITCONの2014のstkofか何かを解いた方が多分理解しやすそう。
chunk size overwrite attack
なんか何故かこれは名前がないのでinazさんの呼び方を引用する。連続する3つのchunk、A, B, Cを考えた時、Bをfreeした上で、脆弱性によりそのsizeをより大きいものに書き換えた場合、BとCを重ねられる。本質的にsizeのみ書き換えられfd, bkとかはノータッチなこととunsorted_bin周りのチェックが雑なことによって、unsorted_binの処理に矛盾しないことによる。
Poisoned NULL Byte
これも本質は同じunsorted_binの整合性にあって、今度は逆にsizeを小さくした時、どうすればexploit出来るかを考えたものと捉えるとよい。
(なんか昔いくつかのPlaidDBのwriteupを眺めていると、他にもoverlapさせる手順があったように思うんだけど覚えてないんで誰か調べて欲しい。とりあえず一番有名な奴を下に書く)
上と同じ状況を考えた時、Bのsizeの下位1byteが0でなければ、NULL byteによるoverwriteは、sizeを小さくすることを意味する。上と同様に、これによる矛盾を検出する機構は今の所glibcには存在しないので、そのままBはunsorted_binにあり動き続ける。小さいsizeのallocationを行って2個chunkを返させる。もちろんよほど小さくない限りはunsorted_binからのallocateが優先されるので、Bがsplitされて2つの領域に別れて返される(B1, B2としよう)。allocationでunsorted_bin内のchunkからsplitした領域を返す時、隣り合うchunkの整合性を保つために次のchunkのprev_sizeを書き換える処理があるが、今回の場合においては、B1やB2のサイズすなわちBのサイズを元に計算するので、Cのprev_sizeではなく、もう少し手前がprev_sizeと認識され書き換えられてしまう。つまりCのprev_sizeは書き換わらないので、Cのprev_sizeを判断基準とする処理では、Cの前にあるchunkはB1のままである。この状態でB1, Cを順にfreeするとconsolidate backwardによりB1がconsolidateされるのでB2がoverwrapする。
(追記:
@potetisensei plaiddb別の思い出したかも。使用中の連続したチャンクABでBのprevinuseを消すと、Aはfreed扱いでprev_sizeはAの最後を参照するから任意の値にできて、B freeするとconsolidate backwardでoverwrap
— インターナショ丸エディタ (@potetisensei) 2016年12月9日
@potetisensei hhc0nullがhouse of なんたらと呼んでいた記憶があるけどそれと同じだったと思う
— インターナショ丸エディタ (@potetisensei) 2016年12月9日
)
unsorted_bin Attack
これもまたunsorted_binの処理の甘さに着目すると分かる手法。unsorted_bin内のchunkのサイズにjust fitなallocationが来た場合、特に整合性のチェックなど無しにchunk->bk->fd = unsorted_chunks (av)した上でchunkを返す処理がある。よってunsorted_bin内のchunkのbkを書き換えておけばmain_arena内のアドレスなどが任意のアドレスに書き込まれる。
これぐらいかなあ。直近で驚いたものといえばHITCONのhouseoforangeのsysmalloc内の_int_freeぐらいなんだけど、今の所作問案が消費されていなくて安心という気持ちしかない。というか早く公開したい気持ちしかないんだけどbinja CTFの予定立たなさすぎて辛い。