Summary
A guest inside a VirtualBox VM using the virtio-net network adapter can trigger an integer overflow leading to out-of-bounds read in src/VBox/Devices/Network/DevVirtioNet.cpp
to cause a denial-of-service or leak information of the hypervisor.
Severity
High - An attacker with high privileges in the guest can cause a denial-of-service or leak information of the hypervisor.
Proof of Concept
The following function handles a VIRTIONET_CTRL_MAC
control command which fetches multiple 32bit cMacs
values from the guest:
static uint8_t virtioNetR3CtrlMac(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
{
LogFunc(("[%s] Processing CTRL MAC command\n", pThis->szInst));
AssertMsgReturn(pVirtqBuf->cbPhysSend >= sizeof(*pCtrlPktHdr),
("insufficient descriptor space for ctrl pkt hdr"),
VIRTIONET_ERROR);
size_t cbRemaining = pVirtqBuf->cbPhysSend;
switch(pCtrlPktHdr->uCmd)
{
// ...
case VIRTIONET_CTRL_MAC_TABLE_SET:
{
VIRTIONET_CTRL_MAC_TABLE_LEN cMacs;
/* Load unicast MAC filter table */
AssertMsgReturn(cbRemaining >= sizeof(cMacs),
("DESC chain too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
/* Fetch count of unicast filter MACs from guest buffer */
virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
cbRemaining -= sizeof(cMacs);
Log7Func(("[%s] Guest provided %d unicast MAC Table entries\n", pThis->szInst, cMacs));
if (cMacs)
{
uint32_t cbMacs = cMacs * sizeof(RTMAC);
AssertMsgReturn(cbMacs <= sizeof(pThis->aMacUnicastFilter) / sizeof(RTMAC),
("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
AssertMsgReturn(cbRemaining >= cbMacs,
("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
/* Fetch unicast table contents from guest buffer */
virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacUnicastFilter, cbMacs);
cbRemaining -= cbMacs;
}
pThis->cUnicastFilterMacs = cMacs;
/* Load multicast MAC filter table */
AssertMsgReturn(cbRemaining >= sizeof(cMacs),
("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
/* Fetch count of multicast filter MACs from guest buffer */
virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
cbRemaining -= sizeof(cMacs);
Log10Func(("[%s] Guest provided %d multicast MAC Table entries\n", pThis->szInst, cMacs));
if (cMacs)
{
uint32_t cbMacs = cMacs * sizeof(RTMAC);
AssertMsgReturn(cbMacs <= sizeof(pThis->aMacMulticastFilter) / sizeof(RTMAC),
("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
AssertMsgReturn(cbRemaining >= cbMacs,
("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
/* Fetch multicast table contents from guest buffer */
virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacMulticastFilter, cbMacs);
cbRemaining -= cbMacs;
}
pThis->cMulticastFilterMacs = cMacs;
// ...
break;
}
default:
LogRelFunc(("Unrecognized MAC subcommand in CTRL pkt from guest\n"));
return VIRTIONET_ERROR;
}
return VIRTIONET_OK;
}
There are only checks on cbMacs
, but not on cMacs
. Hence, it is possible to provide a large value, e.g. 0x2AAAAAAB
to cause an integer overflow. The resulting cbMacs
will wrap around and hence bypass the checks. As a result a big cMacs
can be used for pThis->cUnicastFilterMacs
and pThis->cMulticastFilterMacs
. In the function virtioNetR3AddressFilter()
, both values are truncated to 16bit and used as limits for the arrays pThis->aMacMulticastFilter
and pThis->aMacUnicastFilter
:
static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
{
// ...
for (uint16_t i = 0; i < pThis->cMulticastFilterMacs; i++)
{
if (!memcmp(&pThis->aMacMulticastFilter[i], pvBuf, sizeof(RTMAC)))
{
// ...
return true;
}
}
for (uint16_t i = 0; i < pThis->cUnicastFilterMacs; i++)
if (!memcmp(&pThis->aMacUnicastFilter[i], pvBuf, sizeof(RTMAC)))
{
Log11(("acpt (ucast whitelist)\n"));
return true;
}
// ...
return false;
}
Using a timing attack, it might be possible to exfiltrate the values read out-of-bounds.
Timeline
Date reported: 08/15/2023
Date fixed: 10/17/2023
Date disclosed: 11/16/2023
Summary
A guest inside a VirtualBox VM using the virtio-net network adapter can trigger an integer overflow leading to out-of-bounds read in
src/VBox/Devices/Network/DevVirtioNet.cpp
to cause a denial-of-service or leak information of the hypervisor.Severity
High - An attacker with high privileges in the guest can cause a denial-of-service or leak information of the hypervisor.
Proof of Concept
The following function handles a
VIRTIONET_CTRL_MAC
control command which fetches multiple 32bitcMacs
values from the guest:There are only checks on
cbMacs
, but not oncMacs
. Hence, it is possible to provide a large value, e.g.0x2AAAAAAB
to cause an integer overflow. The resultingcbMacs
will wrap around and hence bypass the checks. As a result a bigcMacs
can be used forpThis->cUnicastFilterMacs
andpThis->cMulticastFilterMacs
. In the functionvirtioNetR3AddressFilter()
, both values are truncated to 16bit and used as limits for the arrayspThis->aMacMulticastFilter
andpThis->aMacUnicastFilter
:Using a timing attack, it might be possible to exfiltrate the values read out-of-bounds.
Timeline
Date reported: 08/15/2023
Date fixed: 10/17/2023
Date disclosed: 11/16/2023