Post

Strings MobileHacking

Mobile Hacking Lab

This time we will take the strings challenge on Mobile Hacking lab.

We connect through adb via openvpn file and try to list the activites from the string application com.mobilehackinglab.challenge. Extract the path of the application

1
2
adb shell pm path com.mobilehackinglab.challenge
package:/data/app/~~p_54bM-A63QkAZxdaHHILQ==/com.mobilehackinglab.challenge-AfKKIwpsKTgv5lPYwRWv0Q==/base.apk

Extract the apk file

1
adb pull /data/app/~~p_54bM-A63QkAZxdaHHILQ==/com.mobilehackinglab.challenge-AfKKIwpsKTgv5lPYwRWv0Q==/base.apk

Then we can analyze the AndroidManifest.xml file

  • Find one activity besides the MainActivity exported explicity and with a intent-filter.
  • A receiver with the exported tag on true and an intent-filter defined inside.

Activity2

Activity2

This activity has a intent-filter which are object to request an accion to other components in the application, it help with the comunication between components through three diferentes ways.

  1. Launch an Activity
  2. Initialize a Service
  3. Transmit an event.

Intent require an action which specifies the generic action to be executed ( ACTION_VIEW or ACTION_SEND ).

It also use Data, which make reference to the data that the action would require or the data type (MIME), like this case android:schema and android:host.

The category contains aditional information about the component type that control the intent, for example CATEGORY_BROWSABLE allows to be initialize for a web browser to show data like images or emails messages.

The source code of the Activity2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    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;
                    }
  1. We need to launch the intent with the specific action android.intent.action.VIEW
  2. The string u_1 from the sharedPreferences has to be the same as the result of the cd() function.
  3. The data schema and host have to be mhl and labs so mhl://labs/<b64Value>
  4. The b64data passed is compared with a cipher text.

Trigger intents

We can trigger intents via adb, this case with this command. But as we don’t know the ds correct value the screen close immediately.

1
adb shell am start -a android.intent.action.VIEW -d "mhl://labs/base64data" -n com.mobilehackinglab.challenge/.Activity2

We saw hardcoded in the code all the data ( secrets, IV,etc ) being able to reproduce the decrypt process.

BASE64DATA

SharedPreferences

Who create the sharedPreferences DAD4?, looking at the MainActivity code we found the KLOW function

1
2
3
4
5
6
7
8
9
    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();
    }

We can launch this function with Frida hooking the MainActivity

1
2
3
4
5
6
7
8
9
10
11
Java.perform(function (){
    setTimeout(function (){
        Java.choose('com.mobilehackinglab.challenge.MainActivity',{
            onMatch: function(instance){
                console.log("Creating SharedPreferences");
                instance.KLOW();
            },
            onComplete: function(){}
        });
    },1000);
})

We can combine this two options to pass all the If statements.

Succes

We saw a success message, but no flag :( ?

DUMP Memory

We can scan memory with frida as mention here

First we need to hook the library memory base address.

Library_memorybase

Define our pattern to search let pattern = 4d 48 4c for MHL.

Dumped

Now we need to read the value on that memory using Memory.scanSync

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Java.perform(function (){
     setTimeout(function (){
        const libflag = Process.getModuleByName('libflag.so');

        // Pattern MHL
        let pattern = "4d 48 4c"

        console.log("Base address: " + libflag.base);
        console.log("Size in memory: " + libflag.size);

        // Memory.scan(address, size, pattern, callback)
        Memory.scan(libflag.base, libflag.size, pattern, {
            onMatch: function (address) {
                // Successful Match Message
                console.log("Match at: " + address);
            },
            onComplete: function () {
                // Scan complete Message
                console.log("Scan complete");
            },
            onError: function (error) {
                console.log("Scan error:" + error);
            },
        });

        const results = Memory.scanSync(libflag.base, libflag.size, pattern);
        console.log('Memory.scanSync() result:\n' + JSON.stringify(results));
        const flag_addr = results[0].address;
        console.log(hexdump(flag_addr,{length: 30}));
     },1000);
})

FLAG

This post is licensed under CC BY 4.0 by the author.