Monday, December 14, 2015

Attacking Bound Services on Android

Many times when exploring a new Android device, I encounter exported Services that expose a Binder interface, known as "bound Services".  Unlike an unbound Service, we can't just use the `am` command to interact with the Service; rather, we'll need to actually create an Android application.  Creating a client to connect to a bound Service can be tricky even with the Service's source code, and without it can be even more confusing. There are plenty of blogs and tutorials that talk about creating bound Services, but I couldn't find any focus on interacting with bound Services from an attacker's perspective (no source).

In this post I'll create an application that exposes a bound Service, show you how to find and interpret bound Services in an application's disassembled Smali code, and present a tool to automate AIDL reconstruction.  This post will not be an in depth description of bound Services.

Creating the Bound Service in Java

First, we'll create an application, "com.jakev.boundserver" that defines our exported bound Service. This bound Service will support a single method, called "exec()", that accepts a command as a String, and executes it, and returns the output (please do not actually add this to your applications). The source code for this project (and APK) can be downloaded from the links at the end of this post.

We'll create the bound Service in 3 steps:
  • Declaring the new component in the "AndroidManifest.xml"
  • Creating the Android Interface Definition Language ("AIDL") interface
  • Creating a new Service class and implementing the "onBind()" method

In our AndroidManifest.xml we declare a new exported Service, ".ITestService":



Next, we create our AIDL interface, called "com.jakev.boundserver.aidl.TestInterface".  As described above, our "exec()" method will accept a single String argument, and return a String value:


Last, we create our Service class "com.jakev.boudserver.ITestService.java".  We need to implement the "onBind()" method, and return an instance to our "TestInterface" interface created above:


In order to interact with this Service, we'll need to create an application and include the AIDL file above.  As a developer, we'd probably distribute a JAR library that contains the AIDL file "TestInterface.aidl" and other supporting classes so that others can develop applications to interface with our new Service. As an attacker, we will likely not have this luxury.

Exploring the Service APK

Let's assume we find the "com.jakev.boundserver" application installed on a device, and stumble on this exported ".ITestService" Service.  We don't have the source code for the application; we just have the APK.  Let's pull the application and unpack it with `apktool`:


As a first step, let's look our ".ITestService" Service class, found at "smali/com/jakev/boundserver/ITestService.smali".  The "onBind()" method is pretty straightforward, and we can see that the Service is instantiating an inner class, "ITestService$1", and returning it.  This is due to the way we called the "new TestInterface.Stub() { }" above.


Opening "smali/com/jakev/boundserver/ITestService$1.smali" shows that the class is implementing a class "com.jakev.boundserver.aidl.TestInterface$Stub", which we have not looked at yet.  In the virtual methods section we see the actual implementation of the "exec()" method.  As a reverse engineer, you'd likely start with this method for determining the capabilities of the "exec()" method.


Next, let's look at what happened to our AIDL file.  There are now 3 different classes, called "TestInterface.smali", "TestInterface$Stub.smali", and "TestInterface$Stub$Proxy.smali", shown below.


Android Studio was nice enough to generate these for us when we built our application. There are plenty of papers that talk about the exact role of each of these, so I wont spend a lot of time talking about that.  The key takeaways here are:

  • The "TestInterface" class will always implement the "android.os.IInterface" class. It contains an abstract definition of each of the Binder methods.
  • The "TestInterface$Stub" class will always implement the "android.os.Binder" class, and contains the Binder transaction identifiers stored as fields.  The naming convention looks like "TRANSACTION_{methodName}".  These are the same ID fields used when calling services with the `service` command-line utility.
  • The "TestInterface$Stub$Proxy" class doesn't implement or extend any class, but contains the return type and parameter information for each Binder method.
You can use the following searches for if you're looking at a new application and want to enumerate Binder interfaces and their method implementation:

Finding any AIDL classes:
grep -r "super.*IInterface" ./smali

Finding implementation of AIDL:
grep -r "super.*${AIDL class from above}" ./smali

With these pieces of information, we can reconstruct the AIDL, and include this in our client application. You can do this manually, or you can use a tool I've created called 'GenerateAIDL.jar' that uses the DexLib2 library to parse an application's DEX code and outputs AIDL files that you can quickly add to your Service client.


