[强网杯2024]初赛pwn2 prpr详细图解
字数 1649 2025-08-23 18:31:34

VM PWN入门:强网杯2024初赛prpr题解

1. 题目概述

这是一道基于printf虚拟机的PWN题目,主要考察逆向分析和虚拟机逃逸技术。题目特点:

  1. 使用register_printf_function注册自定义格式化字符串处理器
  2. 实现了一个完整的虚拟机执行环境
  3. 开启了沙箱保护(仅允许orw和mprotect)
  4. 需要通过虚拟机漏洞实现逃逸,最终获取shell

2. 虚拟机架构分析

2.1 主程序结构

主程序通过printf_chk("%Q%W%B")启动虚拟机:

  • %Q:调用welcome函数(显示"PrPr")
  • %W:初始化沙箱
  • %B:启动虚拟机(初始eip=71)

2.2 虚拟机代码结构

虚拟机代码存储在全局数据段中,结构如下:

struct vm_code {
    char format[8];  // 格式化字符串
    int arg;         // 参数
};

通过逆向可以提取出完整的虚拟机指令集。

2.3 虚拟机内存管理

虚拟机使用一个大型内存块管理:

  • 包含stack和vm_data两个段
  • vm_data是一个共用体,存储char和int混合数据
  • 采用函数调用栈方式管理,每级函数有自己的data段

内存布局示例:

menu -> fun2 -> fun_60

3. 虚拟机指令集分析

逆向出的主要虚拟机函数:

3.1 菜单函数 (eip=71)

71: menu:
    user_input
    pop_r 0
    push_a_num 1
    push_r 0
    is_equal
    if_false_jump 79
    call 1
    jmp 71
79: 
    push_a_num 2
    push_r 0
    is_equal
    if_false_jump 85
    call 32
    jmp 71
85: 
    push_a_num 3
    push_r 0
    is_equal
    if_false_jump 91
    call 110
    jmp 71
91: 
    push_a_num 4
    push_r 0
    is_equal
    if_false_jump 97
    call 141
    jmp 71
97:
    push_a_num 5
    push_r 0
    is_equal
    if_false_jump 103
    call 178
    jmp 71
103:
    push_a_num 6
    push_r 0
    is_equal
    if_false_jump 109
    call 209
    jmp 71
109: exit

3.2 功能函数

  1. fun1 (and加密字符串)

    • 用户指定and值和字符串
    • 漏洞:未校验字符串长度
  2. fun2 (and加密数字)

    • 用户指定and值和数字序列
    • 调用fun60处理数据
  3. fun60 (辅助函数)

    • 被其他函数调用
    • 使用寄存器r5传参
    • 漏洞:寄存器残留问题
  4. fun3 (xor加密字符串)

    • 用户指定xor值和字符串
    • 漏洞:未校验字符串长度
  5. fun4 (xor加密数字)

    • 用户指定xor值和数字序列
    • 调用fun60处理数据
  6. fun5 (or加密字符串)

    • 用户指定or值和字符串
    • 漏洞:未校验字符串长度
  7. fun6 (or加密数字)

    • 用户指定or值和数字序列
    • 调用fun60处理数据

4. 漏洞分析

题目中存在多个关键漏洞:

4.1 字符串加密函数越界加密

  • 位置:fun1/fun3/fun5
  • 原因:未校验字符串长度,仅依赖00终止符
  • 影响:可越界修改内存

4.2 输入字符串不补充00截断

  • 位置:read_s函数
  • 原因:只有输入回车会被替换为00
  • 利用:使用send方法避免00截断

4.3 run_vm未校验负eip

  • 位置:run_vm函数
  • 影响:可指向vm_data段执行shellcode

4.4 data段存取未校验参数正负

  • 位置:栈传参分支
  • 影响:可实现任意地址读写

4.5 fun60寄存器残留

  • 位置:fun60
  • 原因:r5作为循环变量未清零
  • 影响:可越界写ret_addr

5. 攻击链构造

5.1 阶段一:控制RIP

  1. 使用fun6填充一级data段
  2. 使用fun3越界异或ret_addr
  3. 利用fun2在二级data段铺设vm_shellcode

5.2 阶段二:任意代码执行

  1. 利用fun60寄存器残留修改ret_addr
  2. 跳转到预置的vm_shellcode
  3. 通过负偏移实现任意读写

5.3 阶段三:虚拟机逃逸

  1. 泄露堆和栈地址
  2. 修改register指针指向主程序栈
  3. 通过pop操作修改返回地址和rbp
  4. 栈迁移到ROP链

5.4 阶段四:ROP利用

  1. 调用mprotect设置内存可执行
  2. 写入shellcode读取flag
  3. 绕过沙箱限制

6. EXP关键代码解析

# 填充一级data段
sla("| ,", "6")  # 进入fun6
sl("0")         # or值为0
sl("63")        # 读入63个数字
for i in range(64):
    sl(str(0xf1f1f1f1))  # 填充特定值

# 在二级data填入vm_shellcode
sl('2')         # 进入fun2
sl("0")         # add值
sl("63")        # 使r5残留为64

