这道题提供了一个sandbox.so
,当时我连如何运行这道题都不知道……今天阅读了http://acez.re/ctf-writeup-0ctf-2015-quals-login0opsapp-breaking-out-of-a-pin-sandbox/和https://rzhou.org/~ricky/0ctf2015/0ops_app/test.py,终于搞明白这道题目了,在此记录。
具体地,这道题目需要用pin,下载配置好之后,就可以按如下方式来运行:
由于目标二进制文件与login里是同一个,所以漏洞还是格式化字符串攻击,可见之前login的writeup。在那道题,我们通过修改返回地址为读flag的函数来获得flag。在这道题,我们同样可以修改返回地址,从而再次调用有问题的printf
。
如果没有sandbox保护,那我们可以利用格式化字符串攻击来泄露内存地址,获得system
,再修改返回地址。但这道题的sandbox.so
有进行保护。下面是反编译得到的一部分伪代码:
可以看到,如果有调用过alarm
,那么能够进行的syscall就只有read
, write
, open
了。而不幸的是,login
在运行时有调用过alarm
。所以,直接修改login
的执行流程来调用system
或execve
是不可能的了,我们只能通过修改sandbox.so
的执行流程来调用execve
。具体地,如果把sandbox.so
里的exit@got
修改指向我们的shellcode,那么再次调用不符规定的syscall,就会造成sandbox.so
里执行exit
,即执行我们的shellcode了。
特别的,虽然我的系统开了ASLR,但是实验发现,pinbin
每次都是被加载到了固定的地址,而pinbin
是有DT_DEBUG
信息的:
结合之前关于DT_DEBUG
的文章,我们可以遍历来获得sandbox.so
在内存中的地址,进而得到sandbox.so
里的exit@got
的地址。具体地:
我们知道.dynamic
会在0x304a7e3e0
。所以通过不断地printf
打印内存信息,遍历link_map
,获得sandbox.so
的地址。
此外,检查maps发现,内存中有rwx
的区域的,其地址也是每次不变的,应该是pinbin
引入的。所以我们可以先ROP,将shellcode写到那个rwx
的区域;并修改sandbox.so
中的exit@got
指向我们的shellcode,最后调用不符要求的syscall,即达到目的。具体地,ROP我是先通过pop; ret
把几个寄存器的值设好,再调用login
中的一个类似于readline
的函数,来实现写shellcode到指定位置。
下面是具体的代码,有些乱……
write8
那里直接用了https://rzhou.org/~ricky/0ctf2015/0ops_app/test.py的代码。我之前都是从低位到高位按顺序修改的,但他这样先排一次序再写我觉得很好。