HackIt! 2013. Level 5. Cifrado casero

crackle Nos pasan un archivo .rar con la contraseña del nivel 5 cifrada. Descomprimiendo el rar nos encontramos con una estructura de archivos como la siguiente:

entregable-dificil/
└── bin
    └── org
        └── euskal
            └── hackit
                ├── _2013
                │   ├── App.class
                │   ├── PasswordReveal.clazz
                │   ├── prepare
                │   │   └── PrepareHackit2013.class
                │   ├── TestClass1.class
                │   ├── TestClass1.clazz
                │   ├── TestClass2.class
                │   └── TestClass2.clazz
                └── crypt
                    ├── CryptUtil.class
                    └── FileClassLoader.class

Los .class los podemos intentar descompilar con jd-gui para ver qué hacen. Por ejemplo, App.class descompilado tiene esta pinta:

public class App
{
  public static void main(String[] args)
    throws Exception
  {
    ((Runnable)new FileClassLoader("bin", new CryptUtil(
      PasswordReveal.daKey), new String[] { "PasswordReveal", "TestClass1", 
      "TestClass2" }).loadClass(
      "org.euskal.hackit._2013.PasswordReveal").newInstance()).run();
  }
}

La clase FileClassLoader carga a partir del directorio pasado como primer parámetro las clases compiladas pasadas en el array de Strings (en el ejemplo PasswordReveal, TestClass1 y TestClass2) y las cifra con la clase CryptUtil (a la que pasa como parámetro una semilla que recoge del atributo estático PasswordReveal.daKey), dejando el resultado en un fichero con el mismo nombre y extensión .clazz.

Los TestClass1 y TestClass2 nos los dan como ejemplo, pero casualmente falta PasswordReveal.class 🙂 que parece ser que es la clase que contiene el password que nos interesa. Lo que sí tenemos es la clase cifrada PasswordReveal.clazz.

Si pudiéramos descifrarla, estaríamos muy cerca de pasarnos el nivel… De hecho, tenemos el .class de CryptUtil. Si lo descompilamos, encontraremos el método de cifrado (ojo, applyCript es el código descompilado y como tal tiene algunos errores fáciles de solucionar manualmente):

public class CryptUtil
 
 public static final long DA_2012_KEY = 4919L;
 static final long a = 1664525L;
 static final long c = 1013904223L;
 static final char m = ' ';
 long seed;
 long initial_seed;
 
 public CryptUtil(long seed)
 {
   this.seed = seed;
   this.initial_seed = seed;
 }
 
 public void applyCrypt(byte[] data)
 {
   this.seed = this.initial_seed;
   for (int i = 0; i < data.length; i++) {
     this.seed = ((1664525L * this.seed + 1013904223L) % 32L);
     byte mask = (byte)(int)(this.seed & 0xFF);
     int tmp46_45 = i;
     byte[] tmp46_44 = data; tmp46_44[tmp46_45] = ((byte)(tmp46_44[tmp46_45] ^ mask));
   }
 }

Parece que applyCrypt básicamente realiza un XOR entre el contenido del .class y una semilla pasada como parámetro. Supuestamente, si cogemos por ejemplo TestClass1.class y le aplicamos applyCrypt, deberíamos obtener TestClass1.clazz (y viceversa, al ser una simple función XOR!). Habrá que comprobarlo…

HackIt! 2013. Level 4. Trollencio

trollencio Este nivel nos trajo por la calle de la amargura. Tenemos como pista el cómic de la izquierda. Lo siguiente que nos dicen es que la solución está en este fichero. También tenemos esta otra imagen como pista…. ¿Qué es? ¿Un S.O.S.? No lo sabemos…

sosFinalmente, si sacamos los strings del fichero binario obtendremos algo de información. Se ve que hay una cadena que podría ser el password cifrado de alguna forma… también vemos un 2061 (¿tercera parte de la saga de una odisea del espacio?)  Pero no sabemos cuál es el método a usar para pasar esta fase. ¿Y cómo nos la pasamos nosotros? Jugando con lo que supuestamente debería ser el pass cifrado (M5O1V0R81)… pero no nos gustó el procedimiento, así que… ¿alguien (marcan, lothwen, abeaumont…) sabe cuál es la forma ortodoxa de superar este nivel? Agradecería comentarios al respecto, para que no se me quede esta espina clavada 🙂

