Advisories

Oracle VirtualBox e1kInsertChecksum Out-of-Bounds Read (Pwn2Own)

CVE ID

CVE-2020-2894

Tested Versions

  • Oracle VirtualBox 6.1.0 revision r135406

Product URL(s)

  • https://virtualbox.org

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.

Vulnerability

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.

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.

Timeline

  • 2020-04-30 Vendor disclosure via ZDI
  • 2020-04-30 Coordinated public disclosure

Credit

Discovered by anhdaden