Mandiant’s Red Team recently discovered a widespread vulnerability affecting Android devices that permits local privilege escalation to the built-in user “radio”, making it so an attacker can potentially perform activities such as viewing the victim’s SMS database and phone history. The vulnerability exists in a software package maintained by Qualcomm that is available from the Code Aurora Forum. It is published as CVE-2016-2060 and security advisory QCIR-2016-00001-1 on the Code Aurora Forum. We have provided general details in an FAQ, and a technical analysis of the vulnerability follows.
What is CVE-2016-2060?
CVE-2016-2060 is a lack of input sanitization of the "interface" parameter of the "netd" daemon, a daemon that is part of the Android Open Source Project (AOSP). The vulnerability was introduced when Qualcomm provided new APIs as part of the "network_manager" system service, and subsequently the "netd" daemon, that allow additional tethering capabilities, possibly among other things. Qualcomm had modified the "netd" daemon.
How many devices are affected?
There is no solid answer. Since many flagship and non-flagship devices use Qualcomm chips and/or Qualcomm code, it is possible that hundreds of models are affected across the last five years. To provide some API numbers, Android Gingerbread (2.3.x) was released in 2011. This vulnerability was confirmed on devices running Lollipop (5.0), KitKat (4.4), and Jellybean MR2 (4.3), and the Git commit referenced in the post is Ice Cream Sandwich MR1 (4.0.3).
How is the issue being addressed?
Qualcomm has addressed the issue by patching the "netd" daemon. Qualcomm notified their customers (all of the OEMs) in early March 2016. The OEMs will now need to provide updates for their devices; however, many devices will likely never be patched.
FireEye reached out to Qualcomm in January 2016 and subsequently worked with the Qualcomm Product Security Team to coordinate this blog release and security advisory. When contacted by FireEye, Qualcomm was extremely responsive throughout the entire process. They fixed the issue within 90 days – a window they set, not FireEye. FireEye would like to thank Qualcomm for their cooperation throughout the disclosure and diligence with addressing the issues.
Google has included this issue in its May 2016 Android Security Bulletin.
How would an attacker exploit this vulnerability?
There are two ways to exploit this vulnerability, though this does not account for a determined attacker who possesses additional vulnerabilities. The first is to have physical access to an unlocked device, and the second is to have a user install a malicious application on the device.
Any application could interact with this API without triggering any alerts. Google Play will likely not flag it as malicious, and FireEye Mobile Threat Prevention (MTP) did not initially detect it. It’s hard to believe that any antivirus would flag this threat. Additionally, the permission required to perform this is requested by millions of applications, so it wouldn't tip the user off that something is wrong.
What could an attacker do if they successfully exploit this vulnerability?
On older devices, the malicious application can extract the SMS database and phone call database, access the Internet, and perform any other capabilities allowed by the "radio" user. Some examples of potential capabilities of the "radio" user are presented in the blog itself, though it was difficult for all of these to be tested.
Newer devices are affected less. The malicious application can modify additional system properties maintained by the operating system. The impact here depends entirely on how the OEM is using the system property subsystem.
It should be noted that once the vulnerability is exploited, there is no indication to the user that something has happened. For example, there is no performance impact or risk of crashing the device.
Are only Android devices affected?
Since this is an open-source software package developed and made freely available by Qualcomm, people are using the code for a variety of projects, including Cyanogenmod (a fork of Android). The vulnerable APIs have been observed in a Git repository from 2011, indicating that someone was using this code at that time. This will make it particularly difficult to patch all affected devices, if not impossible.
Is this vulnerability being actively targeted or exploited?
No. The MTP team is monitoring usage of this API, but has not discovered anything.
Are FireEye customers protected?
FireEye MTP customers will be able to detect attempted exploitation of this vulnerability.
Next we will dive more deeply into CVE-2016-2060 and demonstrate how an attacker can exploit the vulnerability, but first an introduction to Android system services.
Understanding System Services
A system service is similar to a regular bound service found in an Android application, but a system service typically runs in a privileged process, such as the “mediaserver” or “system_server”. These services are the core of Android, and there are currently 99 system services registered on a default emulator build for Android Marshmallow. When USB debugging is enabled on a device, the `service` utility can be used to list system services registered on the device, shown in Figure 1.
Figure 1: Listing system services using 'service' utility
To illustrate how system services play a role on Android devices, we walk through the process of sending a text message (SMS) from an Android application. Figure 2 shows a Java snippet that can be used to send an SMS message with the content of “Test” to the number 1234567890:
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(“1234567890”, null, “Test”, null, null);
Figure 2: Sending an SMS message from an application (from StackOverflow)
The code first obtains the SmsManager object associated with the default subscription ID using the static method “getDefault()”, and then calls the method “sendTextMessage(..)” with the appropriate arguments. The SmsManager class contains other SMS-related methods such as “downloadMultimediaMessage(..)” and “sendMultipartTextMessage(..)”.
Next, we’ll look at the Java source for the “sendTextMessage(..)” method of the SmsManager class used previously. For simplicity, we view the source for the SmsManager class included as part of Android 4.4 (“KitKat”), shown in Figure 3.
Figure 3: Java source of “sendTextMessage(..)” method
This method performs two functions: it first performs basic argument checking and then attempts to interact with a system service called “isms”. The “getService(..)” method of the ServiceManager class is used to obtain an IBinder object, which is then cast to an ISms object by using the “asInterface(..)” method. At this point, methods can be called from the “isms” system service using the Binder interface, which in this case is the method “sendText(..)”. Note that use of the ServiceManager class is not available to application developers using the Android SDK. This indicates that the SmsManager class is merely a wrapper for the “isms” system service, and this particular pattern is consistent across many other APIs such as location and telephony.
Calling System Services Directly
While Google does not recommend bypassing their official APIs to interact with a system service directly, it is possible to do so. To do this in an application, a developer has to import and use non-standard APIs (namely the aforementioned ServiceManager class). As an alternative, the `service` utility mentioned earlier can be used from the command line. In order to utilize the `service` utility, we need to gather additional pieces of information: the transaction ID of the method we would like to call and information regarding the arguments.
Obtaining a Transaction ID
When creating a bound Service in an Android application, the first step is to define the service interface using the Android Interface Definition Language (AIDL). When an AIDL file is compiled by the `aidl` utility during the application build process, a unique identifier is stored for each method of the interface in the form of a static field of the “Stub” inner-class. Developers rely on the method names and not these transaction IDs, as they will change between API builds and across vendors.
To illustrate this for the previous SMS example, we investigate the class “com.android.internal.telephony.Isms.Stub”, which is typically found on a device in the system JAR file “/system/framework/framework.jar”. By disassembling this JAR, we can determine the transaction ID for the method “sendText()”, which is 0xb hexadecimal or 11 decimal on this device, shown in Figure 4.
Figure 4: Obtaining the transaction ID of the “sendText(..)” method
Determining Method Arguments
Now that we understand the transaction ID, we need to determine how to interact with the method. If the system service of interest is public like the “isms” service, the AIDL source can be reviewed. If it is not, the service implementation needs to be reversed in order to determine the purpose of each argument. If we dissemble the inner-class “com.android.internal.telephony.Isms.Stub.Proxy” included in aforementioned “framework.jar”, we should be able to get an idea of what each of the augments represent and determine the return type. Figure 5 shows the arguments and return value for the “sendText(..)” method.
Figure 5: Method prototype of “sendText(..)” method
In the screenshot above we see that the “sendText(..)” method takes six arguments, denoted by the “pn” notation, and returns a void, as denoted by the trailing “V” in method prototype.
Putting it All Together
Now we can use the `service` utility to interact with the “isms” service and call the “sendText(..)” method. Figure 6 shows the syntax for the `service` utility to call a system service’s method.
service call service_name transaction_id [arguments]
Figure 6: 'service
utility syntax to call service method
The 'service' utility accepts string, integer, and null value arguments, represented by “s16”, “i32”, and “null”, respectively. Note that for anything more complex, a Java application must be used. We can now call the “sendText(..)” method, which has the transaction ID of 11, of the “isms” system service to send a message, shown in Figure 7.
adb shell service call isms 11 s16 "com.fake" s16 "1234567890" s16 "1234567890" s16 "Test" i32 0 i32 0
Figure 7: Invoking “sendText(..)” using 'service' utility
This command shown sends an SMS message with the content of “Test” to the number 1234567890, similar to the Java snippet in Figure 2.
Starting from the Top
Device manufactures and other non-Android Open Source Project (AOSP) vendors add and modify system services on a regular basis. From an attacker’s perspective, new or changed APIs in system services are a prime target. A researcher can enumerate these by comparing the static fields beginning with “TRANSACTION_” found within the “Stub” inner-class of system services on a target device to that of the AOSP. During one such review, we discovered two methods added to the “network_management” system service called “addUpstreamV6Interface(..)” and “removeUpstreamV6Interface(..)”, found in “android.os.INetworkManagementService.Stub”, which is part of the system JAR “/system/framework/framework.jar”. This is depicted in Figure 8 and Figure 9.
Figure 8: “addUpstreamV6Interface(..)” transaction ID 0x1e (30)
Figure 9: “removeUpstreamV6Interface(..)” transaction ID 0x1f (31)
The “addUpstreamV6Interface(..)” method accepts a single string argument, interface_value, and returns void. Viewing the disassembled method “addUpstreamV6Interface(..)” of the class “com.android.server.NetworkManagementService” found in the system JAR “/system/framework/services.jar” indicated that when called, the method passed interface_value to the native daemon “netd” by writing the string shown in Figure 10 to the UNIX socket “/dev/socket/netd”.
tether interface add_upstream interface_value
Figure 10: Command sent to “netd” native daemon
From here, analysis of the “/system/bin/netd” indicated that this daemon executed “/system/bin/radish” using the “execv(..)” function which executes the shell command depicted in Figure 11.
/system/bin/radish –i interface_value –x -t
Figure 11: Arguments passed to “/system/bin/radish”
The “/system/bin/radish” executable then passed the interface_value to the `brctl` utility using the “system(..)” function, using the syntax depicted in Figure 12.
brctl addif bridge0 interface_value
Figure 12: interface_value present in `brctl` command
It is at this point we witness the code execution capabilities of CVE-2016-2060: any value passed to the “addUpstreamV6Interface(..)” is ultimately passed to the “system()” function without being sanitized or validated. A trivial example of this can be achieved with the `service` command shown in Figure 13, which prints the output of the `id` command to the Android log buffers:
adb shell 'service call network_management 30 s16 '\''fake; log -t radio_exe "`id`"'\'''
Figure 13: 'service' command to write the output of 'id' to Android log buffers
This command passes the interface_value of '\''fake; log -t radio_exe "`id`"'\''' to the “/system/bin/radish” executable, which calls the “system()” function with the string shown in Figure 14.
brctl addif bridge0 fake; log -t radio_exe "`id`"
Figure 14: Command injection into `brctl` “system()” function
We can check the Android log buffers to capture the output of our injected commands using the `logcat` utility, shown in Figure 15.
Figure 15: Output of 'id' captured in the Android log buffers
The output of the `id` command indicates that we are running as the Linux UID 1001 (“radio”) and under the SEAndroid context of “netd”. Next, we explore how an attacker would take advantage of this vulnerability and what actions this permits the attacker.
The most feasible way of exploiting CVE-2016-2060 is by creating a malicious application. A malicious application needs only to request access to the “ACCESS_NETWORK_STATE” permission, a widely requested permission. Figure 16 shows how the “addUpstreamV6Interface(..)” method can be used to inject the command 'id'.
Figure 16: Malicious application calling “addUpstreamV6Interface(..)”
Since the commands above are executed as the user “radio”, data owned by applications that are also running as “radio” are accessible to the malicious application. On stock Android devices, this includes the Phone application and the Telephony Providers application, both of which contain sensitive information. The “radio” user also inherits several system permissions not accessible to a third-party application. A short list includes:
- WRITE_SETTINGS_SECURE – Change key system settings
- BLUETOOTH_ADMIN – Discover and pair with Bluetooth devices
- WRITE_APN_SETTINGS – Change APN settings
- DISABLE_KEYGUARD – Disable the key guard (lock screen)
Whether or not an attacker can abuse these permissions depends on the specific model, and may not be possible across all devices.
Beginning in Android 4.4 (“KitKat”), devices utilize Security Enhancements for Android™ (SEAndroid) and set enforcing mode by default. Devices running SEAndroid in this mode are not impacted as significantly as older devices. The “netd” context that the “/system/bin/radish” executable runs as does not have the ability to interact with other “radio” user application data, has limited filesystem write capabilities and is typically limited in terms of application interactions. The “netd” context does have the ability to alter “system_prop” properties of the Android property subsystem, which includes the “service.”, “persist.sys.”, “persist.service.”, and “persist.security.” property keys. Depending on how a particular device manufacture utilizes these properties, it is possible to further compromise a device by altering these properties.
CVE-2016-2060 has been present on devices since at least 2011 and likely affects hundreds of Android models around the world. This vulnerability allows a seemingly benign application to access sensitive user data including SMS and call history and the ability to perform potentially sensitive actions such as changing system settings or disabling the lock screen. Devices running Android 4.3 (“Jellybean MR2”) or older are the most affected by the vulnerability, and are likely to remain unpatched. Newer devices utilizing SEAndroid are still affected, but to a lesser extent.
FireEye would like to thank Qualcomm for both working diligently to address CVE-2016-2060 and for supporting the release of this blog post. For FireEye Mobile Threat Prevention (MTP) customers, FireEye has added detection of CVE-2016-2060 exploitation on devices as of writing this blog post.