BROP - Blind Return Oriented Programming
Información Básica
El objetivo de este ataque es poder abusar de un ROP a través de un desbordamiento de búfer sin ninguna información sobre el binario vulnerable. Este ataque se basa en el siguiente escenario:
Una vulnerabilidad de pila y conocimiento de cómo activarla.
Una aplicación de servidor que se reinicia después de un fallo.
Ataque
1. Encontrar el desplazamiento vulnerable enviando un carácter más hasta que se detecte un mal funcionamiento del servidor
2. Fuerza bruta del canary para filtrarlo
3. Fuerza bruta de las direcciones almacenadas de RBP y RIP en la pila para filtrarlas
Puedes encontrar más información sobre estos procesos aquí (BF Forked & Threaded Stack Canaries) y aquí (BF Addresses in the Stack).
4. Encontrar el gadget de parada
Este gadget básicamente permite confirmar que algo interesante fue ejecutado por el gadget ROP porque la ejecución no se bloqueó. Por lo general, este gadget va a ser algo que detiene la ejecución y se posiciona al final de la cadena ROP al buscar gadgets ROP para confirmar que se ejecutó un gadget ROP específico.
5. Encontrar el gadget BROP
Esta técnica utiliza el gadget ret2csu. Y esto se debe a que si se accede a este gadget en medio de algunas instrucciones, se obtienen gadgets para controlar rsi
y rdi
:
Estos serían los gadgets:
pop rsi; pop r15; ret
pop rdi; ret
Observa cómo con esos gadgets es posible controlar 2 argumentos de una función a llamar.
Además, observa que el gadget ret2csu tiene una firma muy única porque va a sacar 6 registros de la pila. Por lo tanto, enviando una cadena como:
'A' * desplazamiento + canary + rbp + DIRECCIÓN + 0xdead * 6 + STOP
Si se ejecuta el STOP, esto básicamente significa que se utilizó una dirección que está sacando 6 registros de la pila. O que la dirección utilizada también fue una dirección de STOP.
Para eliminar esta última opción, se ejecuta una nueva cadena como la siguiente y no debe ejecutar el gadget STOP para confirmar que el anterior sacó 6 registros:
'A' * desplazamiento + canary + rbp + DIRECCIÓN
Conociendo la dirección del gadget ret2csu, es posible inferir la dirección de los gadgets para controlar rsi
y rdi
.
6. Encontrar PLT
La tabla PLT se puede buscar desde 0x400000 o desde la dirección RIP filtrada de la pila (si se está utilizando PIE). Las entradas de la tabla están separadas por 16B (0x10B), y cuando se llama a una función, el servidor no se bloquea incluso si los argumentos no son correctos. Además, verificar la dirección de una entrada en el PLT + 6B tampoco se bloquea ya que es el primer código ejecutado.
Por lo tanto, es posible encontrar la tabla PLT verificando los siguientes comportamientos:
'A' * desplazamiento + canary + rbp + DIRECCIÓN + STOP
-> sin bloqueo'A' * desplazamiento + canary + rbp + (DIRECCIÓN + 0x6) + STOP
-> sin bloqueo'A' * desplazamiento + canary + rbp + (DIRECCIÓN + 0x10) + STOP
-> sin bloqueo
7. Encontrar strcmp
La función strcmp
establece el registro rdx
a la longitud de la cadena que se está comparando. Ten en cuenta que rdx
es el tercer argumento y necesitamos que sea mayor que 0 para luego usar write
para filtrar el programa.
Es posible encontrar la ubicación de strcmp
en el PLT basándose en su comportamiento utilizando el hecho de que ahora podemos controlar los 2 primeros argumentos de las funciones:
strcmp(<dirección no leída>, <dirección no leída>) -> bloqueo
strcmp(<dirección no leída>, <dirección leída>) -> bloqueo
strcmp(<dirección leída>, <dirección no leída>) -> bloqueo
strcmp(<dirección leída>, <dirección leída>) -> sin bloqueo
Es posible verificar esto llamando a cada entrada de la tabla PLT o utilizando el camino lento de PLT que básicamente consiste en llamar a una entrada en la tabla PLT + 0xb (que llama a dlresolve
) seguido en la pila por el número de entrada que se desea sondear (comenzando en cero) para escanear todas las entradas de PLT desde la primera:
strcmp(<dirección no leída>, <dirección leída>) -> bloqueo
b'A' * desplazamiento + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
-> Bloquearástrcmp(<dirección leída>, <dirección no leída>) -> bloqueo
b'A' * desplazamiento + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
strcmp(<dirección leída>, <dirección leída>) -> sin bloqueo
b'A' * desplazamiento + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
Recuerda que:
BROP + 0x7 apunta a
pop RSI; pop R15; ret;
BROP + 0x9 apunta a
pop RDI; ret;
PLT + 0xb apunta a una llamada a dl_resolve.
Habiendo encontrado strcmp
, es posible establecer rdx
a un valor mayor que 0.
Ten en cuenta que por lo general rdx
ya contendrá un valor mayor que 0, por lo que este paso podría no ser necesario.
### 8. Encontrar Write o equivalente
Finalmente, se necesita un gadget que exfiltre datos para exfiltrar el binario. Y en este momento es posible controlar 2 argumentos y establecer rdx
mayor que 0.
Hay 3 funciones comunes que podrían ser abusadas para esto:
puts(data)
dprintf(fd, data)
write(fd, data, len(data)
Sin embargo, el documento original solo menciona la función write
, así que hablemos de ella:
El problema actual es que no sabemos dónde está la función write dentro de la PLT y no sabemos un número de fd para enviar los datos a nuestro socket.
Sin embargo, sabemos dónde está la tabla PLT y es posible encontrar write basado en su comportamiento. Y podemos crear varias conexiones con el servidor y usar un FD alto esperando que coincida con algunas de nuestras conexiones.
Firmas de comportamiento para encontrar esas funciones:
'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Si se imprime algún dato, entonces se encontró puts'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Si se imprime algún dato, entonces se encontró dprintf'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + (RIP + 0x1) + p64(0x0) + (PLT + 0xb ) + p64(STRCMP ENTRY) + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Si se imprime algún dato, entonces se encontró write
Explotación Automática
Referencias
Documento original: https://www.scs.stanford.edu/brop/bittau-brop.pdf
Last updated