Privilege Escalation Attacks on Android
Android is the most popular mobile operating system in the world today. It allows users to customize their phones by installing third party applications which lets them do a host of activities from browsing the web to playing games to scheduling their activities to listening to music and many more. Android provides tools that make the development of such apps simple. Moreover, the Android project is open source with free access to documentation, which means that anyone can examine the Android model, read the documentation and develop applications on it. This has led to an explosion of apps, which is a good thing for the users in general. However, a lack of proper understanding of Android’s powerful features can lead to gaping security holes that could easily be exploited by attackers to gain unauthorized access to private information. This report tries to understand the security model of Android and the rules that should be followed in order to build secure apps. The report also looks at the kind of attacks that can take place if these rules are not followed. Finally it briefly discusses current and future research on how to mitigate such attacks.
Android is built on top of the Linux kernel. Therefore, it inherits all the security features of the linux kernel like isolation of apps through sandboxing. This means that, every app runs inside it own space and does not have access to the data of other apps. Inter app communication takes place through a permission system which we shall describe later. The core Android libraries along with the Android Runtime System (ART) are built on top of the Linux kernel and use it to communicate with the phone hardware. The core Android libraries are mostly written in C/C++. The Android Runtime System (ART) consists of a few more libraries and the Dalvik Virual Machine(DVM). The DVM is an optimized JVM created especially to run on mobile phones. Every app in the Android system resides in a separate process and runs on a dedicated DVM. The application framework is built on top of the libraries and the ART. The application framework provides numerous high level services or APIs to app developers in the form of Java classes. The last layer is the application layer. All Android apps are developed and installed on this layer. Examples of such applications are Contacts Book, Browsers, SMS and other third party apps. A diagram of the Android architecture is given below.
An Android app is made of four basic components. All classes and methods are extended or derived from them. The components are as follows:
Activity: User interaction in Android takes place through the Activity base class. The activity class takes care of creating a window in which UI can be placed. The activity class has many methods like onCreate(), onStart, onStop(), onPause(),onResume() and onDestroy(). These methods are invoked whenever an event related to an activity happens. For instance, if a window is opened up the onStart() method is called. These methods should be implemented by any class that extends the Activity class.
Service: A service is an application component that can perform long –running operations in the background. Typically services do not have any UI and our mostly associated with an activity, which means that they run on the same thread as an activity but as a background process.
Broadcast Receivers: Broadcast receivers are components that sit in the background and listen for incoming requests. They are mostly used to relay data between activities or services of different app or the same app.
Content Providers: These are mini databases that are associated with an app. They are basically stored in an app’s file system and are protected from outside access unless the developer makes some exceptions.
Intent: Intents are classes that are used for both inter and intra app communication. Communication requests and responses are encapsulated in an intent object and are sent to or received from the various Android components. Intents can be used to start Activities; start, stop and bind Services; and broadcast information to Broadcast Receivers. Intents can be of two types – explicit and implicit. Explicit intents know exactly who the data is being sent to or from where the data is coming. Implicit intents on the other hand are system wide objects that are broadcast and resolved at runtime. Implicit intents are a cause of worry from a security perspective as we shall see later.
Android’s Permission Model
The linux kernel ensures that every app runs inside its own process. Every app has a user Id and has access to resources allowed under that user Id. This is called sandboxing which isolates apps from each other. However, Android has a very complex communication model which allows apps to easily talk to each other provided they have the right set of permissions. For instance, if an app wants to make phone calls then it can do so by communicating with the phone app. In order to do so it has to declare this permission in its manifest file. When the app is installed by a user, Android asks the user if this app should be allowed to make phone calls. If the user agrees then the app is installed else it is rejected. Similarly, if an app wants to access the Internet then it should have the necessary permission to do so. Also, apps can define their own permissions and declare them in the manifest file. Any other app that wants to communicate with this app should have those permissions defined in their manifest file. This is Android’s way of allowing inter-app communication at the same time protecting resources from unauthorized access. All permissions have a protection level that determines how difficult the permission is to acquire. There are four protection levels:
There are four protection levels:
Normal permissions are granted automatically.
Dangerous permissions can be granted by a user during installation. If the permission request is denied, then the application is not installed
Signature permissions are only granted if the requesting application is signed by the same developer who created the permission.
Signature or System permissions are granted if the application meets the signature requirement or if the application is installed in the system applications folder. System applications are mostly pre-installed by a device manufacturer or manually installed by an advanced user on a rooted device.
Quite clearly the last two protection levels are the strongest provided that the signatures have not been compromised. The first two protection levels are relatively weak and can be subject to exploitation. If components or apps are not properly protected by permissions they could be used maliciously to breach Android’s security model. In a sense Android’s permissions system is conceptually flawed because it does not enforce permissions but leaves it to the developer to enforce it. If a developer somehow fails to provide adequate permissions, which is not very uncommon then due to the transitive nature of the permissions system, privilege escalation attacks could take place. For instance, let us say that app A has the right permissions to access app C but app B does not have the permissions to access app C’s resources and is malicious. If app A has some vulnerability in one of its components then app B could exploit that vulnerability to gain access to app C. In the next section we will see some examples of how such attacks could take place.
Privilege Escalation Attacks
Privilege escalation attacks could take place by various methods. We look at a few common techniques like Intent spoofing attacks and the more advanced Return Oriented programming without returns attacks.
Intents are message passing objects that help apps communicate with each other. As mentioned earlier intents are of two types explicit and implicit. A malicious application can launch intent spoofing attacks by sending an intent to an exported component of a victim application that is not expecting intents from that application. This can happen when a component is exported when it was not intended to. Ideally developers should limit access to such components, but they do not always do so. To test this I created two apps one benign but with a vulnerability and one malicious app that exploits that vulnerability. Below is the sample code followed by an explanation.
public class MessagingService extends IntentService {
//code deleted for clarity
protected void onHandleIntent(Intent intent) {
//code deleted for clarity
//loop through each phone no in the hash map and send SMS to each
if(ContactBean.ContactMap!=null && ContactBean.ContactMap.size()>0)
for(String phoneNo : ContactBean.ContactMap.keySet())
smsManager.sendMultipartTextMessage(phoneNo, null, smsParts, piSent, piDeliver);
AppCommonBean.commonErrMsg = AppCommonConstantsClass.NO_CONCT_ADDED;
AppCommonBean.commonErrMsg = AppCommonConstantsClass.COMMON_ERR_MSG;
public void onDestroy() {
//code deleted for clarity
The above code is a service that runs in the background and sends SMS to all numbers in the hash map. Only the parts required for this discussion are shown. The finer details of code have been deleted. This service is part of an emergency app. The purpose of this app is to gather the current location of the user and send SMS to the user’s closest people when the user is in an emergency. The service has SMS_SEND permission which is required for sending SMS. Below is a part of the manifest.xml file of this app.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.idost"
android:versionName="1.0" >
android:minSdkVersion="11"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.INTERNET"/>
android:allowBackup="true"
android:icon="@drawable/ic_launcher4">
<service android:name="com.example.idost.service.MessagingService">
<action android:name="android.intent.action.CALL" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application></manifest>
The manifest file shows that this app has permissions to send sms. However, the Messaging Service class has been declared in the manifest with an intent filter (see the red highlighted portion). In Android if an intent filter is declared for a particular component it makes that component public by default. Therefore, any component can create an intent with a matching action and category and call this service from its own app to send random messages. When the user gets the phone bill at the end of the month he will be surprised to see the high number of messages sent from his phone. The only challenge for the attacker here is to match the intent filter. That should not be very difficult for the attacker since the developer has used a standard action string as its intent filter. Android has a fixed set of action strings which the attacker can brute-force very easily. The sample code of the malicious app is given below.
package com.example.exploityelfp;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
public class MalActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mal);
Intent serv = new Intent(Intent.ACTION_CALL);
protected void onStart() {
The above malicious activity could be part of a useful app created by a malicious attacker. When this activity is called by the user, the messaging service of the other app will be called without the user knowing anything about it since the service invocation code is in the onCreate() method of the malicious activity. The following is the manifest of the malicious app. The interesting thing to note is that this app has no permissions declared in its manifest and yet it is able to escalate its own privileges to send SMS to random phone nos.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.exploityelfp"
android:versionName="1.0" >
android:minSdkVersion="8"
android:targetSdkVersion="18" />
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
android:name="com.example.exploityelfp.MainActivity"
android:label="@string/app_name" >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
android:name="com.example.exploityelfp.MalActivity"
android:label="@string/title_activity_mal" >
<action android:name="android.intent.action.SENDTO" />
<category android:name="android.intent.category.DEFAULT" />
An attack of this sort is a classic case of Intent spoofing and is one of the most common hacking techniques used in Android. The important thing to note is that this would not have been possible if the developer of the Messaging service had been careful and had protected his service with adequate permissions in the manifest file. The best thing for the developer would have been to set the “exported” attribute to be false and use only explicit intents to communicate with the service. However, this is always not possible since services are often required to be called from other apps that the developer trusts. In such cases the developer could either use digital signatures to verify the invoker of the service or he could have used the permissions attribute in the manifest to protect his service. That would have allowed only a component with that permission to start the service.
This sort of attack can also be carried out on other components like Broadcast Receivers and Activities. In the case of broadcast receivers if an exported broadcast receiver blindly trusts an incoming broadcast intent it could operate on malicious data from that intent or it could just propagate the intent to a sensitive service or application. In case of an activity a user could be tricked to click on a button that takes her to the activity of another application, the user might then make changes to the victim app believing that she is still on the malicious app.
Return Oriented Programming without returns
A lot of Android apps use the Java Native Interface (JNI) framework to call native libraries written in C/C++. Libraries written in C/C++ are often subject to buffer overflow attacks. I borrow an exploit technique described in Davi et al. to show how buffer overflow vulnerabilities in native libraries can compromise the security of an app that uses that library. Android has all modern protection techniques like stackguard and data execution implemented in its operating system. Therefore, the only useful technique would be Return Oriented Programming (ROP). However, recent Android versions can also thwart ROP attacks because it detects the presence of instruction streams with frequent returns. Three consecutive sequences of five or fewer instructions ending in return trigger an alarm. Davi et al suggest a technique called ROP without returns to bypass this defense mechanism. If the ROP chain has no returns then this defense mechanism will not detect it. However, returns are essential for the ROP chain to actually work. Therefore, return instructions are replaced by instructions that behave like return. On both the x86 and the ARM which Android uses, such instructions exist. They are called Update-Load-Branch (ULB) instructions. These instruction sequences update the Stack Pointer; load the address of the next instruction sequence to execute based on the stack pointer’s state; and finally branch to the address loaded. However, ULB instructions are not as frequently found as return instructions. To overcome this, a single ULB instruction is designed as a trampoline. Each instruction sequence used to compose a gadget set ends in an indirect jump to the trampoline, which redirects the execution to the next set of instructions.
Given that the Android permission model does not handle transitive permissions as shown with intent spoofing attacks, the idea behind this exploit is to find an app A that makes a call to a vulnerable native library. Let us say this app has permissions to another app B in the system which has a wide set of permissions like sending sms, making calls, accessing Bluetooth, opening camera, internet access and many more. Any malicious app with no permissions to app B can exploit A to gain permission to app B and consequently to all the resources that app B has access to.
Davi et al. in their paper describe an actual scenario like this. They create a vulnerable app A which makes a call to a vulnerable library. The code of the vulnerable native library is given below.
jint Java_com_example_helloJni_HelloJni_doMapFile(JNIEnv* env , jobject thiz)
// A binary file is opened ( not depicted )
struct foo _ f = malloc(sizeof_f) ;
fgets ( f>buffer , sb.stsize , sFile ) ;
Quite clearly there is a potential buffer overflow in the above code. The setjmp function is a system function that creates a special data structure called jmp_buf to save the current state or values of the control registers like ESP, EIP, EBP etc. The function longjmp is also a system function that reads the jmp_buf data structure to restore the register values. If the jmp_buf can be overwritten by attacker controlled data before longjmp is called, then the attacker will be able to run arbitrary code. The fgets function allows the buffer variable to be written with the contents of a binary file without proper bounds checking. Writing more than 460 characters will also overflow the jmp_buf data structure because they are declared inside the same structure. However, care has to be taken to bypass the canary. Android implements a fixed canary to protect the heap, leaving 52 bytes between the jmp_buf and the canary word. The canary is hard coded into libc.so and is therefore device and process independent. So any buffer overflow will have to consider the 52 bytes of space between the canary and the jmp_buf.
Our next goal is to find an app B with a large permission set. Recently, Android has come up with the Android Scripting Environment that is a client-server app that allows developers to rapidly develop application prototypes by writing scripts and executing them from the phone itself. The interpreter is hosted on a server and the server is implemented as an Android component with a wide set of permissions. However, the server component is itself not protected by any permissions and anyone with permission to the client can invoke it. It is assumed that the device being attacked as ASE installed in it. The vulnerable app described previously has permission to the ASE client. When code in the vulnerable library is invoked through the jni, the setjmp vulnerability is exploited to cause a heap overflow. A ROP chain with the ULB trampoline is injected into the heap and a stack flip is caused by making the ESP point to the address in the heap which has the ROP chain. The ROP chain is executed till the system function which invokes the scripting shell is called. The function takes a command and a port number as input. The ASE client running on behalf of the vulnerable app establishes a socket connection with the ASE server passes the command and the port number. The ASE server executes the command like “send 100 sms to a premium-rate no” without checking any permissions and the vulnerable app is allowed to do so even if it wasn’t authorized to do so at install time thus clearly, escalating its privilege.
Privilege escalation attacks are just one class of attacks that happen due to security vulnerabilities in Android apps. There are many other types of attacks as well like intercepting implicit intents and XSS attacks through WebView. While, Android provides a basic security model through sandboxing, it also gives developers a lot of freedom to enable inter-app communication. This expands Android’s attack surface. Therefore, achieving the right balance is important. Android’s security model is enough to prevent malicious attacks if developers understand the security model properly and write secure code. However, it is not always easy to write secure code especially in times of rapid development. Therefore, the only way forward is to create tools that will help developers write secure code. There has been a lot of research on static analysis tools that detect security vulnerabilities. A lot of research has also been done on formalizing Android’s security model and creating a type based system from a security perspective. Some research has also been done on a Model Driven Development approach from a security perspective. However, MDA in Android is still in its nascent stage. The main idea behind MDA in Android is that the developer will create a communication model for her app. MDA will transform that model into a formal model and prove that it is secure. If it is not secure then the developer is asked to correct the model. However, if it is secure then code is generated from that model. I believe that a very good way to prevent attacks is to provide security at the application layer and that can only happen if developers write secure code. Therefore, the future of attack mitigation especially in Android will probably be in the direction of creating tools that facilitate secure development.