Exploit-DB #43878
Description: Oracle VirtualBox < 5.1.30 / < 5.2-rc1 - Guest to Host Escape
Type: local
Platform: multiple
Date: 2018-01-24
Author: SecuriTeam

Exploit:
# SSD Advisory – Oracle VirtualBox Multiple Guest to Host Escape Vulnerabilities
Source: https://blogs.securiteam.com/index.php/archives/3649

## Vulnerabilities summary
The following advisory describes two (2) guest to host escape found in Oracle VirtualBox version 5.1.30, and VirtualBox version 5.2-rc1.

## Credit
An independent security researcher, Niklas Baumstark, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.

## Vendor response
Oracle were informed of the vulnerabilities and released patches to address them.

For more details: http://www.oracle.com/technetwork/security-advisory/cpujan2[PII_REDACTED].html

CVE: CVE-2018-2698

## Vulnerabilities details
The vulnerabilities found in the core graphics framework (VBVA subcomponent) and affect all host operating systems.

provide an arbitrary read/write primitive in the userland VirtualBox host rocess, relative to the guest’s VRAM buffer.

The VGA device emulated by VirtualBox is associated with a certain amount of VRAM, which is mapped contiguously in both the host process running the VM and in guest kernel memory.

Parts of it are used as general-purpose shared memory segment for communication between the host and guest (host-guest shared memory interface, HGSMI).

Using this mechanism, the guest can issue certain commands to the host, for example to implement the mouse pointer integration and seamless windows features.

The guest can also tell the host to copy data around inside the VRAM on its behalf, via a subsystem called VDMA.

## Out-of-bounds read/write in vboxVDMACmdExecBpbTransfer
The VBOXVDMACMD_DMA_BPB_TRANSFER command struct looks as follows (defined in include/VBox/VBoxVideo.h:1435):

```
    typedef struct VBOXVDMACMD_DMA_BPB_TRANSFER
    {
        uint32_t cbTransferSize;
        uint32_t fFlags;
        union
        {
            uint64_t phBuf;
            VBOXVIDEOOFFSET offVramBuf;
        } Src;
        union
        {
            uint64_t phBuf;
            VBOXVIDEOOFFSET offVramBuf;
        } Dst;
    } VBOXVDMACMD_DMA_BPB_TRANSFER, *PVBOXVDMACMD_DMA_BPB_TRANSFER;
```

When issuing a VDMA command of type VBOXVDMACMD_TYPE_DMA_BPB_TRANSFER, a request object of this type resides in the HGSMI heap and is completely controlled by the guest.

On the host, a pointer to the object is eventually passed to the following function inside the file src/VBox/Devices/Graphics/DevVGA_VDMA.cpp:


```
    static int vboxVDMACmdExecBpbTransfer(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer, uint32_t cbBuffer)
    {
        // ...
        uint32_t cbTransfer = pTransfer->cbTransferSize;
        uint32_t cbTransfered = 0;
        // ...
        do
        {
            uint32_t cbSubTransfer = cbTransfer;
            if (pTransfer->fFlags & VBOXVDMACMD_DMA_BPB_TRANSFER_F_SRC_VRAMOFFSET)
            {
                // [[ Note 1 ]]
                pvSrc  = pvRam + pTransfer->Src.offVramBuf + cbTransfered;
            }
            else
            {
                // ...
            }

            if (pTransfer->fFlags & VBOXVDMACMD_DMA_BPB_TRANSFER_F_DST_VRAMOFFSET)
            {
                // [[ Note 2 ]]
                pvDst  = pvRam + pTransfer->Dst.offVramBuf + cbTransfered;
            }
            else
            {
                // ...
            }

            if (RT_SUCCESS(rc))
            {
                memcpy(pvDst, pvSrc, cbSubTransfer);
                cbTransfer -= cbSubTransfer;
                cbTransfered += cbSubTransfer;
            }
            else
            {
                cbTransfer = 0; /* to break */
            }
            // ...
        } while (cbTransfer);

        if (RT_SUCCESS(rc))
            return sizeof (*pTransfer);
        return rc;
    }
```

Note 1 and 2: the guest-controlled offsets pTransfer->Src.offVramBuf and pTransfer->Dst.offVramBuf are added to the VRAM address, without any verification or bounds checks.

A memcpy is then performed with the guest-controlled size pTransfer->cbTransferSize.

This gives us a memcpy(VRAM + X, VRAM + Y, Z) primitive, where we (as the guest)can chose X, Y and Z arbitrarily.

## Out-of-bounds read/write in vboxVDMACmdExecBlt

The VBOXVDMACMD_DMA_PRESENT_BLT command struct looks as follows:


```
    typedef uint64_t VBOXVIDEOOFFSET;
    /* [...] */
    typedef struct VBOXVDMACMD_DMA_PRESENT_BLT
    {
        VBOXVIDEOOFFSET offSrc;
        VBOXVIDEOOFFSET offDst;
        VBOXVDMA_SURF_DESC srcDesc;
        VBOXVDMA_SURF_DESC dstDesc;
        VBOXVDMA_RECTL srcRectl;
        VBOXVDMA_RECTL dstRectl;
        uint32_t u32Reserved;
        uint32_t cDstSubRects;
        VBOXVDMA_RECTL aDstSubRects[1];
    } VBOXVDMACMD_DMA_PRESENT_BLT, *PVBOXVDMACMD_DMA_PRESENT_BLT;
```

When issuing a VDMA command of type VBOXVDMACMD_TYPE_DMA_PRESENT_BLT, a request object of this type resides in the HGSMI heap and is completely controlled by the guest.

On the host, a pointer to the object is eventually passed to the following function inside the file src/VBox/Devices/Graphics/DevVGA_VDMA.cpp:


```
    static int vboxVDMACmdExecBlt(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_PRESENT_BLT pBlt, uint32_t cbBuffer)
    {
        const uint32_t cbBlt = VBOXVDMACMD_BODY_FIELD_OFFSET(uint32_t, VBOXVDMACMD_DMA_PRESENT_BLT, aDstSubRects[pBlt->cDstSubRects]);
        Assert(cbBlt <= cbBuffer);
        if (cbBuffer < cbBlt)
            return VERR_INVALID_FUNCTION;

        /* we do not support stretching for now */
        Assert(pBlt->srcRectl.width == pBlt->dstRectl.width);
        Assert(pBlt->srcRectl.height == pBlt->dstRectl.height);
        if (pBlt->srcRectl.width != pBlt->dstRectl.width)
            return VERR_INVALID_FUNCTION;
        if (pBlt->srcRectl.height != pBlt->dstRectl.height)
            return VERR_INVALID_FUNCTION;
        Assert(pBlt