HackIt! Nivel 3: solución

El primer paso por tanto, será realizar ingeniería inversa sobre el binario .class. Para ello, tras buscar un buen descompilador de clases Java, nos encontramos con Jad, una aplicación gratuita (no-libre) multiplataforma.

Descargaremos la versión 1.5.8e compilada estáticamente para no tener problemas de dependencias con ninguna librería.
A continuación, descargaremos el fichero secApplet.class en el que se basa el reto 3.

$ wget http://hackit2.diariolinux.com/secApplet.class

Y procedemos a la descompilación:

./jad secApplet.class

Tras algunos warnings, obtendremos el fichero secApplet.jad, que al abrirlo, comprobaremos que se trata del
código fuente Java original de la clase secApplet. También veremos que la calidad del trabajo de descompilación realizado por Jad
es muy buena (probablemente debido también a que el autor del .class original no se preocupó de esconder su código).

Entre las funciones de interés del fichero .jad se encuentra el código del método decrypt()

String decrypt(String s)
{
String s1 = "";
StringBuffer stringbuffer = new StringBuffer(s);
for(int i = 0; i < stringbuffer.length(); i++)
switch(stringbuffer.charAt(i))
{
case 65: // 'A'
s1 = s1 + "L";
break;
case 66: // 'B'
s1 = s1 + "M";
break;
...

Si analizamos el flujo del programa veremos que se llama a ese método decrypt pasándole el parámetro basename (del código HTML del applet Java):

String s = getParameter("basename");
if(s != null)
s = decrypt(s);

Bien, preparemos una clase Java a la que daremos el nombre Hack, y que incluirá un método main() de prueba del método decrypt():


public class Hack {
public static void main(String[] args){
System.out.println( decrypt(args[0]) );
}
public static String decrypt(String s)
{
[incluír aquí el código de decrypt]
}
}
Compilamos nuestra clase de prueba:

$ javac Hack.java

Y la ejecutamos, pasándole como parámetro el valor de la variable «basename» que encontramos en el código HTML de la página:

$ java Hack jhtgh.spi
users.dat

Bueno, nos devuelve el nombre users.dat. Leyendo el código Java descompilado anteriormente, vemos que users.dat es el nombre del ficheroque se descarga desde getDocumentBase()+users.dat, es decir, desde http://hackit2.diariolinux.com/users.dat :

URL url = new URL(getDocumentBase(), s);
DataInputStream datainputstream = new DataInputStream(url.openStream());
String s1;
while((s1 = datainputstream.readLine()) != null){

Bien, vamos a ver qué se esconde en ese fichero:

$ wget http://hackit2.diariolinux.com/users.dat

Si hacemos un cat de users.dat vemos que es un string cifrado. Según el código Java descompilado, hay que descifrarlo, usando decrypt() otra vez:

$ java Hack `cat users.dat`
euskal|j4v4t0s|./level4-j4v4t0s.html|_self

Con lo que ya tenemos la clave del nivel 4.

Conclusiones: basar la seguridad de un procedimiento de autenticación en esconder el método de descifrado en un fichero binario es bastante ingenuo. Cualquier atacante con unos mínimos conocimientos de ingeniería inversa podrá saltarse cualquier método de seguridad por ocultación. Le costará más o menos tiempo hacerlo, pero podemos estar seguros de que podrá conseguirlo si se esfuerza lo suficiente…

HackIt! Nivel 3: seguridad por ocultación

Lo primero que vemos al entrar en el nivel 3 es un applet Java (que en Firefox, le cuesta cargar lo suyo…). En dicho applet se nos pide que introduzcamos login y password. Si acertamos, pasamos de nivel. Ok, analicemos el código fuente de la página para ver qué podemos averiguar. Lo primero que veremos será información sobre la clase Java principal que usa este applet: secApplet.class. Esta clase usa también clases internas que podemos encontrar compiladas en secapplet.jar. Todo esto lo podemos saber tras analizar el código HTML del reto:

 <applet code="secApplet.class" archive="secapplet.jar" codebase="." WIDTH=343 HEIGHT=152>
			<param name=numusers value="1">
			<param name=basename value="jhtgh.spi">
			<param name=style value="1">
			<param name=numtries value="3">
			<param name=width value="343">
			<param name=height value="152">
			<param name=l2 value="8|38|53|13|">
			<param name=l3 value="10|76|49|13">
			<param name=t1 value="74|38|245|21">
			<param name=t2 value="74|76|245|21">
			<param name=b1 value="74|108|245|20">
			<param name=bkcolor value="1118566">
			<param name=txcolor value="16777215">
			<param name=alturl value="./level3-3m3d35.html">
			<param name=ltitle value="Enter your Username and Password">
			<param name=mtarget value="_self">
		 </applet>

Parece por tanto que todo lo que tenemos que hacer es descompilar el fichero .class  y analizar el código fuente del applet Java, para ver qué es lo que está ocurriendo internamente (y esperemos que: 1) se pueda descompilar  y 2) el código fuente que genere no sea complicado de entender)

HackIt! Nivel 2: resumen criptográfico

Seguimos con la prueba 2 del HackIt!, donde lo dejamos ayer. Algunos lectores me comentan que mejor no dé la solución directamente sino que vaya dando pistas progresivas. Me parece bien.

Lo primero que haremos, en este y otros niveles, será mirar el código fuente de la página en busca de…. bueno, !pistas! 🙂

La página del nivel 2 incluye una función Javascript llamada Login() que tiene el siguiente aspecto:


function Login() {
var user = document.login.username.value;
var pass = document.login.password.value;
if (user=="euskal" && hex_cypher(pass)=="c79cc1714419a4aaf3c4c53360843294") {
location.href = 'level3-' + pass + '.html';
} else {
alert("ACCESS DENIED!");
};
}

Es decir, está claro que el nombre de usuario es ‘euskal’, pero del password sólo sabemos que al pasarlo por una función llamada hex_cypher devuelve como resultado la cadena «c79cc1714419a4aaf3c4c53360843294». Podemos hacer dos cosas, analizar la función hex_cypher(),
que según el código fuente de esa misma página :

< script type="text/javascript" src="cypher.js" > </script>

se encuentra en el fichero cypher.js o bien usar «pensamiento lateral» 🙂 La primera opción siempre es viable dado el código fuente, por ingeniería inversa, pero nos llevará mucho (MUCHO) tiempo. La segunda opción, es más interesante. Pensemos que estamos en la segunda fase, la solución por tanto no debe de ser muy difícil… el algoritmo usado debe de ser conocido, y el resultado de aplicar ese algoritmo a una cadena de texto devuelve otra cadena de 32 caracteres de longitud… no hay muchas funciones de uso común en seguridad informática con esas características… Dada esa pista, tal vez se os ocurra qué función implementa hex_cypher(). Cuando la conozcáis, habrá que pensar en cómo romperla, es decir, ¿es posible a partir de la cadena resultado (32 caracteres) obtener el passwordoriginal sabiendo la función que se aplicó? (o dicho de otra forma, si la función es f, existe la inversa de f?

UPDATE: bueno, pues ya tenemos respuestas al reto 🙂 Efectivamente, se trata de la función md5, que según la Wikipedia:

«En criptografía, MD5 (Message-Digest algorithm 5) es una función criptográfica de dispersión, ampliamente utilizada e insegura [1] [2] , que dado un texto, devuelve un valor hash de 128 bits (32 caracteres). MD5 es un estándar de Internet, documentado en el RFC 1321, y ha sido utilizado en una gran variedad de aplicaciones de seguridad, así como para comprobar la integridad de archivos. Un hash MD5 se expresa típicamente como una cadena de 32 números hexadecimales.»

Una función hash, o función de dispersión como MD5, NO TIENE INVERSA. Es decir existe f(x) pero no f-1 (x). Sin embargo, se puede atacar el problema por fuerza bruta… de hecho, existen en Internet diversas webs con enormes bases de datos precalculadas («al password x, le corresponde el hash y»). Por ejemplo, plain-text.info, una web que añade además la posibilidad de unirse a una red distribuída de obtención de hash md5 (nuestro ordenador puede participar, generando miles y miles de hashes cada día y añadiéndolos a la enorme base de datos de esa web… por cierto, no sólo de md5, sino también lm o ntlm, algoritmos usados en el cifrado de claves en Windows.

Si pasamos el hash c79cc1714419a4aaf3c4c53360843294 por esa web, nos da la clave de acceso al siguiente nivel : 3m3d35.

Nota: existe un método más elaborado que permite conocer todas (hasta cierto tamaño de clave ) las posibles combinaciones de x (y sus hash) sin tener que guardar explícitamente cada una de ellas (estoy hablando de las Rainbow Tables).