0.前言
蓝帽唯二的pwn题之一,上来就整沙箱,属实是难受。
1.观察
首先,IDA逆向分析,
主函数如下:
init_mem是初始化shellcode写入的空间(他是申请了一个堆块来存放shellcode,并且用mprotect改写了这段内存的权限,使其可执行),并且写入了一段pre_shellcode:
内容用pwndbg调试后得到:
init_flag的功能是打开flag,读入bss段上的flag缓冲区:
sandbox规定了沙箱的规则,采用的是白名单模式,只开放了read和write两个功能:
可以用seccomp-tools进行验证:
go的功能就是让我们续写shellcode,并执行包括pre_shellcode在内的代码:
程序除了canary保护全开(其实开了也不影响):
2.分析
首先,经过测试知道靶机使用的是U18,libc-2.27,和我的U18虚拟机是同样的环境。
这题很明显是要让我们用系统调用打印出flag。难点是flag的地址如何找到。
首先,沙箱只允许我们使用read和write两个功能,因此再打开一遍flag写入到可控内存是无法办到的,所以还是只能考虑去找到flag的地址。
程序的pre_shellcode将一系列有用的寄存器都置0xdeadbeefdeadbeef这么一个无效值,因此我们失去了和栈、代码段、libc的联系。但是注意到rip的值没有被改掉(也不可能被改掉),pre_shellcode是在堆上执行的,因此执行后rip的值应该是堆上的某个地址。
由于这个沙箱机制,堆上必然会有libseccomp的地址,因此,我们可以通过rip的地址找到对应的堆的基地址,然后用sys_write打印堆上的所有内容,从而找到libseccomp的地址,这个地址可以用mov rsi, [rip] and si, 0xf000 add rsi, offset mov rsi, [rsi]这样的方式获得,首先第一条是取rip的内容给rsi,这样rsi就有了堆上的地址,si与上一个0xf000得到堆基地址,先用vmmap查看堆的长度,再用系统调用就可以打印出堆上的所有信息,再找到libcseccomp的地址,确定偏移offset,就可以让rsi指向这个地址,最后mov就可以让rsi的值为这个地址。
打印堆上信息的临时shellcode如下:
1 | lea rsi, [rip] |
找到libseccomp地址后就可以通过偏移去确定libc的地址,然后找environ环境变量(很容易算出偏移是0x3ee098),里面有栈的地址,(同时我也发现了能找到text段的地址,省了一步),然后用同样的方法使这个rsi指向这个地址,并将值mov为这个地址,计算偏移得到程序基地址,再根据flag的偏移可以得到flag的地址,系统调用write打印出flag。
3.exp
完整的exp如下:
1 | from pwn import * |
得到flag: