BROP - Blind Return Oriented Programming

Aprende hacking en AWS desde cero hasta experto con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

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.

También, 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 la tabla 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 en un valor mayor que 0.

Ten en cuenta que por lo general rdx ya tendrá 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 conocemos 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

Última actualización