Mid Station

VirtualBox Exploitation #1

每次逃离
都包含某些艺术
某些虚张声势
《胡迪尼》—— Kay Ryan

终于把一拖再拖的Virtual Machine Escape 研究计划提上了四月日程。

Hardware & Environment

首先是扫清了硬件设备上面的障碍:趁固态硬盘价格暴跌的机会给电脑加了一条NVMe的500GB固态。装上了久违的WIndows系统,把原来的Ubuntu乾坤大挪移原样搬到了Win10的Vmware Workstation虚拟机里面。之前的环境一直是原生Ubuntu里面装Win10的虚拟机,现在完全反过来了。调整的原因是Linux版本的Vmware Workstation其实性能表现不如Windows版本的,内存最多开到2GB,一旦超过2GB就会间歇性假死,IO性能也相当不理想。重装系统环境以后,得益于SSD速度上的提升,IO性能完全不是问题,即使双开甚至三开虚拟机都不在话下。另一方面,前年配的锐龙一七零零八核十六线程加十六吉格比特内存的配置终于派上了用场,资源的利用率得到了大幅提高。总结教训就是下次搭建工作环境的时候直接装Win10系统,Windows系统这两年的进步真的挺大的;Linux可以装到Hyper-V或者Vmware Workstation下面,目前来说应该还是Vmware Workstation比较方便,移植到Windows或者Linux的宿主机也都只是复制粘贴的事情。

言归正传,原本计划直接向Hyper-V下手,后来发觉似乎有点一步登天了,目前能找到的相关研究工作还是比较匮乏,加上逆向就需要耗费大量的时间,即使搭建好了调试环境也还是有种无从下手的感觉,只能说自己还是段位不够吧。后来把目光转向了开源的Virtualbox,相比起来入门的资源就丰富了不少,先是找到了来自长亭的35c3的0day神仙题Writeup:48小时逃逸Virtualbox虚拟机——记一次CTF中的0day之旅,再有YouTube上面Murmus CTF的直播录像(尽管用1.5倍速看完四集以后发现从头到尾基本都在搭环境)。可见搭建调试环境在漏洞研究当中是必不可少而又消耗精力的步骤,下面就讲一讲在搭建35c3-chromacity这道Virtualbox逃逸的0day题目的曲折经过。

Nested VM

题目给的是一个Qemu的img文件,里面安装了Xubuntu系统,带有5.2.22版本的Virtualbox。Virtualbox里面又是一个Lubuntu系统,我们的目标就是利用Virtualbox的漏洞从虚拟机中的Xubuntu逃逸到Qemu的Xubuntu当中。结合开头提到的系统环境,题目成功运行后的系统层级如下图左边所示,涉及三种不同的虚拟机和四个操作系统。后来我又将题目提供的qcow2 img文件通过qemu-img转换为vmdk格式,这样就省掉了中间Ubuntu上跑Qemu的虚拟层,变成下图右边的系统层级。在这种虚拟机多重嵌套的情况下就显示出Intel VT和AMD SVM虚拟化的优势了,开启虚拟化技术以后即使多重嵌套,内部虚拟机的指令仍可以直接在CPU上面运行,达到接近物理机的效率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

+--------------------------Win10----------------------+
| |
| |
| |
| +--------------Vmware-Workstations---------+ | +--------------------Win10-----------------+
| | Ubuntu 16.04 | | | |
| | | | | |
| | +------------Qemu-kvm-------------+ | | | +---------Vmware Workstations-----+ |
| | | | | | | | | |
| | | Xubuntu | | | | | Xubuntu | |
| | | | | | | | | |
| | | +-----Virtualbox-----+ | | | | | +-----Virtualbox-----+ | |
| | | | | | | | | | | | | |
| | | | | | | | | | | | | |
| | | | Xubuntu | | | | | | | Xubuntu | | |
| | | | ++ | | | | +----> | | | ++ | | |
| | | | || | | | | | | | || | | |
| | | +---------||---------+ | | | | | +---------||---------+ | |
| | | vv | | | | | vv | |
| | | escape to here | | | | | escape to here | |
| | +---------------------------------+ | | | +---------------------------------+ |
| | | | | |
| | | | | |
| +------------------------------------------+ | +------------------------------------------+
| |
| |
| |
+-----------------------------------------------------+

Building VIrtualbox with Debug Symbols

然而在比赛提供的镜像上还是没办法进行调试,我们还是需要手动编译一个带debug symbols的Virtualbox。
首先可以在官网页面Download VirtualBox (Old Builds): VirtualBox5.2中下载到5.22版本的源码,然后可以参照Linux build instruction中的说明安装依赖并进行编译。

