CVE-2020-14364
搭建环境
目前公开的两种漏洞利用思路,肖伟前辈在ISC2020
上提出的第二种利用方案中用到了qxl-vga
设备,所以在搭建环境的时候如果想尝试两种方法的话,需要提前安装spice
,添加--enable-spice
参数进行qemu
的编译。
安装spice
:
1 | #安装spice-protocol |
之后就是普通的安装流程,编译qemu
:
1 | git clone git://git.qemu-project.org/qemu.git |
在x86_64-softmmu
目录下可以找到编译好的qemu-system-x86_64
。
制作usb.img
:
1 | qemu-img create -f raw usb.img 32M |
内核镜像和文件系统直接拿之前的就可以使用,具体制作方法参考:http://jiayy.me/2019/04/15/CVE-2015-5165-7504/
启动脚本如下:
1 | qemu/x86_64-softmmu/qemu-system-x86_64 \ |
启动后发现USB
的EHCI
设备号为00:04.0
:
漏洞分析
1 | echi_work_bh |
1 | static void do_token_setup(USBDevice *s, USBPacket *p) |
调试
先用gdb加载qemu,然后source debug.txt
,debug.txt
内容如下:
1 | b usb_process_one |
漏洞利用
实现任意地址读写之前我们要先用越界读获取到USBDevice->data_buf[]
的地址。
用到的结构体:
1 | struct USBDevice { |
用越界读读取USBDevice->data_buf[]
下方的内容,USBDevice->data_buf[]
下方的ep_ctl->dev
处存有USBDevice
对象自身地址,通过USBDevice
的地址我们可以得到USBDevice->data_buf[]
的地址进而实现任意地址读写。
此外,越界读还可以获取到USBDevice->port
指针,其指向EHCIState->ports
,所以读取USBDevice->port
就能获得EHCIState->ports
的地址,减去偏移得到EHCIState
的地址。进而得到EHCIState->irq
地址,为后续劫持程序执行流做准备。
1 | struct EHCIState { |
此外,越界读还可以获取到USBDevice->usb_desc
指针的地址,其指向.data
段的desc_device_high
,利用其可获取到system
地址。
1 | ...... |
任意地址写
1 | static void do_token_in(USBDevice *s, USBPacket *p) |
1 | static void do_token_out(USBDevice *s, USBPacket *p) |
任意地址写因为不用转换setup_buf[0]
标志位,所以比较简单(offset = target_addr - data_buf_addr
):
- 设置
setup_len
为0x1010
。 - 越界写,将
setup_len
设置成offset+0x8
,setup_index
就设置为offset-0x1010
。 - 越界写(向
target_addr
写入8字节数据)。
1 | 流程:(offset = target_addr - data_buf_addr |
代码:
1 | void arb_write(uint64_t target_addr, uint64_t payload) |
任意地址读
任意地址读因为需要设置setup_buf[0]
的标志位,所以要复杂一些(offset = target_addr - data_buf_addr
):
- 设置
setup_len
为0x1010
。 - 越界写将
setup_len
设置为0x1010
,setup_index
设置为0xfffffff8-0x1010
。 - 越界写将
setup_buf[0]
设置为USB_DIR_IN
,将setup_len
设置为0xffff
,将setup_index
设置为offset-0x1018
。 - 越界读,读取
target_addr
处内容。
1 | 流程:(offset = target_addr - data_buf_addr |
代码
1 | unsigned long arb_read(uint64_t target_addr) |
之后的利用思路为:
- 在
USBDevice->data_buf[]
中布置fake_irq
,其函数指针布置为system
,参数为gnome-calculator
字符串地址。 - 获取
EHCIState->irq
指针地址,利用任意地址写将其改为fake_irq
的地址。 - 通过
mmio_write
读写触发ehci_update_irq => qemu_set_irq
。
核心思路其实不难,也比较稳定,不像恶心的堆溢出,我觉得调试过程中遇到最大的问题是怎么绕过那些繁琐的检测,设置各种标志位啥的,很无聊和枯燥。。。这里就不分析了。
PS:irq
真的是个好东西。。。
利用代码我就不放了,网上已经公开,利用效果如下:
参考
https://www.anquanke.com/post/id/227283#h2-7
https://xz.aliyun.com/t/8320#toc-6
https://www.wangan.com/articles/992#ea6f3b
https://github.com/ZhuriLab/Exploits/blob/master/cve-2020-14364/cve-2020-14364_local-exp.c