论坛首页 逆向工程技术区 阅读主题

qemu逃逸系列

103 浏览 10 回复
#1 楼主 2026-06-01 21:09:20
qemu用于模拟设备运行,而qemu逃逸漏洞多发于模拟pci设备中,漏洞形成一般是修改qemu-system代码,所以漏洞存在于qemu-system文件内。而逃逸就是指利用漏洞从qemu-system模拟的这个小系统逃到主机内,从而在linux主机内达到命令执行的目的。因为使用qemu-system模式启动之后相当于在linux内又运行了一个小型linux,所以存在两个地址转换问题;从用户虚拟地址到用户物理地址,从用户物理地址到qemu虚拟地址。用户的物理内存实际上是qemu程序mmap出来的,看下面的launsh脚本,-m 1G也就是mmap一块1G的内存这块内存可以在qemu进程的maps文件下查看,sudo cat /proc/pid/maps
在64位系统内部,虚拟地址由页号和页内偏移组成,我们借用前人的代码来学习一下如何将虚拟地址转换成物理地址。下面的程序申请了一个buffer,并写入字符串——“Where am I?”,之后打印他的物理地址将其打包放到qemu系统内,然后进入qemu内部运行该c文件。再用gdb attach到qemu进程,查看mmap的内存。
找到qemu的基地址之后,用字符串的物理地址与基地址相加,即可得到虚拟地址。 PCI 设备都有一个 PCI 配置空间来配置 PCI 设备,其中包含了关于 PCI 设备的特定信息。这些信息一般只需要关注Device ID和Vendor ID即可。

拥有了这些信息即可在qemu系统内部使用lspci命令来找到该设备,从而能够进行交互。交互问题我们后面再细说。通过kernel提供的sysfs,我们可以直接映射出设备对应的内存,具体方法是打开类似 /sys/devices/pci0000:00/0000:00:04.0/resource0 的文件,并用mmap将其映射到进程的地址空间,就可以对其进行读写了。这里的设备号0000:00:04.0是需要事先在/proc/iomem中看好的。当映射完成后,就可以对这块内存进行读写操作了,内存读写会触发到qemu内设备的mmio处理函数(一般会叫xxxx_mmio_read/xxxx_mmio_write),传入的参数是写入的地址偏移和具体的值。后面会放出一个exp模板。也可以通过使用/dev/mem文件来映射物理内存。内存和 I/O 设备共享同一个地址空间。 MMIO 是应用得最为广泛的一种 I/O 方法,它使用相同的地址总线来处理内存和 I/O 设备,I/O 设备的内存和寄存器被映射到与之相关联的地址。当 CPU 访问某个内存地址时,它可能是物理内存,也可以是某个 I/O 设备的内存,用于访问内存的 CPU 指令也可来访问 I/O 设备。每个 I/O 设备监视 CPU 的地址总线,一旦 CPU 访问分配给它的地址,它就做出响应,将数据总线连接到需要访问的设备硬件寄存器。为了容纳 I/O 设备,CPU 必须预留给 I/O 一个地址区域,该地址区域不能给物理内存使用。在 PMIO 中,内存和 I/O 设备有各自的地址空间。 端口映射 I/O 通常使用一种特殊的 CPU 指令,专门执行 I/O 操作。在 Intel 的微处理器中,使用的指令是 IN 和 OUT。这些指令可以读/写 1,2,4 个字节(例如:outb, outw, outl)到 IO 设备上。I/O 设备有一个与内存不同的地址空间,为了实现地址空间的隔离,要么在 CPU 物理接口上增加一个 I/O 引脚,要么增加一条专用的 I/O 总线。由于 I/O 地址空间与内存地址空间是隔离的,所以有时将 PMIO 称为被隔离的 IO(Isolated I/O)。在linux中可以通过iopl和ioperm这两个系统调用对port的权能进行设置。通过resource0来实现,需要根据需要来修改函数参数是uint64_t还是uint32_t亦或者是charmmap参数PROT_READ(1) | PROT_WRITE(2)可读写,MAP_SHARED共享的内存。为了方便调试,我写了一个解压和压缩脚本,如下该脚本的作用是将文件系统解压在新建的rootfs文件夹内,再将写好的exp.c放到解压的文件系统的root目录下,将其编译成可执行文件,再重打包回rootfs.cpio,最后删掉rootfs文件夹进行调试时,先进行打包操作,然后运行launch.sh文件,再起一个终端sudo gdb ./qemu-system-x86_64,使用attach附加到qemu-system进程之上。可以用ps -aux | grep qemu来获得进程号。在gdb内下断点,就可以愉快的调试了。下面会有介绍在了解了以上的知识之后,就可以进行实操。先观察启动脚本launch.sh,先删掉timeout,否则超时就退出了。由参数"-device pipeline"得我们所主要逆向的部分是在pipeline*,将qemu-system-x86_64放入ida,由于存在符号,所以直接在函数栏里搜pipeline.
漏洞一般存在于pmio_read,pmio_write,mmio_read,mmio_write这些对内存进行读写操作的函数内。
发现根本看不懂,都和opaque这个变量有关,这肯定是结构体,而结构体名字一般和函数名pipeline有相似,我们来转变一下,方法效果如下:

