首页
论坛
专栏
课程

[翻译]虚拟机逃逸——QEMU的案例分析(一)

2017-5-31 22:44 4989

[翻译]虚拟机逃逸——QEMU的案例分析(一)

2017-5-31 22:44
4989

题目:虚拟机逃逸——QEMU的案例分析


目录

1.介绍

2.KVW/QEMU概述

  2.1工作区环境

  2.2 QEMU存储配置

  2.3地址转换

3.内存泄露的利用

  3.1漏洞代码

  3.2配置网卡

  3.3漏洞利用

4.堆溢出的利用

  4.1漏洞代码

  4.2配置网卡

  4.3逆向冗余校验码

  4.4漏洞利用

5.总结

  5.1 RIP控制

  5.2 交互式Shell

  5.3 利用虚拟机逃逸

  5.4 局限

6.结论

7.致谢

8.引用

9.源代码


1.介绍

  如今虚拟机广泛应用于个人或企业中。网络安全供应商通过使用不同的虚拟机,实现在一个可控且封闭的环境中分析恶意软件。一个问题自然而然的出现了:这些恶意软件会从虚拟机中逃逸出来并在宿主机上执行吗?

  去年,安全公司CrowdStrikeJason Geffner,已经发布了一系列QEMU漏洞,通过影响虚拟机软盘驱动代码实现让一个攻击者从虚拟机逃逸[1]到宿主机中。虽然这个漏洞已经在网络社区中引起了广泛关注——也许因为它有一个专有名称(VENOM)——但这已经不是第一次出现这种漏洞了。

  2011年,Nelson Elhage[2]发布并成功利用了一个在QEMU虚拟机热插拔PCI设备的漏洞。 利用过程可参见[3]

  最近,奇虎360的刘旭和汪圣平在HITB 2016黑客大会上展示了KVM/QEMU的成功利用。他们在两种不同的网卡设备仿真模型RTL8139 PCNET上,演示了两个漏洞CVE-2015-5165 CVE-2015-7504)的利用。在展示中,他们列举了在宿主机上运行代码的主要步骤,但没有提供任何漏洞利用或技术细节来再现逃逸过程。

  在本文中,我们对CVE-2015-5165(一个内存泄露漏洞)和CVE-2015-7504(一个堆溢出漏洞)进行了深度分析和利用。同时利用这两个漏洞可以实现虚拟机逃逸并在目标宿主机上运行代码。我们讨论了在QEMU的网卡设备仿真上利用这两个漏洞的技术细节,并提供了继续利用QEMU未来出现的漏洞的通用技术。比如一个用来共享内存区域和共享代码的交互式bindshell

2.KVM/QEMU 概述

  KVMKernal-based Virtual Machine)是一个为用户空间程序提供整个虚拟化基础架构的内核模型。它允许运行多个LinuxWindows映像的虚拟机。

  KVM的用户空间部分包含于处理主要设备仿真的主线QEMU(高速虚拟机)。

 2.1 工作区环境

  为了方便读者使用本文中示例代码,我们在这一节给出了搭建开发环境的主要步骤。

  因为我们使用的漏洞已经被修补了,所以需要找到QEMU仓库的源并恢复到漏洞被修补之前。然后,我们配置QEM只支持x86_64并激活漏洞:

  在我们的测试环境中,使用4.9.2版本的Gcc搭建QEMU

  剩下的,我们假设读者已经有一个Linux x86_64映像并可以执行以下命令:

  我们分配2GB内存空间并创建两个网卡: RTL8139PCNET

  我们在3.16单核x_86_64架构的Debian 7上运行QEMU

 2.2 QEMU内存配置

  为用户分配的物理内存实际上是QEMU虚拟地址空间中的一个mmap映射的私有区域。需要指出的是,为用户分配物理内存时PROT_EXEC参数是不可用的。

  下图说明了用户内存和主机内存是如何共存的。


  此外,QEMUBIOSROM分配了一个内存区域。这些映射保存在QEMU的映射文件中:


 2.3地址转换

  QEMU中有两个地址转换层:

  1.从用户虚拟地址转换到用户物理地址。实验中,我们需要配置DMADirect Memory Access,直接内存存取)方式的网卡。例如,我们需要为传送缓冲区和接收缓冲区提供物理地址来正确配置网卡。

  2.从用户物理地址转换到QEMU虚拟地址空间。实验中,我们需要添加虚拟结构并得到它们在QEMU虚拟地址空间中的准确地址。

  在x64系统中,一个虚拟地址由一个页地址(0-11位)和一个页号构成。在linux系统中,pagemap文件允许有CAP_SYS_ADMIN特权的用户空间进程发现每个虚拟页映射到哪个物理框架。为了便于在内核中记录,pagemap文件包含了每个虚拟页的一个64位的值。参考文章[5]

  为了将虚拟地址转换成物理地址,我们使用Nelson Elhage的代码[3]。下面的程序分配了一段缓冲区,用字符串“Where am I?”填充并打印其物理地址:

---[ mmu.c ]---
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <inttypes.h>

#define PAGE_SHIFT  12
#define PAGE_SIZE   (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN     ((1ull << 55) - 1)

int fd;

uint32_t page_offset(uint32_t addr)
{
    return addr & ((1 << PAGE_SHIFT) - 1);
}

uint64_t gva_to_gfn(void *addr)
{
    uint64_t pme, gfn;
    size_t offset;
    offset = ((uintptr_t)addr >> 9) & ~7;
    lseek(fd, offset, SEEK_SET);
    read(fd, &pme, 8);
    if (!(pme & PFN_PRESENT))
        return -1;
    gfn = pme & PFN_PFN;
    return gfn;
}

uint64_t gva_to_gpa(void *addr)
{
    uint64_t gfn = gva_to_gfn(addr);
    assert(gfn != -1);
    return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}
int main()
{
    uint8_t *ptr;
    uint64_t ptr_mem;
    
    fd = open("/proc/self/pagemap", O_RDONLY);
    if (fd < 0) {
        perror("open");
        exit(1);
    }
    
    ptr = malloc(256);
    strcpy(ptr, "Where am I?");
    printf("%s\n", ptr);
    ptr_mem = gva_to_gpa(ptr);
    printf("Your physical address is at 0x%"PRIx64"\n", ptr_mem);

    getchar();
    return 0;
}

  如果在用户模式下运行以上代码并连接gdbQEMU进程,可以看到我们的缓冲区位于分配给用户的物理地址空间上。更确切的说,输出的地址实际上就是基于用户物理内存基地址的偏移地址。



其他部分链接:

http://bbs.pediy.com/thread-217999.htm   虚拟机逃逸——QEMU的案例分析(二)

http://bbs.pediy.com/thread-218045.htm   虚拟机逃逸——QEMU的案例分析(三)


作者: Mehdi TalbiPaul Fariello

原文链接:http://www.phrack.org/papers/vm-escape-qemu-case-study.html

本文由 看雪翻译小组 Green奇 编译



[招聘]欢迎市场人员加入看雪学院团队!

打赏 + 1.00
打赏次数 1 金额 + 1.00
收起 
赞赏  CCkicker   +1.00 2017/07/12
最新回复 (0)
游客
登录 | 注册 方可回帖
返回