Vagrant虚拟机可访问宿主机整个文件系统的安全风险分析

本文详细分析了Vagrant默认配置下虚拟机可访问宿主机整个文件系统的安全风险,探讨了VirtualBox共享文件夹机制、HGCM服务调用方式,以及通过符号链接实现跨系统文件访问的技术细节。

This is fine: Vagrant guests can access the entire host filesystem

2018年3月25日

• 作者:niklasb

去年九月在使用VirtualBox时,我发现了一些异常行为,最初以为是VirtualBox中存在相当严重的安全漏洞:在使用Vagrant启动的虚拟机中运行非特权程序时,它可以获得对宿主机整个文件系统的读写访问权限。结果发现这实际上不是VirtualBox的bug,而更多是Vagrant的错误配置。

这个问题应该被认为是公开已知的,但仍然存在,尽管Vagrant现在会在每个宿主机上精确显示一次警告。如果你在Vagrant虚拟机中运行不受信任的代码,应该显式设置VAGRANT_DISABLE_VBOXSYMLINKCREATE环境变量,例如通过在.profile/.zprofile中添加:

1
export VAGRANT_DISABLE_VBOXSYMLINKCREATE=1

我在34C3 CTF中以此作为babyvm挑战题,简化版本在3小时内被解决,困难版本也在比赛48小时内被一支队伍解决(向DragonSector的valis致敬!)。因此本文一举两得,既是对Vagrant的温和吐槽,也是两个CTF挑战的解题报告。

问题描述

Vagrant默认会为每个创建的同步文件夹设置SharedFoldersEnableSymlinksCreate标志。这包括vagrant共享,它在每个Vagrant虚拟机中默认启用,除非通过在Vagrantfile中添加以下内容显式禁用:

1
config.vm.synced_folder ".", "vagrant", disabled: true

VirtualBox文档对此标志有以下说明:

出于安全原因,默认不允许客户操作系统创建符号链接。如果你信任客户操作系统不会滥用此功能,可以通过以下方式为"sharename"启用符号链接创建:

1
VBoxManage setextradata "VM name" VBoxInternal2/SharedFoldersEnableSymlinksCreate/sharename 1

虽然文档本可以更清楚地说明启用此标志的含义,但Vagrant显然假设所有虚拟机都是隐式可信的,这至少违背了我的个人直觉:在我的心智模型中,Vagrant只是所使用的虚拟机监控程序的非常薄的包装器,不应该增加额外的攻击面。简而言之,我希望它能给我与VirtualBox相同的安全保证。

从非特权上下文访问共享文件夹

VirtualBox中的共享文件夹是作为HGCM服务(Host-Guest Communication Manager的缩写)实现的。HGCM涉及一个相当简单的RPC协议,通过该协议客户机可以向虚拟机监控程序进行函数调用。VirtualBox的其他几个需要客户机协作的功能也是通过这种机制实现的,例如剪贴板共享、拖放和3D加速。

为了启动HGCM调用,例如读取或写入共享文件夹中的文件,或将数据放入剪贴板,必须向通过PCI暴露给每个虚拟机的特殊VMM(虚拟机监控器)设备发出自定义请求。这通常需要内核权限。

但是,如果安装了VirtualBox客户附加组件,可以通过VBoxGuest驱动程序发出请求,该驱动程序以Windows上的VBoxGuest设备和Linux上的/dev/vboxuser设备的形式暴露给"世界"——即客户机中的非特权进程。由于Vagrant的同步文件夹使用了共享文件夹功能,Vagrant虚拟机通常会预先安装VirtualBox客户附加组件。

使用符号链接访问宿主机文件系统

