|
|||||||
|
|
|
|
|
LinkBack | Herramientas | Desplegado |
|
|
#1 | |||
|
Moderador
![]() Fecha de Ingreso: noviembre-2006
Ubicación: Digital nomad
Amigos 1
Mensajes: 827
Gracias: 96
Agradecido 44 veces en 33 mensajes.
|
El pasado día 17 de enero, Linus Torvalds hizo un commit para corregir
una elevación de privilegios en el kernel Linux 2.6 (CVE-2012-0056). El fallo fue descubierto por Jüri Aedla y según comenta Torvalds en el propio parche, se debe a una incorrecta comprobación de privilegios al abrir el /proc/#pid#/mem de un proceso. Este error no pasó desapercibido a Jason A. Donenfeld que ha escrito y publicado un interesante exploit. Lo ha bautizado como 'Mempodipper'. Donenfeld estudió en detalle el proceso de explotación. De partida se encontró con que efectivamente la comprobación de permisos era débil y además, que la protección contra escritura en el espacio de memoria del proceso fue eliminada del código del kernel. De hecho puede leerse en el comentario del commit correspondiente, que se eliminaba la restricción porque no se consideraba un peligro la escritura en /proc/#pid#/mem. Esto hace virtualmente vulnerables a los núcleos con versión 2.6.39 o superior. Cómo funciona Cuando se abre /proc/#pid#/mem se usa la función 'mem_open'. Dicha función almacena el 'self_exec_id' correspondiente al proceso que solicita la apertura. Esto se hace para comprobar posteriormente si son suficientes privilegios cuando se efectúan operaciones de lectura o escritura sobre el descriptor de fichero obtenido. Código:
static int mem_open(struct inode* inode, struct file* file)
{
file->private_data = (void*)((long)current->self_exec_id);
'self_exec_id' además de llamar a 'check_mem_permission'. En teoría, para escribir en /proc/#pid#/mem, o se es el mismo proceso #pid# o el proceso tiene ciertos permisos especiales relacionados con ptrace. Código:
static ssize_t mem_write(struct file * file, const char __user *buf,
size_t count, loff_t *ppos)
{
...
...
efectúa la operación: Código:
mm = check_mem_permission(task); copied = PTR_ERR(mm); if (IS_ERR(mm)) goto out_free; Código:
'check_mem_permission' simplemente llama a '__check_mem_permission' y como se observa dentro de su código efectúa la comprobación "task ==current": Código:
static struct mm_struct *__check_mem_permission(struct task_struct
*task)
{
struct mm_struct *mm;
mm = get_task_mm(task);
if (!mm)
return ERR_PTR(-EINVAL);
if (task == current)
return mm;
...
...
escriba en su propia memoria y si queremos elevar privilegios necesitaríamos un proceso con 'suid'. Primera aproximación al exploit Donenfeld se figuró cómo hacer esto: $ su "yeeeee haw I am a cowboy" Unknown id: yeeeee haw I am a cowboy Cualquier cosa que se escriba pasa por la memoria del proceso creado por 'su' (que posee 'suid'). Parecia obvio qué hacer: Abrimos '/proc/self/mem' y obtenemos su descriptor de archivo. Ejecutamos 'dup2' para multiplexarlo con 'stderr'. Escribimos nuestro shellcode en él y ejecutando posteriormente con exec obtendríamos root... pero no. Esto no iba a funcionar debido a que aun queda otra restricción más: Código:
if (file->private_data != (void *)((long)current->self_exec_id)) goto out_mm; de ser el mismo que abrió originalmente '/proc/#pid#/mem', recordad más arriba cuando se llama a 'mem_open'. ¿Por qué ha cambiado 'self_exec_id'? Cuando se llama a 'exec' el 'self_exec_id', es aumentado en 1 impidiendo que esto ocurra: Código:
void setup_new_exec(struct linux_binprm * bprm)
{
...
...
current->self_exec_id++;
cualquiera, hacer 'dup2' con 'stderr' y luego hacer 'exec' de un proceso con 'suid' para escribir allí, ya que al hacer 'exec' no van a coincidir los 'self_exec_id'. Donenfeld solucionó esto de manera sencilla pero ingeniosa, muy ingeniosa. El exploit Se hace 'fork' de un proceso y en este hijo se ejecuta 'exec' a un nuevo proceso. De este modo el 'self_exec_id', que se ha heredado del padre, ahora tiene un valor de +1 con respecto al padre. Ahora hacemos que el padre ejecute 'exec' sobre un proceso 'suid' que va a escribir nuestro shellcode. En este momento, al ejecutar 'exec' en el padre su 'self_exec_id' acaba de aumentar a +1 coincidiendo con el del hijo. Mientras tanto en el proceso 'exec' que ha llamado el hijo se obtiene, al abrir, el descriptor del '/proc/#parent_pid#/mem'; y es posible abrirlo porque no existe restricción en la operación de apertura. Es decir, no van a ser comprobados los privilegios del 'exec' que ha creado el hijo. En este momento tenemos un 'exec' del padre con un proceso 'suid' dispuesto a escribir el shellcode. Un 'exec' del hijo con un descriptor del '/proc/#pid#/mem' del padre y la salida 'stderr' acoplada a ese descriptor (llamada a 'dup2') y lo más importante, tenemos los 'self_exec_id' igualados por lo tanto la restricción ha sido evadida. Solo queda que el hijo pase el descriptor al padre y éste va a escribir el shellcode allí ya que cuando llame a 'mem_write', como hemos dicho, las comprobaciones van a ser correctas. Pero aún queda más, averiguar "dónde" se debe escribir en memoria para poder ejecutar de manera exitosa el shellcode. Se necesita una dirección fija de memoria, algo que podría ser complicado si se usa ASLR, pero Donenfeld observó que la mayoría de binarios 'su' de las distribuciones no están compilados con 'PIE'(Position-independent executable) Podemos observarlo si hacemos: $ readelf -h /bin/su | grep Type Type: EXEC (Executable file) Como el mismo Donenfeld apunta, si el 'Type' fuera 'DYN' entonces si podría protegerse con ASLR debido a que el ejecutable permitiría ser reubicado en distintas posiciones de memoria. Al no ser el caso el desplazamiento en memoria siempre va a ser el mismo. Buscando Donenfeld obtuvo la dirección 0x402178, correspondiente a una llamada a 'exit@plt'. Tan solo tenía que escribir en 0x402178 menos la longitud de la cadena 'Unknown id: ' y su shellcode se ejecutaría. En definitiva, un exploit más interesante que la propia vulnerabilidad que explota y una muestra de ingenio por parte de Donenfeld al conseguir la explotación de la forma que hemos visto. Más información: Parche de Linus Torvalds
Commit de la eliminación de la protección de escritura
* Linux Local Privilege Escalation via SUID /proc/pid/mem Write
__________________
![]() ![]() "La imaginación es más importante que el conocimiento. El conocimiento es limitado, mientras que la imaginación no".Albert Einstein Última edición por pamda; 01-feb-2012 a las 17:46 |
|||
|
|
|
|
|
#2 | ||
|
Moderador V2.0
![]() Fecha de Ingreso: agosto-2009
Ubicación: /dev/null
Amigos 4
Mensajes: 813
Gracias: 57
Agradecido 170 veces en 123 mensajes.
|
Ayyyyy esas etiquetas de [CODE] :b
__________________
(Python)
|
||
|
|
|
| El Siguiente Usuario Agradeció a David Novikov Por Este Mensaje: | pamda (01-feb-2012) |
|
|
#3 |
|
Moderador
![]() Fecha de Ingreso: noviembre-2006
Ubicación: Digital nomad
Amigos 1
Mensajes: 827
Gracias: 96
Agradecido 44 veces en 33 mensajes.
|
__________________
![]() ![]() "La imaginación es más importante que el conocimiento. El conocimiento es limitado, mientras que la imaginación no".Albert Einstein |
|
|
|
![]() |
| Herramientas | |
| Desplegado | |
|
|
Temas Similares
|
||||
| Tema | Autor | Foro | Respuestas | Último mensaje |
| 0 day: Elevación de privilegios en Microsoft Windows [251110] | pamda | Noticias | 0 | 25-nov-2010 07:25 |
| Error de regresión en el kernel Linux [09/2010] | pamda | Noticias | 0 | 21-sep-2010 09:34 |
| Actualización del kernel para SuSE Linux Enterprise 10 [040910] | pamda | Noticias | 0 | 04-sep-2010 13:41 |
| Vulnerabilidades locales en el kernel Linux [250810] | pamda | Noticias | 0 | 25-ago-2010 12:31 |
| interesante (y peligroso) fallo en el kernel Linux 2.6 [180810] | pamda | Noticias | 0 | 18-ago-2010 21:38 |