HackIt! 2013. Level 3

2013-08-05 05_50_39-Hackit - Main - IBM NotesHack It 3: Post-It v.1989. Así se titula la prueba. El nivel tiene enlazado un fichero .nsf , es decir, una aplicación Lotus Notes (ahora se llama IBM Notes).  La extensión NSF es un acrónimo de Notes Storage Facility. En estos ficheros se guardan tanto el diseño de la aplicación como los datos asociados. Los datos son documentos  (llamados «notes») que se muestran al abrir el fichero .nsf. Normalmente las aplicaciones se acceden a través de un servidor (Lotus Domino) pero, tal y como se demuestra en este nivel, también pueden abrirse en modo local. No me extenderé en hablar sobre el uso de Lotus IBM Notes, ya tuve en otra vida una experiencia con esta bestia y es por ello que conocía el modo «Diseño» de Notes. En este modo se pueden editar las acciones (normalmente programadas en LotusScript) que se ejecutarán al abrir o cerrar la base de datos, al abrir un documento, periódicamente…

Lo más importante es saber cómo pasar este nivel. Lo primero (y creedme, lo más complicado) es conseguir instalar el cliente Notes. Hay una versión llamada Open Social (IBM Notes 9.0), descargable en modo trial para Linux, MacOSX y Windows. La versión para Linux es nativa… pero me costó horrores instalarla (problemas de dependencias con libgnomeprint, libbonobo, libgnome-desktop… En fin, una vez instalada, abrimos la aplicación password.nsf que nos enlazan en este nivel y vemo que muestra una aplicación con 2 columnas: fecha y datos. Además, salta un mensaje indicando que «Your word for today is:  una ristra de carácteres no imprimibles 🙂

Así que entramos en modo diseño desde Actions / View Options / Design… y aquí es donde nos damos cuenta de que la versión de Notes para Linux está sin terminar :-O No tiene modo «Design» (o yo no lo he encontrado). En fin… probemos con la versión Windows. Anda, en esta sí que está el modo diseño. Lo abrimos y vemos que el evento «postopen» desencadena la ejecución de un script que básicamente encadena los números de la segunda columna, tratándolos como carácteres ASCII y muestra el mensaje indicado. Ok, ¿pero de dónde salen estos números? Si examinamos el diseño de esa columna veremos un código como el siguiente:

@Modulo((Data*123);256);
REM{FIXME. 123 IS WRONG};

Es decir, cada número es igual a calcular el módulo 256 de la fecha de su izquierda (interpretada como número), multiplicada por 123. El problema es que 123 no es el número correcto… ¿qué podemos hacer?
1) Apuntar los números de las fechas en crudo (dejando por ejemplo «Data» como valor a imprimir, sin módulos ni gaitas)
2) Una vez que tengamos la ristra de números en crudo, crear un programa que pruebe todas las opciones posibles de X tales que @Modulo((Data*X);256); nos arroje un mensaje con sentido.

