Return Oriented Programming#

En esta sección, veremos una versión especial de buffer overflow que se aprovecha de código que podría estar presente desde antes (incluso sin que quienes programaron aplicación lo sepan), denominada Return Oriented Programming o ROP (juego de palabras frente al concepto OOP o Object Oriented Programming).

En ROP, el objetivo es preparar el stack de tal forma que se apilen punteros a varias secciones del código terminadas en return. Como un return en condiciones normales cambia el valor del eip/rip por el último elemento de la pila (la que debería estar vacía en este frame), si lo hacemos con una pila modificada estaremos encadenando colas de funciones para armar una nueva función con lo que requiramos.

Existe un caso específico de ROP llamado return-to-libc (o ret2libc), el cual utiliza la librería dinámica libc para obtener gadgets en vez de código estáticamente definido en el ejecutable. Además de eso, el método de explotación es el mismo.

Esta forma de ejecutar código arbitrario puede ayudar a superar medidas de mitigación frente a Buffer Overflow como, por ejemplo, el definir secciones de la memoria no ejecutables para evitar la inyección de código arbitraria. Con ROP, no es necesario escribir código nuevo; basta con encontrar las colas de funciones adecuadas para el fin buscado.

Gadgets#

Entenderemos como gadgets a porciones de código de máquina terminadas en un ret o return . Esto nos limita a usar solo código que termine con return, dado que es la única forma que tenemos de limitar la ejecución hasta un punto determinado. Si quisiéramos seleccionar código que no termina en return como un gadget, ejecutaremos sin poder evitarlo las instrucciones que aparecen después de la línea final que seleccionamos (ya que no tenemos cómo controlar el flujo en esa parte).

En cambio, cuando el código seleccionado sí termina en return, podemos asegurarnos de que no se ejecutará nada distinto a lo que seleccionamos, al menos en ese frame específico.

👷 Pendiente: Un gráfico mostrando el comportamiento de los gadgets ROP.

Cadenas ROP#

Es muy poco probable que solo un gadget sea suficiente para armar un exploit. Es por esto que se hace necesario encadenar gadgets para que estos tengan el efecto deseado. Un conjunto de gadgets que ejecutan código útil para el objetivo del atacante es una cadena ROP o ROP Chain.

Una cadena ROP es un conjunto de direcciones de memoria que apuntan a los gadgets ROP específicos. Como cada gadget ROP finaliza con un return, si colocamos la secuencia de direcciones de memoria en el orden adecuado, podemos forzar a que el programa ejecute cada gadget ROP en el orden que necesitamos. Así, terminamos creando algo muy parecido a shellcode sin necesidad de ejecutar código almacenado en la porción de datos de la memoria.

Las cadenas ROP más comunes son las que permiten al atacante cambiar el proceso actual por una shell interactiva, o ejecutar código arbitrario como el usuario que ejecutó la aplicación vulnerable.

👷 Pendiente: Un gráfico mostrando el comportamiento de las cadenas ROP.

Cómo armar una cadena ROP#

Existen aplicaciones que facilitan encontrar gadgets y cadenas ROP, si se cuenta con el binario que está intentando ser explotado:

  • ROPGadget: Permite buscar gadgets en los binarios para facilitar explotación ROP.
  • Ropper Muestra información de archivos en difrerntes formatos y encuentra gadgets para construir ROP Chains para diferentes arquitecturas.
  • PwnTools: Módulo de Python con muchas herramientas para resolver problemas de CTF (entre ellos, ROP para pwning).

Una vez que se arma una cadena ROP, se puede exportar como código de máquina para ser usada como entrada en un programa vulnerable.

👷 Pendiente: Agregar un ejemplo de armado de cadenas ROP con las librerías mostradas.

Otras referencias#