Escalando privilegios en Linux con Dirty Cow

Recién me animo a escribir un articulo relacionado a esta famosa vulnerabilidad que ha sido reportada hace unas semanas (La cual ya existía desde hace años) relacionada a la manera como el kernel maneja los permisos en determinadas posiciones de memoria. Si, estoy hablando de Dirty Cow (Vease CVE-2016-5195)






Según quienes encontraron y reportaron esta vulnerabilidad se interpreta de la siguiente manera :

Se ha encontrado una condición de carrera en la forma en que el subsistema de memoria del Kernel de Linux administra la rotura de los mapeos de memoria de sólo lectura de tipo COW (copy on write). Un usuario local sin privilegios podría utilizar esto para obtener acceso a partes de la memoria que, de otra forma, serían de solo lectura para él.





Como les dije mas arriba esta vulnerabilidad ya lleva muchos años activa  (Algunos dicen que 9 años y que fue comentado en alguna ocasión por el mismo Linus Torvalds)


Como funciona ? La palabra clave aquí es escalada de privilegios

- El primer paso es mapear el proceso

- El segundo paso es mapear en memoria un archivo de acceso de sólo lectura.

- Tercer paso ejecutar los hilos en paralelo, vamos a escribir sobre el mapa de memoria con permisos de escritura y ademas que el kernel libere


En este vídeo podemos ver a DirtyCow en acción :



Aquí  PoC :)


faultin_page
  handle_mm_fault
    __handle_mm_fault
      handle_pte_fault
        do_fault <- pte is not present
      do_cow_fault <- FAULT_FLAG_WRITE
        alloc_set_pte
          maybe_mkwrite(pte_mkdirty(entry), vma) <- mark the page dirty
                                but keep it RO
# Returns with 0 and retry
follow_page_mask
  follow_page_pte
    (flags & FOLL_WRITE) && !pte_write(pte) <- retry fault
faultin_page
  handle_mm_fault
    __handle_mm_fault
      handle_pte_fault
        FAULT_FLAG_WRITE && !pte_write
      do_wp_page
        PageAnon() <- this is CoWed page already
        reuse_swap_page <- page is exclusively ours
        wp_page_reuse
          maybe_mkwrite <- dirty but RO again
          ret = VM_FAULT_WRITE
((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE)) <- we drop FOLL_WRITE
# Returns with 0 and retry as a read fault
cond_resched -> different thread will now unmap via madvise
follow_page_mask
  !pte_present && pte_none
faultin_page
  handle_mm_fault
    __handle_mm_fault
      handle_pte_fault
        do_fault <- pte is not present
      do_read_fault <- this is a read fault



Aquí un vídeo mas :)






Saludos!

No comments

Powered by Blogger.