diff --git a/content/zh/blog/lijiajie128/2020-09-01-CVE-2020-14364-QEMU-USB-array-out-of-range.md b/content/zh/blog/lijiajie128/2020-09-01-CVE-2020-14364-QEMU-USB-array-out-of-range.md new file mode 100644 index 0000000000000000000000000000000000000000..e9b1f067b8ab45f1ddbb3f024ddab1d63a8b5db5 --- /dev/null +++ b/content/zh/blog/lijiajie128/2020-09-01-CVE-2020-14364-QEMU-USB-array-out-of-range.md @@ -0,0 +1,177 @@ ++++ +title = "CVE-2020-14364 QEMU USB数组越界读写问题" +date = "2020-09-01" +tags = ["Qemu", "CVE", "USB", "Buffer overflow", "Patch"] +archives = "2020-09" +author = "Jiajie Li" +summary = "关于CVE-2020-14364的漏洞描述、分析以及针对性的解决方案" ++++ + + +# CVE-2020-14364 QEMU USB数组越界读写问题 + +漏洞描述 +==== +QEMU(quick emulator)是一款由Fabrice Bellard等人编写的免费的可执行硬件虚拟化的开源托管虚拟机。其与Bochs,PearPC类似,但拥有高速(配合KVM),跨平台的特性。QEMU通过动态的二进制转换,模拟CPU,并且提供一组设备模型,使其能够运行多种未修改的客户机OS。通过QEMU与KVM一起使用,可以实现以接近本地速度来运行虚拟机,被广泛应用到虚拟化和云计算场景中。 + +近日360公司发现QEMU USB控制器模拟源代码hw/usb/core.c之中存在数组越界读写的问题,攻击者可以利用该漏洞获得qemu用户的执行权限进而实现**完整的QEMU虚拟机逃逸**。(CVE-2020-14364) + + +漏洞分析 +==== +* 经过分析,该漏洞存在于USB控制器模拟代码usb_process_one中,s->setup_len未经验证就赋值可能会引入的数组越界读写的风险 。由于libvirt启动的虚拟机默认会有配置有usb设备,而任何usb控制器(如uhci,ehci,xhci)与usb设备(如usb-tablet,usb-mouse等)之间交互都会经过core.c文件中的usb_process_one函数,因此理论上只要虚拟机有使用usb设备都存在漏洞攻击的风险。 此外,usb_process_one函数中可能进入的两个分支do_parameter和do_token_setup均存在该问题,即:**在检查最终需要使用的数组长度前已经提前设置了该数组长度(s->setup_len)**。 +* 该漏洞的影响后果**非常严重**,因为攻击者可以利用该漏洞实现任意地址读取和写入,从而获得qemu进程的所有权限从而实现**虚拟机逃逸**,实现恶意代码执行攻击。此外,如果qemu进程处于root用户组那么攻击者就可以完整获得操作系统的控制权进而执行任意linux系统命令! + +代码分析 +---- + +```c++ +static void do_token_setup(USBDevice *s, USBPacket *p) +{ + int request, value, index; + + if (p->iov.size != 8) { + p->status = USB_RET_STALL; + return; + } + + usb_packet_copy(p, s->setup_buf, p->iov.size); + s->setup_index = 0; + p->actual_length = 0; + s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; //先对s->setup_len赋值 + if (s->setup_len > sizeof(s->data_buf)) { // 后对setup_len进行合法性校验 + 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; + } +``` + +```c++ +static void do_parameter(USBDevice *s, USBPacket *p) +{ + int i, request, value, index; + + for (i = 0; i < 8; i++) { + s->setup_buf[i] = p->parameter >> (i*8); + } + + s->setup_state = SETUP_STATE_PARAM; + s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; //先对s->setup_len赋值 + s->setup_index = 0; + + request = (s->setup_buf[0] << 8) | s->setup_buf[1]; + value = (s->setup_buf[3] << 8) | s->setup_buf[2]; + index = (s->setup_buf[5] << 8) | s->setup_buf[4]; + + if (s->setup_len > sizeof(s->data_buf)) { // 后对setup_len进行合法性校验 + 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; + } +``` + +通过代码可以看到:do_token_setup以及do_parameter两个函数中,都在对s->setup_len做检查之前就设置了具体内容,并且当s->setup_len超出预定buffer大小之后,也没有对其进行更改,这使得其中被污染的数据依旧可以完成之后的越界读写功能。 + +影响性分析 +---- +1. 影响范围 + QEMU 1.x 至今的QEMU的版本源代码之中均存在该漏洞,其中也包括了openEuler社区之前所使用的QEMU的代码。 +2. 触发条件 + 触发该漏洞需要虚拟机至少连接有一个usb设备,而多数情况下libvirt会默认为虚拟机配置USB设备。 + +漏洞修复方法 +---- + +根据360给出的修复方案,当检测到setup_len非法之后,将s->setup_len清零表示丢弃buffer中的USB请求,同时将USB的状态设置为SETUP_STATE_ACK,重新开始接受其他请求。补丁内容为: + +```c +Subject: [PATCH] hw/usb/core.c fix buffer overflow + +Store calculated setup_len in a local variable, verify it, + and only write it to the struct (USBDevice->setup_len) in case it passed the + sanity checks. + +This prevents other code (do_token_{in,out} function specifically) +from working with invalid USBDevice->setup_len values and overruning +the USBDevice->setup_buf[] buffer. +Store +Fixes: CVE-2020-14364 +Signed-off-by: Gred Hoffman +--- + hw/usb/core.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/hw/usb/core.c b/hw/usb/core.c +index 5abd128b..12342f13 100644 +--- a/hw/usb/core.c ++++ b/hw/usb/core.c +@@ -144,6 +144,8 @@ static void do_token_setup(USBDevice *s, USBPacket *p) + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", + s->setup_len, sizeof(s->data_buf)); + p->status = USB_RET_STALL; ++ s->setup_len = 0; ++ s->setup_state = SETUP_STATE_ACK; + return; + } + +@@ -277,6 +279,8 @@ static void do_parameter(USBDevice *s, USBPacket *p) + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", + s->setup_len, sizeof(s->data_buf)); + p->status = USB_RET_STALL; ++ s->setup_len = 0; ++ s->setup_state = SETUP_STATE_ACK; + return; + } + +-- +``` + + + + +## 解决方案 + +* 下载openEuler发布最新的qemu软件包: + * [漏洞SA](https://cve.openeuler.org/#/infoDetails/openEuler-SA-2020-1061) + * [aach64架构qemu软件包]([qemu-4.1.0-17.oe1.aarch64.rpm](https://repo.openeuler.org/openEuler-20.03-LTS/update/aarch64/Packages/qemu-4.1.0-17.oe1.aarch64.rpm)) + * [x86架构qemu软件包](https://repo.openeuler.org/openEuler-20.03-LTS/update/x86_64/Packages/qemu-4.1.0-17.oe1.x86_64.rpm) +* 升级qemu软件包 + + * rpm -Uvh qemu-*.rpm +* 升级完成之后查看qemu软件包的release号码,当release号大于等于17表示漏洞修复成功。 + + * rpm -qi qemu-4.1.0 + + ``` + Name : qemu + Epoch : 2 + Version : 4.1.0 + Release : 17.oe1 + Architecture: aarch64 + Install Date: Mon 10 Aug 2020 04:53:20 PM CST + Group : Unspecified + Size : 19468602 + License : GPLv2 and BSD and MIT and CC-BY + Signature : RSA/SHA1, Thu 09 Jul 2020 11:52:58 AM CST, Key ID d557065eb25e7f66 + Source RPM : qemu-4.1.0-14.oe1.src.rpm + Build Date : Thu 09 Jul 2020 11:44:23 AM CST + Build Host : obs-worker-004 + Packager : http://openeuler.org + Vendor : http://openeuler.org + URL : http://www.qemu.org + ``` + + + +FAQ +==== +1. OpenEuler社区对此漏洞采取了什么措施? + 社区相关人员得知漏洞之后,立刻制作出了相对应的补丁,并且第一时间将补丁合入社区相关分支之中。 +2. 如何获取该漏洞的具体复现场景? + 可以参考360在ISC2020第八届互联网安全大会上的分享: + * https://isc.360.com/2020/detail.html?vid=108 + * https://www.anquanke.com/post/id/215405