“libhal-dev” Not Found

遇到的第一个问题是libhal-dev依赖不满足,搜索后发现该依赖已经从软件仓库中被移除,需要手动添加另外的源:

  • libhal-dev: this package is no longer available in the Ubuntu
    repositories. It used to be replaced by some other package, but this is
    not the case anymore.
    The only viable solution (to the best of my knowledge) is to add a
    private repository, named

mjblenner/ppa-hal**(https://launchpad.net/~mjblenner/+archive/ubuntu/ppa-hal
<https://launchpad.net/%7Emjblenner/+archive/ubuntu/ppa-hal>),
which cointains the last HAL packages.

Qt5

上面找到的回答中还提到从5.0版本开始Virtualbox从Qt4库升级为Qt5,因此要对官方提供的依赖进行修改,整理后的依赖安装命令如下:

1
2
sudo apt-get install gcc g++ bcc iasl xsltproc uuid-dev zlib1g-dev libidl-dev libsdl1.2-dev libxcursor-dev libasound2-dev libstdc++5 libhal-dev libpulse-dev libxml2-dev libxslt1-dev python-dev qt5-default qtdeclarative5-dev qtbase5-dev libcap-dev  libxmu-dev mesa-common-dev libglu1-mesa-dev  linux-kernel-headers libssl-dev libpam0g-dev  libxrandr-dev libxinerama-dev libqt4-opengl-dev makeself  libdevmapper-dev default-jdk  texlive-latex-base  texlive-latex-extra texlive-latex-recommended  texlive-fonts-extra texlive-fonts-recommended  libvpx-dev libopus-dev
sudo apt-get install lib32ncurses5 libc6-dev-i386 lib32gcc1 gcc-multilib lib32stdc++6 g++-multilib

然而./configure以后程序一直卡在Qt5检测处不通过,提示Qt5不存在:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Checking for environment: Determined build machine: linux.amd64, target machine: linux.amd64, OK.
Checking for kBuild: found, OK.
Checking for gcc: found version 5.5.0, OK.
Checking for Open Watcom:
** Open Watcom was not found, using alternative BIOS sources!
Checking for iasl: found version 20160108, OK.
Checking for xslt: found, OK.
Checking for pthread: found, OK.
Checking for libxml2: found version 2.9.4, OK.
Checking for libIDL: found version 0.8.14, OK.
Checking for ssl: found version OpenSSL 1.1.1b 26 Feb 2019, OK.
Checking for libcurl: found version 7.47.0, OK.
Checking for libvpx: found version 1.5.0, OK.
Checking for libopus: found, OK.
Checking for zlib: found version 1.2.8, OK.
Checking for libpng: found version 1.2.54, OK.
Checking for SDL: found version 1.2.15, OK.
Checking for X libraries: found, OK.
Checking for Xcursor: found, OK.
Checking for Xinerama: found, OK.
Checking for Xrandr: found, OK.
Checking for Xmu: found, OK.
Checking for Mesa / GLU: found version 1.4, OK.
Checking for Qt5:
** qt5 not found!
Check /home/matthew/VBox/VirtualBox-5.2.22/configure.log for details

查看configure.log发现在版本检测处出错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Package Qt5Core was not found in the pkg-config search path.
Perhaps you should add the directory containing `Qt5Core.pc'
to the PKG_CONFIG_PATH environment variable
No package 'Qt5Core' found
(Qt5 from '/usr/lib/qt5')
compiling the following source file:
#include <QtGlobal>
extern "C" int main(void)
{
#if QT_VERSION >= 329216
return 0;
#else
return 1;
#endif
}
using the following command line:
g++ -fPIC -g -O -Wall -o /home/matthew/VBox/VirtualBox-5.2.22/.tmp_out /home/matthew/VBox/VirtualBox-5.2.22/.tmp_src.cc "-L/usr/lib/qt5/lib -lQt5CoreVBox -lpthread -I/usr/lib/qt5/include -I/usr/lib/qt5/include/QtCore -DQT_SHARED -std=c++11"
/home/matthew/VBox/VirtualBox-5.2.22/.tmp_src.cc:1:20: fatal error: QtGlobal: No such file or directory
compilation terminated.

再到./configure中查找相关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
check_qt5()
{
foundqt5=
test_header Qt5
cat > $ODIR.tmp_src.cc << EOF
#include <QtGlobal>
extern "C" int main(void)
{
EOF
echo "#if QT_VERSION >= $(($QT5MAJ*65536+$QT5MIN*256))" >> $ODIR.tmp_src.cc
cat >> $ODIR.tmp_src.cc << EOF
return 0;
#else
return 1;
#endif
}
...

主要是检查环境中的Qt版本是否和$QT5MAJ$QT5MIN所指定的版本要新,而跟踪发现要求最低版本是5.6。

1
2
3
4
5
...
QT5DIR_PKGCONFIG=1
QT5MAJ=5
QT5MIN=6
...

不幸的是16.04软件仓库上提供的Qt版本恰好只是5.5:
Package "qtbase5-dev"Package "qtbase5-dev"
那么现在有两种选择,一种是直接升级Ubuntu版本到1804LTS,软件仓库也随之更新,能直接通过apt-get装上新版本的Qt,另一种方法就是手动安装Qt然后configure的时候指定Qt库的目录,受限于糟糕的网络环境,还是对症下药手动安装新版本Qt库比较实在。Qt库可以直接从官网下载安装包,具体方法不再赘述。
安装完成过后还需更改./configure脚本的1528行,添加c++11的编译参数,修改后的指令为FLGQT5="-DQT_SHARED -std=c++11",之后执行

1
./configure --disable-hardening --with-qt-dir=/opt/Qt5.9.7/5.9.7/gcc_64 -d --build-libxml2 --disable-java

终于配置成功。

Compiling

半天过去依赖问题终于解决了,本以为接下来就是设置环境变量执行编译命令外加期待地搓手:

1
2
source ./env.sh
kmk all BUILD_TYPE=debug

当然,编译过程也不会是一帆风顺,在编译驱动模块的时候产生了以下错误:
/tmp/vbox.0/r0drv/linux/memobj-r0drv-linux.c:1126: error: too many arguments to function 'get_user_pages'
好在不是个例,早有用户开了ticket报告这个问题:Kernel modules do not build with linux kernel 4.4.169 -> fixed after 6.0.4/5.2.26 and in test builds,开发人员的回答是直接使用最新的test builds来解决问题。

但是题目的环境必须是在5.2.22版本下,采用新版本就不行了。后来搜索了ticket相关的patch:# Changeset 77464 in vbox,手动打patch以后还需要把Ubuntu的内核从4.15.x降级为4.4.x,终于编译通过。

Run

运行的时候同样需要指定Qt库所在的目录,通过LD_LIBRARY_PATH来加载Qt库,具体运行指令是:

1
2
export LD_LIBRARY_PATH="/opt/Qt5.9.7/5.9.7/gcc_64/lib/"  
./out/linux.amd64/debug/bin/VirtualBox

终于成功看到VIrtualbox运行的界面
Virtualbox 5.2.22Virtualbox 5.2.22随后安装上Ubuntu家族里面最轻量级的Lubuntu系统,并且在Guest端进行以下设置来方便调试。

  1. 安装Guest Additions。执行kmk all的编译命令的时候也会同时生成Guest Additions的ISO文件,路径是VirtualBox-5.2.22/out/linux.amd64/debug/bin/additions/VBoxGuestAdditions.iso,如果没有找到可以重新执行kmk additions-iso单独生成ISO镜像。加载到Guest里面就完成安装了。如果自己编译的版本安装后有问题,比如说出现打开3D加速功能以后系统无法启动的情况,可以尝试一下解决方法:
    1. 下载官方提供的VBoxGuestAdditions.iso并重新安装,可以尝试新版本的Guest Additions。
    2. 加大虚拟机设置中的显存。
  2. 添加共享文件夹。这样就可以在Host上编辑Exp脚本然后直接在Guest中运行,省去来回同步时间。相关指令:mount -t vboxsf SharedUbuntu /mnt/UbuntuShare
  3. 设置NAT并打开ssh客户端。我的做法是将Guest的22端口映射到Host的2222端口,然后在Host上用ssh连接本机2222端口。这样操作可以完全省去Guest的图形界面,因为使用图形界面可以会有一个问题,在调试过程中如果在Guest捕获用户鼠标键盘的情况下触发了断点,整个系统都陷入假死的状态,Guest无法释放鼠标键盘,我们也无法切换到Host的gdb界面执行continue指令。如果通过ssh来对Guest进行操作就不存在上述的问题了。

最后调试的界面如下,左边是ssh连接Virtualbox中的虚拟机,右边是Host上gdb attach上virtualbox的相应进程。可以看到在断点处成功显示相关源码了。
gdbgdb当然这也只是开了个头,后面的工作可能更加曲折。

References