CVE: CVE-2020-2894

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 default virtual network device exposed to guest VMs is an Intel PRO/1000 MT Desktop (82540EM), also known as the E1000. A vulnerability was discovered in the VirtualBox code that calculates the IP checksum, due to improper validation of supplied parameters, allowing the guest to read beyond an allocated buffer.

This vulnerability was disclosed via the Pwn2Own programme by ZDI.

To compute and write internet checksum, the emulated E1000 network adapter call e1kInsertChecksum() function:

// VirtualBox-6.1.0\src\VBox\Devices\Network\DevE1000.cpp:4147

static void e1kInsertChecksum(PE1KSTATE pThis, uint8_t *pPkt, uint16_t u16PktLen, uint8_t cso, uint8_t css, uint16_t cse)
{
    RT_NOREF1(pThis);

    if (css >= u16PktLen)
    {
        E1kLog2(("%s css(%X) is greater than packet length-1(%X), checksum is not inserted\n",
                 pThis->szPrf, cso, u16PktLen));
        return;
    }

    if (cso >= u16PktLen - 1)
    {
        E1kLog2(("%s cso(%X) is greater than packet length-2(%X), checksum is not inserted\n",
                 pThis->szPrf, cso, u16PktLen));
        return;
    }

    if (cse == 0)
        cse = u16PktLen - 1;
    else if (cse < css)
    {
        E1kLog2(("%s css(%X) is greater than cse(%X), checksum is not inserted\n",
                 pThis->szPrf, css, cse));
        return;
    }

    uint16_t u16ChkSum = e1kCSum16(pPkt + css, cse - css + 1);
    E1kLog2(("%s Inserting csum: %04X at %02X, old value: %04X\n", pThis->szPrf,
             u16ChkSum, cso, *(uint16_t*)(pPkt + cso)));
    *(uint16_t*)(pPkt + cso) = u16ChkSum;
}

cse is the offset in packet to stop computing checksum, we can see that the only check against cse is:

...
else if (cse < css)
{
    E1kLog2(("%s css(%X) is greater than cse(%X), checksum is not inserted\n",
             pThis->szPrf, css, cse));
    return;
}

So we can set cse to bigger than u16PktLen (Packet length) and compute checksum of the data outside of the packet body. By increasing 1 byte repeatedly, we can leak the memory behind the packet buffer.

Rimeline:

  • 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.