
PoC and red team app for CVE-2026-28576, a zero-permission SQL injection in the Android Contacts Provider enabling full contacts database exfiltration.
CVE-2026-28576 is a critical access control vulnerability in the Android Contacts Provider framework (ContactsProvider2.queryLocal()). The flaw enables an unprivileged application installed locally with zero declared permissions to exfiltrate an entire device's contacts database, including full names, telephone numbers, postal addresses, and email addresses.
| Attribute | Value |
|---|---|
| CVE ID | CVE-2026-28576 |
| GHSA ID | GHSA-ph86-9mcx-3p6r |
| Android Bug ID | A-488593616 |
| CWE | CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') |
| CVSS v4.0 | 10.0 (Critical) - AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H |
| EPSS Score | 0.148% (4th percentile) |
| Published | June 17, 2026 |
| Component | Android Contacts Provider (ContactsProvider2) |
| Attack Vector | Local (zero-permission app) |
| User Interaction | None required |
The vulnerability exists in ContactsProvider2.queryLocal() method within the Android Contacts Provider framework (packages/providers/ContactsProvider). The method constructs SQL queries using unsanitized user-controlled input, allowing SQL injection through the selection and selectionArgs parameters passed via ContentResolver.query().
ContentResolver.query()
→ ContactsProvider2.query()
→ ContactsProvider2.queryLocal() [VULNERABLE]
→ SQLiteQueryBuilder.buildQuery()
→ Raw SQL execution with unsanitized input
selection (String): WHERE clause without parameterizationselectionArgs (String[]): Arguments that are improperly escapeduri (Uri): Content URI targeting content://com.android.contacts/...READ_CONTACTS, READ_PRIVILEGED_PHONE_STATE, or any dangerous permissionsThe queryLocal() method processes the selection parameter by directly concatenating it into the SQL WHERE clause without proper parameterization:
// Simplified vulnerable pattern in ContactsProvider2.java
public Cursor queryLocal(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(Tables.CONTACTS);
// VULNERABLE: selection directly interpolated into SQL
String whereClause = buildWhereClause(selection, selectionArgs);
Cursor c = qb.query(db, projection, whereClause, null, null, null, sortOrder);
return c;
}
An attacker crafts a malicious selection parameter such as:
"1=1) UNION SELECT _id, display_name, phone_number, email_address, postal_address FROM raw_contacts--"
This bypasses the intended query logic and extracts all contact fields.
graph TD
A[Malicious App Installed] --> B[Request ContentResolver.query]
B --> C[Craft SQL Injection Payload]
C --> D[Target content://com.android.contacts/data]
D --> E[ContactsProvider2.queryLocal]
E --> F[Unsanitized selection parameter]
F --> G[SQL Injection Executed]
G --> H[Full Contacts DB Returned]
H --> I[Exfiltrate via Network/Logs]UNION/UPDATE injection| Android Version | Status |
|---|---|
| Android 14 (U) | Vulnerable |
| Android 15 (V) | Vulnerable |
| Android 16 (W) | Vulnerable |
| Android 17 (June 2026+) | Patched |
rule Android_ContactsProvider_SQLi_CVE_2026_28576 {
meta:
description = "Detects SQL injection payloads targeting ContactsProvider"
cve = "CVE-2026-28576"
severity = "critical"
strings:
$s1 = "content://com.android.contacts" nocase
$s2 = "UNION SELECT" nocase
$s3 = "raw_contacts" nocase
$s4 = "data1" nocase
$s5 = "1=1" nocase
condition:
$s1 and 2 of ($s2, $s3, $s4, $s5)
}
alert http any any -> any any (
msg:"CVE-2026-28576 Contacts Exfiltration";
flow:established,to_server;
content:"contacts";
http.uri;
pcre:"/UNION.*SELECT.*raw_contacts/i";
classtype:web-application-attack;
sid:202628576;
rev:1;
)
ContentResolver.query() via SELinux policyContactsProvider query logs for anomalous selection parameterscontent://com.android.contacts URI usage without READ_CONTACTS permissionDocument generated for CVE-2026-28576 research repository Last updated: September 2025
| Data Category | Fields Exposed |
|---|
| Identity | display_name, phonetic_name, nickname, company, title |
| Phone Numbers | number, type (mobile, home, work, fax, etc.), label |
| Email Addresses | address, type (home, work, other), label |
| Postal Addresses | street, city, region, postcode, country, type |
| Structured Data | mimetype, data1–data15, sync1–sync4 |
| Metadata | contact_id, raw_contact_id, version, dirty, deleted |