# 泄露地址
for i in range(5):
    sl(str_to_int32("%p%p"))
    ...

# 填入vm_shellcode
sl(str_to_int32("%a"))  # 负偏移
sl(str_to_int32("%a"))
sl("0")
sl(str_to_int32("%#V"))  # get_num_form_data
...

# 触发越界写
sl('3')
sl("104")       # 异或值
sd("b"*4)       # 不输入回车

# 跳转到shellcode
sl("-2150")     # 负eip

# 后续ROP链构造...

7. 总结与技巧

  1. 逆向技巧

    • 使用结构体标记vm_code数据
    • 全局替换格式化字符串为函数名
    • 重点分析内存管理机制
  2. 漏洞利用技巧

    • 组合多个小漏洞形成完整攻击链
    • 利用寄存器残留控制执行流
    • 通过负偏移实现任意读写
  3. 防御绕过

    • 利用栈迁移绕过沙箱
    • 使用mprotect+shellcode组合

这道题展示了VM PWN的典型解题思路:逆向虚拟机架构→发现漏洞→构造虚拟机逃逸→最终实现主程序控制。关键在于理解虚拟机的内存管理和执行机制,以及如何将有限的漏洞组合成强大的攻击链。

VM PWN入门:强网杯2024初赛prpr题解 1. 题目概述 这是一道基于 printf 虚拟机的PWN题目,主要考察逆向分析和虚拟机逃逸技术。题目特点: 使用 register_printf_function 注册自定义格式化字符串处理器 实现了一个完整的虚拟机执行环境 开启了沙箱保护(仅允许orw和mprotect) 需要通过虚拟机漏洞实现逃逸,最终获取shell 2. 虚拟机架构分析 2.1 主程序结构 主程序通过 printf_chk("%Q%W%B") 启动虚拟机: %Q :调用 welcome 函数(显示"PrPr") %W :初始化沙箱 %B :启动虚拟机(初始eip=71) 2.2 虚拟机代码结构 虚拟机代码存储在全局数据段中,结构如下: 通过逆向可以提取出完整的虚拟机指令集。 2.3 虚拟机内存管理 虚拟机使用一个大型内存块管理: 包含stack和vm_ data两个段 vm_ data是一个共用体,存储char和int混合数据 采用函数调用栈方式管理,每级函数有自己的data段 内存布局示例: 3. 虚拟机指令集分析 逆向出的主要虚拟机函数: 3.1 菜单函数 (eip=71) 3.2 功能函数 fun1 (and加密字符串) 用户指定and值和字符串 漏洞:未校验字符串长度 fun2 (and加密数字) 用户指定and值和数字序列 调用fun60处理数据 fun60 (辅助函数) 被其他函数调用 使用寄存器r5传参 漏洞:寄存器残留问题 fun3 (xor加密字符串) 用户指定xor值和字符串 漏洞:未校验字符串长度 fun4 (xor加密数字) 用户指定xor值和数字序列 调用fun60处理数据 fun5 (or加密字符串) 用户指定or值和字符串 漏洞:未校验字符串长度 fun6 (or加密数字) 用户指定or值和数字序列 调用fun60处理数据 4. 漏洞分析 题目中存在多个关键漏洞: 4.1 字符串加密函数越界加密 位置:fun1/fun3/fun5 原因:未校验字符串长度,仅依赖00终止符 影响:可越界修改内存 4.2 输入字符串不补充00截断 位置:read_ s函数 原因:只有输入回车会被替换为00 利用:使用send方法避免00截断 4.3 run_ vm未校验负eip 位置:run_ vm函数 影响:可指向vm_ data段执行shellcode 4.4 data段存取未校验参数正负 位置:栈传参分支 影响:可实现任意地址读写 4.5 fun60寄存器残留 位置:fun60 原因:r5作为循环变量未清零 影响:可越界写ret_ addr 5. 攻击链构造 5.1 阶段一:控制RIP 使用fun6填充一级data段 使用fun3越界异或ret_ addr 利用fun2在二级data段铺设vm_ shellcode 5.2 阶段二:任意代码执行 利用fun60寄存器残留修改ret_ addr 跳转到预置的vm_ shellcode 通过负偏移实现任意读写 5.3 阶段三:虚拟机逃逸 泄露堆和栈地址 修改register指针指向主程序栈 通过pop操作修改返回地址和rbp 栈迁移到ROP链 5.4 阶段四:ROP利用 调用mprotect设置内存可执行 写入shellcode读取flag 绕过沙箱限制 6. EXP关键代码解析 7. 总结与技巧 逆向技巧 : 使用结构体标记vm_ code数据 全局替换格式化字符串为函数名 重点分析内存管理机制 漏洞利用技巧 : 组合多个小漏洞形成完整攻击链 利用寄存器残留控制执行流 通过负偏移实现任意读写 防御绕过 : 利用栈迁移绕过沙箱 使用mprotect+shellcode组合 这道题展示了VM PWN的典型解题思路:逆向虚拟机架构→发现漏洞→构造虚拟机逃逸→最终实现主程序控制。关键在于理解虚拟机的内存管理和执行机制,以及如何将有限的漏洞组合成强大的攻击链。