Encrypted Shared Preferences
Starting in Android 6.0 (API Level 23), Android introduced a standard way to encrypt data in the shared_prefs
directory. This is a great way to store small bits of information that the app would need during its operation, but not have it reside on the device in plain text. Encrypted shared preferences use a key-value pair when saving the data.
Encryption is performed using an AES 256-bit scheme. A master key is created using the Secure Random function and is stored in the trusted execution environment. The encrypted file is stored in the regular shared_prefs
directory along with other files that may not be encrypted.
angler:/data/data/com.example.app/shared_prefs # cat Secret_Shared_Pref.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="AQDD1xc4+jVw6PLD0Ov0gcSrm1BkFLNupKxa0Rs=">AX7oI9JcQexVO0QTXWPJQ2nL83X+82jNHYMV65s7qip5onRBNAGNCBULkyLovwt/Zww=</string>
<string name="__androidx_security_crypto_encrypted_prefs_key_keyset__">12a70159480a190fabb9c87f5ebc361b92be4946f76e08864a62f62b4acd6ba4502855f8fc2c8588cffa66f560e2b0264072bbd82f03b02f30ac0e12f1c96170ec6516cf730e3b2c9a89a617f85d56d90c74df5bce17c284422359157761b78b6250e9974fd2e59ec744de06a99fe3b6567858b2cfecc55e2a29c206a6bdc506cd69c688a428be1a667843bfde2bc7ea749d9eb09ca98d3c5b786bb1ba5717ca51e3bb5d0f80f76208401a420897ae8f06123b0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e4165735369764b657910011897ae8f062001</string>
<string name="__androidx_security_crypto_encrypted_prefs_value_keyset__">1288018adf58904be2bc3f85899a590c3e6e7d02126f81bacccf97319483f50cc28c6c13a124e6d6a465535e32f5512f5520a95025785186533a7b1ba4f8e1e5281f1568729ae03464280c22342910bcf43ac0e81c3cc2921dd98fedd34bcd3778674621d4a90c0fb586bd4fb168e32f7bb43b92af74258159b00b20cc6e299e8ce6140ee4388a421912231a4408d2c7a0f707123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e41657347636d4b6579100118d2c7a0f7072001</string>
<string name="AQDD1xeXahcyUdxYxK9Bx4acWtgObma/Pp48B/c=">AX7oI9IbOsakPwxxKXTvw7XYl5H4ypfwxq21Fp1cQOUXAFGiOS4RL3GvHrY+WVNNPvw=</string>
</map>
When looking at the encrypted file, you will see two keysets: one for the key and another for the value.
While we can intercept the keys, or the data before it is encrypted, this is not a finding. This method is still better than storing plain text data on the device.
Recover Encryption Key
Using Frida, we can recover the key that is used for encryption of the data. Use the android-crypto-intercept.js
script to get the keys.
% frida -U -l android-crypto-intercept.js -f com.example.app
[Nexus 6P::com.example.app ]->
message: {'type': 'send', 'payload': 'CIPHER: AES/GCM/NoPadding'} data: None
message: {'type': 'send', 'payload': 'KEY: 63f5fc564819a6fc07a0e312fe477e36f5ae5ebc155597b129d87e7154db8ac1'} data: None
message: {'type': 'send', 'payload': 'doFinal!'} data: None
Once you have recovered the key, check to see if the key is re-used in the app (meaning it is hard-coded). You will need to remove the app first, so do this check when it will not be disruptive to your other testing.
- Remove the app from your device
- Re-install the app from the Play Store (or sideload the APK file if that was provided to you)
- Launch the app, login, and use the app normally so that the encrypted shared preference file is created
- Using the Frida script above, re-run it to recover the keys
If the keys are the same, they are hard coded in the app. Decode & decompile the app and search for those keys. If you have a second device, install the application there and check to see if keys are generated the same on that device. This can help narrow down the search for how the keys are generated.
Re-using the same key – or generating the same key on different devices – should be called out as a finding.
Intercept the Data Before Encryption
To intercept the data before it is encrypted, use the ‘android-encrypted-shared-preferences.js’ script:
% frida -U -l android-encrypted-shared-prefs.js -f com.example.app --no-pause
[Nexus 6P::com.godaddy.gdm.investorapp ]->
EncryptedSharedPrefrence putString()
Username:testuser
Password:P@ssw0rd
[Nexus 6P::com.godaddy.gdm.investorapp ]->