← Back to all reports

Exported Activity + WebView Token Theft to Full Account Takeover

Reported Mar 27, 2026
Severity Critical
Platform Android
Vulnerability Class Exported Component + Token Leakage (CWE-926)
Target Type Cryptocurrency Exchange
Impact Full ATO, trading, withdrawal address injection

The Risk

Any app installed on the same phone could silently steal a user's full session in under 5 seconds, with zero taps and zero permissions. From there, the attacker could view all account balances, execute trades, and add their own withdrawal address to the victim's account. The malicious app could disguise itself as something harmless like a calculator.

The Vulnerability

Four weaknesses in the Android app chained together to create a silent, zero-interaction account takeover from any co-installed app.

1. Exported launcher activity accepts arbitrary intents

The app's main splash activity was exported and processed intent extras without any validation. Any app on the device could send it an explicit intent with a crafted payload.

2. No Firebase validation on push notification handler

The push notification processing method accepted any non-empty message ID string without verifying it actually came from Firebase. A fake notification payload was treated as legitimate.

3. No URL allowlist on WebView

A webPage parameter extracted from the notification payload was passed directly to a WebView activity with no domain validation. Any URL was loaded.

4. Auth tokens sent to arbitrary domains

The WebView sent the user's full JWT as an HTTP header (x-authorization) to whatever URL was loaded. Additionally, a JavaScript bridge responded to messages with a second token, session ID, and device UUID, with no origin check.

The Attack

The proof-of-concept was a simple Android app (approximately 80 lines of Java, zero permissions required) that masqueraded as "Crypto Calculator" in the app drawer.

  1. The malicious app sends an explicit intent to the target's exported splash activity with three extras: a type identifier, a JSON payload containing the attacker's URL, and a fake message ID
  2. The target app launches, processes the fake push notification payload, and loads the attacker's URL in its WebView
  3. The HTTP request to the attacker's server includes the victim's JWT in the request headers (first exfiltration channel)
  4. The attacker's page calls the JavaScript bridge, which responds with a second token, session ID, and device UUID (second exfiltration channel)
  5. The malicious app closes itself. The victim briefly sees a splash screen. Total elapsed time: under 5 seconds

Both dual exfiltration channels were confirmed working on a Pixel 5 (Android 13, rooted) and Pixel 6a (Android 16, non-rooted).

The Impact

Using the stolen token from a separate machine, the following was confirmed:

Full profile access

The user profile endpoint returned full email, user ID, roles, 2FA status, and preferences. The JWT was valid for approximately 7 days.

Trading and financial operations

The order placement endpoint accepted market and limit orders. It only rejected for insufficient balance, not for missing authentication or OTP. Conversions and transfers between spot and perpetual accounts also required no second factor.

Withdrawal address injection

The withdrawal address creation endpoint accepted an empty OTP field for non-memo chains (BTC, ETH, USDT ERC20/TRC20, SOL). A test address was successfully added and verified in the victim's address book. Only memo-required chains (XRP, XLM) enforced OTP.

App navigation hijacking

The JavaScript bridge accepted commands to navigate the victim to any of 47+ internal screens (withdraw, deposit, transfer, trade) with attacker-controlled parameters, enabling pre-filled withdrawal forms.

Remediation

  • Validate that push notification payloads actually originate from Firebase before processing
  • Implement a strict URL allowlist on WebView loading. Only load the company's own domains
  • Never send authentication headers to arbitrary domains. Check the URL before attaching tokens
  • Restrict the JavaScript bridge to respond only when the loaded URL is on a trusted domain
  • Enforce OTP server-side for all withdrawal address changes, not just memo-required chains