If we open this file, we should see a AIDL interface similar to the one we created above:


Keep in mind this is just a trivial example.  There likely will be more than one method (and possibly some imported libraries) in a real-world example.

Attacking Bound Services

Now we're going to use the AIDL we just generated above to create a client application, "com.jakev.serverclient" that interacts with the ".ITestService".  I've tried to make this project as generic as possible, so it can be reused with ease.

The first step is to actually create/import our AIDL.  We want to add the AIDL we generated above to the proper folder, being mindful of the package name:


Next, we'll modify the "MainActivty" class I've created as a template.  First, add the AIDL above as an import, and add an object of this class called "service":


Next, change the class name of the Intent created in the "initService()" method, to the service you're interacting with:


Finally, we modify the "onServiceConnect()" method of our local "ServiceConnection" class to actually interact with the bound Service.  In this example, we're calling the "exec()" method, and supplying the "id" command, then recording this value in the Android logs.


If we build, install, and run our client application, we should see the output of the `id` command in the logs:


Conclusions

Given the correct permissions, an attacker can interact with bound Services just like other components.  It is important to inventory your application and confirm that the components that are exported are the ones you actually want exported.  For any component that performs sensitive actions (such as executing commands!!), make sure you apply the correct permissions.

Resources

  • http://stackoverflow.com/questions/15330233/remote-service-with-aidl-file-serviceconnection-never-called
  • http://developer.android.com/reference/android/content/ServiceConnection.html
  • http://developer.android.com/guide/components/bound-services.html
  • http://developer.android.com/guide/components/aidl.html
  • https://newcircle.com/s/post/48/implementing_remote_interface_using_aidl

Downloads

Friday, May 29, 2015

Hooking SQLCipher with Xposed

Recently, a coworker of mine approached me regarding an Android application that was using the SQLCipher library.  For those unfamiliar with SQLCipher, it provides "transparent, secure 256-bit AES encryption" of Android SQLite3 databases, which is something that many security professionals recommend (myself included).  In order to see the actual data inside the database, it is often necessary to obtain the password used to perform the encryption, which can be tricky if it is not just in plain-text in the APK.

@MDSecLabs wrote a blog post a little over a year ago that describes how the SQLCipher library works, and how it is possible to obtain database encryption keys using the CydiaSubstrate, a hooking framework.  They also provide a Cydia "tweak" that can be used to steal the SQLCipher encryption key used by an application (for testing purposes), which is exactly what my coworker was interested in!

Unfortunately, the CydiaSubstrate doesn't always work on all devices (in my experience), and doesn't overall seem as maintained as the Xposed framework, which accomplishes roughly the same goal.  After downgrading his test device from Lollipop (to Jelly Bean) and installing the MDSec Cydia tweak, he was still unable to get the secret key to print in the log buffers, even though the test application provided by MDSec worked fine.  What gives?

It turns out that the SQLCipher library has changed a bit since MDSec created their tweak, and there are now new overloaded variants of the "openOrCreateDatabase" method, which is what MDSec is attempting to hook (you can see the current methods here).  Depending on how the application you're testing uses the SQLCipher API, you may see the key printed in the logs.

I did some quick Googling and did not see an Xposed version of the MDSec tweak, so I decided to write my own variation.  To use it, you'll need a rooted device and the Xposed framework installed.  You can download the APK from my GitHub.

Now, you can install the APK, reboot your device, and use your SQLCipher enabled app as you normally would. In a different window, check the logcat buffers for the secret key used by the application.  Here are the contents of the log buffers when running the MDSec MyEncryptedApp test application.


Thanks to MDSec for doing the research in determine the appropriate method to hook and for creating the Cydia tweak.  If anyone has any questions or feature requests, feel free to reach out to me!

Links

Monday, February 3, 2014

Is Facebook Really Reading your Text Messages?

The Internet was abuzz last week with news concerning Facebook's recently added permission to access your text messages from their Android application.  This didn't seem to sit well with a lot of people, and now people are afraid to update the application or are choosing to remove the mobile application altogether.  I was shocked to find at least half a dozen news articles talking about how Facebook is now "reading your texts, pictures, and calendars," with absolutely no technical backing besides the fact the application now asks for the permission.  In fact, the only thing remotely close to technical was Facebook's response to whole thing, where they explain why their app does this:


