Saarland University Trusted Systems Group Dr.-Ing. Sven Bugiel

saarland university

computer science

Android Security SS 2016 Exercise 6: IRM M.Sc. Oliver Schranz

Android Security SS 2016

Exercise 6: Inline Reference Monitoring

Introduction In the previous lab sessions, you have already modified applications. Today, we reuse this knowledge to circumvent a protection mechanism and to inject an inline reference monitor into an Android app.

1

Modifying an Android app

In the first exercise, we will again use apktool. The tool is able to decode the compiled dex bytecode of an Android app into a human-readable representation called smali 1 . In this format, you will be able to make changes to the code of the app using a text editor. Finally, you will repackage and sign the modified application. We have created a minimal Android app LabTarget that we will modify in this exercise. The app features a simple (an insecure) password protection to keep unauthorized users at bay. When starting up, the app displays a password prompt and will automatically close if the entered password is not correct. You are going to remove this password check in order to use the app in the second exercise. Let’s have a look at the app by installing it in the emulator. 1. Start your emulator with the Android 4.3 stock image. For the second exercise, it is important to choose the ARM platform, even though it is slower than x86 emulation. Please also make sure that you have enabled back camera emulation. 2. The app LabTarget.apk is readily available. 3. Install the app in the emulator with the adb tool. Check the output to verify that the app has been successfully installed. ~/lab5/apps$ adb install LabTarget.apk

4. Run the app in the emulator. As you can see, the app is not usable without the correct password. One approach to circumvent the password check would be to directly extract the password from the app’s binary source. However, the app developer has chosen not to store the password in plaintext in the source code, making this approach difficult. You will take another route: Remove the call to the checkPassword() function from the code. 5. Use apktool to decode the app’s application package. We are only interested in the code, thus we skip the decoding of resources (Strings, Layout Files, Images, ...) with the -r switch. Check the output for errors. $ apktool d -r LabTarget.apk

After decoding, you should have a folder LabTarget in your current working directory. This folder contains the decoded bytecode in the smali directory, raw app resources in the res directory and the AndroidManifest.xml file. The smali directory is structured according to the original Java package structure and contains a .smali file for each Java class. 6. Find and open the file MainActivity.smali in your favourite text editor. 7. Towards the end of the file, you will find the onCreate() method of the Activity as shown below. As you might remember, onCreate() is called when the activity is first created. 1 More

information on the smali format can be found in the project wiki https://code.google.com/p/smali/w/list

–2–

Android Security SS 2016

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

Exercise 6: Inline Reference Monitoring

.class public Lde/uds/infsec/aslabtarget/MainActivity; .super Landroid/app/Activity; .source "MainActivity.java" ... # virtual methods .method protected onCreate(Landroid/os/Bundle;)V .locals 1 .parameter "savedInstanceState" .prologue .line 33 invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V .line 34 const/high16 v0, 0x7f03 invoke-virtual {p0, v0}, Lde/uds/infsec/aslabtarget/MainActivity;->setContentView(I) V .line 36 invoke-direct {p0}, Lde/uds/infsec/aslabtarget/MainActivity;->checkPassword()V .line 38 invoke-direct {p0}, Lde/uds/infsec/aslabtarget/MainActivity;->init()V .line 39 return-void .end method

Closer inspection of the onCreate() reveals as function call (invoke-direct) to the method checkPassword() in line 22. As the name indicates, this is the function that implements the password protection. 8. Remove or comment out the call to the checkPassword() function in line 22. 9. Re-package the modified app using apktool. Save your modified app as LabTarget-patched.apk. Check the output for errors. $ apktool b LabTarget/ LabTarget-patched.apk

10. Finally, sign the modified app with your Android debug key. The default keystore password is android. $ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ~/.android/debug. keystore LabTarget-patched.apk androiddebugkey

11. Test if your modifications were successful by installing your patched app in the emulator. Since the signature has changed, you will have to uninstall the original app first. s$ adb uninstall de.uds.infsec.aslabtarget $ adb install LabTarget-patched.apk

12. Run the app in the emulator. The password prompt should not appear now.

–3–

Android Security SS 2016

2

Exercise 6: Inline Reference Monitoring

Removing permissions from Android apps

With the password check removed, you are now able to access the app’s awesome functionality. As you might have noticed already, the LabTarget app requests three Android permissions: CAMERA, INTERNET and ACCESS_FINE_LOCATION. All the app does is probe for the presence of these Android permissions. Go ahead and click the Check permissions button; you will see that all tests come back positive. The goal of this second exercise will be to gracefully revoke these permissions: We want to prevent the app from using permission-protected API functions but still allow it to continue running when an illegal access is made. Simply removing the permissions from the AndroidManifest.xml file will not work, as the Android middleware will throw a SecurityException when the app executes permission protected API functions without holding the corresponding permission. Instead, we will alter the app’s code to redirect and suppress calls to critical Android API functions. In the first exercise, you have seen that it is actually quite simple to modify the code of a compiled Android application. However, manual modifications to the decompiled bytecode are tedious and errorprone. In this second exercise, you will use code from AppGuard, a research prototype developed at Saarland University. AppGuard can automatically merge additional Java code into an existing Android app and direct the control flow towards your code when the app starts. Furthermore, it provides an instrumentation library that lets you intercept calls to certain Android API functions, enabling you to suppress undesired function calls and to return "fake" return values instead. In our case, you will implement a very simple Inlined Reference Monitor that will always prevent access to the Camera, Internet and Location. 1. As a starting point, we provide you with a monitor stub project called monitor. Please start Android Studio and import this project into your workspace. 2. Your monitoring code will be added to the de.uds.infsec.monitor.Monitor class. Please open the corresponding file. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

