
अविशेषाधिकार प्राप्त proof-of-concept और x86_64 स्थानीय विशेषाधिकार वृद्धि (local privilege escalation) Linux कर्नेल Binder use-after-free (CVE-2026-64468) के लिए, KASAN प्रयोगशाला और संवेदनशील/सुधारित (vulnerable/fixed) अंतर (differential) के साथ।
binder_free_transaction() प्रक्रिया-जीवनकाल उपयोग-के-बाद-मुक्त
इस रिपॉजिटरी में, Linux कर्नेल Binder उपयोग-के-बाद-मुक्त के लिए, जिसे अपस्ट्रीम कमिट
f223d27a546c1e1f48d38fd67760e78f068fe8c4 द्वारा ठीक किया गया है, निम्नलिखित शामिल हैं:
binder_chain_64468.c — एक विशेषाधिकार-रहित प्रूफ ऑफ कॉन्सेप्ट जो बग तक पहुँचता है और कर्नेल को इसे साबित करने देता है, एक KASAN प्रयोगशाला और एक कमजोर/पैच किया हुआ अंतर (lab/, run.sh, verify.sh) के साथ।exploit.c — x86_64 के लिए एक स्व-निहित स्थानीय विशेषाधिकार वृद्धि। यह gcc -O2 -pthread -o exploit exploit.c के साथ संकलित होता है, एक सामान्य उपयोगकर्ता के रूप में चलता है, और रूट शेल में समाप्त होता है।demo/ — एक प्रयोगशाला जो बिना पैच वाले कर्नेल पर एक वास्तविक Debian 13 यूजरलैंड बूट करती है, ताकि एक्सप्लॉइट को लक्ष्य के अपने gcc द्वारा संकलित किया जा सके और उस मशीन पर चलाया जा सके जिसे वह फिर अपने कब्जे में ले लेता है।सब कुछ एक सामान्य उपयोगकर्ता (uid/gid 1000, कोई क्षमताएँ नहीं, कोई नेमस्पेस नहीं) के रूप में बिना किसी प्रकार के पैच वाले स्टॉक अपस्ट्रीम कर्नेल के विरुद्ध चलता है।
चेतावनी
यह कोड जानबूझकर कर्नेल ऑब्जेक्ट जीवनकाल के साथ रेस करता है और फिर कर्नेल नियंत्रण प्रवाह को हाईजैक करता है। एक खोई हुई रेस कर्नेल हीप स्थिति को दूषित करती है और मशीन को पैनिक या हैंग कर सकती है। इसे केवल एक पृथक, डिस्पोजेबल VM में चलाएँ जो आपका अपना हो। इसे होस्ट पर न चलाएँ।
| स्थिति | दावा |
|---|---|
| पुष्टि | कमजोरी वास्तविक है, एक विशेषाधिकार-रहित प्रक्रिया से पहुँच योग्य है, और अपस्ट्रीम फिक्स इसे हटा देता है। |
| प्रदर्शित | मरते हुए binder_proc का कमजोर डीरेफरेंस बिना पैच वाले कर्नेल पर स्वाभाविक रूप से और बार-बार पहुँचा जाता है, और पैच किए गए कर्नेल पर कभी नहीं। |
| प्रदर्शित | बिना पैच वाले कर्नेल पर KASAN द्वारा रिपोर्ट किया गया पूर्ण उपयोग-के-बाद-मुक्त। |
| प्रदर्शित | हमलावर-नियंत्रित बाइट्स के साथ मुक्त किए गए binder_proc का पुनः अधिग्रहण, कर्नेल नियंत्रण-प्रवाह हाईजैक, और x86_64 पर एक विशेषाधिकार-रहित उपयोगकर्ता से uid 0 तक विशेषाधिकार वृद्धि। |
| दावा नहीं | किसी विशिष्ट विक्रेता या Android डिवाइस पर कोई परिणाम। केवल नीचे सूचीबद्ध अपस्ट्रीम कर्नेल का x86_64 पर परीक्षण किया गया। |
| दावा नहीं | कि शिप किया गया एक्सप्लॉइट बिना संशोधन के डिस्ट्रीब्यूशन कर्नेल के विरुद्ध काम करता है। कौन से सिस्टम प्रभावित हैं देखें: इसे प्रति-कर्नेल ऑफसेट की आवश्यकता होती है, और सर्वेक्षण किए गए हर सामान्य-उद्देश्य डिस्ट्रीब्यूशन पर binder डिवाइस पहले स्थान पर किसी विशेषाधिकार-रहित उपयोगकर्ता के लिए पहुँच योग्य नहीं है। |
binder_free_transaction() t->lock के अंतर्गत लेनदेन से लक्ष्य प्रक्रिया को पढ़ता है, उस लॉक को छोड़ता है, और फिर लक्ष्य का आंतरिक लॉक प्राप्त करता है:```c
spin_lock(&t->lock);
target_proc = t->to_proc;
spin_unlock(&t->lock);
if (target_proc) {
binder_inner_proc_lock(target_proc); /* use after free */
कुछ भी `target_proc` को उस अंतराल में जीवित नहीं रखता। एक प्रक्रिया जिसे समानांतर में
नीचे गिराया जा रहा है, वह बीच में `binder_proc_dec_tmpref() -> kfree()` तक पहुँच सकती है, इसलिए
लॉक मुक्त की गई मेमोरी पर लिया जाता है। अपस्ट्रीम फिक्स `t->to_thread` को पिन करता है जबकि
`t->lock` अभी भी धारित है, जो आंतरिक लॉक के उपयोग और रिलीज़ होने तक स्वामी प्रक्रिया को जीवित रखता है।
यह भेद्यता **एलिस रायल** द्वारा रिपोर्ट की गई थी और **कार्लोस
लामास** द्वारा ठीक की गई थी, दोनों Google से।
[मूल रिपोर्ट](https://lore.kernel.org/all/[email protected]/)
KASAN ट्रेस का संदर्भ रखती है।
### भेद्य एक्सेस तक पहुँचना
एकमात्र कॉलर जो *विदेशी* `to_proc` तक पहुँच सकता है, वह है
`binder_send_failed_reply()`, और यह केवल `t->from_parent` तक चलता है जब
`t->from` `NULL` होता है:```c
target_thread = binder_get_txn_from_and_acq_inner(t);
if (target_thread) { ...; binder_free_transaction(t); return; }
next = t->from_parent;
binder_free_transaction(t);
t = next;
from_parent ठीक एक ही स्थान पर निर्दिष्ट किया गया है, और यह निर्दिष्टीकरण कुछ पंक्तियाँ पहले बाइंडर के खराब ट्रांज़ैक्शन स्टैक जाँच द्वारा सुरक्षित है: एक थ्रेड केवल तभी सिंक्रोनस ट्रांज़ैक्शन भेज सकता है जब उसके स्टैक का शीर्ष एक ट्रांज़ैक्शन हो जिसे वह प्राप्त कर रहा है। इसलिए, उस श्रृंखला की हर कड़ी के लिए, चाइल्ड का भेजने वाला और पैरेंट का प्राप्त करने वाला एक ही थ्रेड होता है।
इसका एक गहरा परिणाम है। binder_thread_release() मरते हुए थ्रेड के स्टैक को proc->inner_lock धारण करके चलता है, और निम्नलिखित दोनों को लिखता है:```
iteration j : [holds child->lock ] child->from = NULL ; unlock child->lock
spin_lock(parent->lock)
iteration j+1 : [holds parent->lock] parent->to_proc = NULL
in **एक ही वॉक के सन्निकट पुनरावृत्तियों** में, प्रत्येक अपने-अपने
`t->lock` के अंतर्गत होता है। एक वॉकर `child->from == NULL` केवल तभी जानता है जब वॉक ने
`child->lock` जारी कर दिया हो, और उसे अपने स्नैपशॉट के लिए `parent->lock` की आवश्यकता होती है। इसलिए पूरा
अवसर उस वॉक के `spin_unlock(&child->lock)` और उसके
`spin_lock(&parent->lock)` के बीच का अंतराल है — कुछ निर्देश। रिलीज़ वॉक एक
स्पिनलॉक रखता है और वहाँ प्रीएम्प्ट नहीं हो सकता; केवल एक इंटरप्ट इसे विलंबित कर सकता है।
यही कारण है कि रेस संकीर्ण है और क्यों प्रूफ ऑफ कॉन्सेप्ट और
एक्सप्लॉइट दोनों संभाव्य हैं।
### प्रूफ ऑफ कॉन्सेप्ट क्या बनाता है
`binder_chain_64468.c` सबसे छोटी श्रृंखला बनाता है जो
कमजोर एक्सेस तक पहुँचती है, ताकि उस अंतराल के भीतर वॉकर का काम उतना ही छोटा हो जितना बाइंडर
अनुमति देता है — दो `t->lock` अधिग्रहण और एक `kfree()`, बिना किसी विदेशी
`inner_proc_lock`, बिना `wake_up` और बिना रिप्लाई डिलीवरी के:```
B thread i --e2 (sync, code 0x4442414b)--> P thread Y_i
P thread Y_i --e1 (sync, code 0x54414c4c)--> B thread i (nested target)
B thread i stack: [ e2 outgoing , e1 incoming (top) ]
P thread Y_i stack: [ e2 incoming , e1 outgoing (top) ]
P फिर अपना binder fd छोड़ देता है, इसलिए binder_deferred_release() Y_1..Y_K को रिलीज़ करता है और अंततः binder_proc को मुक्त कर देता है, जबकि हर B थ्रेड समवर्ती रूप से BINDER_THREAD_EXIT जारी करता है:```
binder_thread_release(B_i) nulls e1->to_proc (own proc) and e2->from
binder_send_failed_reply(e1) e1->from == NULL once Y_i was released
-> binder_free_transaction(e1) target_proc already NULL, kfree(e1)
-> binder_free_transaction(e2) target_proc == P <-- vulnerable access
एक तीसरी प्रक्रिया बाइंडर कॉन्टेक्स्ट मैनेजर है, जिसका उपयोग केवल उन हैंडल्स को बांटने के लिए किया जाता है जिनकी बाकी दोनों को आवश्यकता होती है। `exploit.c` इसी सटीक निर्माण का पुन: उपयोग करता है।
### दो स्वतंत्र स्थितियाँ
KASAN रिपोर्ट के लिए दोनों की आवश्यकता होती है:
* **कमजोर एक्सेस** — वॉकर को उपरोक्त संकीर्ण विंडो के अंदर `parent->lock` लेना चाहिए, ताकि वह अभी भी जीवित `to_proc` का स्नैपशॉट ले सके; और
* **विंडो के अंदर फ्री लैंडिंग** — वॉकर को फिर `t->lock` छोड़ने और पीड़ित के आंतरिक लॉक को लेने के बीच अपना CPU खोना चाहिए, और तब तक उससे दूर रहना चाहिए जब तक कि विलंबित रिलीज़ समाप्त न हो जाए और `binder_proc` मुक्त न हो जाए।
पहली स्थिति का अपना स्वयं का ओरेकल है जिसे KASAN की आवश्यकता नहीं है: बाइंडर प्रिंट करता है```
binder: binder_free_proc: Unexpected outstanding_txns -1
जब भी ऐसा होता है, क्योंकि वॉकर और binder_thread_release() दोनों फिर एक ही लेनदेन के लिए एक ही काउंटर को घटाते हैं। ध्यान दें कि यह अपने आप में कोई कमज़ोर/फिक्स्ड अंतर नहीं है — फिक्स फ्री को रोकता है, दूसरे घटाव को नहीं — इसलिए यह दोनों कर्नेल पर दिखाई देता है। इसका उपयोग यहाँ केवल यह दिखाने के लिए किया गया है कि कमज़ोर कोड पथ का उपयोग हो रहा है।