Skip to content

Commit

Permalink
Updated Decrypting Realm Databases in 0x05d - Testing Data Storage an…
Browse files Browse the repository at this point in the history
…d 0x06d-Testing-Data-Storage (#2570)

* Added Realm Database Intercept - Android

* Added Realm Database Intercept - iOS

* Apply suggestions from code review

---------

Co-authored-by: Rezkon <[email protected]>
Co-authored-by: Carlos Holguera <[email protected]>
Co-authored-by: Sven <[email protected]>
  • Loading branch information
4 people authored May 2, 2024
1 parent 02ce9ce commit 52c8b71
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 1 deletion.
64 changes: 63 additions & 1 deletion Document/0x05d-Testing-Data-Storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,69 @@ Realm realm = Realm.getInstance(config);

```

If the database _is not_ encrypted, you should be able to obtain the data. If the database _is_ encrypted, determine whether the key is hard-coded in the source or resources and whether it is stored unprotected in shared preferences or some other location.
Access to the data depends on the encryption: unencrypted databases are easily accessible, while encrypted ones require investigation into how the key is managed - whether it's hardcoded or stored unencrypted in an insecure location such as shared preferences, or securely in the platform's KeyStore (which is best practice).

However, if an attacker has sufficient access to the device (e.g. root access) or can repackage the app, they can still retrieve encryption keys at runtime using tools like Frida. The following Frida script demonstrates how to intercept the Realm encryption key and access the contents of the encrypted database.

```javascript

'use strict';

function modulus(x, n){
return ((x % n) + n) % n;
}

function bytesToHex(bytes) {
for (var hex = [], i = 0; i < bytes.length; i++) { hex.push(((bytes[i] >>> 4) & 0xF).toString(16).toUpperCase());
hex.push((bytes[i] & 0xF).toString(16).toUpperCase());
}
return hex.join("");
}

function b2s(array) {
var result = "";
for (var i = 0; i < array.length; i++) {
result += String.fromCharCode(modulus(array[i], 256));
}
return result;
}

// Main Modulus and function.

if(Java.available){
console.log("Java is available");
console.log("[+] Android Device.. Hooking Realm Configuration.");

Java.perform(function(){
var RealmConfiguration = Java.use('io.realm.RealmConfiguration');
if(RealmConfiguration){
console.log("[++] Realm Configuration is available");
Java.choose("io.realm.Realm", {
onMatch: function(instance)
{
console.log("[==] Opened Realm Database...Obtaining the key...")
console.log(instance);
console.log(instance.getPath());
console.log(instance.getVersion());
var encryption_key = instance.getConfiguration().getEncryptionKey();
console.log(encryption_key);
console.log("Length of the key: " + encryption_key.length);
console.log("Decryption Key:", bytesToHex(encryption_key));

},
onComplete: function(instance){
RealmConfiguration.$init.overload('java.io.File', 'java.lang.String', '[B', 'long', 'io.realm.RealmMigration', 'boolean', 'io.realm.internal.OsRealmConfig$Durability', 'io.realm.internal.RealmProxyMediator', 'io.realm.rx.RxObservableFactory', 'io.realm.coroutines.FlowFactory', 'io.realm.Realm$Transaction', 'boolean', 'io.realm.CompactOnLaunchCallback', 'boolean', 'long', 'boolean', 'boolean').implementation = function(arg1)
{
console.log("[==] Realm onComplete Finished..")

}
}

});
}
});
}
```

### Internal Storage

Expand Down
44 changes: 44 additions & 0 deletions Document/0x06d-Testing-Data-Storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,50 @@ do {
}
```

Access to the data depends on the encryption: unencrypted databases are easily accessible, while encrypted ones require investigation into how the key is managed - whether it's hardcoded or stored unencrypted in an insecure location such as shared preferences, or securely in the platform's KeyStore (which is best practice).
However, if an attacker has sufficient access to the device (e.g. jailbroken access) or can repackage the app, they can still retrieve encryption keys at runtime using tools like Frida. The following Frida script demonstrates how to intercept the Realm encryption key and access the contents of the encrypted database.

```javascript
function nsdataToHex(data) {
var hexStr = '';
for (var i = 0; i < data.length(); i++) {
var byte = Memory.readU8(data.bytes().add(i));
hexStr += ('0' + (byte & 0xFF).toString(16)).slice(-2);
}
return hexStr;
}

function HookRealm() {
if (ObjC.available) {
console.log("ObjC is available. Attempting to intercept Realm classes...");
const RLMRealmConfiguration = ObjC.classes.RLMRealmConfiguration;
Interceptor.attach(ObjC.classes.RLMRealmConfiguration['- setEncryptionKey:'].implementation, {
onEnter: function(args) {
var encryptionKeyData = new ObjC.Object(args[2]);
console.log(`Encryption Key Length: ${encryptionKeyData.length()}`);
// Hexdump the encryption key
var encryptionKeyBytes = encryptionKeyData.bytes();
console.log(hexdump(encryptionKeyBytes, {
offset: 0,
length: encryptionKeyData.length(),
header: true,
ansi: true
}));

// Convert the encryption key bytes to a hex string
var encryptionKeyHex = nsdataToHex(encryptionKeyData);
console.log(`Encryption Key Hex: ${encryptionKeyHex}`);
},
onLeave: function(retval) {
console.log('Leaving RLMRealmConfiguration.- setEncryptionKey:');
}
});

}

}
```

#### Couchbase Lite Databases

[Couchbase Lite](https://github.com/couchbase/couchbase-lite-ios "Couchbase Lite") is a lightweight, embedded, document-oriented (NoSQL) database engine that can be synced. It compiles natively for iOS and macOS.
Expand Down

0 comments on commit 52c8b71

Please sign in to comment.