
Android deeplink, Intent, and WebView bridge assessment helper for ethical hacking
Android deeplink, Intent, and WebView bridge assessment helper
apk-interceptor is a portable Android testing APK for authorized application security assessments. It helps security engineers verify how an Android app handles external entry points such as custom URI schemes, deeplinks, exported Activities, and WebView JavaScript bridges.
The tool is intentionally constrained:
android.permission.INTERNETcontent:// payload fileDuring Android application security assessments, many findings from static
analysis still need a small on-device proof of concept before they can be
confirmed: registering a custom URI scheme, sending an explicit Intent, serving
a local content:// payload, or checking whether JavaScript can reach a WebView
bridge.
Building a new throwaway test app for each case is repetitive and error-prone. Small differences in manifest entries, authorities, URI grants, package names, or Intent construction can slow down verification and make results harder to reproduce.
apk-interceptor was created to make that confirmation step repeatable. Instead of writing a new PoC APK for every assessment, you build this tool with the authorized scheme or application ID you need, run the test on-device, and keep the workflow constrained by design: no INTERNET permission, no external data transmission, no shell execution, and no root dependency.
apk-interceptor is useful for these assessment tasks:
Detailed vulnerability walkthroughs:
content://The app keeps an in-memory assessment log for sent Intents, received deeplinks, bridge callbacks, JavaScript results, and errors. Logs disappear when the app process is killed. Because logs are not persisted, capture evidence with screenshots or screen recording as you work.
apk-interceptor is a confirmation tool, not a discovery or exploitation framework. It assumes you already know what to test (scheme, Activity class, bridge name) from static analysis, and gives you a safe, on-device way to verify reachability and capture evidence. It is built to be installed on an assessment device and even shared with a client, so it ships no INTERNET permission, no shell execution, no data exfiltration, and no root requirement.
Where it sits next to the usual Android tooling:
The two areas where apk-interceptor has the clearest edge over the alternatives:
adb/static analysis
cannot show.content:// → WebView bridge verification: a non-exported, single-file
provider whose payload is delivered only through a temporary Intent read grant,
plus a local self-test WebView to validate payload syntax first.apk-interceptor treats sending and intercepting differently, and this is the most important thing to understand before using it:
| Action | Module | Custom scheme needed at build time? |
|---|---|---|
| Send an Intent or deeplink to another app | Sender | No, type any URI, package, or Activity at runtime |
| Intercept (receive) a deeplink for a custom scheme | Interceptor | Yes, the scheme is fixed into the APK at build time |
To send a crafted deeplink to the assessed app, you do not need to rebuild: use the Sender tab's Implicit Deeplink mode and type any URI.
To intercept a deeplink, that is, to make Android route a custom scheme to
apk-interceptor so you can observe a possible scheme-hijack, you must build the
APK with that scheme via --scheme. The scheme is fixed at build time on
purpose (a design guardrail); apk-interceptor never registers arbitrary schemes
at runtime. If you change the scheme you are assessing, rebuild and reinstall.
adb for device installation and optional command-line testingBuild the APK with the custom URI scheme you are authorized to assess:
./build-interceptor.sh --scheme <authorized_custom_scheme>
adb install ./out/apk-interceptor-<authorized_custom_scheme>-debug.apk
Optional build flags:
./build-interceptor.sh \
--scheme <authorized_custom_scheme> \
--app-id <custom.application.id> \
--output ./out
--app-id sets the installed application ID (the package identity on the
device and the content://<applicationId>.payload authority) at build time.
The default is com.sterrasec.apkinterceptor. Override it with --app-id when
you need multiple separately installable builds for different assessments. The
Windows equivalent is build-interceptor.bat.
The default scheme intercept-poc-example is a harmless placeholder. The build
script refuses to produce an assessment APK with that default scheme.
On first launch for each app version, apk-interceptor shows an authorized-use dialog. After you tap I understand, the same version does not show the dialog again. The Sender tab still shows a persistent warning because it can send Intents to other apps.
| Sender | Payload | Interceptor |
|---|---|---|
![]() | ![]() | ![]() |
Use this tab to verify custom URI scheme interception.
What it shows:
Basic workflow:
About Send Test Deeplink: it always sends <scheme>://test?<your params>
with a fixed test host, so it is meant for confirming that apk-interceptor
receives and logs the scheme, not for driving the assessed app's specific
deeplink routes. To send a crafted deeplink that matches the assessed app's
required host or path, use the Sender tab's Implicit Deeplink mode instead.
adb example:
adb shell am start -W \
-a android.intent.action.VIEW \
-d 'my-authorized-scheme://test?source=adb\&message=hello%20world'
Use \& when sending multiple query parameters through adb shell; otherwise
the device shell may treat & as a command separator.
Expected result:
RECEIVED log entry appearsUse this tab to send controlled Intents during an authorized test.
Modes:
Intent(ACTION_VIEW, Uri.parse(uri))Fields and controls:
Implicit deeplink workflow:
Explicit Activity workflow:
Notes:
content:// attachment is useful when testing whether a target Activity
passes untrusted Intent data to a WebView.data, which would overwrite the URI you
type in Implicit Deeplink mode, so the option is hidden there.PayloadProvider is not exported. The assessed app can read the attached
content:// payload only because the Intent grants it temporary read access
via FLAG_GRANT_READ_URI_PERMISSION. Keep that flag enabled, and deliver
the URI through the Intent. A content:// URI opened any other way will not
be readable by another app.Use this tab to create a local HTML payload and validate JavaScript bridge syntax in apk-interceptor's own self-test WebView.
What it contains:
content:// URIGenerated payload URI format:
content://<applicationId>.payload/current.html
The provider serves only this fixed file:
filesDir/payloads/current.html
Payload self-test workflow:
localBridge.BRIDGE_RESULT, console.log, and evaluateJavascript result
entries in the log.Example self-test JavaScript:
console.log("payload loaded");
window.localBridge.logResult(window.localBridge.getInfo());
The self-test bridge exposes:
window.<bridgeName>.logResult("message");
window.<bridgeName>.getInfo();
Important limitation:
The self-test WebView confirms that your local payload and bridge-call syntax work inside apk-interceptor. It cannot observe whether another app's WebView executed your payload or called its own bridge. For the assessed app, verify through that app's UI, logs, test hooks, or Chrome DevTools if the app is debuggable.
Risk:
An Android app registers a custom URI scheme instead of a verified App Link. Any other app can register the same scheme, so Android may show an app chooser or route links to a different app.
Use apk-interceptor to check:
Steps:
Evidence to capture:
Risk:
The assessed app trusts deeplink parameters for navigation, URL loading, feature flags, account selection, or rendering without sufficient validation.
Use apk-interceptor to check:
Steps:
Example placeholder:
my-authorized-scheme://open?next=https%3A%2F%2Fauthorized-test.example%2Flanding
Do not use real third-party domains or accounts unless they are explicitly in scope.
Risk:
An exported Activity performs sensitive actions or displays sensitive data without verifying the caller, user state, or required authorization.
Use apk-interceptor to check:
Steps:
content:// payload URI.Evidence to capture:
content://Risk:
The assessed app loads untrusted content:// Intent data into a WebView that
also exposes a JavaScript bridge via addJavascriptInterface.
Use apk-interceptor to check:
content://Steps:
Important limitation:
apk-interceptor cannot receive results from another app unless that app explicitly returns or displays them. The tool is designed to deliver a local payload and validate syntax, not to exfiltrate data.
Verify the APK does not request network access:
aapt dump permissions ./out/apk-interceptor-<scheme>-debug.apk
Expected: no android.permission.INTERNET.
Trigger a deeplink explicitly to apk-interceptor:
adb shell am start -W \
-n com.sterrasec.apkinterceptor/.InterceptActivity \
-a android.intent.action.VIEW \
-d 'my-authorized-scheme://test?source=adb'
Trigger a deeplink through Android's resolver:
adb shell am start -W \
-a android.intent.action.VIEW \
-d 'my-authorized-scheme://test?source=adb'
The explicit command confirms InterceptActivity behavior. The implicit
command confirms the manifest intent-filter and resolver behavior.
Unit tests run on the JVM with Robolectric, so no device or emulator is required.
They cover PayloadProvider, including the path-whitelisting and traversal
checks that keep the provider from serving anything other than the single
current.html payload.
./gradlew testDebugUnitTest
Test results are written to app/build/reports/tests/testDebugUnitTest/index.html.
The same task runs in CI on every push and pull request to main.
android.permission.INTERNET/current.html is served by PayloadProviderMIT
| Scenario | Module | What It Helps Verify |
|---|
| Custom URI scheme hijacking | Interceptor | Whether another app can register the same custom scheme and receive links |
| Deeplink parameter handling | Sender | Whether the assessed app accepts unsafe query/path parameters |
| Exported Activity exposure | Sender | Whether an exported Activity can be launched directly by another app |
WebView bridge exposure via content:// | Payload + Sender | Whether a local HTML payload can reach a WebView JavaScript bridge |
| Local payload syntax check | Payload | Whether your HTML/JS payload runs in the self-test WebView |
| Tool | Role | How apk-interceptor differs |
|---|
| jadx / MobSF / QARK / Semgrep | Find vulnerable entry points (static) | apk-interceptor does not scan or decompile; it confirms a finding you already have |
deep-C / NSdeepLink / adb am start | Enumerate and send deeplinks | apk-interceptor can also send, but its differentiator is receiving a hijacked scheme and showing the exact URI and parameters |
| drozer | General on-device attack framework (agent + often root) | apk-interceptor is a single lightweight APK with deliberate safety guardrails, narrower scope, and easier client-safe distribution |
| Metasploit / Frida | Weaponize or hook (e.g. addJavascriptInterface RCE) | apk-interceptor only checks bridge reachability with a harmless payload; it never exfiltrates or executes shell commands |