WWW2Exec - atexit()
Last updated
Last updated
Aprende y practica Hacking en AWS: HackTricks Training AWS Red Team Expert (ARTE) Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Hoy en día es muy raro explotar esto!
atexit()
es una función a la que se le pasan otras funciones como parámetros. Estas funciones se ejecutarán al ejecutar un exit()
o el retorno del main.
Si puedes modificar la dirección de cualquiera de estas funciones para que apunte a un shellcode por ejemplo, obtendrás control del proceso, pero actualmente esto es más complicado.
Actualmente las direcciones de las funciones a ejecutar están ocultas detrás de varias estructuras y finalmente la dirección a la que apuntan no son las direcciones de las funciones, sino que están encriptadas con XOR y desplazamientos con una clave aleatoria. Por lo tanto, actualmente este vector de ataque no es muy útil al menos en x86 y x64_86.
La función de encriptación es PTR_MANGLE
. Otras arquitecturas como m68k, mips32, mips64, aarch64, arm, hppa... no implementan la función de encriptación porque devuelve lo mismo que recibió como entrada. Por lo tanto, estas arquitecturas serían atacables por este vector.
Puedes encontrar una explicación detallada de cómo funciona esto en https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html
Como se explica en este post, si el programa sale usando return
o exit()
se ejecutará __run_exit_handlers()
que llamará a los destructores registrados.
Si el programa sale a través de la función _exit()
, llamará a la llamada al sistema exit
y los manejadores de salida no se ejecutarán. Por lo tanto, para confirmar que se ejecuta __run_exit_handlers()
puedes establecer un punto de interrupción en él.
El código importante es (fuente):
Observa cómo map -> l_addr + fini_array -> d_un.d_ptr
se utiliza para calcular la posición del array de funciones a llamar.
Hay un par de opciones:
Sobrescribir el valor de map->l_addr
para que apunte a un fini_array
falso con instrucciones para ejecutar código arbitrario.
Sobrescribir las entradas l_info[DT_FINI_ARRAY]
y l_info[DT_FINI_ARRAYSZ]
(que son más o menos consecutivas en memoria), para que apunten a una estructura Elf64_Dyn
falsificada que hará que nuevamente array
apunte a una zona de memoria controlada por el atacante.
Este informe sobrescribe l_info[DT_FINI_ARRAY]
con la dirección de una memoria controlada en .bss
que contiene un fini_array
falso. Este array falso contiene primero una dirección de one gadget que se ejecutará y luego la diferencia entre la dirección de este array falso y el valor de map->l_addr
para que *array
apunte al array falso.
Según la publicación principal de esta técnica y este informe ld.so deja un puntero en la pila que apunta al link_map
binario en ld.so. Con una escritura arbitraria es posible sobrescribirlo y hacer que apunte a un fini_array
falso controlado por el atacante con la dirección de un one gadget por ejemplo.
Siguiendo el código anterior, puedes encontrar otra sección interesante con el código:
En este caso sería posible sobrescribir el valor de map->l_info[DT_FINI]
apuntando a una estructura ElfW(Dyn)
falsificada. Encuentra más información aquí.
__run_exit_handlers
Como se explica aquí, si un programa sale a través de return
o exit()
, ejecutará __run_exit_handlers()
que llamará a cualquier función destructora registrada.
Código de _run_exit_handlers()
:
Código de __call_tls_dtors()
:
Para cada función registrada en tls_dtor_list
, se desmagnetizará el puntero de cur->func
y se llamará con el argumento cur->obj
.
Utilizando la función tls
de este fork de GEF, es posible ver que en realidad la lista dtor_list
está muy cerca del canario de pila y la cookie PTR_MANGLE. Por lo tanto, con un desbordamiento en ella sería posible sobrescribir la cookie y el canario de pila.
Al sobrescribir la cookie PTR_MANGLE, sería posible burlar la función PTR_DEMANLE
estableciéndola en 0x00, lo que significaría que el xor
utilizado para obtener la dirección real es simplemente la dirección configurada. Luego, escribiendo en la lista dtor_list
es posible encadenar varias funciones con la dirección de la función y su argumento.
Finalmente, hay que tener en cuenta que el puntero almacenado no solo se xorará con la cookie, sino que también se rotará 17 bits:
Por lo tanto, necesitas tener esto en cuenta antes de agregar una nueva dirección.
Encuentra un ejemplo en el post original.
__run_exit_handlers
Esta técnica se explica aquí y depende nuevamente de que el programa salga llamando a return
o exit()
para que se llame a __run_exit_handlers()
.
Veamos más código de esta función:
La variable f
apunta a la estructura initial
y dependiendo del valor de f->flavor
se llamarán diferentes funciones.
Según el valor, la dirección de la función a llamar estará en un lugar diferente, pero siempre estará desenmascarada.
Además, en las opciones ef_on
y ef_cxa
también es posible controlar un argumento.
Es posible verificar la estructura initial
en una sesión de depuración con GEF ejecutando gef> p initial
.
Para abusar de esto, necesitas filtrar o borrar la cookie PTR_MANGLE
y luego sobrescribir una entrada cxa
en initial con system('/bin/sh')
.
Puedes encontrar un ejemplo de esto en el post original del blog sobre la técnica.