您现在的位置是:首页 > 编程 > 

QEMU 虚拟机逃逸漏洞(CVE

2025-07-28 15:25:40
QEMU 虚拟机逃逸漏洞(CVE 这是qemu在网络实现的时候的一个指针错误,当重组大量的ipv4分段数据包时会触发错误,这还是大牛通过代码审计发现的,厉害啊。漏洞细节qemu的网络有两部分 1、为虚拟机提供的虚拟网卡(比如PCI网卡) 2、与网络接口控制器交互的网络后端(就是将网络数据包给到主机网络)默认情况下,QEMU将为guest虚拟机创建SLiRP用户网络后端和适当的虚拟网卡(例如e100

QEMU 虚拟机逃逸漏洞(CVE

这是qemu在网络实现的时候的一个指针错误,当重组大量的ipv4分段数据包时会触发错误,这还是大牛通过代码审计发现的,厉害啊。

漏洞细节

qemu的网络有两部分 1、为虚拟机提供的虚拟网卡(比如PCI网卡) 2、与网络接口控制器交互的网络后端(就是将网络数据包给到主机网络)

默认情况下,QEMU将为guest虚拟机创建SLiRP用户网络后端和适当的虚拟网卡(例如e1000 PCI网卡)

而本漏洞是在SLiRP中的数据包重组中出现的错误。

数据包重组那就需要了解ip分片,ip层的结构如下:

代码语言:javascript代码运行次数:0运行复制
 0                   1                   2                   
 0 1 2  4 5 6 7 8 9 0 1 2  4 5 6 7 8 9 0 1 2  4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|  IHL  |Type of Service|          Total Length         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Identification        |Flags|      Fragment Offset    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Time to Live |    Protocol   |         Header Checksum       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Source Address                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Destination Address                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Opti                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

分片在Flags那里,主要是低个bit

代码语言:javascript代码运行次数:0运行复制
Bit 0: 保留为, 必须为0
Bit 1: (DF) 0 = 分片, 1 = 不分片.
Bit 2: (MF) 0 =最后一个ip包, 1 = 后面还有分片
Fragment Offset: 1 bits

下面看下相关的结构体

代码语言:javascript代码运行次数:0运行复制
struct mbuf {
    /* header at beginning of each mbuf: */
    struct mbuf *m_next; /* Linked list of mbufs */
    struct mbuf *m_prev;
    struct mbuf *m_nextpkt; /* ext packet in queue/record */
    struct mbuf *m_prevpkt; /* Flags aren't used in the output queue */
    int m_flags; /* Misc flags */

    int m_size; /* Size of mbuf, from m_dat or m_ext */
    struct socket *m_so;

    char *m_data; /* Current location of data */
    int m_len; /* Amount of data in this mbuf, from m_data */

    ...

    char *m_ext;
    /* start of dynamic buffer area, must be last element */
    char m_dat[];
};

mbuf是储存接收到的ip层的信息。有两个buffer,一个是m_dat ,另一个是m_ext,他是m_dat不足以储存的时候,通过在堆上分配内存解决不足的问题

在nat转换的时候,如果传入的数据包是分片的,则应在编辑和重新传输之前重新组装它们。 这个重组由ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp)函数完成。 ip包含当前的IP数据包数据,fp是包含分段数据包的链表。

ip_reass执行以下操作: 1、如果第一个分配的fp为ULL,创建重组队列并将ip插入此队列。 2、检查片段是否与先前收到的片段重复,然后丢弃它。 、如果收到所有分段的数据包,则重新组装它。 通过修改第一个数据包的头部为新的ip header。

代码语言:javascript代码运行次数:0运行复制
/*
 * Take incoming datagram fragment and try to
 * reassemble it into whole datagram.  If a chain for
 * reassembly of this datagram already exists, then it
 * is given as fp; otherwise have to make a chain.
 */
static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp)
{

    ...
    ...

    /*
     * Reassembly is complete; concatenate fragments.
     */
    q = fp->frag_;
    m = dtom(slirp, q);

    q = (struct ipasfrag *)q->ipf_next;
    while (q != (struct ipasfrag *)&fp->frag_link) {
        struct mbuf *t = dtom(slirp, q);
        q = (struct ipasfrag *)q->ipf_next;
        m_cat(m, t);
    }

    /*
     * Create header for new ip packet by
     * modifying header of first packet;
     * dequeue and discard fragment reassembly header.
     * Make header visible.
     */
    q = fp->frag_;

    /*
     * If the fragments concatenated to an mbuf that's
     * bigger than the total size of the fragment, then and
     * m_ext buffer was alloced. But fp->ipq_next points to
     * the old buffer (in the mbuf), so we must point ip
     * into the new buffer.
     */
    if (m->m_flags & M_EXT) {
        int delta = (char *)q - m->m_dat;
        q = (struct ipasfrag *)(m->m_ext + delta);
    }

这个漏洞在于计算变量delta的值有问题,而上面这个代码假定了第一个分片数据包不会在external buffer中分配(m_ext)。 当分片数据在mbuf-> m_dat(q将在m_dat内)时,计算q-m-> dat有效(q是包含分片链表和数据包数据的结构)。

否则,如果分配了m_ext缓冲区,则q将位于external buffer ,那么delta的计算就是错误的。

q的数据结构是ipasfrag:就是有一个前向指针跟后向指针,以及包含了一个ip头

代码语言:javascript代码运行次数:0运行复制
/*
 * Ip header, when holding a fragment.
 *
 * ote: ipf_link must be at same offset as frag_link above
 */
struct ipasfrag {
    struct qlink ipf_link;
    struct ip ipf_ip;
};

struct qlink {
    void *next, *prev;
};

我们可以调试看看q的某个时刻的状态是怎样的

代码语言:javascript代码运行次数:0运行复制
gdb-peda$ p *q
$0 = {
  ipf_link = {
    next = 0x7f9e08084ed0,
    prev = 0x7f9e0808487c
  },
  ipf_ip = {
    ip_hl = 0x5,
    ip_v = 0x4,
    ip_tos = 0x0,
    ip_len = 0x8,
    ip_id = 0x7fa,
    ip_off = 0x8,
    ip_ttl = 0x40,
    ip_p = 0x1,
    ip_sum = 0x95e,
    ip_src = {
      s_addr = 0xf02000a
    },
    ip_dst = {
      s_addr = 0x202000a
    }
  }
}

可以看到确实是两个ipasfrag前后指针还有ip头信息

我们继续调试运行到下面地方

我们查看下数据结构,可以看到确实此时的q在m_ext的后面,而m_dat在老前面了,那么q - m->m_dat就是负数了

代码语言:javascript代码运行次数:0运行复制
gdb-peda$ p q
$41 = (struct ipasfrag *) 0x7f9e0808882c
gdb-peda$ p *m
$42 = {
  m_next = 0x7f9e080881a0,
  m_prev = 0x7f9e080874c0,
  m_nextpkt = 0x0,
  m_prevpkt = 0x0,
  m_flags = 0xd,
  m_size = 0xcde,
  m_so = 0x0,
  m_data = 0x7f9e08088850 "",
  m_len = 0xc98,
  slirp = 0x56aa67a680,
  resolution_requested = 0x0,
  expiration_date = 0xffffffffffffffff,
  m_ext = 0x7f9e08088810 "",
  m_dat = 0x7f9e08086eb0 ""
}
gdb-peda$ p *q
$4 = {
  ipf_link = {
    next = 0x7f9e0808487c,
    prev = 0x7f9e08087520
  },
  ipf_ip = {
    ip_hl = 0x5,
    ip_v = 0x4,
    ip_tos = 0x1,
    ip_len = 0xc90,
    ip_id = 0x7fe,
    ip_off = 0x0,
    ip_ttl = 0x40,
    ip_p = 0x1,
    ip_sum = 0x1c4,
    ip_src = {
      s_addr = 0xffffff8b
    },
    ip_dst = {
      s_addr = 0x0
    }
  }
}

简单的示意图如下:(忽略了分配了m_ext缓冲区,则q将位于external buffer)

代码语言:javascript代码运行次数:0运行复制
+------------------------------+
|                              |
|                              |
|                              |
|                              |
| m_dat 0x7f9e08086eb0         |
|                              |
|                              |
+------------------------------+
|                              |
|m->m_ext 0x7f9e08088810       |
|                              |
|                              |
|q 0x7f9e0808882c              |
|                              |
|                              |
|                              |
+------------------------------+

之后,新计算的指针q被转换为ip结构并且修改部分字段。由于错误地计算了delta,ip将指向不正确的位置,并且ip_src和ip_dst可用于将我们可控的数据写入错误计算的ip的位置。 如果计算出的ip位于没有映射的内存区域,这就会使qemu崩溃。

代码语言:javascript代码运行次数:0运行复制
slirp/src/ip_:ip_reass
    ip = fragtoip(q);   //转换
    ip->ip_len = next;
    ip->ip_tos &= ~1;
    ip->ip_src = fp->ipq_src;
    ip->ip_dst = fp->ipq_dst;

参考

/

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2019-10-09,如有侵权请联系 cloudcommunity@tencent 删除ipqemu漏洞网络虚拟机

#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格

本文地址:http://www.dnpztj.cn/biancheng/1207275.html

相关标签:无
上传时间: 2025-07-24 13:09:46
留言与评论(共有 6 条评论)
本站网友 中标麒麟操作系统
13分钟前 发表
q); q = (struct ipasfrag *)q->ipf_next; while (q != (struct ipasfrag *)&fp->frag_link) { struct mbuf *t = dtom(slirp
本站网友 有效美白
0秒前 发表
expiration_date = 0xffffffffffffffff
本站网友 一起色
5分钟前 发表
ip_src = { s_addr = 0xffffff8b }
本站网友 门诊病历书写范文
29分钟前 发表
代码语言:javascript代码运行次数:0运行复制/* * Take incoming datagram fragment and try to * reassemble it into whole datagram. If a chain for * reassembly of this datagram already exists
本站网友 彩光去雀斑
13分钟前 发表
创建重组队列并将ip插入此队列