强网杯2022-houseofcat&qwarnup&devnull writeup
前言
好久好久没打比赛,也没写blog了。这次强网杯难得iptl大佬带我打一次,得好好记录一下。题目质量很高,学到不少新姿势。
house of cat
题目有add,delete,show,edit功能,限制只能申请largebin,delete没清空指针,只能edit两次,限制系统调用。题目使用libc-2.35
1 | line CODE JT JF K |
主要参考house of apple2。(现在的house多得记不住了。。。)
攻击思路:
- UAF泄露libc,heap地址
- largebin attack将可控heap地址写入stderr
- 修改top chunk,触发报错
完整利用链为:sysmalloc-->assert->__malloc_assert->fflush(stderr)->IO_jump_t.__sync
,劫持stderr结构体之后进入_IO_wstrn_jumps._IO_wdefault_xsgetn
函数的调用链如下:
1 | _IO_wdefault_xsgetn |
由于题目有seccmop限制,只能用ROP,本次利用svcudp_reply+26
,其中rdi为伪造结构体的地址
1 | 0x7f78243fb1fa <svcudp_reply+26>: mov rbp,QWORD PTR [rdi+0x48] |
1 | def menu(idx): |
qwarnup
1 | void __fastcall __noreturn main(__int64 a1, char **a2, char **a3) |
当申请的堆块足够大时,可以申请到接近libc前面内存。题目中会检查size,如果size小于0x10000,可以循环写,如果大于0x10000只能写一次。需要利用一次libc任意地址写1字节,使程序进入死循环。可以参考:Nightmare: One Byte to ROP , 本地的libc修改_Exit为write会报错,因此不能用此方法,但原理相同。
_dl_fixup_
查找完函数地址后,会将函数地址回填到got表,那么可以通过修改link_map->l_addr
,使回填的函数填到write@got
某个偏移的地方,上述文章将偏移修改到_Exit@got
,此题可以利用libc地址前面8位的高位为0的特点,覆盖在bss段的size,让程序进入死循环。
由于write的函数地址没有成功回填到got表,后面每次调用还会走symbol查找流程。
1 | _dl_fixup (struct link_map *l, ElfW(Word) reloc_arg) |
_dl_lookup_symbol_x
函数的第一个参数就是待查找函数的函数名,而sym->st_name
对于同一个函数而言是一个固定值(write = 34),而strtab
则是来源于于libc上的全局结构体link_map
,那么可以劫持link_map->l_info[DT_STRTAB]
,使其指向可控内存段,达到任意函数调用。
DT_STRTAB
在elf中,由于没有泄露任何地址,目前是通过偏移进行任意地址写,这里找到DT_DEBUG
这个表是指向libc地址,可以通过改写最低位,使link_map->l_info[DT_STRTAB]
指向libc地址,然后我们在DT_DEBUG+34
的地方写入需要调用的函数。
后续通过修改stdout结构体进行libc地址泄露及FSOP,本次继续使用_IO_wdefault_xsgetn
的利用链,ROP照抄上面一题即可,注意需要小改一下。
1 | _IO_flush_all -> _IO_flush_all_lockp --> IO_jump_t.__overflow |
1 | def write(offset, bytes, tag=True): |
devnull
题目关键函数如下:
1 | int sub_40138F() |
解题思路:
第一次输入20个字符将fd覆盖为0,下一次输入就刚好可以覆盖rbp和函数返回地址,同时也会将buf覆盖掉,可以进行任意地址写。由于.data和.bss被改成了只能能执行,可以使用0x3fe000-0x400000这个区域,然后栈迁移过去跑ROP。
本题可用gadget较少,需要一点技巧,主要使用了以下两个gadget。
1 | p.sendafter(b'filename\n',b'a'*0x20) |
后记
本次比赛pwn完成进度3/18,其他估计也做不出来了。决赛见。