题目描述
分发附件:Vmware虚拟机镜像文件
题目描述:该题以qemu虚拟机软件的某CVE漏洞为参考,在qemu某模块中置入存在漏洞的代码,导致一些不可预测的执行流程,从而完成虚拟机逃逸攻击。选手需要在运行在qemu虚拟机中的ubuntu操作系统下进行逃逸操作,尝试触发漏洞并在宿主机(Deepin Linux)中执行任意代码。
展示区网络拓扑:一台交换机连接选手攻击机和靶机。
展示过程及要求:选手携带自己的攻击机上台,通过网线接入交换机。靶机中运行了宿主机Deepin Linux系统,系统内又嵌套运行了qemu下的Ubuntu系统。Ubuntu系统的22端口被映射到宿主机的2222端口。选手使用ssh连接宿主机的2222端口,用户名:ubuntu,密码:123456。选手在Ubuntu系统中执行漏洞利用代码,在规定时间内在宿主机中执行“google-chrome –no-sandbox file:///home/qwb/Desktop/success.mp4”命令,宿主机中弹出chrome浏览器,并显示成功动画即为展示成功。
宿主机运行bash脚本监控并自动重启qemu。如果qemu软件崩溃会自动重启,请选手耐心等待直到客户机ssh连接恢复。如果qemu软件无法重启,选手可要求将虚拟机恢复到初始状态,恢复快照时不停止计时。
开始计时时间:选手第一次成功登陆ubuntu客户机时,开始计时。
launch.sh
1
2
3
4
while true
do ./qemu-system-x86_64 -m 1024 -smp 2 -boot c -cpu host -hda ubuntu_server.qcow2 --enable-kvm -drive file=./blknvme,if=none,id=D22 -device nvme,drive=D22,serial=1234 -net user,hostfwd=tcp::2222-:22 -net nic && sleep 5
done
qemu-system-x86_64 信息
1
2
3qwb@ExecChrome:~/Desktop/QWB$ ./qemu-system-x86_64 --version
QEMU emulator version 4.0.50 (v4.0.0-1043-ge2a58ff493-dirty)
Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers
题目分析
比赛时:
由于是第一次上手qemu,缺乏相关知识积累。第一件事就是从github下载了qemu的源码,载所给的虚机中编译了一遍。在编译的过程中查找了以往ctf中出现的qemu虚拟机逃逸的相关wp。通过wp,了解到一般漏洞设置载加载的虚拟设备中,与虚拟设备的交互一般通过mmio或着portio 。而利用代码的编写一般有两种形式,一种通过编写内核驱动与设备交互,一种通过在将设备空间映射到用户内存空间进行交互。
在看完几篇wp后,虚拟机中的qemu已编译完成。用ida 打开两个编译的qemu和题目所提供的qemu。在加载过程中,想利现成工具进行diff,看到加载过程十分慢(linux wine 的ida放弃折腾)。就放弃了这种做法。经过肉眼对比,发现大部分函数反编译后基本一致。题目环境加载了nvme设备,直接搜索nvme相关函数,重点对比了write和read函数,可以发现nvme_mmio_read和nvme_mmio_write两个文件存在着代码改动。两处的代码改动都减少了一个条件判断,看到这里基本确定载读写过程中存在着问题,可能存在着越界读写。由于在题干中提到以某cve为参考,于是搜寻了一番相关cve。在repo中搜索,发现了cve-2018-16847。
于是开始了poc搜寻之路,一搜索未果。
接着,开始尝试触发漏洞代码,验证之前的设想。参照网上的相关wp,发现套用别人代码无法完成所需要的相关工作。对nvme设备相关的知识又十分匮乏,于是又展开了大胆的想象。第一次尝试使用已加载的nvme驱动间接与nvme设备进行交互。将nvme.ko拷贝出,拖进ida。看了半天,没有找到相关测试样例,放弃。第二次尝试使用nvme_cli来进行交互,发现到达nvme设备相关函数,但是不会到达指定路径。
完成以上工作后,就陷入了死胡同。接下来的一天多时间就开始四处查找资料(划水)。
- nvme_mmio_read
- nvme_mmio_write
比赛后:
两天时间放在这一题,毫无进展,感觉十分挫败。赛后咨询大佬,了解到漏洞代码位置确实是在上图所示的位置,最后劫持rip使用timer。
于是又开始了搜索的道路,这时才知道github上有个叫pcimem的项目,项目代码经过封装后就可以直接在用户空间读写pci设备。在w0lfzhang师傅的vmescap中,可以看到他封装的函数。这里可以直接拿过来用。
从头看题:
通过lspci -vv 可以查看pci设备的详细信息。
1 |
|
查看设备相关的文件。其中resource0 对应着region 0,resource对应着region 4 . resource 是资源空间,对应着 PCI 设备的可映射内存空间。从”PCI 配置空间”解析出来的资源定义段落分别生成的,它们是 PCI 总线驱动在 PCI 设备初始化阶段加上去的,都是二进制属性,但没有实现读写接口,只支持 mmap 内存映射接口。
1 | root@ExecChrome:/home/ubuntu# ls -al /sys/devices/pci0000\:00/0000\:00\:04.0/ |
通过pcimem读写resource0空间进行调试。
1 | root@ExecChrome:/home/ubuntu# ./pcimem /sys/devices/pci0000\:00/0000\:00\:04.0/resource0 0x100 d |
到现在,已经可以很方便的完成leak,控制流的劫持需要修改存n->bar之后的某个指针。在NvmeCtrl结构体中存在admin_cq和admin_sq两个队列结构体。NvmeCQueue结构体中存在QEMUTimer类型的指针,当expire_time的定时到了后就会执行cb(opaque)。具体的定时不会设置,只有通过关机或重启动触发。
1 | gdb-peda$ ptype NvmeCQueue |
漏洞利用
- 使用任意读完成code地址和heap地址的泄漏,由于在qemu的got表中存在system这里就省去了对libc地址的泄漏。
- 通过修改admin_cq的timer,将timer指向id_ctrl最后一串为0的空间,踩坑发现参数摆放和结构体摆放位置要避开堆管理结构体,否则会free出错,或清空fd字段导致结构体错误。
- 对于timer结构体的构造,需要保证timer_list列表正确。
最终exploit
1 |
|
参考链接
- https://www.ssdfans.com/blog/2017/08/03/%e9%98%bf%e5%91%86%e5%ae%9e%e6%88%98nvme%e4%b9%8b%e4%b8%83/
- https://stfpeak.github.io/2017/07/15/how-to-debug-qemu-devices/
- https://www.ibm.com/developerworks/cn/linux/l-cn-sysfs/index.html
- https://www.tuicool.com/articles/MzqYbia
- https://github.com/qemu/qemu/commit/5e3c0220d7e4f0361c4d36c697a8842f2b58340
- http://www.phrack.org/papers/vm-escape-qemu-case-study.html
- https://github.com/w0lfzhang/vmescape
- https://github.com/billfarrow/pcimem
- https://www.anquanke.com/post/id/86636
- https://kitctf.de/writeups/hitb2017/babyqemu
- https://www.w0lfzhang.com/2018/12/05/xnuca-SSD-QEMU-escape/
- https://github.com/Kira-cxy/qemu-vm-escape/blob/master/writeup_zh.md