package de.uds.infsec.monitor; import android.app.Application; public class Monitor extends Application { static { // API Instrumentation goes here } @Override public void onCreate() { // Called when the application starts super.onCreate(); } }

AppGuard will register this Monitor class as the main application class such that it is the first code executed when the app starts. The Monitor will then instrument a number of API functions and redirect them to itself. The usage of the instrumentation API is exemplified in the following snippet. 1 2 3 4 5 6 7 8 9 10

package com.example.test; public class Test { public Test() { Test.foo(); // prints "foo() called" int orgMethod = Instrumentation.redirectMethod( "com.example.test.Test->foo", "com.example.test.Test->bar"

–4–

Android Security SS 2016

11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

Exercise 6: Inline Reference Monitoring

} Test.foo(); // prints "bar() called" Instrumentation.callStaticVoidMethod( orgMethod, Test.class); // prints "foo() called" } public static void foo() { System.out.println("foo() called"); } public static void bar() { System.out.println("bar() called"); } }

In this snippet, the static function foo() is redirect to the function bar(). Three things are important to note: First, function foo() and function bar() need to have identical (or compatible) signatures. Second, when calling the original function, you have to use the appropriate Instrumentation.call*Method() function, depending on the return type of the function hooked. Third, the redirection target must be a static function. Let’s have a look at another example to see how to redirect virtual functions. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

package com.example.test; public class Test { public Test() { this.foo(); // prints "foo() called" int orgMethod = Instrumentation.redirectMethod( "com.example.test.Test->foo", "com.example.test.Test->bar" } this.foo(); // prints "bar() called" Instrumentation.callVoidMethod( orgMethod, this); // prints "foo() called" } public void foo() { System.out.println("foo() called"); } public static void bar(Test _this) { System.out.println("bar() called"); } }

In this snippet, the function foo() is virtual, i.e. it is invoked on an object (this). The object is implicitly passed to the function as the first argument. In order to redirect to the function bar(), the bar() function needs to declare this object as its first argument. Finally, note that the call to Instrumentation.call*Method() has changed. Now, let’s begin by instrumenting the first Android API function: android.hardware.Camera->open. Because we want the redirection to occur as early as possible (and only once), we will put the redirectMethod() calls in the static initializer of the Monitor class. 3. Declare a static int field called org_Camera_open. 4. Define a redirection target function Camera_open(int). The original API function is static, thus the redirection target does not need to declare the object parameter. The method must public and static and return a android.hardware.Camera object. It should log the arguments passed to the function and then call through to the original implementation. –5–

Android Security SS 2016

Exercise 6: Inline Reference Monitoring

5. Add a call to redirectMethod() in the static initializer which redirects Camera->open to your target function Monitor->Camera_open. Store the return value in the org_Camera_open field. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

... public class Monitor extends Application { private static int org_Camera_open; static { org_Camera_open = Instrumentation.redirectMethod( "android.hardware.Camera->open", "de.uds.infsec.monitor.Monitor->Camera_open"); } ... public static Camera Camera_open(int cameraId) { Log.d(TAG, "Camera.open() called: cameraId=" + cameraId); return (Camera) Instrumentation.callStaticObjectMethod(org_Camera_open, Camera.class , cameraId); } }

6. Build your monitor. You can find its apk file under app/build/outputs/apk/app-debug.apk. 7. Extract your monitor’s classes.dex file from the monitor’s apk file and rename it to monitor.dex 8. Use the provided inliner tool to merge your monitor into the patched LabTarget app. The first argument is the target app, the second argument is the monitor dex file, the third argument is the monitors main class, the fourth argument is the keystore and the fifth argument is the directory that contains the native libraries needed by the hooking library. $ java -jar inliner.jar LabTarget-patched.apk monitor.dex de.uds.infsec.monitor. Monitor ~/.android/debug.keystore LabTarget-inlined.apk ./libs/

9. Install the inlined app in the emulator and run it. Press the "Check permissions" button and check the Android log for the call to the instrumented Camera->open function. ~/lab5/monitor$ adb install -r LabTarget-inlined.apk

10. Now, let’s modify our Camera_open function to simulate that no camera is present on this device. We accomplish this by returning a null object instead of the Camera object returned by the original implementation. 1 2 3 4 5 6 7 8

... public static Camera Camera_open(int cameraId) { Log.d(TAG, "Camera.open() called: cameraId=" + cameraId); return null; } ...

11. Rebuild your monitor, extract the dex file, re-run the inliner tool and install the newly instrumented app onto the device. Whenever you make changes to your monitor, you will have to execute these steps. –6–

Android Security SS 2016

Exercise 6: Inline Reference Monitoring

12. Run the app in the emulator. The test for the Camera permission should now give a negative result. Now that you have seen how to instrument Android API functions, complete this exercise by instrumenting the following functions: 13. Instrument the java.net.URL->openConnection() function to prevent access to the internet. Note that this is a virtual function that returns a java.net.URLConnection object. To suppress the connection, throw an IOException in your redirection target. This will simulate a network error to the instrumented app. Throwing an exception is ok in this case as IOExceptions are checked exceptions and therefore expected to occur. 14. Instrument the android.location.LocationManager->getProviders(boolean) function to prevent access to the location of the device. This is a virtual function that returns a List of Strings representing the names of the available providers. Simulate that no providers are present by returning an empty list. 15. Deploy your modified app to the emulator to see the effect of your instrumentation. All permissions tests should now come back negative.

3

Bonus exercise 1. Instrument the org.apache.http.impl.client.DefaultHttpClient->execute() function to block another route to access the internet. To suppress the connection, throw an IOException.

4

Bonus exercise II Question 1: Whats the password for the LabTarget app?

–7–