This is fine: Vagrant guests can access the entire host filesystem
When playing around with VirtualBox last September, I came across some curious behaviour which I initially thought was a pretty severe vulnerability in VirtualBox: When running an unprivileged program inside a box spawned using Vagrant, it can obtain read and write access to the entire filesystem of the host. Turns out it was not actually a VirtualBox bug, and more of a misconfiguration by Vagrant.
This problem should be considered publicly known but still exists,
although Vagrant now prints a warning exactly once on each host. If you are
running untrusted code inside a Vagrant box, you should explicitly set the
VAGRANT_DISABLE_VBOXSYMLINKCREATE
environment variable, e.g. by adding
export VAGRANT_DISABLE_VBOXSYMLINKCREATE=1
to your .profile
/.zprofile
.
I posed this as the babyvm
challenge at 34C3
CTF, where
a simplified version was solved within 3 hours, and the hard version was also
solved by one team within the 48 hours of the contest (shout out to valis from
DragonSector!) So this article kills two birds with one stone by posing as both
a mild rant about Vagrant and a writeup for the two CTF challenges.
The Problem
Vagrant by default sets the flag
SharedFoldersEnableSymlinksCreate
for each synced folder that it creates. This includes the vagrant
share, which
is enabled in each Vagrant box by default, unless supressed explicitly by
adding
config.vm.synced_folder ".", "vagrant", disabled: true
to the Vagrantfile
. The
VirtualBox documentation
has the following to say about this flag:
For security reasons the guest OS is not allowed to create symlinks by default. If you trust the guest OS to not abuse the functionality, you can enable creation of symlinks for “sharename” with:
VBoxManage setextradata "VM name" VBoxInternal2/SharedFoldersEnableSymlinksCreate/sharename 1
While the documentation could admittedly be clearer about the implications of enabling this flag, Vagrant apparently assumes that all boxes are implicitly trusted, which goes at least against my personal intuition: Vagrant in my mental model is just a very thin wrapper around the hypervisor used, and should not add additional attack surface. In short, I would expect it to give me the same security guarantees that VirtualBox gives me.
Accessing shared folders from an unprivileged context
Shared folders in VirtualBox are implemented as a HGCM service (short for Host-Guest Communication Manager). HGCM involves a rather simple RPC protocol through which the guest can make function calls to the hypervisor. Several other features of VirtualBox which also require guest cooperation are implemented through this mechanism, such as clipboard sharing, drag & drop and 3D acceleration.
In order to initiate a HGCM call, for example to read or write
a file inside a shared folder, or put data into the clipboard, a custom request
has to be made to the special VMM (Virtual Machine Monitor) device exposed to every
VM via PCI. This would usually require kernel privileges.
However, if the VirtualBox guest additions are installed, requests can be made
through the VBoxGuest driver, exposed to the “world” – i.e. unprivileged
processes in the guest – in the form of the VBoxGuest
device on Windows, and the
/dev/vboxuser
device on Linux. Since Vagrant’s synced folders make use of the
shared folders feature, Vagrant boxes will typically come with the VirtualBox
guest additions already installed.
A quick side note: After I reported CVE-2018-2693, a privilege escalation + DoS
in the guest via /dev/vboxuser
on Linux, Oracle disabled the device
completely in the 5.2.6 release of the VirtualBox guest additions. However, this was
quickly reverted in the 5.2.7 testing and 5.2.8 stable release, since it turns
out some of their features rely on this device being available, a fact which had
somehow eluded them during their testing…
Accessing the host file system using symlinks
The core of the SharedFolders
HGCM service is implemented by the function
svcCall
in the VirtualBox source file
src/VBox/HostServices/SharedFolders/service.cpp
. Relevant HGCM functions for our
purposes are:
SHFL_FN_CREATE
to open a file in a shared folderSHFL_FN_{READ,WRITE}
to read/write from/to a file opened viaSHFL_FN_CREATE
SHFL_FN_SYMLINK
to create a symlink in a shared folder (this requires theSharedFoldersEnableSymlinksCreate
flag to be set)
If a shared folder is mounted via the normal guest addition filesystem driver, and a symlink inside the share is opened, it will be resolved inside the guest:
# mount -t vboxsf <sharename> /mnt
# ls -alih /mnt
vagrant@ubu1710:/mnt$ ls -alih /mnt
? drwxr-xr-x 1 root root 44K Mar 25 12:19 .
2 drwxr-xr-x 24 root root 4.0K Nov 5 11:39 ..
3 lrwxrwxrwx 1 root root 11 Mar 25 12:19 test -> /etc/passwd
# sha1sum /mnt/test /etc/passwd
6ba8b11066c8159ea796209eed6b911d5cf1d6bd /mnt/test
6ba8b11066c8159ea796209eed6b911d5cf1d6bd /etc/passwd
In particular, the filesystem driver will first query the file type to figure
out if it is a symlink, then issue a SHFL_FN_READLINK
call to resolve the
link, and then recurse back into the kernel to open the resulting path.
But instead, since SHFL_FN_CREATE
does not check if the requested file path
is a symlink (and it would probably be racy if it tried), we can just as well
force the symlink to be opened on the host.
My CTF challenge had an unintended easy way to obtain root privileges inside
the guest – it turns out passwd -d root
does not “delete” the root password
but empties it; maybe I should read manpages more often. Team “pasten” noticed
that the official filesystem driver had a flag that would make it open
symlinks host-side, such that the following was an easy solution to the
challenge:
$ su
# umount /vagrant
# rmmod vboxsf
# modprobe vboxsf follow_symlinks=1
# mount -t vboxsf vagrant /vagrant
# ln -s /flag /vagrant/flag
# cat /vagrant/flag
This would print the contents of the file /flag
on the host. Had I not
explicitly marked the shared folder as read-only in order to make the challenge
somewhat robust, writing a file on the host would have been possible just as
easily.
After they came up with this solution only 3 hours after the beginning of the
CTF when I intended this challenge to be on the more difficult end of the
scale, I decided to add a second version of the challenge called babyvm2
,
where hopefully it was not possible to easily become root. Instead the
participants were supposed to abuse the guest additions to achieve the same
effect.
Exploitation via /dev/vboxuser
There, I called it an exploit. When clearly this is all intended behaviour.
Anyways, I already described everything that is needed to “use” this behaviour
via the /dev/vboxuser
device, what remains is just a programming
challenge. We have to find out how to make HGCM calls through the device and
what specific order of HGCM calls we need to achieve what we want. Because I’m
lazy, instead of reimplementing the HGCM protocol, I patched an existing
client, called VBoxControl
, and added the additional “features”.
The
patch
should apply cleanly to the VirtualBox 5.2.4 codebase. I’m going to skip build
instructions here since they are described in detail by the VirtualBox
documentation.
Building VirtualBox is generally fairly unpleasant. Arch Linux users have
a certain advantage because they can just modify the official PKGBUILD
:
$ svn checkout --depth=empty svn://svn.archlinux.org/community
$ cd community
$ svn update virtualbox
$ cd virtualbox/trunk
The modified VBoxControl
has two additional commands called symlink
and
getfile
, which can be used as follows:
$ ./VBoxControl symlink vagrant foo /flag
$ ./VBoxControl getfile vagrant foo ./test
$ cat ./test
This will dump the contents of the file /flag
on the host. The putfile
command can be used to write to a previously created symlink:
$ ./VBoxControl symlink vagrant foo /home/niklas/.bashrc
$ echo 'echo owned' > ./test
$ ./VBoxControl putfile vagrant foo ./test 0644
Vagrant’s response
I reported this issue to Hashicorp. Their solution is as follows: If you run
vagrant up
for the very first time on your computer, it will show the
following warning:
Vagrant is currently configured to create VirtualBox synced folders with
the `SharedFoldersEnableSymlinksCreate` option enabled. If the Vagrant
guest is not trusted, you may want to disable this option. For more
information on this option, please refer to the VirtualBox manual:
https://www.virtualbox.org/manual/ch04.html#sharedfolders
This option can be disabled globally with an environment variable:
VAGRANT_DISABLE_VBOXSYMLINKCREATE=1
or on a per folder basis within the Vagrantfile:
config.vm.synced_folder '/host/path', '/guest/path', SharedFoldersEnableSymlinksCreate: false
After showing this warning, the file
~/.vagrant.d/data/vbox_symlink_create_warning
is created, preventing it from
ever being shown again. I’m not sure for how many potentially vulnerable
installations this will actually catch the attention of the right person, but
at least they give you the chance of doing the right thing, which is to set
the environment variable globally if you don’t 100% trust code running inside
your boxes.