May 9, 2023

    Protecting Against StrandHogg

    StrandHogg exploits an oversight in Android’s task management that allows a malicious application to insert a malicious activity at the top of the task stack for a targeted application. In other words, it lets applications impersonate other applications and e.g. steal sensitive information.

    Two variants of StrandHogg vulnerabilities have been identified. The first variant (v1) involves setting the android:taskAffinity attribute to the task affinity of the targeted application. This is easy to detect, which means that Google Play will reject such applications. The second variant (v2) uses somewhat more complex code to inject the malicious activity, and so is more difficult to detect automatically.

    In this blog post, we cover which applications are vulnerable and several measures you can take to protect your own application.

    Should I care about StrandHogg Attacks?

    v1 works on devices running Android up to and including version 10, while v2 works on devices running Android up to and including version 9. This means that Android 11 devices are completely protected from StrandHogg.

    How do I protect against StrandHogg Vulnerabilities?

    The chart below will guide you through how to protect your own application, starting from the top left.

    A few things to keep in mind as you read the chart:

    • Setting android:taskAffinity=”” for all exported activities will prevent you from using functionality that involves maintaining multiple tasks for your application at the same time.
    • The Android Developer Docs recommend against using android:launchMode=”singleInstance” since it has major implications for user interaction. See bonus section 1 at the bottom of this post for the details.
    • The source code for an app that demonstrates the POC techniques is included with this blog post. The remainder of the post describes how the POC works in detail.

    Protecting-Against-StrandHogg-1

    POC detection app

    The principles behind the techniques that are employed in this app are based on a paper by researchers at the University of Würzburg with the title “RIP StrandHogg: A Practical Detection Method on Android”. We encourage you to read it if you want an in-depth explanation of StrandHogg and how the detection techniques were discovered.

    Protection mechanism

    It is possible to launch a minimized activity that is unnoticeable to the user by using FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS and calling moveTaskToBack(true) in its onCreate(). Android will then move the task the activity belongs to into the background. Due to the specified flag, Android will also destroy the activity, but preserve the reference to it.

    When the app is launched after this, Android will revive the already existing task and move it into the foreground instead of creating a wholly new task. Crucially, Android will again call onCreate() on the already existing minimized activity. This behavior not only applies when the user launches the app, but also whenever either StrandHogg variant does so.

    However, if the user moves the app to the background and sometime later brings the app to the foreground again, Android does not call the minimized activity’s onCreate().

    We therefore have two places in the app where we should try to detect StrandHogg in Android:

    • The onCreate() method of the minimized activity, to cover the case where the user has not previously launched the app
    • As part of a foreground service, to cover the case where the user has previously launched the app, but then moved it to the background

    Architecture

    This diagram shows all components that make up the app, and how they interact:

    Protecting-Against-StrandHogg-2

    The app consists of the following activities:

    • MinimizedActivity, the entry point into the app. This activity simply has a blank screen.
    • MainActivity, which has the first meaningful content the user sees
    • SecondActivity, ThirdActivity and FourthActivity which are activities the user navigates to when they press the “Next screen” button

    When the user launches the app, MinimizedActivity will immediately launch MainActivity and then close itself.

    The app includes a MyBootReceiver which receives the BOOT_COMPLETED action. This means it runs after the device has fully booted. It simply starts MinimizedActivity and immediately moves it into the background.

    The app also has a RestartMinimizedActivityService. This service starts MinimizedActivity again and moves it to the background if the app is closed.

    With this receiver and service, we guarantee that a MinimizedActivity reference remains present in the system if the user has not launched the app yet or after the user closes the app.

    Finally, the app contains a ForegroundDetectionService. Whenever the MainActivity is stopped, we start the ForegroundDetectionService. We stop the service whenever we resume MainActivity. This service runs the detection procedure every two seconds.

    Attack detection procedure

    The procedure starts with retrieving all tasks that are running for this app with ActivityManager::getRunningTasks(). It then checks the numActivities property for each task. This number must never be greater than the amount of activities that are created legitimately through regular use of the app. Otherwise, we forcibly finish and remove all tasks for the app. Note that RestartMinimizedActivityService will immediately place a new minimized activity reference.

    When the app has fully initialized and reached the initial screen, the allowed numActivities is 1. The allowed limit increases by 1 whenever you navigate to the next activity, and decreases by 1 when navigating back to the previous activity.

    This procedure is generally effective at detecting StrandHogg attacks. Android still throws us some curveballs which we have to account for; see bonus section 2 if you’re interested in how we catch those.

    Conclusion

    We have provided an overview of which applications are vulnerable to StrandHogg. There are some measures that can protect against it which are very simple to activate. Unfortunately, they each have various tradeoffs. If these tradeoffs are a dealbreaker, we’ve showcased a technique which does not impact the functionality of your app, but requires a little more effort to integrate.

    Bonus section 1: Launch mode singleInstance

    You can summarize the Android documentation for launch mode singleInstance as follows:

    If an instance of the activity currently exists, it is always the single activity of its own dedicated task.

    Imagine your application currently has one task that contains two activities:

    Protecting-Against-StrandHogg-3

    Now, imagine you’ve declared an activity C, and it’s configured with launch mode singleInstance. If you launch this activity C, your application will have two tasks:

    Protecting-Against-StrandHogg-4

    If activity C launches another activity D, it will end up at the top of task 1:

    Protecting-Against-StrandHogg-5

    If all exported activities are declared as singleInstance, it is indeed impossible for StrandHogg to insert a malicious activity on top. However, it’s also clear why the official Android documentation states it isn’t appropriate for most applications. In this example, if the user returns to task 1 and pops activity D, they will next see activity B instead of activity C, as they would probably expect. The navigation behavior that arises from singleInstance is less intuitive than the default behavior.

    Bonus section 2: Implementation gotchas

    Naturally, some details of the implementation are there to deal with curveballs that Android throws at us. This bonus section describes these problems and how we solve them.

    Duplicate instances on app launch

    When the user initially launches the app, it is possible that Android creates the task for the app with two instances of the app’s launchable activity. For this reason, when we detect that both the base activity and the top activity are instances of MinimizedActivity, we use an allowed numActivities of 2 instead of 1. Otherwise, a legitimate launch of the app will mistakenly be considered a StrandHogg attack, and the app will crash immediately after the user taps its icon.

    Android 9 and MinimizedActivity

    Starting from Android 9, if a task is created for the app which does not have MinimizedActivity as its root, Android will discard the already existing task with the minimized activity reference. This means that the minimized activity’s onCreate() will not be called when StrandHogg attacks it. This leaves the app vulnerable to StrandHogg v1, and also to v2 if the v2 attack app targets an activity different from MinimizedActivity for which android:exported is set to true.

    We can work around this simply by making the minimized activity launch a new instance of itself with the exclude from recents flag set in its onDestroy().

    Thomas Vochten

    Discover how Guardsquare provides industry-leading protection for mobile apps.

    Request Pricing

    Other posts you might be interested in