Exploit-DB #41904
Description: Oracle VM VirtualBox - Guest-to-Host Privilege Escalation via Broken Length Handling in slirp Copy
Type: local
Platform: multiple
Date: 2017-04-20
Author: Google Security Research

Exploit:
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1086

There is a vulnerability in VirtualBox that permits an attacker with
root privileges in a virtual machine with a NAT network interface to
corrupt the memory of the userspace host process and leak memory
contents from the userspace host process. This probably permits an
attacker with root privileges inside the guest to execute arbitrary
code in userspace context on the host.

The issue is in the copy of slirp that is shipped in VirtualBox, in
the function ip_input() in src/VBox/Devices/Network/slirp/ip_input.c:

void
ip_input(PNATState pData, struct mbuf *m)
{
    register struct ip *ip;
    [...]
    ip = mtod(m, struct ip *);
    [...]
    {
        [...]
        /*
         * XXX: TODO: this is most likely a leftover spooky action at
         * a distance from alias_dns.c host resolver code and can be
         * g/c'ed.
         */
        if (m->m_len != RT_N2H_U16(ip->ip_len))
            m->m_len = RT_N2H_U16(ip->ip_len);
    }
    [...]
}

This code does not seem to be present in the upstream version of
slirp.

The assignment `m->m_len = RT_N2H_U16(ip->ip_len)` overwrites the
trusted length field `m_len` of the buffer `m` with the untrusted
length field in the IP header of the received packet. At this point,
the IP header has not been validated at all. All following code that
processes packets relies on the correctness of `m->m_len`, so by
sending an IP header with a bogus length field, an attacker can cause
all following code to operate on out-of-bounds data.

In particular, an attacker can use this bug to obtain the following
attack primitives:

 - The attacker can leak out-of-bounds heap data by sending a UDP
   packet to a host on the internet with checksum 0 and a bogus length
   field in the IP header.
   The host process will send a (possibly fragmented) UDP packet to
   the specified host on the internet that includes out-of-bounds heap
   data.
   This method requires a cooperating host on the internet that the VM
   can talk to using the NAT network interface.
 - The attacker can leak out-of-bounds heap data by sending an ICMP
   Echo Request with a bogus length field in the IP header
   to the CTL_DNS address. The VM host then responds with an ICMP Echo
   Reply that includes out-of-bounds heap data.
   This approach has the advantage of not requiring a cooperating,
   reachable server on the internet, but has the disadvantage that
   the attacker needs to guess the 16-bit ICMP checksum.
 - The attacker can corrupt the heap by sending a UDP packet with a
   bogus length whose IP header contains IP options. The host process
   will then attempt to strip the IP headers via ip_input -> udp_input
   -> ip_stripoptions -> memcpy, which moves the IP payload - including
   out-of-bounds heap data - to a lower address. This can
   in particular be abused to overwrite a slirp heap chunk header
   (struct item) with attacker-controlled packet data.

I have attached a crash PoC. Copy it into a VM whose only network
interface is a NAT interface, compile it with
"gcc -o crasher crasher.c" and run it with "sudo ./crasher". The VM
should die after a few seconds, with something like this appearing in
dmesg on the host:

[[PII_REDACTED]98] traps: EMT-0[66638] general protection ip:7fc6a26076e8 sp:7fc6d2e27ad0 error:0 in VBoxDD.so[7fc6a24e2000+36d000]

I have tested my crasher in VirtualBox version "5.1.14 r112924".

The bug was introduced in SVN revision
<https://www.virtualbox.org/changeset/23155/vbox>.

################################################################################

Without modifications,
the exploit should work under the following conditions:

 - host runs Ubuntu 14.04 (trusty), 64-bit
 - host uses libc6 package version 2.19-0ubuntu6.9 (most recent
   version)
 - VirtualBox version is 5.1.14~112924~Ubuntu~trusty (official build)
   (most recent version)
 - guest runs Linux
 - main network interface of the VM is a NAT interface (default
   config)

The exploit is able to run an arbitrary shell command on the host
system. The command is hardcoded to "id > /tmp/owned_from_guest".


Some things about the exploit that might be of interest to you:

The exploit operates on memory that belongs to the zone zone_clust of
the UMA heap.
The UMA heap is relatively easy to attack, partly because the sanity
checks are compiled out in userland code in release builds. For
example, the check
`Assert((zone->magic == ZONE_MAGIC && zone == it->zone))` in
uma_zfree_arg() becomes a no-op, and the LIST_CHECKs in LIST_REMOVE()
have no effect. In particular, because the `zone == it->zone`
assertion is not compiled into release builds, an attacker who can
overwrite an item header and point its member ->zone to a controlled
memory area can cause an arbitrary function it->zone->pfFini to be
called when the item whose header was overwritten is freed.
It might make sense to turn assertions in the allocator into something
that is also active in release builds.

For exploiting the bug, it was very helpful that the VirtualBox binary
is built as non-relocatable, meaning that the binary is always loaded
at the same virtual address. The exploit uses a hardcoded address to
leak the contents of the GOT (global offset table), which can then be
used to locate the addresses of libc functions.
It's probably a good idea to build the VirtualBox binaries as
relocatable code to prevent attacks from simply using
hardcoded addresses - and this mitigation is pretty simple to
implement, you just have to add some compiler flags (`-pie -fPIE`
or so). To verify that it's working, run VirtualBox, then as root,
grep the contents of /proc/{pid of VirtualBox}/maps for VirtualBox and
verify that the mappings don't have low ranges like [PII_REDACTED],
but use high addresses like 7ffb0f62e000 instead.

As far as I can tell from