house of emma
参考: 桥下少年
emma也是利用house of kiwi的触发assert机制,
house of kiwi是去走的fflush-->__sync__
house of emma 是走的 fxprintf --> __locked_vfxprinf --> __vfprintf_internal --> _IO_default_xsputn
通过修改_IO_file_jumps
为_IO_cookie_jumps+offset
,使得最后+偏移为_IO_cookie_write
然后在_IO_cookie_write
中会直接调用指针,设置好偏移就可以去控制执行流
1、assert去触发调用IO_file_jumps调用函数的利用
这里的思路还是控制vtable去执行我们想要的地方
虽然存在vtable_check,不过可以在他规定范围去修改,这里利用的就是_IO_cookie_jumps
1 | static const struct _IO_jump_t _IO_cookie_jumps libio_vtable = { |
里面存在的_IO_cookie_read、_IO_cookie_write、_IO_cookie_seek、_IO_cookie_close
1 | static ssize_t |
这几个函数中都存在直接的函数调用
1 | /* Special file type for fopencookie function. */ |
当然在函数调用前存在一个检测 PTR_DEMANGLE (这里也只是了解)
检测逻辑:将其ROR移位11位后再和指针异或
调试过程可以发现,利用的 fs[0x30],可以去修改该处值为我们已知值
这里我一直搞不明白的就是这个值在libc_base-0x2890 ,只能记住吗、还是有方法去调试得知
SigreturnFrame()与setcontext
SigreturnFrame()其实也是利用setcontext来实现的,所有在布置setcontext的偏移时可以直接利用SigreturnFrame()来实现
利用思路
1、修改stderr --> _IO_2_1_stderr
_
2、修改libc_base - 0x2890
为已知值
3、构造IO_FILE ,将vtable修改到对应偏移位置
比如_IO_cookie_jumps + 0x40
,去调用__sync
时就是到_IO_cookie_write
4、设置好偏移去执行secontext-->orw
5、assert触发IO
2021湖湘杯house of emma
参考 :
House Of Emma | wjh’s blog
Ayakaaaa师傅的博客
IDA:
第一次见这样形式的菜单堆,刚开始就懵了qwq
1 | void __fastcall __noreturn main(__int64 a1, char **a2, char **a3) |
就是输入之间是没用输出的,就直接把要选项参数直接一起输入,然后调用5返回main,然后一直循环
1 | __int64 __fastcall sub_1289(__int64 a1) |
add:限制chunk_size在0x410到0x500之间,且idx小于等于0x10
1 | _DWORD *__fastcall sub_149C(__int64 a1) |
delete:uaf
show:正常打印
edit:正常修改
思路及过程
有了上文章house of emma的学习这题也就有了大致的思路,
存在uaf,largebin attack挺方便的,先去泄露地址,然后largebin修改stderr、libc_base-0x2890,然后就是布置偏移、伪造IO_FILE,不过实际过程还是有点绕
1、先泄露地址
2、然后利用 largebin attack去修改stderr:
3、利用largebin attack修改libc_base-0x2890:
4、修改top_chunk的size,为触发assert做准备,前几步都比较常规吧、就不粘payload的了
5、伪造IO_FILE:网上师傅可能伪造了_IO_write_ptr
等数据,但xshhc师傅分享了这条利用链过程,实际只需要_lock
写入一个可写入的地址,_mode也不用限制,最重要的就是修改vtable
1 | fake_io = p64(0) |
_IO_cookie_jumps + 0x40 + 0x38 –> _IO_cookie_write
进入_IO_cookie_write之后 两个重要的地方 (rdi = stderr) 这里可以回溯一下rdi,发现是开始从stderr中赋值来的
rax = rdi+0xf0 (要绕过检测) –> call rax
rdi = rdi + 0xe0
所以我们可以控制这两块地址去控制执行流,但怎么利用call去执行orw呢?这里就接触到了一个新知识
思路就是还是去利用setcontext+61–>orw,但setcontext需要rdx,这里去找一个符合的gadget
1 | ROPgadget --binary ./libc.so.6 --only "mov|call" | grep "rdx" | grep "rdi" |
就是通过我们已经控制的rdi去控制rdx,并可以继续控制执行流的magic_gadget (自己根本找不到qwq)
然后去控制rsop_adr+0x8以及srop_adr+0x8+0x20,然后就可以去执行setcontext+61–>orw
1 | pay = p64(0) + p64(srop_adr) #rdx=srop_adr |
开始一直以为网上师傅是有执行rt_sigreturn,调试了好久qwq,自己都蒙圈了,最后发这里只是利用这个来布置栈qwq,顺便把open的参数也设置好了
还未解决的小疑惑
因为我写orw一直都是 open、read、write函数来调用,但这题好像不太行
1 | orw = flat(pop_rdi,srop_adr+0xf8,pop_rsi,0,op, |
最后还是用的网上师傅的orw,我感觉是没啥区别的 orz
1 | orw = [ |
完整EXP
1 | from pwn import * |
IO流程
从__malloc_assert开始
rbx = stderr
rdi = rbx+0x88
下面图中rbx就是我们伪造的跳表地址进入_IO_cookie_write后就是rdi+0xf0,当然有 检测在rdi (chunk0) +8 位置存放 chunk1 地址,然后chunk1+0x20放入setcontext 同时利用frame来布置栈空间,就可以控制程序流了
- 本文作者: 1uckyc
- 本文链接: https://1uckyc.github.io/2023/09/17/house-of-emma学习以及复现/