lunes, 16 de noviembre de 2009

Exploiting (I): Buffer Overflow != Stack Overflow

Según la definición de la Wikipedia, un Buffer Overflow es un error de programación que tiene lugar al copiar una cantidad de datos sobre un área que no es lo suficientemente grande como para contener dichos datos, produciendose así la sobreescritura de zonas de memoria.

En general, la mayoría de gente con la que hablo sobre estos temas y le preguntas "¿Qué es un Buffer Overflow?", te contesta: "sí, eso de que escribes un montón de caracteres hasta que sobreescribes la dirección de retorno en la pila y así te salta al código del shellcode..." (eso si entiende un poco de seguridad, sino la respuesta puede ser "lo que?"). Probablemente el Stack Overflow sea uno de los tipos de Buffer Overflow más conocidos y espectaculares, pero el término Buffer Overflow es un poco más genérico.

Para demostrarlo, hemos desarrollado un pequeño código de ejemplo que presenta un Buffer Overflow que puede ser explotado para saltarse un "proceso de autenticación" (si alguien se trave a llamar así a lo que hemos programado) sin sobreescribir la pila, ni inyectar un shellcode, ni nada similar. Veamos pues el código:



Si ejecutamos el código, basicamente tendremos un prompt que nos pide la contraseña y que no nos permite el acceso al resto de funcionalidad (en este caso ninguna, es solo un ejemplo). Sin embargo, podemos observar que la función scanf recoge la cadena que introduzcamos por teclado sin tener en consideración su longitud, por lo que si introducimos una cadena de más de los 10 caracteres que tenemos reservados se producirá un Buffer Overflow.

¿Cómo podemos aprovechar este fallo para saltarnos la autenticación? Seguro que muchos ya se han dado cuenta, pero vamos a pegarle un vistazo a la memoria con OllyDbg a ver si eso nos ayuda más a verlo.





Quizá se vea más claro en la representación gráfica que hemos preparado, donde podemos ver en gris la zona de memoria reservada para la cadena de entrada, y en rojo la zona de memoria reservada para la cadena que guarda la contraseña con la que se compara.

Ahora bien, ¿qué pasa si la cadena introducida es muy larga?





Cómo podemos ver, se ha producido un desbordamiento de buffer de tal manera que hemos sobreescrito la cadena que contenía la contraseña de acceso. De esta manera, en este momento si el programa accede a esta cadena ya no contendrá la cadena "p3nt3st3r", sino la cadena "a". Veamoslo:



Bueno, pues ya está hecho, hemos aprovechado un Buffer Overflow para evadir un proceso de autenticación. El ejemplo es bastante tonto, es cierto, es difícil que nos encontremos con un caso exactamente como este, pero nos sirve para ilustrar y mostrar un tipo de vulnerabilidades que podemos encontrarnos.

13 comentarios :

Alberto dijo...

ía querido ver un ejemplo del funacionamiento de este tipo de vulnerabilidades, la teoría genelamente es fácil conocerla, pero la práctica siempre es algo complicadillo.

Interesante el post ;)

Jose Selvi dijo...

Sí, la verdad es que el desarrollo de exploits es una de esas cosas que casi todo el mundo conoce la teoría, pero que poca gente prueba. Generalmente la gente suele quedarse en utilizar los exploits que están publicados y poco más.

También hay que decir que estos ejemplos que vamos a poner son sencillos, pero que la cosa se puede complicar hasta límites insospechados.

Si no pasa nada raro la semana que viene haremos una demo con otro código de ejemplo de un Stack Overflow.

Maria dijo...

Puede q sea porque no veo la segunda imagen (maldito websense...) pero no entiendo muy el caso.

Entiendo que al insertar más caracteres de los esperados sobreescribas el espacio de memoria reservado para la clave a comparar pero aún así la clave introducida será PATATAAAAAA y la tendrá q comprar con el caracter "A" lo cual devolverá q no es cierto no?

Además en este caso veo demasiada casualidad q el espacio de memoria reservardo para el scanf esté justo antes q el reservado para la cadena a comparar no? realmente podría haber reservado la memoria en otro orden e incluso en posiciones no correlativas no?

Saludos.

Anónimo dijo...

En el ejemplo de ejecución se ve como escribe la contraseña 2 veces.
En la primera sobrescribe la contraseña almacenada con 'a' y en la segunda es cuando introduce como contraseña la 'a'.

Jose Selvi dijo...