But of course in the midst of all the NSA and related news, everyone put their tinfoil hats on and is a bit skeptical on the whole deal.  I do think it's fine to question things like this, but there does need to be some closure on the whole thing.  I decided to take a look at the Facebook mobile app and see whats really going on.

Before going any further I'd like to make a few things clear.  First, I don't work for Facebook, and I'm not trying to bash them in any way.  I also have no malicious intent of exposing the company; I'm simply trying to demonstrate on a technical level what this change means.  If you're someone who doesn't care for all the technical fun stuff, skip right to the conclusions section.

So with that, let's get started!

I first downloaded the most recent version of the Facebook mobile application (version 5.0.0.26.31) to a test device, and then pulled the application to my testing machine.  A quick check confirmed that this application does in fact ask to read SMS messages:


What does this mean?  This means that the application is permitted to access the Android ContentProvider URLs "content://sms", "content://mms," and "content://mms-sms," the databases containing your SMS and MMS data.  It does not allow the application to intercept incoming text messages or send them (regardless of what Kaspersky claims).  These actions require the permissions "android.permission.RECEIVE_SMS" and "android.permission.SEND_SMS," respectively.  This application did not ask for permission to do these actions.

I unpacked the APK with apktool and was surprised to find that the application did not obfuscate the class names.  It did obfuscate class fields and methods (standard issue ProGuard), probably to save space.  It also did not appear to use any string encryption or obfuscation, which made my job a lot easier.

As a first test, I wanted to install a modified version of the APK with debugging, so I changed the "android:debuggable" attribute to "true" in the "AndroidManifest.xml" file.


When I attempted to rebuild the APK, I was greeted by a bunch of resource repackaging errors, due to missing dependencies in the "framework-res.apk" I was using to repack the application.  This is pretty common in large applications that are developed with proprietary resources.  I won't go into the details (you can learn more here), but with a few changes to the "/res/values/styles.xml" file I was able to rebuild it successfully.

I then installed the new APK and attempted to launch the application, and was presented with a message indicating that the application has crashed.  I checked the Android logs and saw this error:


The message was strange, since I don't recall that being a feature of Android.  I did a quick string search for the error and found that in the method "c()V" of the class "smali/com/facebook/katana/app/FacebookApplicationImpl.smali" there was a quick check to make sure the "android:debuggable" flag was not set to true, which is actually a good security feature!  If the flag was set to true, the application threw a RuntimeException and the application crashed.


To get by this, I simply removed the check, and repackaged the application.


The application could now be installed without errors.

I switched gears and started running some searches on the application's disassembled class files.  Since I knew the application was not encrypting strings, searches for "mms","sms," "sms/inbox", "content://sms" should have returned something interesting.  Instead, I found absolutely nothing interesting. Weird.  I considered maybe the application asked for the permission, but didn't use it (yet), but that seemed unlikely.

I decided to rerun the app on my device and check the logs. As the app loaded, I noticed something very interesting - the application was copying additional DEX files, and loading them!


I've personally never seen this, but it appears that the application copied three additional XZ compressed assets, uncompressed them, and then used a DexClassLoader to load them.  These 3 DEX files also contained "canary" checks, but it was still possible to modify the classes and rebuild.  Here are the files:


I decompressed and disassembled all three, and realized there were now over 10,000 new classes to search through.   I repeated my search above, and this time found something a bit more interesting.


We now have a few leads to look at.  In particular, we have the class "/secondary-2/com/facebook/confirmation/task/ReadSmsConfirmAccountBackgroundTask.smali" and the classes "/secondary-1/android_src/provider/Telephony*.smali."  I'll start with the class "ReadSmsConfirmAccountBackgroundTask.smali."

The file "/secondary-2/com/facebook/confirmation/task/ReadSmsConfirmAccountBackgroundTask.smali" contained the string "content://sms/inbox," which proceeded a query to the SMS content provider.  This was a good starting point.


