We have recently disclosed a new vulnerability to the Android Security Team. The vulnerability affected many apps, including Settings (the one that is found on every Android device), Gmail, Google Now, DropBox and Evernote. To be more accurate, any App which extended the PreferenceActivity class using an exported activity was automatically vulnerable. A patch has been provided in Android KitKat. If you wondered why your code is now broken, it is due to the Android KitKat patch which requires applications to override the new method, PreferenceActivity.isValidFragment, which has been added to the Android Framework.

In this blog post we will begin with a short introduction on Android and its sandbox, and then we will deep-dive into the vulnerability itself. For the sake of simplicity we omitted some of the details which can be found in our whitepaper.

The UI building blocks of Android Apps

The UI of Android apps is made of activities. An Activity provides a single screen with some functionality (for instance: a browser’s bookmarks manager). A Fragment can be considered as a sub-activity. It is a piece of the App’s UI. Fragments provide some flexibility as they allow reuse in different activities. While a Fragment instance is coupled with the Activity it resides inside, different instances can be embedded in different activities. See Figure 1 for the relation between activities and fragments.

Figure 1: The relation between activities and fragments.

Android Sandboxing, Permissions, Inter-app communication and Attacks by Malicious Apps

In Android, apps are isolated from each other and subject to their declared set of permissions by the use of Sandboxing. An app cannot normally access sensitive data of another. That being said, it is possible for one app to invoke application components (such as activities) of another app for feature reuse. For example, Chrome invokes the Google play app when opening Google Play URLs. The invocation is done using Intents which are IPC objects that the source application passes to the corresponding API. Intents do not just specify the target, they also contain data in two places. The first location is the data attribute (of URI type) and the second one is a dictionary (Bundle) that can contain an arbitrary amount of information (also known as Intent extras). Activities can be invoked by an external app if they are exported in the application’s manifest file (ApplicationManifest.xml). Making an activity public (exported) creates a potential hole in the Android Sandbox. Since activities access the input Intent’s data, a bad app can invoke the exported activity and provide it with malicious data, that may trigger vulnerabilities if the data are not properly santizied or validated in the target application. As of Fragments, they can receive input by accessing the embedding activity thus its input Intent or as Fragment-specific arguments. See Figure 2 for an attack outline.

Figure 2: Attacking exported activities.

The PreferenceActivity

