深入分析QEMU虚拟机逃逸漏洞
字数 1460 2025-08-03 16:49:46
QEMU虚拟机逃逸漏洞CVE-2019-14378深入分析与利用
漏洞概述
CVE-2019-14378是QEMU网络后端中发现的一个指针计算错误漏洞,当重新组装大型IPv4分段数据包时触发。该漏洞存在于SLiRP用户网络后端的数据包重组代码中,可能导致虚拟机逃逸。
技术背景
QEMU网络架构
QEMU内部网络功能分为两部分:
- 提供给客户机的虚拟网络设备(如PCI网卡)
- 与模拟NIC交互的网络后端(如将数据包推送至宿主机的网络)
默认情况下,QEMU会为guest虚拟机创建SLiRP用户网络后端和适当的虚拟网络设备(如e1000 PCI卡)。
IP分段机制
IP协议在传输数据包时,将数据包分为若干分段进行传输,并在目标系统中重组,称为IP分段(Fragmentation)。分段标志位:
- Bit 0: 保留未用,必须为零
- Bit 1: (DF) 0 = 允许分段,1 = 不允许分段
- Bit 2: (MF) 0 = 最后分段,1 = 更多分段
漏洞分析
关键数据结构
struct mbuf {
struct mbuf *m_next; // 链接列表中的下一个mbuf
struct mbuf *m_prev;
struct mbuf *m_nextpkt; // 队列/记录中的下一个数据包
struct mbuf *m_prevpkt;
int m_flags; // 杂项标志
int m_size; // mbuf大小,来自m_dat或m_ext
struct socket *m_so;
char *m_data; // 当前数据位置
int m_len; // 此mbuf中的数据量
char *m_ext; // 动态缓冲区起始位置
char m_dat[]; // 内部缓冲区
};
漏洞位置
漏洞位于ip_reass()函数中,当处理IP分段重组时:
if (m->m_flags & M_EXT) {
int delta = (char *)q - m->m_dat; // 错误计算
q = (struct ipasfrag *)(m->m_ext + delta);
}
问题在于:
- 代码假设第一个分段数据包不会被分配到外部缓冲区(m_ext)
- 当数据包数据位于mbuf->m_dat中时,
q - m->dat计算正确 - 如果分配了m_ext缓冲区,q位于外部缓冲区中,delta计算错误
漏洞触发流程
- NAT转换时,如果传入数据包是分段的,首先需要重组
- 重组由
ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp)完成 - 重组步骤:
- 第一个分段到达(fp == NULL)时创建重组队列并插入ip
- 检查是否与先前分段重复
- 收到所有分段后重组数据包
- 修改第一个数据包的头部创建新IP数据包头部
漏洞利用
堆布局控制
利用堆喷射技术控制内存分配:
malloc(0x670) // mbuf对象
if(pkt_len + TCPIPHDR_DELTA + 2 > 0x608)
malloc(pkt_len + TCPIPHDR_DELTA + 2) // 外部缓冲区
if(ip->ip_off & IP_MF)
malloc(0x670) // 分段队列
任意写操作实现
- 发送ID为"0xdead"且MF位为1的数据包
- 发送ID为"0xcafe"且MF位为1的数据包
- 触发漏洞覆盖0xcafe数据包的m_len,使m_data+m_len指向0xdead数据包的m_data
- 发送ID为"0xcafe"且MF位为0的数据包,触发重组并用目标地址覆盖"0xdead"数据包的m_data
- 发送ID为"0xdead"且MF位为0的数据包,将其内容写入m_data
数据泄露技术
利用ICMP回应请求服务泄露数据:
- 通过任意写操作在堆上创建伪ICMP报头
- 发送设置了MF位的ICMP请求
- 部分覆盖m_data指向伪造的报头
- 发送MF位为0的数据包结束ICMP请求
- 接收从宿主机泄漏的重要数据
代码执行实现
利用QEMU定时器机制:
struct QEMUTimer {
int64_t expire_time; // 纳秒
QEMUTimerList *timer_list;
QEMUTimerCB *cb; // 回调函数
void *opaque; // 参数
QEMUTimer *next;
int scale;
};
struct QEMUTimerList {
QEMUClock *clock;
QemuMutex active_timers_lock;
QEMUTimer *active_timers;
QLIST_ENTRY(QEMUTimerList) list;
QEMUTimerListNotifyCB *notify_cb;
void *notify_opaque;
QemuEvent timers_done_ev;
};
攻击步骤:
- 创建伪造的QEMUTimer,设置回调函数为system,参数为命令
- 创建伪造的QEMUTImerList包含伪造的QEMUTimer
- 使用伪造的QEMUTimerList覆盖main_loop_tlg的元素
防御建议
- 修复delta计算逻辑,正确处理m_ext情况
- 添加对m_len的边界检查
- 启用QEMU的所有安全保护机制(ASLR, PIE等)
- 限制虚拟机网络访问权限
- 及时更新QEMU到修复版本