In 2019, Google released Jetpack to provide developers with an easier means to adhere to the Android best practices principals. Although the documentation about utilizing Jetpack is still sparse, a few things stood out as improvements for writing secure code on Android. Overall, it seems that Jetpack makes utilizing the secure Keystore and proper encryption much easier for developers.

The struggle that Google and developers at-large face, is to keep cryptographic information separated from memory processes that can be accessed by common mobile penetration testing tools like Frida.

Compromising secured mobile apps with Frida.

To take a deeper look at how well the Jetpack security libraries protect cryptographic keys, shared preferences, and encrypted files, we’ll make a simple application that utilizes these features and then attempt to compromise the app using Frida and Fridump.

The application will present a login activity when started. The username and password will be encrypted and saved into the application’s shared preferences following login. It’s important to note that is not something you should do in an actual application, but it will help us test the Jetpack library.

After logging in, the user is presented with a notepad where any typed or pasted information can be saved to an encrypted file on external storage. If the user returns with a valid login the contents of the encrypted file will be displayed for editing, otherwise the file will be deleted, and a new note will be created.

To make this as easy as possible I chose one of the default example applications from Android Studio, a basic login application. The default app creates the following files, based in Kotlin.

Figure 1

When opened, we see a simple Login Activity that does no verification for user credentials by default.

Figure 2

The quick and basic code we added to make use of Jetpack and encrypt the login info is shown below. The app will encrypt and store the username and password for the login within the shared preferences for the app.

Figure 3

If the user information we supply the application matches what we saved, then the code below will decrypt and display the information in our encrypted file. Some more code was added to support the note editing activity after login and save its contents into an encrypted file.

Figure 4

We can target the app with Frida in a few ways to try and get the plaintext values of the username and password. The first method targets the memory used by the application. By targeting the package name of the installed application and using Fridump, we can pull dumps of the internal memory.

Python3 fridump.py -s com.example.myapplication
Strings dump/strings.txt > human.txt

These two commands will get us human readable information from the application memory. When looking through the results, we were unable to locate either the username or password used, or the encryption information used to encrypt the stored data.

This result is what we hoped for, but it also means we need to try something else. In addition to dumping the memory strings, we can target the function-level execution in the app itself. It may be possible to get the application itself to disclose the stored encrypted values.

The next step is to try enumerating all the stored values for the SharedPreferences at runtime for good measure. It’s unlikely we’ll see anything other than the encrypted values, but it pays to check.

Figure 5
Figure 6

As expected, only we’re only seeing the encrypted information which does not help us gain any further access.

We can overload the writeString() method used within the class to get the values for username and password on their way into storage for a new user. This would make it possible to compromise the login information, and thereby the encrypted file for any new users.

Figure 7
Figure 8

Considering that we obtained a username and password this is progress, but it doesn’t help us with existing encrypted information already stored behind a username/password. To get access to the encrypted notes, we still have an additional option.

Remember that even though we are utilizing the Jetpack libraries to securely encrypt and store the username, password and note, the app logic is what tests for a returning user before decrypting our saved note. This logic itself can be attacked with Frida. The code we will want to overload is pictured below as Figure 3.

Figure 9

We should be able to entirely bypass the check for a valid user even though we can’t obtain the original username or password. The idea is to have Frida replace the login method with one that’s been manipulated to do what we want.

We will need the class name com.example.myapplication.data.LoginDataSmyce which owns the login method. The Frida script will theniterate over classes until there is a match, and then use our implementation of the login() method instead. The method is pictured below and returns a successful login for a fake user no matter what username and password are provided.

Figure 10

We saved the script as frida-java-invoke-login.js and it can be injected into the application with the following command.

frida -U -l frida-java-invoke-login.js com.example.myapplication

Once it succeeds and we attempt a login with any username and password the application will decrypt and display the contents of the file.

Figure 11

Figure 12

While we compromised the app to display the information encrypted following Android Jetpack best practices, this is not a compromise of the Jetpack libraries themselves. I was unable to extract any encryption information from device memory or even the EncryptedSharedPreferences within the app functions.

The “decrypt if logged in” functionality of the app can still be attacked in order to get the decryption functions to work as they normally would. Even though the secret note was effectively encrypted the app is relying on an application state that can still be manipulated.

Overall, the Jetpack libraries seem to be a very effective means of making proper encryption easier for Android developers. However, it’s not going to fix issues where the application relies on a particular “state” or value that can be spoofed or manipulated using Frida.

If you need any assistance with Frida, Jetpack or mobile security in general, simply contact us. We are standing by to help.