Piece of cake! Y así llegamos al temido «Trollencio», level 4, que nos tuvo a casi todos los grupos epatados (que no empatados 😉

HackIt!2013: Level 2 (y II)

Según esta web, la contraseña para acceder a un repo Maven puede ser generada a partir de una contraseña maestra así:

$ mvn --encrypt-master-password Oone3vei
{wsJL3n5FpasHjLctHj2HuHIoc8DBGtpIWp2bc40vkBU=}
$ mvn --encrypt-password Thu8luuV
{uD995k4e9YEHeRC0LWz4jIEv/kAt5Mt/up3X62RoJIs=}

Es decir, si la contraseña maestra fuera Oone3vei, se generaría esto: {wsJL3n5FpasHjLctHj2HuHIoc8DBGtpIWp2bc40vkBU=}, lo que deberíamos guardar en el fichero settings-security.xml. A partir de esa contraseña maestra, podremos generar otras (¿para distintos repos Maven?¿para distintos usuarios del mismo repo?). En el ejemplo, tomando el password Thu8luuV, y a partir de la contraseña maestra, se generará el pass
{uD995k4e9YEHeRC0LWz4jIEv/kAt5Mt/up3X62RoJIs=}, que se guardará en setttings.xml.

Teniendo ambos ficheros, ¿es posible obtener el string que se usó como password y que generó el pass cifrado de settings.xml? Sí, usando el siguiente código Java:

import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
import org.sonatype.plexus.components.cipher.PlexusCipher;
import org.sonatype.plexus.components.cipher.PlexusCipherException;
 
public class Prueba {
 
	public static void main(String[] args) {
		PlexusCipher cipher;
		try {
			cipher = new DefaultPlexusCipher();
			String masterPw = cipher.decrypt("CO0lvhBKZAMHPlguhfnJAWS6zgpLe5BoQO/AwhVwJX/4UEPxkeqBjVKAq+yK37ft", "settings.security");
		String appPw = cipher.decrypt("9FANUCx4GboHlC12nghO/i+oVV4RRSw1grsm6or+KiYJ2tSEAG5BSWAgq1QCmejj9Q4kpppWwU8caX2PioJD1w==", masterPw);
		System.out.println(appPw);
 
	} catch (PlexusCipherException e) {
		e.printStackTrace();
	} 
	}
 
}

Por cierto, necesitarás instalar los paquetes .jar de Plexus Cipher para poder compilar ese código:

$ sudo apt-get install libplexus-cipher-java

Hackit!2013 : Level 2

Nos dicen que tenemos que recuperar el contenido de un archivo .rar que han protegido con un password. Dentro de ese archivo parece ser que podremos encontrar información sobre un repositorio de código (al que accederemos con una contraseña, que tendremos que recuperar). Pero el primer paso será obtener el password que protege el archivo RAR. Lo único que se sabe es que ese password cumple lo siguiente:

<? echo sha1(md5($password)); ?>
77134aa1b02b61cd841d2a81bf64796a31234e28

Teóricamente las funciones de resumen criptográfico (o funciones hash) md5 y sha1 no tienen función inversa. O dicho de otro modo, existe md5(X) para todo X, pero no existe una función Y tal que Y(md5(x))=x. Para colmo de males, el resultado de md5(x) se ha pasado por la función sha1 : sha1(md5(x)). Entonces, ¿qué podemos hacer? Bueno, supongamos que alguien decide generar una tabla para «todo» X, almacenando los resultados. Algo como md5(«a») –> guardar, md5(«aa»)–>guardar, md5(«aaa») –> guardar, md5(«aab»)–> guardar…. etc. Guardar todo eso puede ser muy costoso, sobre todo si la longitud de X empieza a crecer y además, combinamos letras (mayúsculas y minúsculas), números y caracteres no alfanuméricos. Hay «trucos» para no tener que guardarlo todo (ver Rainbow Tables), pero en general, es inviable llegar más allá de passwords de más de 8 o 9 caracteres si tratamos con pass que contienen todos esos símnbolos (Ophcrack guarda esas tablas.. ojito, habamos de ficheros de entre 500GB y 2TB 🙂

¿Y nos tenemos que descargar esos mastodontes sólo para probar? No. Además, los enlaces que indico no sirven para el algoritmo sha1… Vale, ¿entonces qué? Bueno, hay otros sitios web que nos permiten pasarle un hash sha1 o md5 para ver si están indexados en sus bases de datos. Crackstation.net es una de esas webs 😉 Y qué casualidad, tienen ese pass indexado… Una vez recuperado, procederemos a descomprimir el .rar. Veremos que hay un directorio .m2, con 2 archivos: settings.xml y settings-security.xml. Son 2 ficheros que se usan para acceder a un repositorio de código Maven… El 2º guarda una contraseña maestra, a partir de la cual se ha generado la que aparece en settings.xml. ¿Hay alguna forma de, teniendo la contraseña maestra, saber cuál es la palabra o frase que se cifró para obtener la contraseña de settings.xml? Por supuesto, ése será nuestro siguiente paso…