Chunk Extend and Overlapping
Overview
chunk extend 是堆漏洞的一种常见利用手法,通过 extend 可以实现 chunk overlapping 的效果。这种利用方法需要以下的时机和条件:
- 程序中存在基于堆的漏洞
- 漏洞可以控制 chunk header 中的数据
ptmalloc对堆进行操作时使用的宏
chunk extend 技术能够产生的原因在于 ptmalloc 在对堆 chunk 进行操作时使用的各种宏;
在 ptmalloc 中,获取 chunk 块大小的操作如下:
/* Get size, ignoring use bits */
#define chunksize(p) (chunksize_nomask(p) & ~(SIZE_BITS))
/* Like chunksize, but do not mask SIZE_BITS. */
#define chunksize_nomask(p) ((p)->mchunk_size)即使用当前块指针加上当前块大小。
在 ptmalloc 中,获取前一个 chunk 信息的操作如下:
/* Size of the chunk below P. Only valid if prev_inuse (P). */
#define prev_size(p) ((p)->mchunk_prev_size)
/* Ptr to previous physical malloc_chunk. Only valid if prev_inuse (P). */
#define prev_chunk(p) ((mchunkptr)(((char *) (p)) - prev_size(p)))即通过 malloc_chunk->prev_size 获取前一块大小,然后使用本 chunk 地址减去所得大小。
在 ptmalloc,判断当前 chunk 是否是 use 状态的操作如下:
#define inuse(p)
((((mchunkptr)(((char *) (p)) + chunksize(p)))->mchunk_size) & PREV_INUSE)即查看下一 chunk 的 prev_inuse 域,而下一块地址又如我们前面所述是根据当前 chunk 的 size 计算得出的。
具体为什么是这样以及更多操作详见 CTF Wiki - 堆相关数据结构。
通过上面几个宏可以看出,ptmalloc 通过 chunk header 的数据判断 chunk 的使用情况和对 chunk 的前后块进行定位。简而言之,chunk extend 就是通过控制 size 和 pre_size 域来实现跨越块操作从而导致 overlapping 的。
基本示例 1:对 inuse 的 fastbin 进行 extend
- 利用的效果是通过更改第一个块的大小来控制第二个块的内容。
- 示例都是在 64 位的程序。如果想在 32 位下进行测试,可以把 8 字节偏移改为 4 字节。
int main(void)
{
void *ptr,*ptr1;
ptr=malloc(0x10);//分配第一个0x10的chunk
malloc(0x10);//分配第二个0x10的chunk
*(long long *)((long long)ptr-0x8)=0x41;// 修改第一个块的size域
free(ptr);
ptr1=malloc(0x30);// 实现 extend,控制了第二个块的内容
return 0;
}我们进pwndbg中调试一下,观察“当两个 malloc 语句执行之后,堆的内存分布”、“代码中把 chunk1 的 size 域更改为 0x41”、“执行 free 之后,chunk2 与 chunk1 合成一个 0x40 大小的 chunk”和“通过 malloc(0x30) 得到 chunk1+chunk2 的块”;
我分别在
main+22(第一个malloc返回处)、main+36(第二个malloc返回处)、main+51(把 chunk1 的 size 域更改为 0x41后返回处)、main+58(free前)、main+63(free后)和main+73(通过 malloc(0x30) 得到 chunk1+chunk2 的块前)下断点。
pwndbg> disassemble main
Dump of assembler code for function main:
0x0000000000401156 <+0>: endbr64
0x000000000040115a <+4>: push rbp
0x000000000040115b <+5>: mov rbp,rsp
0x000000000040115e <+8>: sub rsp,0x10
0x0000000000401162 <+12>: mov edi,0x10
0x0000000000401167 <+17>: call 0x401060 <malloc@plt>
0x000000000040116c <+22>: mov QWORD PTR [rbp-0x8],rax
0x0000000000401170 <+26>: mov edi,0x10
0x0000000000401175 <+31>: call 0x401060 <malloc@plt>
0x000000000040117a <+36>: mov rax,QWORD PTR [rbp-0x8]
0x000000000040117e <+40>: sub rax,0x8
0x0000000000401182 <+44>: mov QWORD PTR [rax],0x41
0x0000000000401189 <+51>: mov rax,QWORD PTR [rbp-0x8]
0x000000000040118d <+55>: mov rdi,rax
0x0000000000401190 <+58>: call 0x401050 <free@plt>
0x0000000000401195 <+63>: mov edi,0x30
0x000000000040119a <+68>: call 0x401060 <malloc@plt>
0x000000000040119f <+73>: mov QWORD PTR [rbp-0x10],rax
0x00000000004011a3 <+77>: mov eax,0x0
0x00000000004011a8 <+82>: leave
0x00000000004011a9 <+83>: ret
End of assembler dump.
pwndbg> b *main+22
Breakpoint 1 at 0x40116c: file main.c, line 7.
pwndbg> b *main+36
Breakpoint 2 at 0x40117a: file main.c, line 10.
pwndbg> b *main+51
Breakpoint 3 at 0x401189: file main.c, line 12.
pwndbg> b *main+58
Breakpoint 4 at 0x401190: file main.c, line 12.
pwndbg> b *main+63
Breakpoint 5 at 0x401195: file main.c, line 13.
pwndbg> b *main+73
Breakpoint 6 at 0x40119f: file main.c, line 13.
pwndbg>其实我这里的多下了一个断点,所以我这里c了一次
pwndbg> c
Continuing.
Breakpoint 2, main () at main.c:10
10 *(long long *)((long long)ptr-0x8)=0x41;// 修改第一个块的size域
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────────────────
*RAX 0x4042c0 ◂— 0
RBX 0
RCX 0x21
RDX 0
RDI 0
*RSI 0x4042d0 ◂— 0
R8 0x21001
*R9 0x4042c0 ◂— 0
R10 0xfffffffffffff000
R11 0x7ffff7e1ace0 (main_arena+96) —▸ 0x4042d0 ◂— 0
R12 0x7fffffffdcf8 —▸ 0x7fffffffdfb5 ◂— 0x657a2f656d6f682f ('/home/ze')
R13 0x401156 (main) ◂— endbr64
R14 0x4030e8 (__do_global_dtors_aux_fini_array_entry) —▸ 0x401120 (__do_global_dtors_aux) ◂— endbr64
R15 0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0
RBP 0x7fffffffdbe0 ◂— 1
RSP 0x7fffffffdbd0 {ptr1} ◂— 0x1000
*RIP 0x40117a (main+36) ◂— mov rax, qword ptr [rbp - 8]
──────────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]───────────────────────────────────────────────────────────────────────────────
b+ 0x40116c <main+22> mov qword ptr [rbp - 8], rax [{ptr}] <= 0x4042a0 ◂— 0
0x401170 <main+26> mov edi, 0x10 EDI => 0x10
0x401175 <main+31> call malloc@plt <malloc@plt>
► 0x40117a <main+36> mov rax, qword ptr [rbp - 8] RAX, [{ptr}] => 0x4042a0 ◂— 0
0x40117e <main+40> sub rax, 8 RAX => 0x404298 (0x4042a0 - 0x8)
0x401182 <main+44> mov qword ptr [rax], 0x41 [0x404298] <= 0x41
b+ 0x401189 <main+51> mov rax, qword ptr [rbp - 8] RAX, [{ptr}] => 0x4042a0 ◂— 0
0x40118d <main+55> mov rdi, rax RDI => 0x4042a0 ◂— 0
b+ 0x401190 <main+58> call free@plt <free@plt>
b+ 0x401195 <main+63> mov edi, 0x30 EDI => 0x30
0x40119a <main+68> call malloc@plt <malloc@plt>
────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]────────────────────────────────────────────────────────────────────────────────────────
In file: /home/zer0ptr/Pwn-Research/Heap-overflow-basic/Chunk_extend_overlapping/对inuse的fastbin进行extend/main.c:10
5 void *ptr,*ptr1;
6
7 ptr=malloc(0x10);//分配第一个0x10的chunk
8 malloc(0x10);//分配第二个0x10的chunk
9
► 10 *(long long *)((long long)ptr-0x8)=0x41;// 修改第一个块的size域
11
12 free(ptr);
13 ptr1=malloc(0x30);// 实现 extend,控制了第二个块的内容
14 return 0;
15 }
────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffdbd0 {ptr1} ◂— 0x1000
01:0008│-008 0x7fffffffdbd8 {ptr} —▸ 0x4042a0 ◂— 0
02:0010│ rbp 0x7fffffffdbe0 ◂— 1
03:0018│+008 0x7fffffffdbe8 —▸ 0x7ffff7c29d90 (__libc_start_call_main+128) ◂— mov edi, eax
04:0020│+010 0x7fffffffdbf0 ◂— 0
05:0028│+018 0x7fffffffdbf8 —▸ 0x401156 (main) ◂— endbr64
06:0030│+020 0x7fffffffdc00 ◂— 0x1ffffdce0
07:0038│+028 0x7fffffffdc08 —▸ 0x7fffffffdcf8 —▸ 0x7fffffffdfb5 ◂— 0x657a2f656d6f682f ('/home/ze')
──────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────────────────
► 0 0x40117a main+36
1 0x7ffff7c29d90 __libc_start_call_main+128
2 0x7ffff7c29e40 __libc_start_main+128
3 0x401095 _start+37
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> x/10gx 0x4042a0
0x4042a0: 0x0000000000000000 0x0000000000000000
0x4042b0: 0x0000000000000000 0x0000000000000021
0x4042c0: 0x0000000000000000 0x0000000000000000
0x4042d0: 0x0000000000000000 0x0000000000020d31
0x4042e0: 0x0000000000000000 0x0000000000000000
pwndbg>当两个 malloc 语句执行之后,堆的内存分布如上;
之后,我们把 chunk1 的 size 域更改为 0x41,0x41 是因为 chunk 的 size 域包含了用户控制的大小和 header 的大小。如上所示正好大小为 0x40。在题目中这一步可以由堆溢出得到。
pwndbg> n
Breakpoint 4, 0x0000000000401190 in main () at main.c:12
12 free(ptr);
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────────────────
*RAX 0x4042a0 ◂— 0
RBX 0
RCX 0x21
RDX 0
*RDI 0x4042a0 ◂— 0
RSI 0x4042d0 ◂— 0
R8 0x21001
R9 0x4042c0 ◂— 0
R10 0xfffffffffffff000
R11 0x7ffff7e1ace0 (main_arena+96) —▸ 0x4042d0 ◂— 0
R12 0x7fffffffdcf8 —▸ 0x7fffffffdfb5 ◂— 0x657a2f656d6f682f ('/home/ze')
R13 0x401156 (main) ◂— endbr64
R14 0x4030e8 (__do_global_dtors_aux_fini_array_entry) —▸ 0x401120 (__do_global_dtors_aux) ◂— endbr64
R15 0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0
RBP 0x7fffffffdbe0 ◂— 1
RSP 0x7fffffffdbd0 {ptr1} ◂— 0x1000
*RIP 0x401190 (main+58) ◂— call free@plt
──────────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]───────────────────────────────────────────────────────────────────────────────
b+ 0x40117a <main+36> mov rax, qword ptr [rbp - 8] RAX, [{ptr}] => 0x4042a0 ◂— 0
0x40117e <main+40> sub rax, 8 RAX => 0x404298 (0x4042a0 - 0x8)
0x401182 <main+44> mov qword ptr [rax], 0x41 [0x404298] <= 0x41
b+ 0x401189 <main+51> mov rax, qword ptr [rbp - 8] RAX, [{ptr}] => 0x4042a0 ◂— 0
0x40118d <main+55> mov rdi, rax RDI => 0x4042a0 ◂— 0
► 0x401190 <main+58> call free@plt <free@plt>
ptr: 0x4042a0 ◂— 0
b+ 0x401195 <main+63> mov edi, 0x30 EDI => 0x30
0x40119a <main+68> call malloc@plt <malloc@plt>
b+ 0x40119f <main+73> mov qword ptr [rbp - 0x10], rax
0x4011a3 <main+77> mov eax, 0 EAX => 0
0x4011a8 <main+82> leave
────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]────────────────────────────────────────────────────────────────────────────────────────
In file: /home/zer0ptr/Pwn-Research/Heap-overflow-basic/Chunk_extend_overlapping/对inuse的fastbin进行extend/main.c:12
7 ptr=malloc(0x10);//分配第一个0x10的chunk
8 malloc(0x10);//分配第二个0x10的chunk
9
10 *(long long *)((long long)ptr-0x8)=0x41;// 修改第一个块的size域
11
► 12 free(ptr);
13 ptr1=malloc(0x30);// 实现 extend,控制了第二个块的内容
14 return 0;
15 }
────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffdbd0 {ptr1} ◂— 0x1000
01:0008│-008 0x7fffffffdbd8 {ptr} —▸ 0x4042a0 ◂— 0
02:0010│ rbp 0x7fffffffdbe0 ◂— 1
03:0018│+008 0x7fffffffdbe8 —▸ 0x7ffff7c29d90 (__libc_start_call_main+128) ◂— mov edi, eax
04:0020│+010 0x7fffffffdbf0 ◂— 0
05:0028│+018 0x7fffffffdbf8 —▸ 0x401156 (main) ◂— endbr64
06:0030│+020 0x7fffffffdc00 ◂— 0x1ffffdce0
07:0038│+028 0x7fffffffdc08 —▸ 0x7fffffffdcf8 —▸ 0x7fffffffdfb5 ◂— 0x657a2f656d6f682f ('/home/ze')
──────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────────────────
► 0 0x401190 main+58
1 0x7ffff7c29d90 __libc_start_call_main+128
2 0x7ffff7c29e40 __libc_start_main+128
3 0x401095 _start+37
pwndbg> x/4gx 0x404290
0x404290: 0x0000000000000000 0x0000000000000041
0x4042a0: 0x0000000000000000 0x0000000000000000这里小回顾一下chunk的结构,就可以解释为什么是看
0x404290这个地址了(这个地址是chunk的prev_size,而我们修改的就是size域),下面是一张参考图:

执行 free 之后,我们可以看到 chunk2 与 chunk1 合成一个 0x40 大小的 chunk,一起释放了:
pwndbg> bins
tcachebins
0x40 [ 1]: 0x4042a0 ◂— 0
fastbins
empty
unsortedbin
empty
smallbins
empty
largebins
empty之后我们通过 malloc(0x30) 得到 chunk1+chunk2 的块,此时就可以直接控制 chunk2 中的内容,我们也把这种状态称为 overlapping chunk。