This class is actually quite large, and makes several obfuscated calls to other classes.  I rewrote the class in Java to help me understand what was going on.  For this post, I'll just write some (oversimplified) pseudo-code:


Even though this example is oversimplified, it still demonstrates what the method does.  This method iterates over your text messages, first checking the date (to make sure it's within a threshold), then checks the address to make sure it's from a Facebook sender, and finally it performs a regex on the body.  It adds all potential confirmation codes entries to a list and returns them (note that "m.group(1)" will only return the confirmation code, not the message body).  I did not analyze what the Facebook application does with the values more than two method calls up.

Now the question is: when does this get called?  To help me figure this out, I added a quick Log statement to the method that queries the SMS database.  For the entirety of my testing, I ran the command "adb logcat | grep "TESTING".  If the Facebook app tried to read your SMS, I should see it in the logs.


In addition, I ran my tests from a dev version of CobraDroidJB, which reported all ContentProvider queries to a designated log buffer (commit).  I then tried just about ever combination of logging in, logging in with two factor, logging in from an unrecognized device, etc.  No evidence that our watched method above was called, or that the SMS/MMS databases were accessed.  I tried it from two devices, and a test version of CobraDroidJB; the results were the same in all cases.  I even installed and logged into the Facebook Messaging application just in case that mattered.


This was me adding my phone number and confirming it.


Even though I used the "Login Approvals"  setting which required a confirmation code, I would have to manually enter it.  You can even see in the second picture that I have the unread text message (with my code!).



In my testing, I was unable to make Facebook access my SMS or MMS.  I'm not saying its not possible, but I tried several different devices and settings and was unable to make the app call this particular method.

Not satisfied, I dug around the code surrounding the SMS query method and found mentions of a property called "read_sms_bg_account_confirmation" which seemed interesting.  This parameter is stored in the Sqlite3 database file "/data/data/com.facebook.katana/databases/prefs_db," and is not populated until a user authenticated to Facebook.  No matter what my account settings were, this value was always set to 0 (the "2" is the type).


Even when I manually set this value to 1, the application still did not read my SMS.  At this point I decided to call it a day.  If reading my SMS was a "feature" of the new app, it was pretty difficult to use this "feature."

The second instance of the strings "sms" found in the secondary DEX files was the file "/secondary-1/android_src/provider/Telephony$MmsSms.smali".  This class contained static SMS and MMS URI strings for other classes to use.  Here is what the class "Telephony" might look like in Java:


Did any other class actually use this class?  It depends.  I looked at two iterations of the Facebook Mobile application version 5.0.0.26.31 - One from a GingerBread device which was version code 1056406, and one from a JellyBean device which was version code 1056414.  If you installed 5.0.0.26.31/1056406, the application contained the class "/secondary-2/com/facebook/contacts/upload/events/ContactInteractionEventsFetcher.smali," which accessed the "Telephony" class above; however, if you used the more recent 5.0.0.26.31/1056414, no classes used this class.  In my testing of version code 1056406, I was not able to get this class to be called, so I believe this was testing, deprecated, or experimental code.

Conclusions

Overall, the media coverage on this topic is quite misleading and borderline fear mongering.  Here are some facts:
  • The Facebook application did contain code to access your SMS and MMS messages.
  • The application contained code to search the SMS database between select time-frames, for select senders, and 4-10 length numbers in the message body.  Only the matched number was returned (not the whole text message).
  • Some versions of the application did contain additional code to read your SMS/MMS database for a specified phone number.
  • The Facebook application does not routinely read your SMS.
I'm not entirely convinced this feature is completely implemented. If it is implemented, it would seem difficult to cause the application to actually use it.  If anyone has experience with this feature and can say "yes, it definitely is enabled," I would love to hear it!

I also want to point out this analysis only pertains to a single version of the application.  Facebook may very well decide to use this permission in the future, so remember that!  They could also add class name and string obfuscation, which would make reverse engineering the application significantly more difficult.

Monday, July 29, 2013

An Introduction to CobraDroid 1.0

I'm finally happy to say that the beta for my ongoing project called "CobraDroid" is fully released!  The project was actually released back in March on my website, but the source was just pushed to GitHub recently.  I wanted to talk a little about what CobraDroid is, what it can do for analysts, and what you can expect in the upcoming months.

What Can CobraDroid 1.0 Beta Do?

CobraDroid is a modified Android emulator image designed for individuals who want to perform application security assessments or analyze malware for the Android platform. It is not a service where you upload an application and receive a summary of activity, and it is not a black-box (there are plenty of those out there).  It should help you with performing your analysis, but keep in mind it will not perform all the analysis for you. It is released under the Apache Software License 2.0, so you are free to download, modify, and use the source.  The beta is mostly a teaser for what is to come, but it includes:
  • Configurable radio values (MIED, MDN, IMSI, SIM card serial number, and voicemail number)
  • Dynamically configurable “build.prop” values
  • Configurable SSL certificate validation bypassing 
  • Enhanced proxy capabilities
  • Additional user-space utilities
This should be enough to get you started, and already provides more functionality than the standard Android emulator.

What's Next?

The beta version of CobraDroid 1.0 is still lacking in terms of functionality and features.  Luckily, I've been hard at work on lots of new features.  I don't want to give away all the fun, but from a high level you can expect 1.0 to include:
  • A newer, hand-ported kernel (say goodbye to the 2.6.29 Goldfish kernel)
  • Full Bash shell + Busybox
  • Application specific packet-capturing
  • Java bytecode instrumentation
  • Mercury and LiME integration 
  • ..and more!
Many of these features are actually already implemented, but in a state of QA.  CobraDroid 1.0 should be ready to roll by the end of the summer, so stay tuned!

BruCON 2013

If by chance you are attending BruCON 2013, come check out my talk on CobraDroid and see a demo of the tool!  Hopefully I'll be presenting the tool in the states soon as well sometime soon.

If you are interested in trying CobraDroid, check out the getting started page.  Of course, feedback and comments are always welcome.

-jakev

Monday, April 15, 2013

Fixing Resource Identifiers in Disassembled Apps

If you have ever attempted to reverse an Android application, you've probably noticed that resource identifiers (think "R" values) show up as cryptic constants in disassembled smali code. This can be frustrating when attempting to reverse and understand the application's flow.  A rather contrived example of this issue is demonstrated below.

Imagine we write an application with a single activity. This activity simply inflates and shows a defined layout, which I've called "my_layout".


We defined this layout in our "res/layout/" directory. Its contents are not really important, but they are listed below anyways.


Once we start reversing this application, we will likely use apktool (or just baksmali) to disassemble the DEX bytecode. If we then examine the disassembled "ExampleActivity.smali" file, we notice that the "onCreate" method looks as follows:


What does the value 0x7f03 mean? What I didn't show before was that while we were coding away, Eclipse was helping us by bridging the gap between our resource files and our Java sources with the "R" class. This class creates an anonymous class for each resource type, and sets an integer value for every resource we define. We can then access our resources in our Java code with the syntax like "R.layout.my_layout", or "R.string.secret_message".  When compiled, this value is replaced with the integer value.

To make the mapping, we could inspect the various R$*.smali anonymous classes, but there is actually a better place to find these value. These values also appear in the "res/values/public.xml" file created by the Android application packaging tool ("aapt") during the build.


Since "public.xml" is an XML file, we can parse it using python with relative ease. This is where "fix_resources.py" comes in. "fix_resources.py" will parse the "public.xml" file, then search for instances of resource identifiers in our smali sources.  It will append a useful string on these lines, to make our job a little easier.


It will also resolve any string-type resource identifiers from the "res/values/strings.xml" file.  Let's say that we changed our "onCreate" method to copy a string from a resource file (no idea why you would want to do this), and print the value to the log:


Now, when we run apktool and "fix_resources.py" on our app, we will see that "fix_resources.py" has actually included the string's value in the code (it will truncate the string if it is too long).


I don't expect many applications to use this technique to obfuscate or hide sensitive strings, but overall "fix_resources.py" is a quick and painless way to add some addition annotations to your disassembled code.  Because the modifications are comments, apktool/smali will have no problem repackaging the code if you want to modify and rebuild the application.

"fix_resources.py" requires the lxml python module to run, but it's a pretty straightforward install.  You can grab the "fix_resources.py" script from my GitHub account.  Feel free to report any bugs or comments with the code!

-jakev

Thursday, October 4, 2012

Building a Better Emulator - Part 1

I prefer using the Android emulator to a real phone for some application assessments for many reasons.  Unfortunately, not all apps run properly on the emulator.  This series of posts will focus on modifications we can make to our emulators to allow more control and flexibility during application assessments.  This first post will focus on controlling device identifiers and numbers.

Sometimes an application will obtain the device phone number and device identifiers and submit these to a remote server, maybe for authentication or authorization (such as white-listing phone numbers per provider).  The easiest way to obtain this information in an application is the use the TelephonyManager class.  We will focus on the following methods of the TelephonyManager class:
  • getDeviceId()
  • getLine1Number()
  • getVoiceMailNumber()
  • getSubscriberId()
  • getSimSerialNumber()
Some details about how this class works can be found here.  Basically these methods are used to access some values that are stored in the base-band emulator.  On Linux, the emulator is a file called  "$ANDROID_SDK_HOME/tools/emulator-arm" and the values are contained in this binary.  By default, the device ID (on the emulator it's the IMEI) is hard-coded as 000000000000000, and our phone number is  1555521%s, where "%s" is the 4 digit TCP port that the emulator is running on.  If we want to control these values on the emulator we are out of luck (without doing some modifications).  It is possible to do a find/replace in the "emulator-arm" binary for the IMEI and this post provides a patch to recompile the "emulator-arm" file to allow for these values to be set in a configuration file which is another alternative.  But even with this patch we can not change the phone number and voicemail number.  The method in this post will involve modifying some Android system libraries to allow for control of these values.

We have two options for performing the modifications: 1) Download the Android source code from the AOSP page, make the modifications in the Java source, and then build a custom version of Android, or 2) we can make the modifications to the compiled system libraries that are present on our Android emulator.  I chose to take option two because of the hardware requirements and testing speed of options one (building Android can take awhile).  So the first step was to find the TelephonyManager files in the Android source.  Note: For the remainder of this post, I will be using Android API 10.

 Not hard to find, they're in the "/frameworks/base/telephony/java/android/telephony/" directory.


