sábado, 24 de marzo de 2012

Explotación de vulnerabilidades - Stack Buffer Overflow (Parte II)

Tal y como comenté en la anterior entrada, donde vimos los principios básicos de la vulnerabilidad Stack Buffer Overflow, hoy pasaremos a explicar como podemos sacarle más partido a esta vulnerabilidad, llegando a la ejecución de código arbitrario en la aplicación objetivo.

Para llevar a cabo la explotación de este tipo de vulnerabilidad es necesario que aclare cuatro conceptos que no se vieron en detalle en la serie de entradas “introducción a la ingeniería inversa”.

Registro EIP (Extended Instruction Pointer): Este registro apunta a la siguiente dirección de memoria que el procesador va a ejecutar.

image

Si os fijáis en la captura anterior después de almacenar en la PILA la cadena “Enter your name:”, se indica en el registro EIP la dirección de memoria 0x004013E8, la cual es la siguiente del código a ejecutar, en este caso la función printf.

Instrucción RETN: Es la instrucción encargada de recoger el valor de ESP y almacenarlo en el registro EIP, de este modo el valor de ESP será la próxima dirección de memoria que el procesador va a ejecutar.

image

Dirección de retorno: Es el valor exacto que nos indica la dirección de memoria donde habíamos dejado la aplicación ante de entrar en una subfunción, para así cuando esta termine volver a la posición exacta donde nos quedamos. Este valor se encontrará almacenado en la siguiente dirección de memoria del EBP de la subfunción. Podéis entenderlo de una forma más clara con la imagen de la explicación de la PILA de primer post de la serie “introducción la ingeniería inversa”.

Shellcode: Si pensamos en una definición genérica, podemos decir que se trata de la acción u orden que se desea realizar una vez hemos explotado una vulnerabilidad. Para algo más completo, os dejo la definición de la Wikipedia.

Una vez hemos afianzados estos términos, pasamos a explicar cómo aprovecharnos de la vulnerabilidad “Stack Buffer Overflo”. Supongo que ya os habréis echo una idea de lo que necesitamos conseguir, de forma rápida podemos decir que nuestro principal objetivo es llegar a sobrescribir la dirección de retorno (almacenada en la PILA) con un valor que apunte a nuestra shellcode. De este modo conseguiremos que al ejecutarse la instrucción RETN de la subfunción, la aplicación vulnerable ejecute el código (shellcode) que nosotros queramos.

Para no perder la costumbre, realizaremos un ejemplo sencillo con la función vulnerable strcpy y con el siguiente código:

image

Si os fijáis el código es muy simple. En la primera función realizamos una llamada a la segunda pasando como parámetro el primer argumento de la aplicación, mientras en la segunda función, recogemos dicho argumento y lo copiamos a una variable local de 16 bytes.

El proceso de explotación lo voy a dividir en cuatro partes:

1 - Detección de la vulnerabilidad

Existen multitud de formas de detectar la existencia de la vulnerabilidad “Stack Buffer Overflow” en una aplicación, aunque pocas de ellas se pueden considerar sencillas, nosotros no vamos a tener problemas al disponer del código.

Al tratarse de una vulnerabilidad “Buffer Overflow”, una forma sencilla de detectarla, es mediante la introducción de una cadena lo suficientemente larga para que sobrescriba parte de la PILA de la aplicación y así dejarla sin funcionalidad.

image

Al debuggear la aplicación añadiendo una ristra de “X” como argumento, se puede identificar la PILA sobrescrita con el valor “58”, la letra “X” mayúscula en hexadecimal. Si leemos el mensaje de error, nos indica que la dirección de memoria 0x58585858 no puede ser leída por la aplicación, lo que nos indica que hemos llegado a sobrescribir un valor de la PILA que posteriormente iba a ser utilizado como dirección que ejecutaría alguna instrucción, en nuestro caso la dirección de retorno.

2 - Localización de la dirección de retorno

La localización de la dirección de retorno en la explotación de este tipo de vulnerabilidad es clave, ya que es necesario sobrescribir la misma con la dirección que nosotros queramos, para que posteriormente el procesador tome dicha dirección como el próximo punto de salto.

¿Pero cómo sabemos su posición exacta?

Debido a que hemos introducido siempre el mismo carácter, no es posible identificar que posición exacta es la de la dirección de retorno. Un modo rápido es ir utilizando diferentes caracteres en el buffer para posteriormente cotejar con la dirección del mensaje de error, o utilizar la aplicación pattern_create.rb del framework metasploit, encargada de generar buffers sin repeticiones.

image

Introducimos los caracteres generados como argumento y observamos la nueva dirección que nos proporciona el error.

image

A diferencia del anterior error, este nos está devolviendo la dirección 0x62413961. Si nosotros transformamos dicho valor a ASCII, obtendríamos la siguiente cadena “bA9a”, el cual sería erróneo ya que la forma de almacenar valores en la pila es Little-endian.

image

Por lo que haría falta invertir el orden de la dirección y transformar el valor a ASCII:

image

Si buscamos la cadena obtenida en la ristra de caracteres generada por la aplicación pattern_create.rb, podremos localizar el primer carácter en la posición 29, con lo cual, existen 28 caracteres (bytes) antes de la dirección de retorno.

Sustituiremos los valores 29, 30, 31 y 32 con el carácter “X” para comprobar rápidamente que concuerdan con los valores de la dirección de retorno.

Cadena: 

Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8AXXXX0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac
3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A

image

De este modo ya sabemos la posición exacta donde se encuentra la dirección de retorno. Como ayuda, podíamos haber utilizado la aplicación pattern_offset.rb de framework metasploit, que realiza el trabajo de búsqueda por nosotros.

image

Con esto finaliza la entrada de hoy, en la próxima entrega veremos como llegar a explotar la vulnerabilidad saltando a nuestra shellcode anteriormente introducida en la PILA.

Un Saludo!!

3 comentarios:

  1. estan geniales tus articulos pero tengo un problema compilando el programa, para ser exactos es en esta linea:

    for(int i=0; i<300; i++){
    buff[i]= "A"; <------- aqui esta el error
    }

    estoy compilando el programa en Dev-C++ y el error que me da es el siguiente:

    [Error] invalid conversion from 'const char*' to 'char' [-fpermissive]

    ResponderEliminar
  2. @Anónimo Yo estoy utilizando el compilador MinGW y no me devuleve ningún error, prueba con este si quieres.

    Un Saludo!

    ResponderEliminar
  3. Anónimo, eso te pasa porque estás intentando asignar a un char * un char. Como ya te indica en el propio error si indicas -fpermissive te compilará. Seguramente si le pones "const char buf" dejará de darte el error.

    Un saludo.

    ResponderEliminar