How2heap学习笔记-2
Ubuntu16.04
glibc2.23
unsafe_unlink
Exploiting free on a corrupted chunk to get arbitrary write
unlink分析
正常的unlink目的是把一个处于双向链表中的空闲块拿出来
free当前堆块,分别判断前后的chunk是否处于空闲,若处于空闲则进行合并,执行unlink

unlink的具体步骤1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
FD = P->fd;
BK = P->bk;
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) //检查
malloc_printerr (check_action, "corrupted double-linked list", P);
else {
FD->bk = BK;
BK->fd = FD; [*]
if (!in_smallbin_range (P->size) //下面的步骤对于small chunk不涉及
&& __builtin_expect (P->fd_nextsize != NULL, 0)) {
assert (P->fd_nextsize->bk_nextsize == P);
assert (P->bk_nextsize->fd_nextsize == P);
if (FD->fd_nextsize == NULL) {
if (P->fd_nextsize == P)
FD->fd_nextsize = FD->bk_nextsize = FD;
else {
FD->fd_nextsize = P->fd_nextsize;
FD->bk_nextsize = P->bk_nextsize;
P->fd_nextsize->bk_nextsize = FD;
P->bk_nextsize->fd_nextsize = FD;
}
} else {
P->fd_nextsize->bk_nextsize = P->bk_nextsize;
P->bk_nextsize->fd_nextsize = P->fd_nextsize;
}
}
}
}

相应的检查:1
2
3
4
5
6
7
8
9
10
11
12// 由于 P 已经在双向链表中,所以有两个地方记录其大小,所以检查一下其大小是否一致(size检查)
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))
malloc_printerr ("corrupted size vs. prev_size");
// 检查 fd 和 bk 指针(双向链表完整性检查)
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
malloc_printerr (check_action, "corrupted double-linked list", P, AV);
// largebin 中 next_size 双向链表完整性检查
if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)
|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))
malloc_printerr (check_action,
"corrupted double-linked list (not small)",
P, AV);
示例代码
1 |
|
触发条件:
- 存在位于bss段的指针指向一个堆
- 拥有堆溢出的漏洞
步骤:
- bss段上存放着指向chunk0的指针(大小为small chunk,要位于双向链表中)
- 申请一个chunk1位于chunk0后面,地址
0x602078处存放着指向chunk0的指针。此时我们在chunk0里面伪造fd和bk,使fd指向0x602078-0x18,bk指向0x602078-0x10处,这样在索引的时候恰好使其FD的bk指向自己,BK的fd也指向自己,满足检查if (__builtin_expect (FD->bk != P || BK->fd != P, 0))。同时修改chunk1的pre_size位和pre_inuse位,pre_size位与伪造的fd和bk对应的堆大小要对应 - free chunk1,执行
FD->bk = BK; BK->fd = FD;,则地址0x602078处原本指向chunk0的指针被改写指向0x602078-0x18,由此我们已经可以控制指针指向任意地址
示例程序里在栈上存了一个字符串,然后让指针指向栈上存放字符串的地址,成功修改了字符串的数据,也就是实现了任意地址写
(示例程序中伪造后的样子)1
2
3
4
5
6
7
8
9
10
11
12
13gef➤ x/40gx 0x603010-0x10
0x603000: 0x0000000000000000 0x0000000000000091 ->chunk0
0x603010: 0x0000000000000000 0x0000000000000000
0x603020: 0x0000000000602060 0x0000000000602068 ->fake fd&fake bk
0x603030: 0x0000000000000000 0x0000000000000000
0x603040: 0x0000000000000000 0x0000000000000000
0x603050: 0x0000000000000000 0x0000000000000000
0x603060: 0x0000000000000000 0x0000000000000000
0x603070: 0x0000000000000000 0x0000000000000000
0x603080: 0x0000000000000000 0x0000000000000000
0x603090: 0x0000000000000080 0x0000000000000090 ->chunk1
0x6030a0: 0x0000000000000000 0x0000000000000000
0x6030b0: 0x0000000000000000 0x0000000000000000
house_of_spirit
Frees a fake fastbin chunk to get malloc to return a nearly-arbitrary pointer
思路
核心原理是通过free一个伪造的chunk来控制本来无法读写的区域
关键在于free前要控制chunk的size和下一个chunk的size的值
想要让伪造的chunk被成功释放并放入fastbin中,需要绕过一些检测
- fake chunk的ISMMAP位不能为1(mmap的chunk在free时单独处理)
- fake chunk地址需要对齐
- fake chunk的size大小要满足fastbin要求,同时也要对齐
- fake chunk的next chunk大小应大于
2*ISZE_SZ并小于av->system_mem - fake chunk对应的fastbin链表头部不能是该chunk(否则double free异常)
house_of_spirit的想法在于我们想要控制的区域控制不了,但它前面和后面都可以控制,所以伪造好数据将它释放到fastbin里面,后面将该内存区域当做堆块申请出来,致使该区域被当做普通的内存使用,从而目标区域就变成了可控的了
示例代码
1 |
|
在栈上把相应值伪造好后如下所示1
2
3
4
5
6
7pwndbg> x/20gx 0x7fffffffdc68-0x8
0x7fffffffdc60: 0x0000000000000001 0x0000000000000040 ->size
0x7fffffffdc70: 0x00007ffff7ffe168 0x0000000000000000
0x7fffffffdc80: 0x0000000000000001 0x00000000004008ed
0x7fffffffdc90: 0x0000000000000000 0x0000000000000000
0x7fffffffdca0: 0x00000000004008a0 0x0000000000001234 ->next size
0x7fffffffdcb0: 0x00007fffffffdda0 0x874c13b92e793d00
然后让指针指向0x7fffffffdc70,free,伪造的chunk就被放入fastbin中了1
2
3
4
5
6
7
8
9────────────────────────────── Fastbins for arena 0x7ffff7dd1b20 ──────────────────────────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] ← Chunk(addr=0x7fffffffdc70, size=0x40, flags=)
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
接着我们再申请一个0x30大小的堆,就能得到这块区域了




