← Back to all reports

In-App Phishing via Deep-Link SDK Lands Account Takeover

ReportedMay 12, 2026
SeverityHigh
PlatformAndroid & iOS
Vulnerability ClassAuthentication Bypass (CWE-287)
Target TypeLive Experiences / Events Marketplace
ImpactFull account takeover of any installed user

The Risk

An attacker could send a single branded link by chat or text. Any customer who had the company's app installed and tapped it would see what looked like a real "confirm your account" page inside the genuine app, with their real name, photo, and email already filled in. A verification email would arrive at the same moment from the company itself. The attacker captured the typed code and signed into the account from a different phone, with full access to tickets, billing, and personal details.

The Vulnerability

Three issues chained together:

  • The live production key for a third-party deep-link attribution service was shipped inside both the Android and iOS binaries. With that key, anyone could mint links on the company's branded short domain that pointed at any HTTPS URL of their choosing.
  • The mobile deep-link router dispatched any inbound link whose first path segment matched a particular value (e.g. /movies/{id}) directly into an in-app WebView, with no host allowlist. Attacker-controlled HTML loaded inside the trusted app shell, with no address bar or trust indicator visible.
  • The signed-in WebView shared localStorage with the rest of the app, so attacker JavaScript could read the logged-in user's email, user id, first name, city, and avatar URL.
  • The unauthenticated email-OTP endpoint accepted any email as a request body. Multiple unredeemed codes were valid simultaneously for 30 minutes and were not invalidated when a new one was sent.

The Attack

  1. The attacker minted a branded short link using the leaked deep-link key, pointing at a server they controlled. The deep-link service rendered an Open Graph preview card with the brand's name and an inviting "Confirm your account" title.
  2. The attacker sent the link by SMS, WhatsApp, or any chat app to a phone number list. Recipients without the app saw a plain preview and ignored it. Recipients with the app installed had it open automatically when they tapped.
  3. Inside the app's WebView, attacker JavaScript read the user's identity from localStorage and rendered a "Hi <FirstName>, confirm it's you" page with the victim's real avatar, city, and email.
  4. The attacker page silently POSTed to the public OTP-send endpoint with the victim's email. A genuine verification email arrived in the victim's inbox within seconds.
  5. The victim typed the 6-character code into the fake form. The attacker server captured it and showed a "you're all set" confirmation.
  6. From a separate device, the attacker entered the victim's email at the real login screen and typed the captured code. The server accepted it (concurrent OTPs are valid simultaneously), and the attacker landed on the victim's logged-in home screen.

Cross-platform reach from a single mint

The same deep-link key and the same Universal Link domain were registered in both the Android and iOS binaries. A single minted link served victims on both platforms.

The Impact

Anyone with the app installed could be taken over. The attacker needed no prior knowledge of the victim, not their email, not whether they had an account. The blast list self-filtered to real users because only devices with the app registered would dispatch the link into the WebView.

After takeover, the attacker had full access to tickets, billing details, account email, address, phone number, and transfer/payment flows. Refresh tokens were long-lived and replayable.

Remediation

  • Rotate the live deep-link key shipped in both binaries. Use per-platform keys so a future leak does not affect both audiences. Restrict the rotated key's link-creation permissions in the deep-link dashboard.
  • Enforce the URL allowlist on every destination field in the deep-link service, not only the platform-specific fallbacks.
  • Add a host allowlist in the mobile deep-link router so the WebView only loads URLs whose host is in a company-controlled list. Reject everything else.
  • Render a visible address bar or trust indicator in the in-app WebView whenever the loaded host is not first-party. This single change removes the phishing surface even if the other items are bypassed.
  • Invalidate any previously issued unredeemed OTP codes when a new code is sent for the same email.
  • Tighten the OTP validity window from 30 minutes to roughly 5 minutes for fresh logins, and bind the OTP to the device that requested it.
  • Add device, IP, approximate location, and a "wasn't you?" link in the OTP email body.