VirtualBox虚拟机逃逸漏洞分析
字数 1349 2025-08-29 08:32:24

VirtualBox虚拟机逃逸漏洞分析与利用教学文档

漏洞概述

本漏洞存在于VirtualBox的3D加速功能中,具体位置在src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c文件的crUnpackExtendShaderSource函数中。该漏洞允许攻击者从虚拟机内部突破到宿主机,实现虚拟机逃逸。

漏洞背景

  • 发现时间:2018年10月
  • 影响版本:VirtualBox v5.2.22及之前版本
  • 相关CVE:CVE-2019-2446(信息泄露漏洞)

漏洞代码分析

关键函数:crUnpackExtendShaderSource

void crUnpackExtendShaderSource(void) {
    GLint *length = NULL;
    GLuint shader = READ_DATA(8, GLuint);
    GLsizei count = READ_DATA(12, GLsizei);
    GLint hasNonLocalLen = READ_DATA(16, GLsizei);
    GLint *pLocalLength = DATA_POINTER(20, GLint);
    char **ppStrings = NULL;
    GLsizei i, j, jUpTo;
    int pos, pos_check;
    
    if (count >= UINT32_MAX / sizeof(char *) / 4) {
        crError("crUnpackExtendShaderSource: count %u is out of range", count);
        return;
    }
    
    pos = 20 + count * sizeof(*pLocalLength);
    if (hasNonLocalLen > 0) {
        length = DATA_POINTER(pos, GLint);
        pos += count * sizeof(*length);
    }
    
    pos_check = pos;
    if (!DATA_POINTER_CHECK(pos_check)) {
        crError("crUnpackExtendShaderSource: pos %d is out of range", pos_check);
        return;
    }
    
    for (i = 0; i < count; ++i) {
        if (pLocalLength[i] <= 0 || pos_check >= INT32_MAX - pLocalLength[i] || !DATA_POINTER_CHECK(pos_check)) {
            crError("crUnpackExtendShaderSource: pos %d is out of range", pos_check);
            return;
        }
        pos_check += pLocalLength[i];
    }
    
    ppStrings = crAlloc(count * sizeof(char*));
    if (!ppStrings) return;
    
    for (i = 0; i < count; ++i) {
        ppStrings[i] = DATA_POINTER(pos, char);
        pos += pLocalLength[i];
        if (!length) {
            pLocalLength[i] -= 1;
        }
        Assert(pLocalLength[i] > 0);
        
        jUpTo = i == count -1 ? pLocalLength[i] - 1 : pLocalLength[i];
        for (j = 0; j < jUpTo; ++j) {
            char *pString = ppStrings[i];
            if (pString[j] == '\0') {
                Assert(j == jUpTo - 1);
                pString[j] = '\n';
            }
        }
    }
    
    cr_unpackDispatch.ShaderSource(shader, count, ppStrings, length ? length : pLocalLength);
    cr_unpackDispatch.ShaderSource(shader, 1, (const char**)ppStrings, 0);
    crFree(ppStrings);
}

漏洞点分析

  1. 边界检查缺陷

    • 在第一个循环中,pos_check增加了一个数组的长度
    • 验证只在下次迭代中进行,最后一个元素的长度从未被验证
    • 导致最后一个元素的长度可以任意大
  2. 堆溢出机会

    • 嵌套循环将每个\0字节转换为\n字节
    • 对于任意长度,循环可以越界,导致堆溢出

利用技术

关键数据结构

  1. CRVBOXSVCBUFFER_t

    typedef struct _CRVBOXSVCBUFFER_t {
        uint32_t uiId;
        uint32_t uiSize;
        void* pData;
        _CRVBOXSVCBUFFER_t *pNext, *pPrev;
    } CRVBOXSVCBUFFER_t;
    
  2. CRConnection对象

    • 包含各种函数指针和指向缓冲区的指针
    • 破坏此对象可获得任意读写和代码执行能力

