Protecting Data at Rest in Android Applications

This article is part of the series of blog posts about Android application security. In this series we try to learn Android application security by looking at one particular vulnerable application, identify vulnerabilities, and show how to fix them.

Prerequisites

  • InsecureBankv2 installed on Android device or emulator
  • adb client connected to a device

Optional:

Getting information

Protecting data at rest is important for applications that deal with sensitive data, like bank application we are working with. For these types of applications, client (i.e. device) should be considered untrusted. It may happen that a malicious application is installed on the device and, after obtaining root access, is able to get access to files belonging to other applications. Because of that, your application should not store any sensitive data on the device.

Let’s navigate inside the application directory, and see all files that application works with.

root@donatello:/data/data/com.android.insecurebankv2 # ls -lR

.:
drwxrwx--x u0_a73   u0_a73            2016-12-02 16:25 app_webview
drwxrwx--x u0_a73   u0_a73            2016-12-02 16:24 cache
drwxrwx--x u0_a73   u0_a73            2016-12-02 16:24 databases
lrwxrwxrwx install  install           2016-12-19 16:17 lib -> /data/app-lib/com.android.insecurebankv2-1
drwxrwx--x u0_a73   u0_a73            2016-12-02 19:09 shared_prefs

./app_webview:
-rw------- u0_a73   u0_a73      38912 2016-12-02 16:25 Web Data
-rw------- u0_a73   u0_a73        512 2016-12-02 16:25 Web Data-journal

./cache:

./databases:
-rw-rw---- u0_a73   u0_a73      20480 2016-12-02 19:09 mydb
-rw------- u0_a73   u0_a73      12824 2016-12-02 19:09 mydb-journal

./shared_prefs:
-rw-rw---- u0_a73   u0_a73        165 2016-12-02 16:24 com.android.insecurebankv2_preferences.xml
-rw-rw---- u0_a73   u0_a73        209 2016-12-02 19:09 mySharedPreferences.xml

Inspecting SQLite databases does not show any potential vulnerabilities:

cd /data/data/com.android.insecurebankv2/
sqlite3 mydb
sqlite> .tables
android_metadata  names
sqlite> select * from android_metadata;
en_US
sqlite> select * from names;
1|jack

Let’s see what we have stored in shared preferences:

cd /data/data/com.android.insecurebankv2/shared_prefs
cat com.android.insecurebankv2_preferences.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="serverport">8888</string>
    <string name="serverip">192.168.0.1</string>
</map>

cat mySharedPreferences.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="EncryptedUsername">amFjaw==
    </string>
    <string name="superSecurePassword">nH/HGFyUPFtmqK/gHS4Adw==
    </string>
</map>

While the first file just keeps settings for our connection to the server part of the application, second file reveals some interesting information. When you look at the username, even though the key name says encrypted, based on the == in the suffix, you may realize that this looks like base64 encoding. It is trivial to obtain unencoded base64 value, just search for some online decoder, and you will get a value jack. However, when we try the same with superSecurePassword, we get no luck, as the result does not seem to contain readable characters that would indicate a password.

It looks like we would need to look into the source code to understand if we can decrypt superSecurePassword. Let’s open Bytecode Viewer and search for all classes that use superSecurePassword:

Searching for all classes that use password values stored in shared preferences in Bytecode Viewer

If we open LoginActivity.fillData method, we will see that we retrieve encrypted password and use CryptoClass to decrypt it: final String string2 = sharedPreferences.getString("superSecurePassword", (String)null); this.Password_Text.setText((CharSequence)new CryptoClass().aesDeccryptedString(string2));

So, we need to look inside CryptoClass:

Source code of CryptoClass as seen in Bytecode Viewer

OK, from the decompiled code we can see that symmetric key is hardcoded into source code and initialization vector is also present nearby and set to all zeros.

Exploit

Since we have the decompiled source code, we are in a possession of code that can decrypt stored data. Just look into aesDeccryptedString and aes256decrypt methods from CryptoClass.

Fix

For applications that deal with highly sensitive data, you should consider your client as being untrusted. This means that you should not store any sensitive data on the client. In our case, it would be that we would not store password on the device in shared preferences. The fact that we store full password, not merely a hash, is also concerning. However, imagine a scenario where one does need to store sensitive data on a device. What to do in this case?

A potential solution is to use Password Based Encryption when you generate a key for symmetric encryption from a user provided data. One might take the following approach:

  • When an application is installed, generate a random value which will be later used as a salt for password-based key derivation function
  • Ask user for password when the value has to be encrypted
  • Use Key Derivation Function with large number of iterations and sufficient salt length to generate the key. If I were to determine the current recommended values for which function to use for password-based key derivation, I would probably check with U.S. National Institute of Standards and Technology - NIST, you can look here and here. At the time of writing the recommendations are PBKDF2 with salt value of 32 bits or more, at least 10000 iterations of hash function. Key length was recommended to be 112 bits as of December 2010, however, at least twice as large value is probably warranted. Again, you should check for updated values yourself.
  • Use the generated key to encrypt password with symmetric encryption. AES-256 with Cipher Block Chaining mode (CBC) is considered fine at the time of writing (use NIST recommendations for up-to-date values).

Another option to having user specifying password for key derivation is to store data for key derivation outside of the device, i.e. on the server side. User still has to be authenticated to the server before application can obtain this data from the server (otherwise an attacker might as well retrieve this data himself).

Please, note that our view of locally stored data would be incomplete if we didn’t look at /sdcard. It is worth remembering that all data stored on SD card is world-readable, so, if your application does store any data there, make sure that it is not sensitive. As for the InsecureBank application, we will take a look at SD card storage when we analyze Web Views in one of the other posts.

Summary

In this post we looked at the data that was being stored on the device. We found that user’s password was encrypted with a key hard-coded into application and stored in user preferences file, which means that it can be easily obtained by a malicious application which has root access on the device.