Skip to content

QEMU PCNET network device emulation overflow

QEMU'nun AMD PC-Net II (pcnet) emulation'ında bir heap buffer overflow (CVE-2015-7504): loopback mode'da ADDFCS'li bir received frame, sabit 4096-byte'lık buffer'ın ötesine bir CRC ekleyerek host'taki komşu bir qemu_irq pointer'ını corrupt eder.

Mechanism

Note

QEMU'nun pcnet NIC model'i, received-frame byte'larını PCNetState içindeki sabit bir uint8_t buffer[4096]'da tutar. Isolation invariant'ı, pcnet_receive()'in o buffer'a asla 4096 byte'tan fazla yazmamasıdır. Bug, loopback test mode'unun ADDFCS transmit-descriptor flag'i ile birleşmesindedir: ADDFCS set edildiğinde, model frame'e 4-byte'lık bir Ethernet FCS/CRC ekler (src[size+0..3] = ...; size += 4;) ama size'ı buffer'a karşı yeniden kontrol etmez. Tam 4096-byte'lık bir loopback frame süren bir guest, CRC append'inin 4096..4099 index'lerine yazmasını sağlar — out of bounds, doğrudan struct'ta buffer'ı takip eden field'ın, yani qemu_irq irq pointer'ının üzerine.

Bu, guest→host sınırını aşar çünkü PCNetState QEMU host process'inde yaşar. Overflow, bir host function/IRQ pointer'ının üzerine guest-controlled byte'lar yazar; QEMU daha sonra device IRQ'yu raise ettiğinde (qemu_set_irq() corrupt olmuş IRQState'i dereference eder ve handler'ını çağırır), control flow yeniden yönlendirilebilir — QEMU ayrıcalıklarıyla host code execution. Bir guest'in controller'ı loopback'e programlaması için CAP_SYS_RAWIO gerekir. (Phrack QEMU case study tam olarak bu struct adjacency'sini weaponize eder.)

Kardeş not QEMU RTL8139 network device emulation overflow ile karıştırma: bu (PCNET, CVE-2015-7504) bir OOB write/overflow primitive'idir (host pointer'ı corrupt eder), RTL8139 (CVE-2015-5165) ise farklı bir device'ta farklı bir bug class olan bir out-of-VM read / heap info-leak'tir. Tam bir escape'te ikisi tamamlayıcıdır — leak ASLR'yi yener, bu write control flow'u ele geçirir — ama ayrı CVE'ler ve ayrı tekniklerdir.

Walkthrough

Public, halihazırda patch'lenmiş materyal: Phrack 70 "VM escape — QEMU Case Study" ve Red Hat/Debian advisory'leri. Yalnızca kavramsal reproduction path'i:

  1. Guest'ten, pcnet controller'ını loopback test mode'una programla ve transmit descriptor'da ADDFCS'i etkinleştir.
  2. Loopback'te pcnet_receive()'in buffer'a ~4096 byte kopyalayacağı şekilde boyutlandırılmış bir frame transmit et.
  3. ADDFCS path'i, size'dan sonra 4 CRC byte'ı ekleyerek buffer[4096]'in ötesine, komşu irq pointer field'ına yazar.
  4. Frame'i, hesaplanan CRC'si seçilen bir değere eşit olacak şekilde seçerek attacker, irq'nun üzerine controlled bir pointer yazar; bir sonraki qemu_set_irq() onu dereference eder.

Warning

Tarihsel, patch'lenmiş bir issue (CVE-2015-7504). Yalnızca kavramsal — burada hiçbir host heap layout'u, gadget address'i ya da tam escape chain'i sağlanmıyor.

Vulnerable shape (illustrative, from the public write-up)
/* hw/net/pcnet.c, pcnet_receive() — src = s->buffer (4096 byte) */
if (!s->looptest) {
    memcpy(src, buf, size);
    /* non-loopback: 4 zero FCS byte'ı eklenir (yine size += 4) */
    src[size] = src[size+1] = src[size+2] = src[size+3] = 0;
    size += 4;
} else {                                /* loopback test mode + ADDFCS */
    uint32_t fcs = ~0;
    uint8_t *p = src;
    while (p != &src[size]) CRC(fcs, *p++);
    *(uint32_t *)p = htonl(fcs);        /* size may already == 4096 -> */
    size += 4;                          /* OOB CRC write over adjacent irq ptr */
}

Beklenen gözlem: bir guest'ten gelen loopback-mode pcnet aktivitesinin ardından QEMU crash/corruption; non-exploit bir denemede ise host tarafında bir abort.

Detection

  • Host tarafı: pcnet loopback aktivitesi ile korele QEMU qemu/qemu-kvm crash'i; ASan pcnet_receive'de bir heap-buffer-overflow write flag'ler.
  • Behavioral: bir guest'in pcnet loopback test mode + ADDFCS'i etkinleştirmesi ve max-size frame'ler göndermesi, herhangi bir gerçek driver için anormaldir.
  • İlgili kardeş CVE-2015-7512, non-loopback large-MTU overflow varyantını kapsar.

Mitigation

  • CVE-2015-7504 için QEMU fix'ini uygula (pcnet_receive() içinde receive size'ını — eklenen FCS dahil — sizeof(buffer)'a karşı bound et).
  • Modern bir NIC model'i (virtio-net / e1000e) tercih et ve ihtiyaç duymayan guest'lerden legacy pcnet device'ını kaldır.
  • Defense-in-depth: QEMU'yu sandbox'lanmış/deprivilege edilmiş olarak çalıştır (seccomp, SELinux/sVirt); böylece bir host-process overflow'u tam host kontrolü olmaz.

References

  • Talbi & Fariello, "VM escape — QEMU Case Study", Phrack 70:5: https://phrack.org/issues/70/5
  • Red Hat Bugzilla (CVE-2015-7504, pcnet heap overflow / XSA-162): https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2015-7504