Strings (Mobile Hacking Lab)
This writeup details the solution to the Strings challenge available here:
https://www.mobilehackinglab.com
The lab requires static analysis of the android APK file to identify methods which need to be called with frida. Finally, the flag can be retrieved by scanning the memory of the application.
Initially running the application shows the following:
Step 1: Static Analysis of the APK
There are two exported activites detailed in the AndroidManifest.xml; the MainActivity and Activity2.
The MainActivity retrieves the "Hello from C++" string in the onCreate, a second method KLOW()
is defined by not called which relates to shared preferences.
public final void KLOW() {
SharedPreferences sharedPreferences = getSharedPreferences("DAD4", 0);
SharedPreferences.Editor editor = sharedPreferences.edit();
Intrinsics.checkNotNullExpressionValue(editor, "edit(...)");
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy", Locale.getDefault());
String cu_d = sdf.format(new Date());
editor.putString("UUU0133", cu_d);
editor.apply();
}
Activity2 seems more interesting.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_2);
SharedPreferences sharedPreferences = getSharedPreferences("DAD4", 0);
String u_1 = sharedPreferences.getString("UUU0133", null);
boolean isActionView = Intrinsics.areEqual(getIntent().getAction(), "android.intent.action.VIEW");
boolean isU1Matching = Intrinsics.areEqual(u_1, cd());
if (isActionView && isU1Matching) {
Uri uri = getIntent().getData();
if (uri != null && Intrinsics.areEqual(uri.getScheme(), "mhl") && Intrinsics.areEqual(uri.getHost(), "labs")) {
String base64Value = uri.getLastPathSegment();
byte[] decodedValue = Base64.decode(base64Value, 0);
if (decodedValue != null) {
String ds = new String(decodedValue, Charsets.UTF_8);
byte[] bytes = "your_secret_key_1234567890123456".getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)");
String str = decrypt("AES/CBC/PKCS5Padding", "bqGrDKdQ8zo26HflRsGvVA==", new SecretKeySpec(bytes, "AES"));
if (str.equals(ds)) {
System.loadLibrary("flag");
String s = getflag();
Toast.makeText(getApplicationContext(), s, 1).show();
return;
}
...
A number of checks are performed looking for the same details we saw defined in KLOW()
. Additionally, the intent to this activity must be an android.intent.action.VIEW
. A Uri scheme is defined in the AndroidManifest.xml which is checked for here: mhl://labs
. This uri accepts a path as a Base64 value which is then checked against a Base64 encoded and AES encrypted string. If all these conditions are met then the flag library is loaded and getflag()
is called. Irrespective of the outcome of the checks the application terminates so it's unlikely we'll be able to read the flag from the application.
AES Decryption
We have all the pieces of the AES encryption including the secret in Activity2
:
...
byte[] bytes = "your_secret_key_1234567890123456".getBytes(Charsets.UTF_8);
...
the IV in Activity2Kt
:
public final class Activity2Kt {
private static String cu_d = null;
public static final String fixedIV = "1234567890123456";
}
And the cipher text:
...
String str = decrypt("AES/CBC/PKCS5Padding", "bqGrDKdQ8zo26HflRsGvVA==", new SecretKeySpec(bytes, "AES"));
...
I used cyberchef to decode the Base64 and unecrypt the AES cipher text. The result needs to be Base64 encoded for the uri but I'll do that at the end.
Creating Saved Preferences
Next we need to meet the saved preferences condition. I wrote the following frida script to call KLOW()
in the MainActivity.
Java.perform(function(){
setTimeout(function(){
Java.choose("com.mobilehackinglab.challenge.MainActivity",{
onMatch : function(instance){
console.log("[+] Calling KLOW() in the MainActivity...");
instance.KLOW();
},
onComplete:function(){
console.log("[+] Done!");
}
});
}, 1000);
})
frida -U -f com.mobilehackinglab.challenge -l klow.js
____
/ _ | Frida 16.1.11 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
. . . .
. . . . Connected to Android Emulator 5554 (id=emulator-5554)
Spawned `com.mobilehackinglab.challenge`. Resuming main thread!
[Android Emulator 5554::com.mobilehackinglab.challenge ]-> [+] Calling KLOW() in the MainActivity...
[+] Done!
And we can see the shared_prefs file has been created:
emu64a:/data/data/com.mobilehackinglab.challenge/shared_prefs # ls -la
total 16
drwxrwx--x 2 u0_a162 u0_a162 4096 2024-06-17 14:20 .
drwx------ 6 u0_a162 u0_a162 4096 2024-06-17 11:39 ..
emu64a:/data/data/com.mobilehackinglab.challenge/shared_prefs # ls -la
total 24
drwxrwx--x 2 u0_a162 u0_a162 4096 2024-06-17 14:20 .
drwx------ 6 u0_a162 u0_a162 4096 2024-06-17 11:39 ..
-rw-rw---- 1 u0_a162 u0_a162 117 2024-06-17 14:20 DAD4.xml
emu64a:/data/data/com.mobilehackinglab.challenge/shared_prefs # cat DAD4.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="UUU0133">17/06/2024</string>
</map>
emu64a:/data/data/com.mobilehackinglab.challenge/shared_prefs #
Running Activity2
Everything to run Activity2 should now be in place, we can Base64 encode the result of the AES decryption now and add it the uri:
adb shell am start -W -a android.intent.action.VIEW -n com.mobilehackinglab.challenge/.Activity2 -d "mhl://labs/bWhsX3NlY3JldF8xMzM3"
Running this we get a "Success" toast message:
Extracting the Flag from memory
The final step is to retrieve the flag from the application memory. I used fridump for this which is available here:
https://github.com/Nightbringer21/fridump
python3 fridump.py -U -s Strings
# look for flag in dump folder
cat dump/strings.txt| grep -a MHL
MHL{IN_THE_MEMORY}