这个题首先会在交互的时候卡一下,直接输入option并不能交互
提示format错误,推断出要按照一定格式发,猜测protobuf、cjson等。
逆向分析发现是cjson的解析。
所以要按照json格式发送数据:
1 | def new(len,message): |
查看libc版本是2.31-0ubuntu9.16,直接把小版本为9的ld拉过来也能用。
然后再去看各个功能的实现
new:
限制了大小是0x10-0x400,tcache的范围,而且一共只能malloc七个堆块,那么就没法通过free塞满tcache的方式进入unsorted bin。
rm:
对idx有检查,赤裸裸的free,白给的UAF
view:
正常的view,对idx也有检查
modify:
对idx有检查,对len无检查,赤裸裸的覆盖,白给的堆溢出。
目前看来思路非常简单,不能用数量塞进ub的话,就通过溢出改后面堆块的size,直接free进unsorted bin。然后泄露libc、tcache poison直接梭。但实际上这题有让不少选手(包括我)卡住的地方。
改完size还需要把prevsize修好为0,不然free的时候有报错。
modify的时候,message会被0截断,就像这样:
我们现在执行的是modify(0,0x360,b"A"*0x358+p64(0x611))
可以看到message末尾的引号和右花括号被0截断了,因此报了格式错误,那么我们就应该直接手写小端序字节。
正确的修改方式是:modify(0,0x360,b"A"*0x358+b"\x11\x06")
但需要注意的是要恢复前面的0
这道题比较难吃的地方在于,它解析json会给各个字段分配堆块,导致堆空间有一点点乱,而且会导致覆盖的下一个chunk的距离比较大,但这个问题不大。
我们知道这个modify的消息体会被0截断,那么我们要怎么写0呢?
我的做法是控制message的长度比第二个参数len短一定字节,这样memcpy从输入缓冲区读数据的时候,会按照len,把message后面的\x00也复制到destination。
modify(0,0x358,b"A"*0x350)
然后它就被修好了,能直接free进unsorted bin了
接下来就是我后来被卡住的点,怎么泄露libc,这题的问题在于解析json的时候会malloc很多小堆块,会导致我们free进unsorted bin的堆块被切割,而导致其脏数据的地址发生变化,直接view(idx)并不能输出libc上的数据,而会被切割出来的小chunk开头的0截断,所以没有回显。
后来问了Tokamaine师傅,得到了解答
高端的黑客往往只需采取最朴素的方式~
调了一下发现确实是这样。
这张图中的libc上的地址之前已经被我控制好长度覆盖了,会出现这些小脏数据的原因是我在rm之后执行了一次modify,解析modify的时候把ub的chunk切割了,而切割的字段的堆块的大小其实都是固定的,因此这些小脏数据的偏移地址也是固定的,那就只要这一次modify的时候顺便覆盖到小脏数据之前就可以了,留个0xcafebabecafebabe作为接收的标志位,然后view前一个堆块就能输出。
有了libc基址之后就是非常常规的tcache poison了,需要注意的还是modify的0截断,因此要把地址手动转bytes用小端序存储传输有效字节。
我最终的exp如下:
1 | from pwn import* |
如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !