viernes, marzo 04, 2022

Cómo crear un bug de "Buffer Overflow" para crear su exploit y aprender

La ciberseguridad sigue su auge y la necesidad, y demanda, formativa es muy grande. La transversalidad de la ciberseguridad en cualquier paradigma tecnológico hace que su necesidad crezca y que la formación en perfiles de ciberseguridad sea muy demandada por las empresas. Esto daría para un artículo y hablar de esta necesidad y de las necesidades que tienen que cubrir las empresas, pero el artículo de hoy va más ligado a la parte técnica y al cómo crearse entornos para practicar lo estudiado, para practicar e interiorizar los conocimientos que uno va aprendiendo.

Figura 1: Cómo crear un bug de "Buffer Overflow" para crear su exploit y aprender

Una de las cosas más importantes es poder probar lo que uno estudia y para ello, el artículo de hoy, muestra cómo crearnos un pequeño escenario para estudiar y entender el Buffer Overflow. Hay muchos recursos en Internet que explican muy bien la teoría del desbordamiento de un buffer, es más, hay muchos que nos enseñan la teoría y nos dan ejemplos prácticos. Todo esto es fantástico y debemos valorarlo. No vale con ver cómo otros lo hacen en una clase. Debemos esforzarnos en entenderlo a bajo nivel, a pegarme con ello y una de esas posibilidades las obtengo cuando tengo que montarme el escenario.

Figura 2: Linux Exploiting. Un libro para aprender a explotar bugs
y hacer exploits funcionales .de 0xword.

Es como si quiero practicas técnicas de pivoting y me monto un laboratorio con máquinas virtuales, con diferentes redes y voy jugando con las diferentes formas de hacer pivoting. Esto me ayudará a profundizar mi aprendizaje, a encontrarme con problemas que el profesor no tuvo y a entender por qué suceden ciertas cosas y, sobre todo, interiorizar el conocimiento. Como digo esto nos daría para un artículo sobre la cultura del esfuerzo y la dedicación que es algo que en parte se está perdiendo y sustituyendo por la cultura de la inmediatez. 

Figura 3: RootedLab de Practical Pentesting

El entrenamiento es algo fundamental y eso es lo que se verá en el Rooted Lab sobre entrenamiento a través de la resolución de escenarios. El próximo 9 de marzo un día completo de pentesting práctico a través de la resolución de escenarios de todo tipo.

Creando un programa en C

En primer lugar, vamos a crear una aplicación vulnerable a Buffer Overflow. El objetivo no es aprender en este post a detectar un buffer overflow y explotarlo, el objetivo es mostrar cómo se crea un escenario donde uno puede profundizar e ir entrando en materia sobre la teoría que nos puedan impartir. Lo primero es mostrar el código del programa en pseudocódigo y ver la idea que se tiene:
  • El programa escrito en C recibirá un argumento (argv[1]) el cual será la ristra de bytes que queremos pasar a un buffer y provocar el buffer overflow.
  • Una vez recibimos la entrada, la cual es un string la vamos a convertir en hexadecimal (eso sí, siguen siendo bytes).
  • - Una vez que lo tenemos en hexadecimal aplicamos la función strcpy(b, buffer) dónde buffer almacena la ristra de bytes en hexadecimal de la entrada al programa y la variable b será el destino. La variable buffer será más grande que la variable b de modo que provocaremos el overflow al utilizar strcpy. La función strcpy es una función insegura, la cual no debe utilizarse en programación segura.
La idea es sencilla y un ejemplo rápido sería:

Figura 4: Un código en C con la función strcpy

Este es el esqueleto de la aplicación. Se puede ver cómo podríamos ir jugando a mano introduciendo en la variable chrs posibles valores para ir detectando el desbordamiento. Por ejemplo, si ponemos “a pelo” el valor chrs = “aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa” podríamos llegar a detectar el bug con el buffer overflow de forma sencilla. Eso sí, lo tendríamos "hardcodeado", luego necesitaríamos introducirlo como un argumento de la aplicación o, si montamos un ejemplo más avanzado, a través de un socket.

Figura 5: Ejemplo a través de un socket

El código para convertir el string a hexadecimal es sencillo, aunque en este caso lo que queremos es que la entrada a la aplicación ya venga en un string de hexadecimal, es decir, el string que recibirá la aplicación ya simula ser código hexadecimal, por lo que debe hacer nuestro código es convertirlo a hexadecimal que no dejará de ser un array de caracteres ‘unsigned’.

Probando y detectando overflow

Para este ejemplo sencillo y tras compilar el código, vamos a probar con un debbugger que el overflow está bien creado. Como debugger vamos a usar x64dbg, el cual es un debbugger Open Source. Para pasarle argumentos a nuestra aplicación vulnerable debemos hacerlo a través de la línea de comandos, como se puede ver en la siguiente imagen.

Figura 6: debugging overflow.exe con parámetro desbordante

Se puede ver un gran número de 41414141… para identificar el offset del overflow deberíamos usar un patrón, creado con mona o con pattern_create, pero no es el objetivo del artículo enseñar el buffer overflow, si no el de crear un entorno y ver qué funciona. Ahora, al arrancar el debbugger vemos que hay un desbordamiento y se ve reflejado en el registro EIP, tal y como se puede ver en la imagen.

Figura 7: Desbordamiento de Buffer en el debugger

Para ver cómo controlamos el EIP y estamos “machacando” el valor de retorno de la función strcpy del código en Lenguaje C, podemos modificar la entrada y meter las XA’ hasta llegar a la posición de la dirección de retorno de la función y le metemos 4 ‘B’. Se pueden ver reflejadas en la siguiente imagen, la aparición en el EIP de las 4 ‘B’ (\x42\x42\x42\x42).

Figura 8: Localización de las 'B' en el EIP

Revisamos sobre nuestro código en el debugger si podemos encontrar instrucciones del tipo JMP ESP o CALL ESP para poder redirigir el flujo de la ejecución hacia nuestro código. Después tendremos que meter el número de NOPs que creamos necesario, así como el código ejecutable o shellcode que queremos lanzar.

Figura 9:  Redirigir el retorno de strcpy a call ESP

Vamos ahora con la parte de la ejecución. En el buffer overflow básico el payload tendrá este aspecto:
  • bytes basura +
  • dirección RET modificada (apuntando a una instrucción de salto al ESP) +
  • NOPs
  • + Shellcode.
En la siguiente imagen, se puede ver toda la entrada en hexadecimal y cómo se pueden ver los N Bytes basura (\x41 = “A”) más la dirección utilizada para saltar de nuevo a la pila (76b19689) más 8 bytes con NOPs (\x90) y después el código de la shellcode. En este caso se ha creado un código que es una llamada a una calculadora.

Figura 10: Ejecución del payload

Si revisamos el escenario, este código funciona perfectamente en un entorno Windows 10. Esto es debido a que DEP no viene por defecto para binarios de 32 bits. Además, se ha compilado sin meter protecciones en la pila.

Figura 11: Ejecución de calculadora al abrir el debugger

Como podéis ver, es un ejercicio interesante para hacer y poder aprender más sobre el buffer overflow creando una pequeña aplicación en la que uno tiene vulnerabilidades para poder aprender con ellas. Un escenario didáctico creado para la formación, y si queréis aprender más, os recomiendo el libro de "Linux Exploiting" de 0xWord que trae muchos de estos ejemplos didácticos.

Saludos,

 Figura 12: Contactar con Pablo González

No hay comentarios:

Publicar un comentario