发现一个很重要的结构体贯穿四个读写函数;
逆向结果如下,总体就是从EncPipeLine或DecPipeLine内读取数据,没有越界读,最后return处有个"8",因为
EncPipeLine或DecPipeLine的data变量在偏移位4处,然后v4为结构体处-4。需要静心去逆。 与pipeline_mmio_read函数大同小异,漏洞并不在这里,功能是将val写入EncPipeLine或DecPipeLine内。 addr为0返回idx,为4返回size。 猜测漏洞就在这里了,因为巨长。addr为0返回idx,addr是4写入sizeaddr为12时,看到了encode,在pipeline_instance_init函数中发现,好像是base64加密的实现。并且会将加密后的数据放入DecPipeLine结构体内,同理addr为16时,就是解密,会将解密后的数据放入EncPipeLine结构体的data变量内以上就是函数基本功能 因为qemu类型的题目大部分都是越界读写的问题,所以我们把注意力着重放在size上。我把注释写到下面的代码内。使用0xff进行base64编码作为测试数据,这样在解码后得到的溢出字符为0xff,如果后续溢出size,0xff为最大值:下面测试漏洞会不会覆盖掉size位看效果gdb attach之后,将断点下到pipeline_mmio_write,就可以愉快的c了执行pmio_write(16,0)之前; 执行pmio_write(16,0)之后,看到decPipe[3]的size被修改为了0xff,溢出达到,可以进行越界读写。 既然已经完成了size位的劫持,再通过mmio_read泄露encode函数地址,修改encode指针为

...(已截断)

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-275216.htm
#2 2026-06-01 21:09:20
绝了~牛
#3 2026-06-01 21:09:20
很6
#4 2026-06-01 21:09:20
#5 2026-06-01 21:09:20
射哥,膜
#6 2026-06-01 21:09:20
请问师傅,pipeline这个题目中的libxenctrl-4.9.so => not found是在ibxenctrl-dev这个下的吗?但是找了半天找不到这个包
#7 2026-06-01 21:09:20
chenepe


请问师傅,pipeline这个题目中的libxenctrl-4.9.so => not found是在ibxenctrl-dev这个下的吗?但是找了半天找不到这个包

应该不是,这个问题大概是qemu的动态链接库问题,考虑换成ubuntu18的虚拟机试试
#8 2026-06-01 21:09:20
e*16 a


应该不是,这个问题大概是qemu的动态链接库问题,考虑换成ubuntu18的虚拟机试试

感谢
#9 2026-06-01 21:09:20
0r2师傅tql
#10 2026-06-01 21:09:20
感谢分享
#11 2026-06-01 21:09:20
请问师傅,关于交互那一部分不是很了解,“内存读写会触发到qemu内设备的mmio处理函数”这是如何在代码上体现的

请登录后参与讨论

立即登录 注册账号