CVE: CVE-2020-2575
Tested Versions:
- Oracle VirtualBox 6.1.0 revision r135406
Product URL(s):
Description of the vulnerability
VirtualBox is a x86 and AMD64/Intel64 virtualization product for enterprise as well as home use. It is a solution commercially supported by Oracle, in addition to being made available as open source software. It runs on various host platforms like Windows, Linux, Mac and Solaris and also supports a large number of guest operating systems.
The emulated OHCI controller that handles requests contains a vulnerability due to the lack of proper initialization.
This vulnerability was disclosed via the Pwn2Own programme by ZDI.
vusbMsgSetup() Function
If we send a Setup Message URB Request to the OHCI device, vusbMsgSetup() function will handle that request:
// VirtualBox-6.1.0\src\VBox\Devices\USB\VUSBUrb.cpp:664
static bool vusbMsgSetup(PVUSBPIPE pPipe, const void *pvBuf, uint32_t cbBuf)
{
PVUSBCTRLEXTRA pExtra = pPipe->pCtrl;
const VUSBSETUP *pSetupIn = (PVUSBSETUP)pvBuf;
...
/*
* Check that we've got sufficient space in the message URB.
*/
if (pExtra->cbMax < cbBuf + pSetupIn->wLength + sizeof(VUSBURBVUSBINT))
{
uint32_t cbReq = RT_ALIGN_32(cbBuf + pSetupIn->wLength + sizeof(VUSBURBVUSBINT), 1024);
PVUSBCTRLEXTRA pNew = (PVUSBCTRLEXTRA)RTMemRealloc(pExtra, RT_UOFFSETOF_DYN(VUSBCTRLEXTRA, Urb.abData[cbReq]));
if (!pNew)
{
Log(("vusbMsgSetup: out of memory!!! cbReq=%u %zu\n",
cbReq, RT_UOFFSETOF_DYN(VUSBCTRLEXTRA, Urb.abData[cbReq])));
return false;
}
if (pExtra != pNew)
{
pNew->pMsg = (PVUSBSETUP)pNew->Urb.abData;
pExtra = pNew;
pPipe->pCtrl = pExtra;
}
pExtra->Urb.pVUsb = (PVUSBURBVUSB)&pExtra->Urb.abData[cbBuf + pSetupIn->wLength];
pExtra->Urb.pVUsb->pUrb = &pExtra->Urb;
pExtra->cbMax = cbReq;
}
...
return true;
}
pSetupIn->wLength is the size of the new control pipe extra state data, if pSetupIn->wLength is bigger than the current one, the control pipe extra state data object will be reallocated with the new size:
uint32_t cbReq = RT_ALIGN_32(cbBuf + pSetupIn->wLength + sizeof(VUSBURBVUSBINT), 1024);
PVUSBCTRLEXTRA pNew = (PVUSBCTRLEXTRA)RTMemRealloc(pExtra, RT_UOFFSETOF_DYN(VUSBCTRLEXTRA, Urb.abData[cbReq]));
After that, pExtra->Urb.pVUsb object will be reside in the reallocated buffer but with a new offset pSetupIn->wLength and it only initialize the pUrb field:
pExtra->Urb.pVUsb = (PVUSBURBVUSB)&pExtra->Urb.abData[cbBuf + pSetupIn->wLength];
pExtra->Urb.pVUsb->pUrb = &pExtra->Urb;
In vusbUrbQueueAsyncRh() function, pExtra->Urb.pVUsb is used to get the pDev pointer:
// VirtualBox-6.1.0\src\VBox\Devices\USB\VUSBUrb.cpp:439
int vusbUrbQueueAsyncRh(PVUSBURB pUrb)
{
#ifdef LOG_ENABLED
vusbUrbTrace(pUrb, "vusbUrbQueueAsyncRh", false);
#endif
/* Immediately return in case of error.
* XXX There is still a race: The Rh might vanish after this point! */
PVUSBDEV pDev = pUrb->pVUsb->pDev;
PVUSBROOTHUB pRh = vusbDevGetRh(pDev);
if (!pRh)
{
Log(("vusbUrbQueueAsyncRh returning VERR_OBJECT_DESTROYED\n"));
return VERR_OBJECT_DESTROYED;
}
RTCritSectEnter(&pDev->CritSectAsyncUrbs);
int rc = pDev->pUsbIns->pReg->pfnUrbQueue(pDev->pUsbIns, pUrb);
...
}
The pUrb->pVUsb->pDev
still stay uninitialized after the reallocation, so we can spray the heap before the RTMemRealloc()
call then control the pUrb->pVUsb->pDev
pointer.
Timeline:
- 2020-04-30 Vendor disclosure via ZDI
- 2020-04-30 Coordinated public disclosure
Vendor Response
The vendor has acknowledged the issue and released an update to address it.
The vendor advisory can be found here: https://www.oracle.com/security-alerts/cpuapr2020.html.