The PreferenceActivity is a base class (provided by the Android Framework) for showing a hierarchy of preferences to the user. The preferences are associated with a PreferenceFragment. The PreferenceActivity consumes a few Intent extras. One of them is :android:show_fragment which tells the PreferenceActivity which fragment to display first. The dynamic Fragment loading is done by a chain of calls which start at the activity creation and end at Fragment.instantiate (see Figures 3,4). The latter loads the Fragment using the Java Reflection API.

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT); Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); if (savedInstanceState != null) { } else { if (initialFragment != null && mSinglePane) { // If we are just showing a fragment, we want to run in // new fragment mode, but don't need to compute and show // the headers. switchToHeader(initialFragment, initialArguments); } else { if (mHeaders.size() > 0) { if (!mSinglePane) { if (initialFragment == null) { } else { switchToHeader(initialFragment, initialArguments); } } } } } } public void switchToHeader(String fragmentName, Bundle args) { setSelectedHeader(null); switchToHeaderInner(fragmentName, args, 0); } private void switchToHeaderInner(String fragmentName, Bundle args, int direction) { getFragmentManager().popBackStack(BACK_STACK_PREFS, FragmentManager.POP_BACK_STACK_INCLUSIVE); Fragment f = Fragment.instantiate(this, fragmentName, args); FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); transaction.replace(com.android.internal.R.id.prefs, f); transaction.commitAllowingStateLoss(); }

Figure 3: Chain of calls in PreferenceActivity ending at Fragment.instantiate (as implemented in Android 4.3)

public static Fragment instantiate(Context context, String fname, Bundle args) { try { Class<?> clazz = sClassMap.get(fname); if (clazz == null) { // Class not found in the cache, see if it's real, and try to add it clazz = context.getClassLoader().loadClass(fname); sClassMap.put(fname, clazz); } Fragment f = (Fragment)clazz.newInstance(); if (args != null) { args.setClassLoader(f.getClass().getClassLoader()); f.mArguments = args; } return f; } ... }

Figure 4: Android 4.3 implementation of Fragment.instantiate.

The Vulnerability

A malicious application can invoke any exported PreferenceActivity class and supply it with an :android:show_fragment Intent extra in order to make it load an arbitrary class. The goal of the attacker is to execute some code that will break the Android Sandbox, i.e. access sensitive information pertaining to the vulnerable app, or abuse its permissions. Since the attacker cannot supply his own classes as he is limited to ones found under the class loader of the vulnerable app (Android Framework classes and the App classes), the attack is not trivial. In our whitepaper we depict a couple of exploit techniques, one of them is pretty cool. The malicious app can make the PreferenceActivity load an arbitrary Fragment of the vulnerable app, which is normally loaded inside a non-exported Activity to pratically take it from its natural habitat to a hazardous area where input should not be trusted as the malicious app can now control it. See Figure 5 for the attack outline.

Figure 5: Attacking a Fragment

Attacking the Android Settings

As we mentioned above, all apps which make use of PreferenceActivity are vulnerable. We targeted the Settings app since it is a highly privileged application. By exploiting the the vulnerability we managed to successfully subvert its integrity. The app’s main activity (which is exported), com.android.settings.Settings, extends PreferenceActivity thus it is vulnerable. We searched for interesting fragments in the app’s package, one of them is ChooseLockPassword$ChooseLockPasswordFragment. This Fragment is responsible of handling the credentials changing of the device’s lock screen and is subject to the Device Management Policy. The Fragment’s natural habitat is the ChooseLockPassword Activity which is non-exported. Normally the Fragment first asks the user to insert his old credentials (see Figure 6), unless the embedding activity is supplied with an Intent extra, ‘confirm_credentials’ set to false.

Figure 6: Normal behavior of ChooseLockPasswordFragment

Since the activity is not exported, this parameter cannot be easily manipulated by a malicious app, however by exploiting the Fragments Injection vulnerability, we can embed the ChooseLockPassword$ChooseLockPasswordFragment inside an exported activity, com.android.settings.Settings, and supply it with malicious data, confirm_credentials set to false. See Figure 7 for the attack outline, and Figure 8 for the result. Since this exploit requires user-intervention it cannot be used remotely thus it requires a physical attacker that wants to change the credentials. Please note that by exploiting this vulnerability the attacker can also override Device Management Policy such as the minimum password requirements.

Figure 7: Attacking Android’s Settings

Figure 8: User does not need to supply his old credentials

The Fix

Google has provided a patch in Android 4.4 KitKat by adding a new protected API, PreferenceActivity.isValidFragment, which is called before the Fragment is dynamically instantiated by PreferenceActivity (see Figure 9). The isValidFragment method must be overridden otherwise the default implementation throws an exception, as documented in the SDK Reference. We encourage developers to properly implement this method (e.g. as a white-list) since a weak implementation will keep your app vulnerable.

private void switchToHeaderInner(String fragmentName, Bundle args, int direction) { getFragmentManager().popBackStack(BACK_STACK_PREFS, FragmentManager.POP_BACK_STACK_INCLUSIVE); if (!isValidFragment(fragmentName)) { throw new IllegalArgumentException("Invalid fragment for this activity: " + fragmentName); } Fragment f = Fragment.instantiate(this, fragmentName, args); FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); transaction.replace(com.android.internal.R.id.prefs, f); transaction.commitAllowingStateLoss(); }

Figure 9: Potentially safe Fragment instantiation in Android KitKat.

Vulnerable versions

Android 4.3 Jelly Bean and below.

Non-Vulnerable versions

Android 4.4 KitKat.

Disclosure Timeline

12/05/2013 Reply from Android Security Team: “Issue is fixed”.

12/05/2013 Requested a status update.

11/11/2013 Reply from Android Security Team: “Fix in progress”

10/24/2013 Requested for status update.

07/14/2013 Reply from Android Security Team: “We are now looking into the issue”.

07/12/2013 Disclosure to Android Security Team.



About the IBM Application Security Research Team

The Application Security Research Team researches new security threats and vulnerabilities and provides regular rule updates to AppScan.