SharedFolders HGCM服务的核心由VirtualBox源文件src/VBox/HostServices/SharedFolders/service.cpp中的函数svcCall实现。对我们目的相关的HGCM函数有:

  • SHFL_FN_CREATE:在共享文件夹中打开文件
  • SHFL_FN_{READ,WRITE}:从通过SHFL_FN_CREATE打开的文件中读取/写入
  • SHFL_FN_SYMLINK:在共享文件夹中创建符号链接(这需要设置SharedFoldersEnableSymlinksCreate标志)

如果共享文件夹通过正常的客户附加组件文件系统驱动程序挂载,并且打开了共享中的符号链接,它将在客户机内部解析:

1
2
3
4
5
6
7
8
9
# 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

具体来说,文件系统驱动程序将首先查询文件类型以确定它是否是符号链接,然后发出SHFL_FN_READLINK调用来解析链接,然后递归回到内核以打开结果路径。

但是,由于SHFL_FN_CREATE不检查请求的文件路径是否是符号链接(如果尝试检查可能会存在竞争条件),我们也可以强制在宿主机上打开符号链接。

我的CTF挑战有一个意外的简单方法来获取客户机内的root权限——事实证明passwd -d root不会"删除"root密码而是清空它;也许我应该更经常阅读man页面。团队"pasten"注意到官方文件系统驱动程序有一个标志可以使它在宿主机端打开符号链接,因此以下是挑战的简单解决方案:

1
2
3
4
5
6
7
$ su
# umount /vagrant
# rmmod vboxsf
# modprobe vboxsf follow_symlinks=1
# mount -t vboxsf vagrant /vagrant
# ln -s /flag /vagrant/flag
# cat /vagrant/flag

这将打印宿主机上文件/flag的内容。如果我没有为了使挑战更加健壮而显式将共享文件夹标记为只读,写入宿主机上的文件也同样容易。

在他们仅在CTF开始3小时后就想出这个解决方案时(我本意是让这个挑战在难度上属于较难的一端),我决定添加第二个版本的挑战,称为babyvm2,希望在那里不容易获得root权限。相反,参与者应该滥用客户附加组件来实现相同的效果。

通过/dev/vboxuser进行利用

在那里,我称之为利用。虽然这显然都是预期的行为。无论如何,我已经描述了通过/dev/vboxuser设备"使用"此行为所需的一切,剩下的只是一个编程挑战。我们必须找出如何通过设备进行HGCM调用,以及我们需要什么样的HGCM调用顺序来实现我们想要的目标。因为我懒,我没有重新实现HGCM协议,而是修补了一个现有的客户端,称为VBoxControl,并添加了额外的"功能"。

这个补丁应该可以干净地应用到VirtualBox 5.2.4代码库。我将跳过构建说明,因为它们由VirtualBox文档详细描述。构建VirtualBox通常相当不愉快。Arch Linux用户有一定的优势,因为他们可以只修改官方的PKGBUILD:

1
2
3
4
$ svn checkout --depth=empty svn://svn.archlinux.org/community
$ cd community
$ svn update virtualbox
$ cd virtualbox/trunk

修改后的VBoxControl有两个额外的命令,称为symlinkgetfile,可以如下使用:

1
2
3
$ ./VBoxControl symlink vagrant foo /flag
$ ./VBoxControl getfile vagrant foo ./test
$ cat ./test

这将转储宿主机上文件/flag的内容。putfile命令可用于写入先前创建的符号链接:

1
2
3
$ ./VBoxControl symlink vagrant foo /home/niklas/.bashrc
$ echo 'echo owned' > ./test
$ ./VBoxControl putfile vagrant foo ./test 0644

Vagrant的响应

我向Hashicorp报告了这个问题。他们的解决方案如下:如果你在计算机上第一次运行vagrant up,它将显示以下警告:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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

显示此警告后,将创建文件~/.vagrant.d/data/vbox_symlink_create_warning,防止它再次显示。我不确定这实际上会吸引多少潜在易受攻击的安装的正确人员的注意力,但至少他们给了你做正确事情的机会,如果你不完全信任在虚拟机内运行的代码,就全局设置环境变量。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计