← Back to all reports

Deep Link + JS Bridge Chain to Full Account Takeover

Reported Mar 2, 2026
Severity Critical
Platform Android
Vulnerability Class Deep Link Hijack + JS Bridge Abuse
Target Type Cryptocurrency Exchange
Impact Full account takeover + trading access

The Risk

A single tap on a link was all it took for an attacker to gain full control of any user's account on a cryptocurrency exchange. From there, they could view all balances, access personal information, and execute trades on the victim's behalf. The attack required no special access, no passwords, and no interaction beyond that one tap. Any of the exchange's users could have been targeted.

The Vulnerability

1. No URL validation on deep links

The app's deep link handler accepted a url query parameter and loaded it directly into a WebView with no domain allowlist. Any app or webpage could trigger it:

appscheme://host/path?url=https://attacker-controlled.com/

The activity dispatched the URI to an internal router which passed it straight to a WebView activity. No confirmation dialog, no user prompt.

2. Broken origin regex on JS bridge

The WebView injected a JavaScript bridge object (window.control) with 27+ methods. An origin filter was applied using a regex fetched from a public config endpoint:

.*exchange[.]com

No start anchor. This means evil-exchange.com, testexchange.com, or any domain ending in the exchange's name would pass the check. Registering a matching domain costs about $10.

3. Unrestricted authenticated API proxy

One bridge method, requestAction, forwarded attacker-supplied JSON (an API path and parameters) to a Flutter service that made authenticated API calls using the app's full access token. Responses were returned directly to the calling JavaScript. Another method, sendTokenToWeb, returned the user's refresh token, enabling cross-platform session hijack.

The Attack

  1. Attacker registers a domain matching the regex (e.g., test-exchange.com)
  2. Attacker hosts a simple HTTPS page with a deep link button
  3. Victim visits the page and taps the button. The deep link opens the exchange app
  4. The app loads the attacker's page in its privileged WebView. No prompt shown
  5. JavaScript calls control.sendTokenToWeb() to capture the refresh token, user ID, and device ID
  6. JavaScript calls control.requestAction() to hit authenticated API endpoints: balances, order placement, order cancellation, user profile
  7. All exfiltrated data is sent to the attacker's server in real time

The Impact

From a single tap, the following was confirmed on a live account:

ActionResult
Full profile extraction (email, phone, login IP)Confirmed
All spot balances (400+ entries)Confirmed
All derivatives balancesConfirmed
Cancel any open orderAuth passed, business logic rejection only
Place ordersAuth not rejected
Refresh token theft (cross-platform ATO)Confirmed
Navigate app to any screenConfirmed
Force logout (DoS)Available

The order cancellation proof was key. The API returned a business logic error ("Order does not exist"), not an authentication error, confirming requestAction makes fully authenticated calls. An attacker could place adverse trades on illiquid pairs from their own account, then execute against them using the victim's session.

Why This Matters

Deep link handlers in mobile apps are rarely audited. This vulnerability existed because three separate assumptions all failed:

  • The deep link handler assumed only trusted sources would trigger it
  • The JS bridge assumed its regex would filter malicious origins
  • The API proxy assumed only the app's own pages would call it

Each assumption was reasonable in isolation. Together, they created a one-tap path to full account takeover on a live cryptocurrency exchange.

Remediation

  • Allowlist URLs in the WebView activity to only load domains the app owns
  • Anchor the JS bridge regex: ^https?://([\w-]+\.)?exchange\.com(/|$)
  • Remove or heavily restrict the requestAction bridge method
  • Bind session tokens to their originating platform server-side
  • Implement android:autoVerify on App Links to prevent deep link spoofing