Otro Capture The Flag como todos en el mundillo conocemos o "Reto de Ciberseguridad" de esta serie de CTF con el Nivel 3 al que hemos llamado "Nop Nop Nop".
Podéis bajaros el ejecutable pinchando en la imagen:
Resolución CTF - Nivel 3 : Nop Nop Nop
Hola Hackers!!!!
Nivel 3. Tenemos 2 binarios.
$ md5sum Level3 Level3.exe
47d9ef2069d646522dbb9cd9b303414e Level3
02be333c8732920d1131a195e5753eb1 Level3.exe
Antes de nada vamos a ejecutarlo para ver qué hace el programa:
Parece que espera algo por parámetro y siempre sale un “90909090” (NopNopNopNop).
Lo cargamos con radare2 (de Linux) (después lo haré con IDA para ver las diferencias):
Lo analizamos:
Desensamblo el main:
Y tenemos una llamada a lo que parece una función en la que se algo relacionado con xor:
Justo después hace una comparación con algo. Después estará el mensaje con la cadena 90909090.
Si nos hemos fijado al principio de main:
Tenemos 2 string: “90909090” (local_18h) y “Prueba” (local_20h).
Primero de definen una serie de variables, como vemos (esas que se ven de Prueba o 90909090 y un par más, justo las 2 de arriba local_8h y local_10h).
Comienza el programa y comprueba si se le pasa un parámetro. Si es que sí se va a la t y si es que no a la f:
Mostrando el correspondiente mensaje de “no input”. Si hemos introducido algo, vamos a la función “xorencrypt”. Obtiene un resultado y la compara con 0. Sino es igual a cero (jne) salta a 0x40080e y muestra un mensaje (local_18h) que era 90909090.
Si es igual mostrará otro (local_28h) que salía del “xorencrypt”.
Veamos la función “xorencrypt”:
Aparecen nuestras variables local_28h y local_30h en donde obtiene el tamaño de lo que se le pasa en uno de los parámetros y lo guarda.
Lo mismo ocurre con el otro parámetro y local_10h y local_30h.
Después incrementa en 1 una de ellas (local_10h) y reserva memoria (malloc) con su contenido.
Inicializa otra (local_4h) a cero y comienza lo que sería un bucle.
Algo así como:
For (local_4h = 0; local_4h < local_10h; local_4h++).
Y ahí está el Michael Knight (KIT) de la cuestión…
Tenemos local_4h, local_20h, local_20h y local_30h, todas vistas anteriormente.
Sería algo como:
String_cifrado[local_4h] = cadena_con_xor_original [local_4h] xor Clave_por_parametro [local_4h % longitud_ Clave_por_parametro]
La clave de todo esto está en ese “xor eax,esi” que se ve y ¿por qué no es tan evidente? Porque depende de lo que le pases por parámetro, no está “a fuego”.
Si le pasas una “A”, “A” será un 0x41 (65), “B” será 0x42 (66), etc… De ahí que no se vea directamente, por lo que hace falta hacer algo, conocido como… ¡¡¡FUERZA BRUTA!!!
Tranquilos, lo puse fácil, está entre la A y la Z.
O sentido común. En la Wikipedia podemos encontrar una explicación de lo que haría la función XOR
Supongamos que queremos realizar un xor de la palabra “Hola” con la “A” (0x41) y nos saldría, antes:
Resultado:
Si realizáramos la operación inversa, tendríamos, después:
Resultado:
Mismo resultado por lo que podéis ver.
Por último solamente queda comparar lo que salga de la función con los 6 primeros caracteres y comprobar que empieza por “Prueba”. Si es así, mostrará el mensaje completo, utilizará el valor único (1 solo carácter – 0x[a-z0-9]{2} para cada valor del array).
Vamos al IDA, las variables:
Aterrizamos en la función main:
Igual que con radare2, se ve lo mismo.
Función xorencrypt que vimos antes, no me quiero repetir….
Vamos a verlo gráficamente:
Con los cortes en las imágenes no se ven tan bien, sin ellos, sí se ve.
La función xorencrypt:
Y el xor del que hemos hablado:
Es exactamente igual.
¿Y cómo resolvemos esto? No sé lo que tengo que pasarle.
Solución 1 (Shell version):
¿Qué charset utilizaré? A..Z, a..z, 0..9.
Por lo que ya sabemos el valor del contenido de:
Solución 2 (Didier Stevens version):
Didier Stevens se ha currado una herramienta muy útil con la idea de buscar en un fichero cadenas cifradas con XOR, justo lo que hace nuestro programa. Y además sabemos que lo compara con algo, “Prueba”, por lo que podemos utilizarla de la siguiente manera:
Solución 3 (Radare2 version):
Abrimos el CTF con Radare2, analizamos, buscamos la cadena que realizará el XOR, nos posicionamos en el offset adecuado, le damos un tamaño, realizamos el xor y vemos el resultado.
Solución 4 (r2pipe version):
¿Qué es r2pipe?
La API para interactual con radare2.
`import r2pipe
offset = "0x004008b4"
xorkey = "52"
r2 = r2pipe.open("./Level3")
r2.cmd("e io.cache=true")
print ("Antes...")
r2.cmd("s " + offset)
print r2.cmd("ps 16 @ " + offset)
r2.cmd("b 16") # le da un tamanio al buffer
print ("Aplicando xor con 0x" + xorkey + "...")
r2.cmd("wox " + xorkey + " @ " + offset) # realiza un xor con 0x41 en donde esta la cadena cifrada
print ("Despues...")
print r2.cmd("ps 16 @ " + offset) # muestra la cadena descifrada
`
Solución 5 (IDA version):
Abrimos IDA y vemos a la zona donde está la cadena cifrada:
Pulsamos <SHIFT><F2>
, nos abre una ventana donde podemos ejecutar scripts de IDA (IDC) y añadimos el siguiente código:
Ponemos una dirección de inicio, una de final y pulsamos “Run”.
BONUS - CASO REAL:
Mirai
mirai.x86: 7e17c34cddcaeb6755c457b99a8dfe32
Localizamos la zona en donde se realiza el descifrado de la configuración:
En este caso, se descifra realizando un xor con 0x22.
Esta son las cadenas cifradas y que hay que descifrar.
Aplicamos el xor con 0x22 a dicha zona y ya vemos el contenido de la configuración.
Vemos la cadena MIRAI
Espero que os haya gustado.