一道题了解虚拟机本身的机制实现的shellcode
字数 1498 2025-08-22 12:22:15
虚拟机机制实现的Shellcode分析与利用
前言
本文记录了一种利用虚拟机(VM)自身机制实现Shellcode的技术,而非传统的通过数据越界泄露libc的方法。这种技术在2024 SCTF的vmcode题目中首次遇到,具有独特的技术特点。
程序保护机制分析
程序采用了沙盒规则限制,反编译工具无法直接查看,需要通过汇编代码分析和调试来理解其工作原理。
虚拟机工作机制
关键跳转逻辑
-
程序通过
rax寄存器值进行关键跳转控制:rax需要大于等于0x21- 跳转地址计算方式:
0x123a + offset[i]
-
地址计算过程:
0x5f2bae170257 <main+238> mov rcx, rax 0x5f2bae17025a <main+241> lea rax, [rip + 0xdbf] 0x5f2bae170261 <main+248> mov ax, word ptr [rax + rcx*2] 0x5f2bae170265 <main+252> movzx rax, ax 0x5f2bae170269 <main+256> pop rcx 0x5f2bae17026a <main+257> push rcx 0x5f2bae17026b <main+258> push rcx 0x5f2bae17026c <main+259> add rax, rcx 0x5f2bae17026f <main+262> push rax 0x5f2bae170270 <main+263> and edi, 0xf 0x5f2bae170273 <main+266> ret
功能函数分析
虚拟机提供了19个功能函数,通过传入大于0x21的值加上0x123a来控制执行流程:
-
0x21 ($0x1274):
- 将传入值与初始值
0x44相加,结果传给rsi作为后续code的索引值
- 将传入值与初始值
-
0x22 ($0x1299):
- 调整
rdi值作为索引传给esi,影响后续code索引
- 调整
-
0x23 ($0x12A7):
- 将栈中
stack[v1]和stack[v1-8]异或,结果存入stack[v1-8]
- 将栈中
-
0x24 ($0x12c4):
- 交换
stack[v1]和stack[v1-0x10]位置
- 交换
-
0x25 ($0x12e0):
- 交换
stack[v1]和stack[v1-0x8]位置
- 交换
-
0x26 ($0x12fc):
- 从code中取值放入stack段
-
0x27 ($0x1319):
- 将
stack[v1]的值变为stack[v1的低位]
- 将
-
0x28 ($132e):
rdi减一
-
0x29 ($0x1332):
- 取出栈中的值右移8位后放回
-
0x2a ($0x1348):
- 将
stack[v1]放入stack[v1+8]
- 将
-
0x2b ($0x135c):
- 取出栈中的值左移8位后放回
-
0x2c ($0x1372/0x138c):
- 从stack中读值,如果是0则
rsi加2返回,否则取code值处理
- 从stack中读值,如果是0则
-
0x2d ($0x13a3):
- 从栈中获取两个相邻值
- 使用第二个值作为循环右移位数对第一个值进行循环右移
- 结果写回栈的前一个位置,同时减少索引
rdi
-
0x2e ($0x13c0):
- 类似0x2d,但执行循环左移
-
0x2f ($0x13dd):
- 位与操作
-
0x30 ($0x13fa):
- 关键系统调用:可以控制
rax、rdi、rsi、rdx进行系统调用
- 关键系统调用:可以控制
-
0x31 ($0x1425):
- 将
stack[v1-1]的值存入stack[v1]
- 将
-
0x32 ($0x1439):
- 将code地址偏移
rsi后存入stack段
- 将code地址偏移
漏洞利用分析
关键利用点
-
系统调用控制(0x30):
- 可以完全控制
rax、rdi、rsi、rdx寄存器 - 实现ORW(Open-Read-Write)操作链
- 可以完全控制
-
PIE绕过:
- 通过
[rbx+rdi*8-8]地址读取绕过PIE保护 - 解决了'flag'字符串地址获取问题
- 通过
ORW构造过程
-
open('./flag\x00',0,0):
push_4_byte(reversed_string_to_int(b'flag'[::-1])) + push_rsp() + push_4_byte(0) + swap_8_10() + push_4_byte(2) + syscall() -
read(0,adr,len):
push_rsp() + copy_and_push()*6 + # 保证读入地址不变,抬高栈防止覆盖 push_4_byte(0x100) + swap_8_10() + push_4_byte(3) + push_4_byte(0) + syscall() # read(3,addr,0x100) -
write操作:
pop() + copy_and_push() + push_4_byte(0x100) + swap_8_10() + push_4_byte(1) + push_4_byte(1) + syscall()
技术要点总结
- 通过虚拟机自身的跳转机制实现代码执行控制
- 利用系统调用功能(0x30)实现ORW链
- 通过栈操作和寄存器控制绕过PIE保护
- 仅用61字节即可实现完整的flag读取功能
- 关键点在于精确控制栈布局和寄存器值
这种技术展示了在受限环境下如何利用虚拟机自身机制实现攻击,为虚拟机逃逸和沙盒绕过提供了新的思路。