Being in the "framework" directory gives us some hints as to where the files exist on the device.  When we build Android, all of these sources will eventually make their way into Dalvik DEX byte-code, and are zipped (but with JAR extension + META-INF directory) along with any other necessary files. They are placed in the "/system/framework/" directory on the device.


When the system boots, the Android system will take the "classes.dex" from each of these system library JARs, verify and optimize them, then store them on the "data" partition in the directory "/data/dalvik-cache/".  We'll need to modify the JARs if we want to make any system library changes.  The JAR that contains our compiled TelephonyManager DEX code is "/system/framework/framework.jar".

We first need to pull this file off the device.  We can then unzip the "framework.jar", and use "baksmali" to disassemble the "classes.dex" file.  Now we can do a simple grep to find our class of interest.


Now lets switch gears quick and get the replacement code ready.  I decided to replace our methods of interest with code that opened a file on the SD card and read the values from there.  The following code can be used as replacement code for the getDeviceId() method.


The code is pretty straightforward.  It reads "/mnt/sdcard/device_ids.txt" and uses some matching to get the desired value.  If at any point an exception occurs, the method logs this and just returns the default value.  The other methods were slightly different, but the concept was the same.

To get the Smali equivalent code, I used "ant" to build the app, them used "apktool" to decode the APK file. From here, it was just a direct replacement into the getDeviceId() method of the "TelephonyManager.smali" file.


