← Back to all reports

Exported Activity + JS Bridge to 30-Day API Token Theft on Crypto Exchange Android

Reported Mar 30, 2026
Severity Critical
Platform Android
Vulnerability Class Exported Component + JS Bridge (CWE-926, CWE-749)
Target Type Cryptocurrency Exchange
Impact 30-day API token theft, full trading access

The Risk

Any other app installed on the same phone, even a fake calculator with zero permissions, could silently steal a customer's full session from a centralized crypto exchange's Android app. The stolen session lasted 30 days, worked from anywhere in the world, and let the attacker view the victim's email and balances, place trades, and cancel pending orders without any warning, code, or second factor. The whole attack completed in under two seconds with nothing for the user to see, tap, or approve.

The Vulnerability

Three code-level defects in the Android app of a centralized cryptocurrency exchange chained together to allow a zero-permission co-installed app to silently steal the user's full session token. Tested on Pixel 5 (Android 13), zero user interaction required after launch.

1. Exported activity accepts intents from any app

A push notification click activity was exported with no permission check. Its handler had an undocumented code path: when the intent contained page=h5, the value in symbol was passed straight to a method that opened the URL in a WebView.

2. URL validation bypass via the data: URI scheme

The URL validation function only blocked http:// and https:// URLs that did not match the exchange's own domain. The data: URI scheme bypassed this entirely, allowing attacker-controlled HTML and JavaScript to load directly inside the app's WebView.

3. JS Bridge registered with no origin check

Every WebView in the app unconditionally registered a JavaScript bridge interface with no origin validation. The bridge's getAppInfo command returned the session token to any calling JavaScript. A second bridge command, loadUrl, opened any URL in a new WebView. When that new WebView loaded, the app attached the API auth token as an HTTP header to the outgoing request, delivering it directly to whatever server was on the other end.

The Attack

The proof-of-concept was a small co-installed Android app that requested zero permissions and appeared as "Crypto Calculator" in the launcher. It did not even need the INTERNET permission. All network traffic happened inside the target app's own WebView process using the target app's permissions.

  1. The malicious app fires a single intent at the exported push click activity with page=h5 and symbol=data:text/html;base64,...
  2. The target app launches and opens the attacker's HTML in a WebView with the JS bridge attached
  3. Attacker JS calls Bridge.postMessage("getAppInfo") and receives the session token
  4. Attacker JS calls the bridge's loadUrl command pointing at the attacker's server
  5. The app sends the auth token as an HTTP request header to the attacker's server
  6. The attacker's server logs the 30-day token and uses it to fetch the victim's profile server-side

The chain completed in under two seconds. No dialogs, no taps, no prompts. The victim briefly saw the exchange's splash screen and nothing more.

Bonus finding

The production app shipped a developer testing HTML page in its assets directory with buttons that exercised every bridge command including the token-returning one and arbitrary WebView navigation. Not used in the attack chain itself, but it confirmed the full bridge surface area.

The Impact

Using the stolen token from an unrelated machine on a different network, the following was confirmed:

EndpointData / Action
User profileEmail, user ID, referral code, 2FA status
Account balancesFull balances across all wallets
Open positionsAll open trading positions
Order historyComplete order and trade history
Place spot ordersAccepted with no 2FA
Cancel all pending ordersExecuted immediately
Withdrawal pre-checkReturned readiness data

Thirteen out of fifteen tested endpoints returned user data. The token was valid for 30 days with no IP binding, confirmed by using it from a VPS while the victim device was on a residential connection on a different continent. With a funded account, an attacker could place trades at manipulated prices, cancel pending orders, open leveraged positions, or front-run the victim by reading their open orders. Only the final withdrawal step required additional 2FA, but trading was enough to drain a leveraged account through deliberate liquidation.

Remediation

  • Remove the export from the push notification click activity, or add a signature-level permission check
  • Allowlist URI schemes in the URL validator: only permit https:// matching first-party domains. Block data:, javascript:, blob:, and file:
  • Add origin validation to the JavaScript bridge: check the WebView's current URL before responding to sensitive commands
  • Stop attaching the long-lived API auth token in WebView request headers. Use a scoped, short-lived WebView token instead
  • Remove developer testing HTML pages from production builds