María, tal y como dice byteinsanity, el truco es que en un primer intento escribo "patataaaaaaa" que sobreescribirá la contraseña. Me la rechaza, porque está comparando "patataaaa" con "a" y por lo tanto da error, pero como ya he sobreescrito, en el siguiente intento puedo teclear como contraseña "a" y entro sin problemas.

El tema de los espacios reservados, todo depende del compilador que se utilice, pero generalmente las cadenas de caracteres declaradas de forma consecutiva son situadas en memoria también de forma consecutiva, aunque al final para esto no te puedes fiar del código C y tienes que irte a mirar directamente el código compilado, ya que está totalmente en función de como lo haga el compilador.

Espero haber resuelto tus dudas, si no es así dimelo.

Gracias por vuestros comentarios!

Maria Algarra dijo...

Vale, ya lo entiendo, es q al no ver la imagen no lo entendía muy bien..

Aún así tienes que tener conocimiento del tamaño reservador para el scanf no? si no no podrás saber en el segundo intento cuantas "a" tienes q introducir.

Muchas Gracias

Jose Selvi dijo...

No necesitas el código fuente, simplemente te basta con tener el binario. De hecho es mejor que te fies del binario que del código fuente, porque el código fuente luego cuando se compila... a saber en que lo convierte el compilador. Mientras que el binario es exactamente lo que está ejecutando el sistema.

Lo que sí que necesitas es el binario, pero en general esto siempre va a ser así, porque detectar una vulnerabilidad y desarrollar un exploit sin tener el binario... puede ser de locos.

Jose Selvi dijo...

Perdona, igual no contesté, sé la cantidad de A's que hay que meter porque escribo PATATA y miro la memoria del proceso, busco PATATA en la memoria y veo que está encima de la clave.

Sabiendo eso, sólo tengo que contar cuantas A's más me hacen falta para sobreescribir la otra cadena. El \0 ya lo pone automáticamente el scanf al acabar de escribir el contenido en la cadena.

Si sigue sin entenderse dimelo y lo intentaré explicar de otra manera.

Gracias por tus comentarios! ;)

Maria dijo...

Pero sabes cuál es el espacio reservado para la contraseña porque dentro del binario has reconocido la cadena "P3NT3ST3R", si no cómo sabrías cuál es el espacio reservado para la contraseña?

Gracias por tus respuestas, soy un poco novata en esto pero siempre me ha interesado.

Jose Selvi dijo...

El programa es solo un ejemplo, evidentente, si tenemos el binario con la contraseña hardcodeada no necesitamos hacer un Buffer Overflow, ya tenemos la contraseña...

Sin embargo, imagina que es algún tipo de software al que tú le defines la contraseña y esta se hardcodea en el código. Yo tengo mi binario, le defino la contraseña "p3nt3st3r" y luego lanzo el programa, pongo muchas A's de clave, y el programa pega un casque, eso quiere decir que no está controlando muy bien el tamaño. Ahora quiero saber que puedo sobreescribir, para eso lanzo otra vez el programa y uso la contraseña "patata", la busco en memoria después de introducirla y miro hacia abajo. Todo eso, a priori, lo puedo sobreescribir, y entre ellos, la contraseña (la reconozco porque la he elegido yo mismo).

Ahora, teniendo este "exploit", me puedo ir al mismo software pero implantado en otro sitio y al que se le ha puesto otra contraseña, y saltarme la autenticación.

Como he comentado antes, el programita es solo un ejemplo bastante tonto para que se vea que se puede aprovechar un Buffer Overflow sin tener que llegar a sobreescribir la pila.

Juan Carlos Montes dijo...

El articulo esta bien, pero estaría mejor si no usaras un ejemplo de stack overflow para decir que un buffer overflow no es lo mismo que un stack overflow.

Al definir las variables dentro del main, lo que provocas es que el espacio reservado para ellas sea la pila, por lo que realmente estas provocando un stack overflow.

Jose Selvi dijo...

Touchè ;)

Era solo un ejemplo para ilustrar que puedes sobreescribir variables para conseguir vulnerar la seguridad sin sobreescribir la dirección de retorno en la pila, inyectar un shellcode, etc.

De todas maneras, aunque sacaramos las dos variables de main, todo lo demás que se ha explicado valdría perfectamente, lo único es que ambas variables estarían en otra zona de la memoria, pero vamos... es lo mismo, tampoco creo que ese detalle afecte a la calidad (o no) del artículo xD.

Si nos ponemos puristas puristas... sí, cualquier Buffer Overflow dentro de la pila ES un Stack Overflow, tienes razón :P

Lautaro Linquiman dijo...

Realmente un blog excelente, Felicitaciones, lo voy a agregar a mis rss.

Saludos!