how2heap是由shellphish团队制作的堆利用教程,介绍了多种堆利用技术,后续系列实验我们就通过这个教程来学习。环境可参见从零开始配置pwn环境:优化pwn虚拟机配置支持libc等指令-CSDN博客
1.理解glibc分配机制
pwndbg> r
Starting program: /ctf/work/how2heap/first_fit
尽管这个例子没有演示攻击效果,但是它演示了 glibc 的分配机制
glibc 使用首次适应算法选择空闲的堆块
如果有一个空闲堆块且足够大,那么 malloc 将选择它
如果存在 use-after-free 的情况那可以利用这一特性
首先申请两个比较大的 chunk
第一个 a = malloc(0x512) 在: 0x603010
第二个 b = malloc(0x256) 在: 0x603530
我们可以继续分配
现在我们把 "AAAAAAAA" 这个字符串写到 a 那里
第一次申请的 0x603010 指向 AAAAAAAA
接下来 free 掉第一个...
接下来只要我们申请一块小于 0x512 的 chunk,那就会分配到原本 a 那里: 0x603010
第三次 c = malloc(0x500) 在: 0x603010
我们这次往里写一串 "CCCCCCCC" 到刚申请的 c 中
第三次申请的 c 0x603010 指向 CCCCCCCC
第一次申请的 a 0x603010 指向 CCCCCCCC
可以看到,虽然我们刚刚看的是 a 的,但它的内容却是 "CCCCCCCC"
[Inferior 1 (process 60) exited normally]
pwndbg>
2.first_fit程序
这个程序并不展示如何攻击,而是展示glibc的一种分配规则。glibc 使用一种first-fit算法去选择一个free-chunk。如果存在一个free-chunk并且足够大的话,malloc会优先选取这个chunk。这种机制就可以在被利用于use after free(简称 uaf) 的情形中.
使用命令gcc -g first_fit.c -o first_fit
编译,-g参数会保留代码的文字信息,便于调试。以下为first_fit.c程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
fprintf(stderr, "尽管这个例子没有演示攻击效果,但是它演示了 glibc 的分配机制\n");
fprintf(stderr, "glibc 使用首次适应算法选择空闲的堆块\n");
fprintf(stderr, "如果有一个空闲堆块且足够大,那么 malloc 将选择它\n");
fprintf(stderr, "如果存在 use-after-free 的情况那可以利用这一特性\n");
fprintf(stderr, "首先申请两个比较大的 chunk\n");
char* a = malloc(0x512);
char* b = malloc(0x256);
char* c;
fprintf(stderr, "第一个 a = malloc(0x512) 在: %p\n", a);
fprintf(stderr, "第二个 b = malloc(0x256) 在: %p\n", b);
fprintf(stderr, "我们可以继续分配\n");
fprintf(stderr, "现在我们把 \"AAAAAAAA\" 这个字符串写到 a 那里 \n");
strcpy(a, "AAAAAAAA");
fprintf(stderr, "第一次申请的 %p 指向 %s\n", a, a);
fprintf(stderr, "接下来 free 掉第一个...\n");
free(a);
fprintf(stderr, "接下来只要我们申请一块小于 0x512 的 chunk,那就会分配到原本 a 那里: %p\n", a);
c = malloc(0x500);
fprintf(stderr, "第三次 c = malloc(0x500) 在: %p\n", c);
fprintf(stderr, "我们这次往里写一串 \"CCCCCCCC\" 到刚申请的 c 中\n");
strcpy(c, "CCCCCCCC");
fprintf(stderr, "第三次申请的 c %p 指向 %s\n", c, c);
fprintf(stderr, "第一次申请的 a %p 指向 %s\n", a, a);
fprintf(stderr, "可以看到,虽然我们刚刚看的是 a 的,但它的内容却是 \"CCCCCCCC\"\n");
}
3.调试程序
root@pwn_test1604:/ctf/work/how2heap# gdb ./first_fit
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 ./first_fit...done.
pwndbg> b 22
Breakpoint 1 at 0x40078b: file first_fit.c, line 22.
pwndbg> b 35
Breakpoint 2 at 0x400893: file first_fit.c, line 35.
pwndbg> r
Starting program: /ctf/work/how2heap/first_fit
尽管这个例子没有演示攻击效果,但是它演示了 glibc 的分配机制
glibc 使用首次适应算法选择空闲的堆块
如果有一个空闲堆块且足够大,那么 malloc 将选择它
如果存在 use-after-free 的情况那可以利用这一特性
首先申请两个比较大的 chunk
第一个 a = malloc(0x512) 在: 0x603010
第二个 b = malloc(0x256) 在: 0x603530
我们可以继续分配
现在我们把 "AAAAAAAA" 这个字符串写到 a 那里
Breakpoint 1, main () at first_fit.c:22
22 fprintf(stderr, "第一次申请的 %p 指向 %s\n", a, a);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
RAX 0x603010 ◂— 'AAAAAAAA'
RBX 0x0
RCX 0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0
RDI 0x2
RSI 0x4141414141414141 ('AAAAAAAA')
R8 0x3b
R9 0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887
R10 0x1
R11 0x246
R12 0x400550 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe6b0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe5d0 —▸ 0x4008c0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe5b0 —▸ 0x4008c0 (__libc_csu_init) ◂— push r15
RIP 0x40078b (main+325) ◂— mov rax, qword ptr [rip + 0x2018ce]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
► 0x40078b <main+325> mov rax, qword ptr [rip + 0x2018ce] <0x602060>
0x400792 <main+332> mov rcx, qword ptr [rbp - 0x18]
0x400796 <main+336> mov rdx, qword ptr [rbp - 0x18]
0x40079a <main+340> mov esi, 0x400b38
0x40079f <main+345> mov rdi, rax
0x