编译
本文使用的是qemu-2.10.1
,linux-4.10.10
编译qemu时关闭优化并开启调试信息,方便调试
1 | cd qemu-2.10.1/ |
启动
1 | ~/qemu-2.10.1/x86_64-softmmu/qemu-system-x86_64 \ |
启动后在另一个窗口用attach pie
调试进程
通过lspci命令,查看usb设备所在在总线号,这里是00:03.0
漏洞分析
usb_process_one
1 | static void usb_process_one(USBPacket *p) |
qemu\hw\core.c
中的usb_process_one
是对usb包进行处理的函数,参数是USBPacket结构,根据Packet中的pie进行设置(do_token_setup)、读(do_token_in)、写(do_token_out)三个操作中的一个
do_token_setup
1 | static void do_token_setup(USBDevice *s, USBPacket *p) |
qemu\hw\core.c
中的do_token_setup
函数;
【1】处s->setup_len
由s->setup_buf[6]`
s->setup_buf[7]`决定,所以len可控制
【2】处的判断,过大的s->setup_len
会进行返回,但是s->setup_len
已经被赋值,所以该处的检查没有起到效果。
do_token_in
1 | static void do_token_in(USBDevice *s, USBPacket *p) |
qemu\hw\core.c
中的do_token_in
函数;
【3】处使用s->setup_len
获得len
,而p->iov.size
可由用户控制,例如设置p->iov.size
为0x2000
,则最后len为0x2000,所以会造成【4】处的复制操作造成越界访问
do_token_out
1 | static void do_token_out(USBDevice *s, USBPacket *p) |
qemu\hw\core.c
中的do_token_out
函数;
同do_token_out
函数,【5】处可控制 len,【6】处造成越界写
USBDevice
1 | struct USBDevice { |
EHCIState
1 | struct EHCIState { |
观察ehci_update_irq
,(qemu\hw\usb\hcd-ehci.c)可以发现它调用了EHCIState->irq结构体中的函数指针,并且参数也在irq结构体中
1 | /* update irq line */ |
qemu_set_irq
位置qemu\hw\core\irq.c
1 | void qemu_set_irq(qemu_irq irq, int level) |
漏洞利用
qemu虚拟地址转成物理地址
参考链接:https://xz.aliyun.com/t/6562
首先通过mmio_mem = mmap(0,0x1000,PROT_READ | PROT_WRITE,MAP_SHARED,mmio_fd,0);
映射到usb设备的内存。
接着对这块内存操作可以直接设置opreg的内容。
整体利用思路
1 | do_token_setup -> 先发一个正常包设置 s->setup_state |
发包
发送一个基本USBPacket的过程如下,需要构造的部分主要是红框中的部分
qtd->token包含了p->pid和p->iov.size,而p->pid决定了USBPacket 进入do_token_setup/in/out
1 | 以图中为例 |
step1
仅需构造正常的packet ,在do_token_setup中设置s->setup_state = SETUP_STATE_DATA (2),为后续做准备
step2
继续进入do_token_setup,设置s->setup_len为0x5000,sizeof(s->data_buf)为0x1000,因此可以越界,且此时s->setup_state仍为2
step3
由于step1中 s->setup_buf[0]被设置成了0,因此现在只能进入do_token_out进行写操作,结合USBDevice结构体,data_buf后方可劫持s->setup_index,并且setup_buf位置data_buf前方
1 | usb_packet_copy(p, s->data_buf + s->setup_index, len); |
usb_packet_copy的起始位置是data_buf + s->setup_index,因此只要将index改为负数 ,下次写入就可以控制 setup_buf,使得可以进入do_token_in进行越界读
step4
控制 s->setup_buf 使其能进入do_token_in进行读操作,同时设置 s->setup_index控制读的位置
step5
进入do_token_in 读操作,泄漏text(text段)、irq(heap段)地址
step6
因为s->setup_buf[0]已经为0x80,无法进入do_token_out进入写操作,所以重新发送一个正常包恢复状态
step7
发包进入do_token_setup 设置s->setup_len 为0x5000
step8
进入do_token_out越界写控制 s->setup_index指向irq->handler为下一步准备
step9
进入do_token_out覆盖irq->handler为system@plt,最后通过mmio读写触发ehci_update_irq->qemu_set_irq,最终执行system(‘xcalc’),完成利用
exp
1 |
|