Today on Hacker News (where I sadly get much of my news), the post "Linux Local Privilege Escalation via SUID /proc/pid/mem Write" hit the front page. This article 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.
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).
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).
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.
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!
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.
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).
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.
Once compiled (or downloaded pre-compiled), 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.
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).
$ ./mempodroid 0xd7f4 0xad4b mount -o remount,rw '' /system $ ./mempodroid 0xd7f4 0xad4b sh #
- Acer A200 Tablet 4.0.3: 0xd9f0 0xaf47
- Galaxy Nexus 4.0.1: 0xd7fe 0xad57
- Galaxy Nexus 4.0.2: 0xd7f4 0xad4b
- Motorola RAZR 4.0.3: 0xd6c4 0xad33
- Nexus S 4.0.3: 0xd7cc 0xad27
- Transformer Prime 4.0.3: 0xd9ec 0xaf47
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.
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 :(.