利用步骤

  1. 堆信息泄露

    • 利用未初始化内存漏洞泄漏CRConnection对象指针
    • 即使打了CVE-2018-3055补丁,仍可利用此方法
  2. 堆喷射

    • 使用alloc_buf()喷射大量CRVBOXSVCBUFFER_t对象
    • 典型参数:spray_len = 0x30spray_num = 0x2000
  3. 第一次溢出

    • 构造恶意消息触发漏洞
    • 覆盖相邻CRVBOXSVCBUFFER_t的ID和大小字段
    • 计算:消息长度0x30,pString偏移0x28,glibc块头0x10,最终需要0x22字节
  4. 寻找损坏的缓冲区

    • 遍历ID列表找出被损坏的ID
    • \0替换为\n以匹配损坏的ID
  5. 第二次溢出

    • 使用损坏的缓冲区覆盖第二个CRVBOXSVCBUFFER_t
    • 构造伪对象指向CRConnection对象
  6. 建立任意读原语

    • 通过覆盖CRConnectionpHostBuffer和大小字段
    • 使用SHCRGL_GUEST_FN_READ命令触发任意读取
  7. 实现任意代码执行

    • 覆盖CRConnection的函数指针(如Free()
    • system()地址写入函数指针位置
    • 触发函数调用执行任意命令

地址计算

  1. 从泄漏的crVBoxHGCMFree地址开始:
    self.crVBoxHGCMFree = self.read64(self.pConn + OFFSET_CONN_FREE)
    self.VBoxOGLhostcrutil = self.crVBoxHGCMFree - 0x20650
    self.memset = self.read64(self.VBoxOGLhostcrutil + 0x22e070)
    self.libc = self.memset - 0x18ef50
    self.system = self.libc + 0x4f440
    

实际利用示例

获取flag

由于路径长度限制,使用分段命令:

p.rip(p.system, "mv Desktop a\0")
p.rip(p.system, "mv a/flag.txt b\0")
p.rip(p.system, "mousepad b\0")

防御建议

  1. 修复边界检查缺陷,确保所有元素的长度都被验证
  2. 初始化所有堆分配的内存,防止信息泄露
  3. 更新到最新版本的VirtualBox
  4. 禁用不必要的3D加速功能

总结

该漏洞利用展示了如何通过精心构造的堆操作和边界条件缺陷,实现从虚拟机到宿主机的逃逸。关键在于:

  1. 利用未初始化内存泄露关键地址
  2. 通过堆喷射控制内存布局
  3. 利用边界检查缺陷实现堆溢出
  4. 通过覆盖关键数据结构获得任意代码执行能力

理解此类漏洞有助于提高虚拟化环境的安全性设计和防御能力。

VirtualBox虚拟机逃逸漏洞分析与利用教学文档 漏洞概述 本漏洞存在于VirtualBox的3D加速功能中,具体位置在 src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c 文件的 crUnpackExtendShaderSource 函数中。该漏洞允许攻击者从虚拟机内部突破到宿主机,实现虚拟机逃逸。 漏洞背景 发现时间:2018年10月 影响版本:VirtualBox v5.2.22及之前版本 相关CVE:CVE-2019-2446(信息泄露漏洞) 漏洞代码分析 关键函数:crUnpackExtendShaderSource 漏洞点分析 边界检查缺陷 : 在第一个循环中, pos_check 增加了一个数组的长度 验证只在下次迭代中进行,最后一个元素的长度从未被验证 导致最后一个元素的长度可以任意大 堆溢出机会 : 嵌套循环将每个 \0 字节转换为 \n 字节 对于任意长度,循环可以越界,导致堆溢出 利用技术 关键数据结构 CRVBOXSVCBUFFER_ t : CRConnection对象 : 包含各种函数指针和指向缓冲区的指针 破坏此对象可获得任意读写和代码执行能力 利用步骤 堆信息泄露 : 利用未初始化内存漏洞泄漏 CRConnection 对象指针 即使打了CVE-2018-3055补丁,仍可利用此方法 堆喷射 : 使用 alloc_buf() 喷射大量 CRVBOXSVCBUFFER_t 对象 典型参数: spray_len = 0x30 , spray_num = 0x2000 第一次溢出 : 构造恶意消息触发漏洞 覆盖相邻 CRVBOXSVCBUFFER_t 的ID和大小字段 计算:消息长度0x30,pString偏移0x28,glibc块头0x10,最终需要0x22字节 寻找损坏的缓冲区 : 遍历ID列表找出被损坏的ID 将 \0 替换为 \n 以匹配损坏的ID 第二次溢出 : 使用损坏的缓冲区覆盖第二个 CRVBOXSVCBUFFER_t 构造伪对象指向 CRConnection 对象 建立任意读原语 : 通过覆盖 CRConnection 的 pHostBuffer 和大小字段 使用 SHCRGL_GUEST_FN_READ 命令触发任意读取 实现任意代码执行 : 覆盖 CRConnection 的函数指针(如 Free() ) 将 system() 地址写入函数指针位置 触发函数调用执行任意命令 地址计算 从泄漏的 crVBoxHGCMFree 地址开始: 实际利用示例 获取flag 由于路径长度限制,使用分段命令: 防御建议 修复边界检查缺陷,确保所有元素的长度都被验证 初始化所有堆分配的内存,防止信息泄露 更新到最新版本的VirtualBox 禁用不必要的3D加速功能 总结 该漏洞利用展示了如何通过精心构造的堆操作和边界条件缺陷,实现从虚拟机到宿主机的逃逸。关键在于: 利用未初始化内存泄露关键地址 通过堆喷射控制内存布局 利用边界检查缺陷实现堆溢出 通过覆盖关键数据结构获得任意代码执行能力 理解此类漏洞有助于提高虚拟化环境的安全性设计和防御能力。