
Linux cifs.ko DACL हीप ओवरफ़्लो (CVE-2026-97555) के लिए विश्लेषण, POC और arm64 एक्सप्लॉइट चेन, जो संदर्भ बिल्ड पर uid-0 क्रेडेंशियल ओवरराइट तक एस्केलेट करता है।
English | 简体中文
SMB क्लाइंट DACL रीराइट में कर्नेल हीप ओवरफ़्लो। id_mode_to_cifs_acl() सर्वर द्वारा लौटाए गए DACL से आउटपुट सिक्योरिटी डिस्क्रिप्टर का आकार निर्धारित करता है, लेकिन replace_sids_and_copy_aces() प्रत्येक ACE को नए owner/group SID (अधिकतम 68 बाइट) के साथ फिर से लिखता है। इसलिए, एक सर्वर जो शॉर्ट-SID DACL लौटाता है, रीराइट को आवंटन से बड़ा कर देता है और kmalloc ऑब्जेक्ट से आगे लिख देता है।
यह रिपॉज़िटरी विश्लेषण, एक POC जो क्रॉस-ऑब्जेक्ट राइट को पुन: उत्पन्न करता है, और एक EXP प्रदान करती है जो संदर्भ बिल्ड पर ओवरफ़्लो को एक नियंत्रित एंड-टू-एंड चेन (हीप लीक, पेज-टेबल रिक्लेम, मनमाना PTE राइट, uid 0 पर स्व-क्रेड ओवरराइट) में बदल देता है।
| आइटम | मान |
|---|---|
| CVE | CVE-2026-97555 |
| वर्ग | id_mode_to_cifs_acl() में हीप बफ़र ओवरफ़्लो (राइट, CWE-122) |
| घटक | cifs.ko (SMB क्लाइंट) |
| प्रभावित | Linux cifs.ko बिना फ़िक्स के (6.12 GKI-आधारित Android कर्नेल के विरुद्ध सत्यापित) |
| पैच | अपस्ट्रीम 0ee150794c75 (स्टेबल fb736ab4fb8c) |
| संदर्भ बिल्ड | Android 16 GKI-आधारित 6.12 कर्नेल, arm64, 4 KiB पेज, 64-बाइट struct page |
| POC | क्रॉस-ऑब्जेक्ट राइट पुन: उत्पन्न (696 बाइट का SET_INFO सिक्योरिटी डिस्क्रिप्टर) |
| EXP | संदर्भ बिल्ड पर uid-0 शेल तक पूरी चेन |
/proc/kpageflags पढ़ता है। माउंट क्षमता प्राप्त करना एक परीक्षण फ़िक्स्चर है, बग का हिस्सा नहीं, और इस रिपॉज़िटरी के दायरे से बाहर है।uid/suid/euid/fsuid फ़ील्ड को फिर से लिखकर वापस एस्केलेट करता है; किसी भी क्षमता या securebits फ़ील्ड को नहीं छुआ जाता। क्लीनअप एक रीबूट है।src/exp.c में स्थिरांक (सिंबल एड्रेस, vmemmap ज्यामिति, BTF ऑफ़सेट) संदर्भ बिल्ड के हैं। किसी अन्य बिल्ड के लिए चलाने से पहले उन्हें फिर से प्राप्त करें; scripts/derive_offsets.sh देखें।id_mode_to_cifs_acl() आवंटन की गणना dacl_ptr->size या num_aces * sizeof(struct smb_ace) से करता है, फिर replace_sids_and_copy_aces() प्रत्येक ACE को नए owner/group SID के साथ पुनर्निर्मित करता है। जब सर्वर के SID छोटे होते हैं (0-subauth) और नया SID 68 बाइट का होता है, तो रीराइट किया गया DACL आवंटन से लंबा हो जाता है और build_sec_desc() स्लैब ऑब्जेक्ट से आगे लिख देता है।
कर्नेल का डायनामिक FORTIFY इंस्ट्रुमेंटेशन इनलाइन किए गए ACE-हेडर स्टोर्स की जाँच करता है, लेकिन SID कॉपी आउट-ऑफ़-लाइन cifs_copy_sid() से होकर जाती हैं और इंस्ट्रुमेंटेड नहीं हैं। एक DACL जिसे इस प्रकार संरेखित किया गया हो कि प्रत्येक ACE हेडर nsecdesclen - 28 के भीतर रहे, कमज़ोर कॉपी तक पहुँच जाता है।
cross7 सर्वर लेआउट (7 ACE, स्रोत आकार 76/76/76/44/44/16/16) आवंटन को ठीक 512 बाइट बनाता है; 7वें ACE की 68-बाइट SID कॉपी ऑफ़सेट 492 पर शुरू होती है और ऑब्जेक्ट सीमा को पार करती है, और रीराइट किया गया डिस्क्रिप्टर 696 बाइट तक बढ़ जाता है — ऑब्जेक्ट से 184 बाइट आगे। विस्तारित डिस्क्रिप्टर अभी भी SET_INFO में डिलीवर किया जाता है, इसलिए राइट सर्वर पक्ष से देखा जा सकता है।
fb736ab4fb8c के बिना बने 6.12 GKI-आधारित कर्नेल वाला arm64 Android डिवाइस।kpageflags, माउंट, chown) और cifs idmap upcall के लिए एक SELinux-अनुमतिप्रद विंडो (या समकक्ष नीति अनुमति)।src/poc_smb2_acl.py द्वारा प्रदान किया गया शेयर, जिसे मानक कर्नेल क्लाइंट (scripts/mount_cifs.sh) के माध्यम से cifsacl/modefromsid के साथ माउंट किया गया हो। माउंट स्वयं एक विशेषाधिकार प्राप्त फ़िक्स्चर है; परीक्षक उस विशेषाधिकार को कैसे प्राप्त करता है, यह इस रिपॉज़िटरी के बाहर है।cifs.idmap कुंजियाँ: संदर्भ वातावरण में कोई /sbin/cifs.idmap upcall हेल्पर नहीं है (src/poc.c और src/exp.c उन्हें पहले से लोड करते हैं; src/acl_key_preload.c इसे अलग से करता है)।# host
python3 src/poc_smb2_acl.py # ACL_LAYOUT=cross7 for the overflow
adb reverse tcp:1468 tcp:1468
# device (root)
sh /data/local/tmp/e2e/mount_cifs.sh 127.0.0.1 share /mnt/cifs
make && make push
/data/local/tmp/poc /mnt/cifs
अपेक्षित प्रमाण: सर्वर लॉग प्रिंट करता है
SET_INFO security: len=696 ... (cross7), अर्थात् 512-बाइट आवंटन से लंबा
एक डिस्क्रिप्टर, और scripts/trace_acl02.sh chown के आसपास
id_mode_to_cifs_acl प्रवेश/वापसी दिखाता है।
POC केवल कमज़ोर पथ को ट्रिगर करता है; यह groom, leak या escalate नहीं करता। ट्रिगर एक out-of-bounds कर्नेल लेखन करता है: जिन बिल्डों का लेआउट गतिशील FORTIFY जाँचों को ट्रिप करता है, वे पैनिक कर सकते हैं, इसलिए इसे केवल उसी परीक्षण डिवाइस पर चलाएँ जिसे आप रीबूट कर सकते हैं।
EXP संदर्भ बिल्ड पर पूरी श्रृंखला है, आरंभ से अंत तक:
cross7 ओवरफ़्लो पड़ोसी key payload के
datalen को फिर से लिखता है; keyctl_read एक जीवंत pipe_buffer
सरणी सहित 64 KiB डंप करता है। लीक हुआ anon_pipe_buf_ops पॉइंटर KASLR स्लाइड देता है और
बफ़र पेज P वास्तविक struct page लक्ष्य देता है।buffer[0] को
page=P, len=0x1234, ops=default_pipe_buf_ops के साथ फिर से लिखा जाता है।P मुक्त हो जाता है; base पर एक fault
इसे पेज-टेबल पेज के रूप में पुनः प्राप्त करता है, जिसे एक kretprobe द्वारा नियंत्रित किया जाता है जो सिद्ध करता है
कि __alloc_pages_noprof ने __pte_alloc से P लौटाया।P+16 (PTE[2]) से शुरू होता है; इसके पहले आठ बाइट P के लिए ही
एक self-map डिस्क्रिप्टर हैं। फिर userland base+8 KiB के माध्यम से
पेज-टेबल पेज को पढ़ता और लिखता है।KPF_SLAB पेजों के बैच नए PTEs के माध्यम से मैप किए जाते हैं
और इस कार्य के comm के लिए स्कैन किए जाते हैं; pid/tgid, लीनियर-मैप
विंडो और cred->uid/euid हिट का क्रॉस-चेक करते हैं, और नए, कभी उपयोग न किए गए
VAs भौतिक पेज को पुनः सत्यापित करते हैं।setresuid(2000) करती है (कर्नेल एक नया cred आवंटित करता है), self-map के माध्यम से task->cred पढ़ती है,
केवल uid/suid/euid/fsuid को शून्य करती है, getuid()==0 की पुष्टि करती है, self-map को निष्क्रिय करती है
और /system/bin/sh को execve करती है। शेल uid 0 के रूप में चलता है।make exp
make push
# on the device, as root:
sh /data/local/tmp/e2e/launch_exp.sh /mnt/cifs 10 1 r1 4000 400 2400 7 0 0 cred
# then read /data/local/tmp/e2e/e2e_status.txt and
# /data/local/tmp/e2e/root_shell.txt
cred चरण अंतिम आर्ग्युमेंट (cred) के माध्यम से ऑप्ट-इन है; scout केवल-पढ़ने वाले सत्यापन के बाद रुक जाता है। EXP अपने PTE को प्रोसेस के अंदर रखता है और क्लीनअप एक रीबूट है।
आवश्यकताएँ: Android NDK r29 (NDK= से ओवरराइड करें), GNU make, adb।
make # POC (arm64, API 24)
make exp # EXP
make build-all # both
make check # push and run the read-only pre-check (poc --check)
make push # push binaries and helper scripts
make run # push and run the POC against $(MNT) (default /mnt/cifs; destructive)
make host # static POC build for qemu-aarch64
make emu # host build and run
make logs # dump logcat/dmesg into logs/
make help