Dirty Cow es un bug que lleva presente 11 años en Linux y permite a cualquiera elevar privilegios como root. El fallo se encuentra desde la versión 2.6.22 (de 2007) y ha sido corregida el 18 de Oct, 2016.
Su nombre es considerado un BWAIN (Bug With An Impressive Name) y COW viene de Copy On Write (copiar al escribir), un truco que los sistemas operativos más modernos utilizan para ahorrar tiempo cuando se copia algo (por ejemplo un archivo o un bloque de memoria), que en realidad consiste simple y llanamente en que por debajo no realizan la copia de inmediato. Es decir, cuando accedes a un elemento copiado sigues accediendo al elemento original, y realmente sólo se copia si se modifica porque sólo entonces habrá dos versiones distintas que necesitan divergir.
Por supuesto, si hay un fallo en el mecanismo COW y la copia no sucede cuando debe, o sucede cuando debería no ser así, los cambios se pueden perder, o realizarse en lugar equivocado.
El bug DirtyCOW, conocido oficialmente como CVE-2016-5195, es un fallo del segundo tipo: los datos se escriben en una ubicación de memoria incorrecta.
Concretamente es una condición de carrera que se ha encontrado en la forma en la que el subsistema de memoria del kernel de Linux maneja las asignaciones del acceso de escritura de las operaciones COW. Un usuario local sin privilegios podría usar este fallo para obtener acceso de escritura a mapas de memoria de sólo lectura y aumentar así sus privilegios en el sistema.
Ya se han publicado varias PoCs que demuestran cómo funciona y puede explotarse esta vulnerabilidad. Los exploit hacen más o menos lo siguiente:
- Primero mapean su propio proceso en lo que es conocido como un mapeo privado de escritura. Como decimos, con COW no se copiará inmediatamente por podremos modificar ese mapeo privado sin afectar al programa en ejecución.
- Segundo mapean en memoria un archivo con acceso de sólo lectura. En teoría, COW debería ser irrelevante aquí: no se tiene acceso de escritura al archivo por lo que tampoco se tiene acceso de escritura en el mapa de memoria y no se debería poder cambiarlo en absoluto.
- Y tercero, se ejecutan dos threads o hilos en paralelo. Uno que escribe los cambios en el mapa de memoria escribible y privado y el otro que le dice al kernel que puede liberar temporalmente cualquier memoria utilizada para el archivo asignado. Ambos hilos "colisionarán" y los datos que se están intentando escribir en el memory map privado "accidentalmente" se volcarán en el memory map del archivo de sólo lectura.
El resultado es que un usuario normal puede alterar de forma permanente los archivos del sistema que normalmente requieren un login de root para modificarlos. Eso podría incluir archivos de configuración importantes como ssh_config, las claves de cifrado privadas, o incluso el software del sistema, como el /bin/login.
Irónicamente, este error se abordó por primera vez hace once años por el propio Linus Torvalds, pero la solución entonces fue descartada debido a que causaba problemas en la versión mainframe de IBM de Linux.
Linus lo describe como un error meramente "teórico" en ese entonces pero que ha terminado por ser perfectamente práctico:
"Se trata de un antiguo error que en realidad se trató de corregir una vez (mal) por mí hace once años [...], pero que entonces se deshechó debido a problemas en el s390 ... Lo que era una condición de carrera puramente teórica en ese entonces se ha convertido en más fácil de desencadenar".
Por ahora ya está el parche para el kernel, pero hay que esperar a que las principales distribuciones de Linux lo publiquen o bien aplicar el parche y recompilar el kernel por uno mismo...
Detalles de la vulnerabilidad
https://github.com/dirtycow/dirtycow.github.io/wiki/VulnerabilityDetails
Listado de PoCs (https://github.com/dirtycow/dirtycow.github.io/wiki/PoCs):
- El exploit se basa en en escribir a /proc/self/mem en una única condición de carrera.
- ptrace(PTRACE_POKEDATA) puede escribir en mapeos de sólo lectura.
- El ataque se basa en lanzar en paralelo la llamada al sistema con madvise(MADV_DONTNEED) a la vez que se tiene la página del ejecurable mapeada en memoria.
Referencias:
- https://nakedsecurity.sophos.com/2016/10/21/linux-kernel-bug-dirtycow-easyroot-hole-and-what-you-need-to-know/
- https://bugzilla.redhat.com/show_bug.cgi?id=1384344
- https://access.redhat.com/security/vulnerabilities/2706661
- https://plus.google.com/+KeesCook/posts/UUaXm3PcQ4n
- https://twitter.com/nelhage/status/789196293629370368
- https://bugzilla.suse.com/show_bug.cgi?id=1004418#c14
Su nombre es considerado un BWAIN (Bug With An Impressive Name) y COW viene de Copy On Write (copiar al escribir), un truco que los sistemas operativos más modernos utilizan para ahorrar tiempo cuando se copia algo (por ejemplo un archivo o un bloque de memoria), que en realidad consiste simple y llanamente en que por debajo no realizan la copia de inmediato. Es decir, cuando accedes a un elemento copiado sigues accediendo al elemento original, y realmente sólo se copia si se modifica porque sólo entonces habrá dos versiones distintas que necesitan divergir.
Por supuesto, si hay un fallo en el mecanismo COW y la copia no sucede cuando debe, o sucede cuando debería no ser así, los cambios se pueden perder, o realizarse en lugar equivocado.
El bug DirtyCOW, conocido oficialmente como CVE-2016-5195, es un fallo del segundo tipo: los datos se escriben en una ubicación de memoria incorrecta.
Concretamente es una condición de carrera que se ha encontrado en la forma en la que el subsistema de memoria del kernel de Linux maneja las asignaciones del acceso de escritura de las operaciones COW. Un usuario local sin privilegios podría usar este fallo para obtener acceso de escritura a mapas de memoria de sólo lectura y aumentar así sus privilegios en el sistema.
Ya se han publicado varias PoCs que demuestran cómo funciona y puede explotarse esta vulnerabilidad. Los exploit hacen más o menos lo siguiente:
- Primero mapean su propio proceso en lo que es conocido como un mapeo privado de escritura. Como decimos, con COW no se copiará inmediatamente por podremos modificar ese mapeo privado sin afectar al programa en ejecución.
- Segundo mapean en memoria un archivo con acceso de sólo lectura. En teoría, COW debería ser irrelevante aquí: no se tiene acceso de escritura al archivo por lo que tampoco se tiene acceso de escritura en el mapa de memoria y no se debería poder cambiarlo en absoluto.
- Y tercero, se ejecutan dos threads o hilos en paralelo. Uno que escribe los cambios en el mapa de memoria escribible y privado y el otro que le dice al kernel que puede liberar temporalmente cualquier memoria utilizada para el archivo asignado. Ambos hilos "colisionarán" y los datos que se están intentando escribir en el memory map privado "accidentalmente" se volcarán en el memory map del archivo de sólo lectura.
El resultado es que un usuario normal puede alterar de forma permanente los archivos del sistema que normalmente requieren un login de root para modificarlos. Eso podría incluir archivos de configuración importantes como ssh_config, las claves de cifrado privadas, o incluso el software del sistema, como el /bin/login.
Irónicamente, este error se abordó por primera vez hace once años por el propio Linus Torvalds, pero la solución entonces fue descartada debido a que causaba problemas en la versión mainframe de IBM de Linux.
Linus lo describe como un error meramente "teórico" en ese entonces pero que ha terminado por ser perfectamente práctico:
"Se trata de un antiguo error que en realidad se trató de corregir una vez (mal) por mí hace once años [...], pero que entonces se deshechó debido a problemas en el s390 ... Lo que era una condición de carrera puramente teórica en ese entonces se ha convertido en más fácil de desencadenar".
Por ahora ya está el parche para el kernel, pero hay que esperar a que las principales distribuciones de Linux lo publiquen o bien aplicar el parche y recompilar el kernel por uno mismo...
Detalles de la vulnerabilidad
https://github.com/dirtycow/dirtycow.github.io/wiki/VulnerabilityDetails
Listado de PoCs (https://github.com/dirtycow/dirtycow.github.io/wiki/PoCs):
-
https://github.com/dirtycow/dirtycow.github.io/blob/master/dirtyc0w.c
- Permite al usuario escribir en ficheros de sólo lectura.
-
https://gist.github.com/rverton/e9d4ff65d703a9084e85fa9df083c679
- Permite al usuario sobrescribir
/usr/bin/passwd
o un binario suid.
- Permite al usuario sobrescribir
-
https://gist.github.com/scumjr/17d91f20f73157c722ba2aea702985d2
- Permite root parcheando la llamada getuid de libc e invocando
su
.
- Permite root parcheando la llamada getuid de libc e invocando
-
https://github.com/dirtycow/dirtycow.github.io/blob/master/pokemon.c
- Permite al usuario escribir en ficheros de sólo lectura.
-
https://github.com/xlucas/dirtycow.cr
- Permite al usuario escribir en ficheros de sólo lectura.
-
https://github.com/timwr/CVE-2016-5195
- Permite al usuario escribir en ficheros de sólo lectura (android).
-
https://github.com/rapid7/metasploit-framework/pull/7476
- Módulo de Metasploit basada en la PoC de
cowroot
.
- Módulo de Metasploit basada en la PoC de
-
https://github.com/scumjr/dirtycow-vdso
- Da al usuario root parcheando vDSO escapando contenedores/SELinux no necesita suid.
-
https://gist.github.com/mak/c36136ccdbebf5ecfefd80c0f2ed6747
- Permite al usuario inyectar un shellcode en un fichero SUID.
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 and we will get pagecache
page!
- El exploit se basa en en escribir a /proc/self/mem en una única condición de carrera.
- ptrace(PTRACE_POKEDATA) puede escribir en mapeos de sólo lectura.
- El ataque se basa en lanzar en paralelo la llamada al sistema con madvise(MADV_DONTNEED) a la vez que se tiene la página del ejecurable mapeada en memoria.
Referencias:
- https://nakedsecurity.sophos.com/2016/10/21/linux-kernel-bug-dirtycow-easyroot-hole-and-what-you-need-to-know/
- https://bugzilla.redhat.com/show_bug.cgi?id=1384344
- https://access.redhat.com/security/vulnerabilities/2706661
- https://plus.google.com/+KeesCook/posts/UUaXm3PcQ4n
- https://twitter.com/nelhage/status/789196293629370368
- https://bugzilla.suse.com/show_bug.cgi?id=1004418#c14
Comentarios
Publicar un comentario