Android: arbitrary code execution via third-party package contexts

Introduction
There are apps for Android that have the ability to add extra functionality by using external modules. Some load native libraries or third-party dex or app files, but in this article we will be looking at modules that are installed from markets as third-party apps and are used by the main app as a set of additional functions: camera filters, themes, font sets, etc. According to Oversecured statistics, at least one in every 50 popular apps are subject to this vulnerability.

post-header-image

After the module is installed, the main app begins to search for it among all apps installed on the same device by using certain templates (package name prefix, values from AndroidManifest.xml). If the verification is weak, an attacker's app may be treated as a legitimate module. At the moment the module is loaded, code from it is executed in the context of the main app – leading to arbitrary code execution. As a result, the attacker may gain the ability to steal any sensitive data that the user enters into the app or that the app receives from the server, as well as to substitute this data, disclose financial details, and track the user.

The essence of the vulnerability
Such apps can scan the device as follows

public static void searchModules(Context context) {
    for (PackageInfo info : context.getPackageManager().getInstalledPackages(0)) {
        String packageName = info.packageName;
        if(packageName.startsWith("com.victim.module.")) {
            processModule(context, packageName);
        }
        //...

and then work, generally insecurely, with these third-party apps

public static void processModule(Context context, String packageName) {
    Context appContext = context.createPackageContext(packageName, CONTEXT_INCLUDE_CODE | CONTEXT_IGNORE_SECURITY);
    ClassLoader classLoader = appContext.getClassLoader();
    try {
        Object interface = classLoader.loadClass("com.victim.MainInterface").getMethod("getInterface").invoke(null);
        //...

In this example the vulnerable app obtains the ClassLoader of any app whose package begins with com.victim.module., tried to find com.victim.MainInterface and call its getInterface method. The danger is that an attacker can create their own app with a package name that begins with the right prefix, create the specified class with this method, and include in that method code that will then be executed in the context of the victim app.

package com.victim;

public class MainInterface {
    public static Object getInterface() {
        try {
            Runtime.getRuntime().exec("...attacker..controlled..command...").waitFor();
        }
        catch (Throwable th) {
        }
        return null;
    }
}

In fact, apps cannot just call methods using the Java Reflection API: they can also create class instances, obtain field values, etc., all of which also leads to arbitrary code execution.

Oversecured finds this kind of vulnerability automatically: for an example see our Oversecured Vulnerable Android App (https://github.com/oversecured/ovaa) and Sample Report (https://oversecured.com/oversecured_sample_report.pdf)

vulnerability

Remedy
A good defense in this case would be to verify the signature of the current app and the module. The method could be rewritten as follows:

public static void searchModules(Context context) {
    PackageManager packageManager = context.getPackageManager();
    for (PackageInfo info : packageManager.getInstalledPackages(0)) {
        String packageName = info.packageName;
        if(packageName.startsWith("com.victim.module.")
                && packageManager.checkSignatures(packageName, context.getPackageName()) == PackageManager.SIGNATURE_MATCH) {

            processModule(context, packageName);
        }
        //...

The attacker would not be able to sign their app with the same certificate that had been used to sign the app that was being attacked, which would prevent the illegitimate module being loaded.