With the modifications complete we can reverse the process: convert the Smali code back to DEX with "smali", zip the files back up as "framework.jar", and push the new archive to the "/system/framework/" directory.


The final step is to create a new system partition, and we can use the method posted here.  Restarting the emulator with the new system partition will result in our custom library being loaded.

And there we go! The return of any of these methods is now controlled by the contents of the "/mnt/sdcard/device_ids.txt" file.  For those who want to just make the changes and go, I've included a working version of the "framework.jar" for API 10 if people would like to grab it, push it to your emulator, then build a new system image.  If at any point you no longer want to control these values, you can just remove the "/mnt/sdcard/device_ids.txt" file, and the "real" getXX() method will be called.  If anyone observes any unusual instability or errors, please contact me!  Note: Use this library at your own risk.  I do not suggest using this library on a real device!

To make things even easier, I wrote an application to help manage the "/mnt/sdcard/device_ids.txt" file, so you can make changes instantly.  You can grab the app source or APK file from my Github repositories, as well as the "framework.jar" and a sample "device_ids.txt".


I'll continue to post more changes to help with application assessments over the next few months. :)

-jakev

Friday, August 31, 2012

Maliciously Loading an Application's Native Libraries

I recently reviewed an interesting Android application that was distributed on a per-device basis.  The reason for this is that the application used a unique identifier that was hard-coded into the application.  Generally speaking this is an unsafe process.  However, usually I see secret keys hard-coded into the Java files, which would make it difficult for a malicious application to access.  This application, however, contained a per-device compiled native shared library that contained the key.  How does this affect the overall security of the key? Lets find out.

