Thursday, 13 February 2014

Hooking SQLCipher Crypto Keys with CydiaSubstrate

Security conscious developers often turn to SQLCipher [1] to encrypt content stored on a device’s file system. SQLCipher is a slightly extended version of SQLite, which allows 256-bit AES encryption to be used transparently, so that developers don’t have to deal with crypto internals and other abstractions.

SQLCipher accepts a passphrase that is fed into the PBKDF2 [2] algorithm to derive an encryption key, which is ultimately used to encrypt the database. In cases where by the developer doesn’t want to prompt the user for a passphrase due to perceived negative effect on  user experience, an algorithm may be used to derive a passphrase.

In the event that an App is heavily obfuscated, it can sometimes be time consuming to identify how the key ultimately used to encrypt content is derived. To reduce the overhead in reverse engineering the App, it is possible to use a CydiaSubstrate [3] tweak to hook SQLCipher’s internal methods and extract the key. CydiaSubtrate provides a programmable framework for hooking native and managed code methods on iOS and more recently Android and is an essential tool in a mobile App pentester’s and reverse engineer’s arsenal. Word to the wise, if you decide to use CydiaSubstrate on Kitkat, be sure to set your SELinux mode to permissive first.

Browsing the SQLCipher documentation, it soon becomes clear that the openOrCreateDatabase method is an ideal candidate for hooking as it is used to return a SQLiteDatabase object for accessing the database and takes the encryption key as a parameter.

For example when assuming a static key of “password1”, the following is a valid use of the openOrCreateDatabase API:

SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databaseFile, "password1", null);

Creating a simple App “MyEncryptedApp” that generates a device specific passphrase, we’re able to create an encrypted database using the following code:

String id = new DeviceIdentity(this).generateDeviceIdentifier();
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databaseFile, id, null);
database.execSQL("create table t1(a, b)");
database.execSQL("insert into t1(a, b) values(?, ?)", new Object[]{"one for the money","two for the show"});

i.e. the device ID is being used as the database’s passphrase.

This creates an encrypted database, as shown below:


Using this information, it is possible to write a simple CydiaSubstrate App to hook the method within the JVM and subsequently call a pointer to the original method, allowing execution to continue.

To achieve this we can use the MS.hookClassLoad method which will wait for the class we want to hook to load in order to get a reference to it. In this case, the openOrCreateDatabase method resides in the net.sqlcipher.database.SQLiteDatabase class, so we use this as a parameter to MS.hookClassLoad to indicate that this is the class we are concerned with:

static void hookCryptoKey() {
     MS.hookClassLoad("net.sqlcipher.database.SQLiteDatabase",
          new MS.ClassLoadHook() {
               public void classLoaded(Class<?> arg0) {
               Log.d("MDSecHook", "##### Class Loaded\n");

When the callback is loaded, we then subsequently proxy the implementation through the MS.MethodHook instance, which allows us to replace the method’s implementation with our own code:

new MS.MethodHook() {
     public Object invoked(Object arg0, Object... args)
          throws Throwable { Log.d("MDSecHook", "##### Error: " + Thread.currentThread().getStackTrace());
          Log.d("MDSecHook", "###### Method hooked, stealing key: " + args[1]);
     return old.invoke(arg0, args);
     }
}, old);

The “old” object is a reference to the original implementation of openOrCreateDatabase, which we retrieve using an instance of MS.MethodPointer().  

Going back to our openOrCreateDatabase method, we want to retrieve the 2nd parameter, since this is where the passphrase used to encrypt the database is passed in; this can be extracted using the 2nd element of the args[] array, i.e. args[1].

Installing our CydiaSubstrate extension on the device and reopening the “MyEncryptedApp” causes our openOrCreateDatabase hook to be executed, and the passphrase is therefore printed to the debug log by our code:

The passphrase can the be verified using the SQLCipher command line client:


The source code for our “MyEncryptedApp” and the “keyHooker” CydiaSubstrate tweak can be found on our github page.

If you’re interested in learning about how to hack mobile Apps, get in touch to find out more about our mobile security training courses – or catch us at @44con