
CVE-2023-3269: लिनक्स कर्नेल विशेषाधिकार वृद्धि भेद्यता
Linux कर्नेल 6.1 में स्टैक विस्तार के प्रबंधन में एक दोष पाया गया से 6.4 तक, जिसे "Stack Rot" कहा जाता है। वर्चुअल मेमोरी क्षेत्रों के प्रबंधन के लिए जिम्मेदार maple tree, MM राइट लॉक को ठीक से प्राप्त किए बिना नोड प्रतिस्थापन से गुजर सकती है, जिससे use-after-free समस्याएँ उत्पन्न होती हैं। एक अविशेषाधिकार प्राप्त स्थानीय उपयोगकर्ता इस दोष का उपयोग करके कर्नेल से समझौता कर सकता है और अपने विशेषाधिकार बढ़ा सकता है।
चूंकि StackRot मेमोरी प्रबंधन सबसिस्टम में पाई जाने वाली Linux कर्नेल भेद्यता है, यह लगभग सभी कर्नेल कॉन्फ़िगरेशन को प्रभावित करती है और न्यूनतम क्षमताओं की आवश्यकता होती है। हालाँकि, यह ध्यान दिया जाना चाहिए कि maple नोड्स RCU कॉलबैक का उपयोग करके मुक्त किए जाते हैं, जिससे वास्तविक मेमोरी डीलोकेशन RCU ग्रेस अवधि के बाद तक विलंबित होती है। परिणामस्वरूप, इस भेद्यता का शोषण करना चुनौतीपूर्ण माना जाता है।
मेरी जानकारी के अनुसार, वर्तमान में कोई सार्वजनिक रूप से उपलब्ध शोषण नहीं हैं जो use-after-free-by-RCU (UAFBR) बग्स को लक्षित करते हैं। यह पहला उदाहरण है जहाँ UAFBR बग्स शोषण योग्य साबित हुए हैं, यहाँ तक कि CONFIG_PREEMPT या CONFIG_SLAB_MERGE_DEFAULT सेटिंग्स की उपस्थिति के बिना भी। विशेष रूप से, यह शोषण Google kCTF VRP द्वारा प्रदान किए गए वातावरण में सफलतापूर्वक प्रदर्शित किया गया है (bzImage_upstream_6.1.25, config).
StackRot भेद्यता Linux कर्नेल में संस्करण 6.1 से मौजूद है, जब VMA ट्री संरचना बदल दी गई red-black trees से maple trees में।
जब भी किसी मेमोरी मैपिंग को स्थापित करने के लिए mmap() सिस्टम कॉल का उपयोग किया जाता है,
कर्नेल उस वर्चुअल मेमोरी क्षेत्र (VMA) को दर्शाने के लिए vm_area_struct नामक एक संरचना उत्पन्न करता है।
यह संरचना विभिन्न जानकारी संग्रहीत करती है,
जिसमें फ़्लैग, गुण और अन्य प्रासंगिक विवरण शामिल हैं, जो
मैपिंग से संबंधित हैं।```c
struct vm_area_struct {
long unsigned int vm_start; /* 0 8 /
long unsigned int vm_end; / 8 8 /
struct mm_struct * vm_mm; / 16 8 /
pgprot_t vm_page_prot; / 24 8 /
long unsigned int vm_flags; / 32 8 /
union {
struct {
struct rb_node rb attribute((aligned(8))); / 40 24 /
/ --- cacheline 1 boundary (64 bytes) --- /
long unsigned int rb_subtree_last; / 64 8 /
} attribute((aligned(8))) shared attribute((aligned(8))); / 40 32 /
struct anon_vma_name * anon_name; / 40 8 /
} attribute((aligned(8))); / 40 32 /
/ --- cacheline 1 boundary (64 bytes) was 8 bytes ago --- /
struct list_head anon_vma_chain; / 72 16 /
struct anon_vma * anon_vma; / 88 8 /
const struct vm_operations_struct * vm_ops; / 96 8 /
long unsigned int vm_pgoff; / 104 8 /
struct file * vm_file; / 112 8 /
void * vm_private_data; / 120 8 /
/ --- cacheline 2 boundary (128 bytes) --- /
atomic_long_t swap_readahead_info; / 128 8 /
struct vm_userfaultfd_ctx vm_userfaultfd_ctx; / 136 0 */
/* size: 136, cachelines: 3, members: 14 */
/* forced alignments: 1 */
/* last cacheline: 8 bytes */
} attribute((aligned(8)));
इसके बाद, जब कर्नेल को पेज फॉल्ट या अन्य मेमोरी-संबंधित सिस्टम कॉल का सामना करना पड़ता है, तो उसे केवल एड्रेस के आधार पर VMA की त्वरित खोज की आवश्यकता होती है। पहले, VMAs को red-black trees का उपयोग करके प्रबंधित किया जाता था। हालाँकि, Linux kernel संस्करण 6.1 से, maple trees में स्थानांतरण हुआ। [Maple trees][mt] RCU-safe B-tree डेटा संरचनाएँ हैं जो गैर-अतिव्यापी रेंजों को संग्रहीत करने के लिए अनुकूलित हैं। फिर भी, उनकी जटिल प्रकृति कोडबेस में जटिलता जोड़ती है और StackRot भेद्यता को प्रस्तुत करती है।
[mt]: https://docs.kernel.org/6.4/core-api/maple_tree.html
मूल रूप से, एक maple tree maple nodes से बनी होती है। हालाँकि पेड़ की संरचना जटिल हो सकती है, यह ध्यान रखना महत्वपूर्ण है कि यह जटिलता StackRot बग से कोई संबंध नहीं रखती है। इसलिए, इस लेख में यह मान लिया गया है कि maple tree केवल एक node, यानी root node, से बनी है।
यह root node 16 तक intervals धारण कर सकता है। ये intervals या तो एक gap का प्रतिनिधित्व कर सकते हैं या किसी VMA की ओर इंगित कर सकते हैं। चूँकि gaps भी intervals के रूप में गिने जाते हैं, सभी intervals क्रमिक रूप से जुड़े होते हैं, जिसके परिणामस्वरूप node की संरचना में केवल 15 endpoints, जिन्हें pivots भी कहा जाता है, की आवश्यकता होती है। ध्यान दें कि सबसे बाएँ endpoint और सबसे दाएँ endpoint को छोड़ दिया गया है, क्योंकि उन्हें parent node से प्राप्त किया जा सकता है।```c
struct maple_range_64 {
struct maple_pnode * parent; /* 0 8 */
long unsigned int pivot[15]; /* 8 120 */
/* --- cacheline 2 boundary (128 bytes) --- */
union {
void * slot[16]; /* 128 128 */
struct {
void * pad[15]; /* 128 120 */
/* --- cacheline 3 boundary (192 bytes) was 56 bytes ago --- */
struct maple_metadata meta; /* 248 2 */
}; /* 128 128 */
}; /* 128 128 */
/* size: 256, cachelines: 4, members: 3 */
};
maple_range_64 संरचना, जैसा कि ऊपर दिखाया गया है, एक maple नोड का प्रतिनिधित्व करती है।
pivots के अलावा, slots का उपयोग VMA संरचना को संदर्भित करने के लिए किया जाता है जब
नोड leaf नोड के रूप में कार्य करता है, या अन्य maple नोड्स को संदर्भित करने के लिए जब नोड
interior नोड के रूप में कार्य करता है। यदि कोई अंतराल एक gap के अनुरूप है, तो slot
में केवल NULL मान होगा। pivot बिंदुओं और slots की व्यवस्था को
नीचे चित्रित अनुसार देखा जा सकता है:```
Slots -> | 0 | 1 | 2 | ... | 12 | 13 | 14 | 15 |
┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬
│ │ │ │ │ │ │ │ └─ Implied maximum
│ │ │ │ │ │ │ └─ Pivot 14
│ │ │ │ │ │ └─ Pivot 13
│ │ │ │ │ └─ Pivot 12
│ │ │ │ └─ Pivot 11
│ │ │ └─ Pivot 2
│ │ └─ Pivot 1
│ └─ Pivot 0
└─ Implied minimum
समवर्ती संशोधन के संबंध में, maple tree एक विशेष प्रतिबंध लागू करता है
अर्थात् लेखकों को एक अनन्य लॉक धारण करना चाहिए (*Rule W*)। VMA tree के
मामले में, अनन्य लॉक MM write lock के अनुरूप होता है। पाठकों के लिए,
दो विकल्प उपलब्ध हैं। पहला विकल्प MM read lock धारण करना है
(*Rule A1*), जिसके परिणामस्वरूप लेखक MM read-write lock द्वारा अवरुद्ध
हो जाता है। वैकल्पिक रूप से, दूसरा विकल्प RCU क्रांतिक खंड में प्रवेश
करना है (*Rule A2*)। ऐसा करने से, लेखक अवरुद्ध नहीं होता, और पाठक
अपने कार्य जारी रख सकते हैं क्योंकि maple tree RCU-safe है। जबकि
अधिकांश मौजूदा VMA एक्सेस पहले विकल्प (अर्थात, Rule A1) को चुनते हैं,
Rule A2 कुछ प्रदर्शन-महत्वपूर्ण परिदृश्यों में उपयोग किया जाता है, जैसे lockless page faults.