Some of the guys over at Intrepidus Group posted a few months ago about some of the "gotchas" with using native libraries in your Android application, and this post should complement it.  The first interesting thing about native libraries is that, by default, they have lax read permissions when installed on your device.



The fact that it is world-readable introduces an interesting question - is a third party application able to load this library?  The answer to the question is, well, yes!

For anyone who has read my post on debugging native code, I'm going to use the code from that post here as well (application "DebugNative").  As a refresher for those who have not read it, the application contained a native library which checked a user supplied password against a password generated within the native shared library.  The Java section of the code passed the supplied password to the native library using the JNI, the verification occurred, and the return value indicated if the password was correct.

Lets suppose this password is dynamic generated, or is a per-device password, and we want to access it.  We have a copy of the application so we can investigate!

First I pulled the application APK file off the device and decoded using "apktool".  Next I inspected the "lib/armeabi/" directory for the native libraries.  Running the "file" command on "lib/armeabi/libnative.so" shows that it is stripped, meaning that the symbol table (.symtab) has been removed, but that doesn't mean we cannot call functions from this library by viewing the dynamic symbol table.

 

"generatePassword" seems like a good start.  Using IDA, we can derive the prototype of this function.



Pretty straightforward - it doesn't look like anything is being passed to the function, and the return value (R0) and UI supplied password are compared with the "strcmp" function.  We can assume this function looks like:  char *generatePassword();

Next, we write our own malicious application that trie to load the DebugNative's shared library and use this function.  Our application will start, load its own shared library, execute a function using JNI, and print the result to the log.  All the magic will take place from within our shared library.  The main activity looks like the following (imports omitted):



Now we implement our shared library, "aunative.c".



I'll explain what this function does:
  • First we attempt to open the shared library "/data/data/com.jakev.debugnative/lib/libnative.so" using dlopen().
  • Next we attempt to resolve the symbol for the function "generatePassword" using dlsym().  We store the return in a function pointer, getpassword, that mimics the prototype of the "generatePassword" function.
  • We call the getpassword() function pointer and store the return in the buffer password.
  • We open the file "/mnt/sdcard/password.txt" for binary writing (just in case) and write the contents of password to the file.  We then close the file.
  • Finally, we unload the "libnative.so" shared library with dlclose().
If all works properly, we should see the file "/mnt/sdcard/password.txt" with our password (I hid the password just in case people wanted to try to figure it out themselves using this method or debugging the application).  It's worth noting that this application would require the "WRITE_EXTERNAL_STORAGE" permission.




So that's that problem, what can we do about this?  Googling around you can find a couple of options to help prevent this.  One I have seen used effectively is the keyword static when defining your functions.  This will effectively limit the scope of function and will help restrict access.  Thus our password generating function would look like: static char *generatePassword();

When we attempt to inspect the new shared library we notice that our string is no longer present in the file, anywhere.



Then, when I attempt to run the malicious application with the new DebugNative installed, I notice a return value of -2 in the Android logs, which indicated our call to dlsym() has failed.

I hope this post demonstrates the risk associated with inadequately protecting Android native libraries!

-jakev