【PAPER.0x04】论文笔记:HYPER-CUBE: High-Dimensional Hypervisor Fuzzing
本文最后更新于:2024年7月31日 晚上
hyper 的 不是 visor,是我 cube 哒!
0x00. 一切开始之前
笔者最近刚好在弄虚拟化相关的工作(本来以为从字节离职后就不会碰到和虚拟化相关的开发了),而刚好笔者看到有一系列论文(还得是论文作者👍)给出了利用系统虚拟化技术来辅助漏洞挖掘以及针对虚拟化系统进行漏洞挖掘的好东西,因此笔者还是打算抽空浅读一下:)
Abstract
虚拟机管理器(Virtual Machine Manager,VMM,aka Hypervisor)是云技术(尤其是 IaaS)的基石,因此其安全问题十分重要
本文给出 Hyper-Cube
——一个通用的对 hypervisor 进行 fuzzing 的工具
0x01. Introduction
没必要讲但还是简单扯几句
系统虚拟化技术是云计算的基石,但 VMM 若有漏洞则会影响到其他 VM 以及服务提供商的安全,因此我们需要一种高效的手段找出 VMM 中的漏洞
模糊测试(Fuzzing)是一个高效的找漏洞手段,但现在我们很难高效地 fuzz VMM:
- 不同于传统 fuzz(只有 stdin 和 syscall 作为输入),fuzz VMM 需要与多种类型的接口 (MMIO、PIO、hypercall、…)进行交互
- VMM crash 后的重启开销较大
- 很难高效地 fuzz(例如不能多线程)
论文写作时(笔者注:2020年)最先进的 hypervisor fuzzer 是由 AFL 改来的 VDF,只能 fuzz MMIO/PIO;另一个 hypervisor fuzzer IOFUZZ 则只能往随机端口写随机值,都弱爆了
为了解决这些挑战,本文给出三个可以提升的目标:
- 较高的测试用例吞吐量
- 能同时与所有可用接口进行交互
- 能为一组不同的 hypervisor 生成稳定(stable)与确定(deterministic)的测试用例
基于此,作者设计出了 HYPER-CUBE
:一个基于一个最小客制化操作系统的通用 hypervisor fuzzer,总结下来贡献有如下三点:
- 作者设计了一个多维度的(multi-dimentional)、无平台依赖的(platform-independent)能够有效且高效测试不同接口的模糊测试方法
- 作者描述了一个能够高效 fuzz hypervisor 的独立于待测平台的方法
- 作者通过一个名为
HYPER-CUBE
的客制操作系统实现了这种方法,并能在真实世界的 hypervisor 中找出漏洞
项目代码开源于 https://github.com/RUB-SysSec/hypercube
0x02. Technology Background
感觉不如直接看系统虚拟化导论
在开始之前先介绍 X86 虚拟化的一些基础知识、X86 中 OS 的启动过程、Guest OS 与 hypervisor 的通信接口
A. x86 Boot Process
在 x86 机器上最先运行的程序通常是 Basic Input/Output System
(BIOS) 或 Unified Extensible Firmware Interface
(UEFI),本文将这样的程序称为 固件
(firmware),之后便是运行 boot loader (如 GRUB)来准备环境并引导操作系统内核,再由内核来配置其他硬件(如中断控制器、PCI 设备)
B. Input/Output on x86
x86 上与设备通信的方式有:
Port I/O
:传统的端口地址总线,通过in
、out
指令访问Memory-Mapped I/O
:外设寄存器/内存被映射到物理地址总线上,从而可以通过传统内存访问方式进行访问Direct Memory Access
:这种机制允许外设直接访问物理内存,本文主要关注 PCI/PCIe DMA,而非 ISA DMA(因为难搞)
表 1 给出接口总览:
C. Hypervisor
Hypervisor 为 VM 提供可控的虚拟环境(虚拟 CPU、虚拟内存、模拟中断),当 VM 需要进行特权操作时便会触发 VM-Exit
将控制权返还给 hypervisor,由其完成模拟操作后返还控制权——称为 Trap and Emulate
机制
通过提供完全虚拟化的环境,hypervisor 可以在物理机上同时运行多个 VM
1) CPU and Memory Virtualization
过去人们用 二进制转译 (binary translation)技术实现完全的 CPU 与内存虚拟化,通过 trap and emulate
模型捕获形如 mov cr3
这样的特权指令,但性能开销巨大,于是 Intel 和 AMD 都各自引入了自己的硬件辅助虚拟化支持
2) Device Emulation
hypervisor 还需要模拟包括中断控制器在内的标准硬件,其主要有两种交互机制:MMIO 与 port I/O,不过 hypervisor 不需要中断 DMA 内存访问
图 1 给出一个通过 trap and emulate 模型实现设备虚拟化的例子(QEMU/KVM):
3) Para-Virtualization
虚拟机并不需要直接接触到实际的硬件,半虚拟化 (para-virtualization)由此诞生,这种技术本质上需要我们去修改 OS,例如 VirtIO 便提供了一个统一的虚拟设备协议,OS 只需要实现一套虚拟设备驱动即可
现代的硬件加速的(hardware-accelerated)虚拟化则引入了新的指令叫 hypercall
,用来主动触发 VM-exit
以实现一些任务(以 Intel 为例为 vmcall
指令)
D. Fuzzing Hypervisor
对 hypervisor 的 fuzzing 存在接口繁多、重启开销大的挑战,目前绝大部分研究来自工业界,例如 Tang 实现了一个定制于 QEMU 的 SeaBIOS 的 AFL 扩展,学术界则仅有一个 VDF
0x03. Design
A. Threat Model
攻击者有着对虚拟机的完全控制权,其目的为获取宿主机上其他虚拟机或是宿主机本身的控制权,DoS 攻击也纳入考虑
B. Challenge in Fuzzing Hypervisors
如图 2 所示,hypervisor 与 guest 间的交互接口众多,且非所有接口都有文档,需要 fuzzer 能实现与 hypervisor 间的有意义交互;VM 中系统启动过程也可能影响漏洞寻找过程,但重启系统耗时较长;硬件加速也可能带来不确定性
C. Architecture
1) High-Level Overview
文章中的 fuzzer 包含如图 2 所示的三个主要组成部分:
HYPER-CUBE OS
在虚拟机内启动并枚举硬件接口,定制的系统使得我们对 VM 有着完全的控制权限- 随后启动的用来 fuzz hypervisor 的字节码解释器
TESSERACT
- 一组额外的工具,用于向
TESSERACT
提供字节码流、反编译执行的字节码程序、使用如串口等接口观测 hypervisor
这样的架构允许我们实现上文所述的三个目标:
High Performance Fuzzing
:客制操作系统HYPER-CUBE OS
比 COTS(Commercial Off-The-Shelf) OS 更轻量级,从而在 crash 后能快速重启系统;编译执行程序进行 fuzz 也较耗时,因此作者使用自制的运行在 VM 中 ring0 的TESSERACT
字节码解释器,以 fuzzer-friendly 的方式设计字节码:最大化产生有用指令的可能性、对内存地址不作为指针而是作为大小与偏移值进行编码(TESSERACT
会记录那些有趣的内存区域);为了提高生成合理字节码的概率,所有参数都被映射到模范围中Generic High-Dimensional Fuzzing
:现有的 hypervisor fuzzer 通常关注于某一接口,而TESSERACT
则可以与所有可用接口交互Stable and Deterministic Fuzzing
:此前的 hypervisor fuzzer 基于 COTS OS 从而引入了大量属于系统本身的不确定性,作者开发的操作系统HYPER-CUBE OS
则以对环境的控制权避免了这个问题,且能进行一些有趣的操作
2) HYPER-CUBE OS
客制操作系统 HYPER-CUBE OS
为该 fuzzer 的核心,其实现了 multiboot 2 规范,并为我们的 fuzzer (TESSERACT
)提供了一个通用平台,其有着两个主要任务:物理内存管理与设备枚举,前者需要管理所有的物理内存,后者则需要枚举 MMIO 与 port I/O 的地址范围,并与 PIC/APIC 交互,有的信息(如 MMIO 区域)通过 BIOS/UEFI 传递,而有的信息(如 I/O 端口与 PCI 设备)则又需要手动枚举
3) TESSERACT
HYPER-CUBE OS
启动后会与 hypervisor 进行随机交互,这通过自定义字节码解释器 TESSERACT
定义,字节码可以来自于 VM 外,也可以来自于该解释器内置的伪随机数生成器,字节码的设计形式使得任意字节串都是一个合法程序
解释器解码时会将所有的值模到一个范围中,根据 opcode 调用不同的处理函数(进行单个/多个动作);解释器还维护一组单个为 4k 页的会被被定期覆盖为随机数据的 scratch 区域,这使得 TESSERACT
可以在其上创建自定义数据结构作为输入
4) External Tools
该 fuzzer 的最后一部分为运行在 host 侧的三个独立的辅助工具(通常在 fuzzing 之后使用):
- logger 在 fuzzing 时岚姐 VM 的串口通信并存储以供后续分析
- minimization tool 在找到 bug 后使用同样的种子重新生成程序并在随机移除部分段后观测结果,算法收敛后通常能获得包含数十条指令的程序,出于调试目的我们还可以将
TESSERACT
作为独立的 ring 3 程序运行 - decompiler 将给定的(最小化)字节码转换为等价的 C 程序以分析找到漏洞的字节流程序,这样的 C 程序可以被编译为 HYPER-CUBE OS 的一个模块或是插入到 COTS OS 内核驱动中进行调试
0x04. Implementation Details
这一节很多基础知识,懒得摘抄太多了
A. HYPER-CUBE OS
1) Boot Process
在启动阶段固件会从不同的外设(称为 Option ROMs)中载入程序以检测硬件并生成数据信息,随后加载 bootloader 以装载内核,multiboot 标准被制定来标准化 bootloader (并扩展至第二代以支持 UEFI)以在不同 BIOS/UEFI 间通用,遵循 multiboot2 的内核可以被构建为 ELF 文件,HYPER-CUBE OS
便基于 multiboot2 规范并使用 GRUB 进行引导,从而使得其可以通过传统 BIOS 或 UEFI 固件进行启动,且在入口点便进入保护模式
Initializing Interrupts
:HYPER-CUBE OS
会配置 PIC/APIC 以初始化所有基本的中断/异常的 handlers,并通过 masking OS 内的所有终端寄存器以屏蔽了所有外部中断,从而确保 fuzzing 过程不会被中断
2) Memory Management
作者实现了一个单次分配一整张页面的简易堆管理器,减少了内存碎片并提高了撸棒健壮性,实际上仅在枚举设备时 HYPER-CUBE OS
会为 TESSERACT
分配少量内存,因此额外开销可以忽略不计
现代操作系统通常使用分页机制支撑虚拟空间,但一些任务(如页表和 MMIO)需要直接访问物理内存,因此 HYPER-CUBE OS
维护一个一对一直接映射到物理内存的区域(0x0 ~ 0x100000
),此外由 BIOS/UEFI 传来的可用物理内存信息会被用作内核堆,最后 HYPER-CUBE OS
会创建另一个对 MMIO 区域的重映射,内存布局如图 3 所示:
3) Device Enumeration
PCI 这样的硬件设备或是 APIC 或 高精确事件计时器(High Precision Event Timer,HPET)可以将内部寄存器映射到物理内存上,访问对应的 MMIO 区域则可以直接影响这些设备的状态,HYPER-CUBE OS
会枚举可用来 fuzzing 的不同接口,这需要枚举所有外设所用的 MMIO 及 port I/O 地址
Core Components
:由 APIC 或 HPET 提供的 MMIO 区域信息通过名为高级配置与电源接口
(Advanced Configuration and Power Interface,ACPI)表的形式进行描述,HPET 与 APIC MMIO 区域的基址存放在对应的 ACPI 表中(由 multiboot bootloader 提供),ACPI 表中所有的基址指针都在TESSERACT
中注册为 fuzzing 目标地址PCI-/PCIe-Enumeration
:HYPER-CUBE OS
依赖于传统 PCI 配置 I/O 接口或基于位于增强配置机制(Enhanced Configuration Mechanism
,ECAM)基指针的 MMIO 区域的现代 PCI 配置空间进行 PCI 设备枚举,ECAM 信息同样存放在 ACPI 表中ISA-Enumeration / I/O Port Probing
:ISA 设备没有系统的枚举与检测的办法,因此HYPER-CUBE OS
会对所有的 216 个端口进行读写,发生改变的端口值则被认为是一个模拟设备
B. TESSERACT
TESSERACT
是一个复杂指令集解释器,总的来说实现了如下指令:
字节流输入将被 TESSERACT
解码为不同的指令操作,在没有外部字节码流作为输入的情况下其使用伪随机数生成器来生成随机指令(如图 4 所示),在找到造成崩溃的初始 PRNG 状态后,作者使用相同的状态重新生成相同的字节串(可能长达数百万到数千万条指令),并将其嵌入到 HYPER-CUBE OS
镜像中进行引导,若依然崩溃则会随机删除 50% 指令片段并重复此过程以获取最小字节串,随后通过反汇编器转为人类可读的 C 程序形式
0x05. Evaluation
作者希望能够回答以下四个研究问题:
- 使用
HYPER-CUBE
是否能够在不同 hypervisor 中发现新的漏洞? - 是否能重新发现那些已知的漏洞(如 QEMU 的 CVE-2015-3456)?
- 与其他的 hypervisor fuzzer 相比在覆盖率方面如何?
- 与其他的 hypervisor fuzzer 相比在性能方面如何?
贴几个表看看实力,具体的原文就不摘抄了,反正很强就对了,建议看原论文
0x06. Related Work
过去的项目使用客制操作系统来测试 hypervisor,例如 CrashOS 是包含了一组手写测试用例的 OS,但不提供发现新漏洞的方法;Intel CHIPSEC 套件则提供了不同的 fuzz 模拟设备的组件;Ormandy 则写了一个用于在不同 hypervisor 中创建随机 I/O 访问的 fuzzer;Henderson 等人则提出了通过修改开源 hypervisor 并提供 AFL 支持来 fuzz 特定模拟设备的方法;Tang 等人则通过[在 QEMU 的 SeaBIOS 上实现接口](https://www.blackhat.com/docs/eu-16/materials/
eu-16-Li-When-Virtualization-Encounters-AFL-APortable-Virtual-Device-Fuzzing-Framework-WithAFL-wp.pdf)以提供 AFL 与 QEMU 间的互相操作性;来自 MWR Labs 和微软安全研究与防御部门的 Amardeep Chana 则引入了用于模糊测试 Hyper-V hypercall(VMBus)的模糊测试工具(附加链接)
0x07. Discussion
这个工具很强大,但仍有一些可以提升的点
A. Coverage-Guided Hypervisor Fuzzing
与覆盖率信息进行结合可能会提高发现漏洞的概率,为了发现新的覆盖范围或有趣行为,需要逐步构建 hypervisor 中的状态,而可能的操作空间巨大,让状态增长到有趣的事情发生(如 crash)似乎会极大增加吞吐量
B. Hyper-V
HYPER-CUBE
暂不支持 Hyper-V
,因为其不支持 64 位 UEFI 引导(Hyper-V
要求操作系统以此模式进行引导);同时 HYPER-CUBE 对半虚拟化的支持有限,目前只实现了不太可能单独触发有趣覆盖的通用操作
0x08. Conclusion
没啥好说的