La conferencia de seguridad Ekoparty se realizo hace unos días nuevamente, aun asi con una pandemia de por medio, la misma se realizo de forma online por suerte :)

Este año juntos con unos compañeros y amigos, estuvimos participando del Main CTF de la Ekoparty, para ser el primer año participando y un equipo recién armado, obtuvimos muy buenos resultados en la tabla de posiciones.

Los desafíos del CTF estaban enfocados en malware, los retos eran binarios reales de malware mayormente Ransomware que fueron modificados o aprovechados de alguna forma para poder proporcionar un flag.

Este es un Writeup del reto llamado Lucky, el cual lo resolvimos junto a Matias Lang. Les recomiendo mucho seguir su blog, el contenido que publica es de una gran calidad técnica y muy interesante!

Lucky-Descripcion

Descargando el archivo Zip vemos que el mismo contiene dos archivos: un PCAP y un archivo sin extension que resulta ser el malware en si.

Cuando abrimos el PCAP podemos observar que hay una petición HTTP que es el único trafico capturado:

Lucky-PCAP

Por el otro lado, tenemos el binario del malware en si, llamado “lucky” sin ninguna extension. Lo primero que hicimos fue subirlo a Virustotal y asi poder identificar que malware estábamos enfrentando:

  • Win32.Trojan-Ransom.Locky.A

Locky es muy parecido a Lucky… No debe ser coincidencia, eso no suele pasar en los CTFs… Mirando la pestaña Community de Virustotal nos encontramos con el engine de análisis Intezer, el cual tiene una funcionalidad muy interesante: comparación de samples basándose en sus “genes”.

De esta manera usando Intezer, obtuvimos que la muestra original tiene el SHA 256 03f6ab1b482eac4acfb793c3e8d0656d7c33cddb5fc38416019d526f43577761 y usando AnyRun obtuvimos una muestra del Ransomware original.

El siguiente paso, fue buscar un poco de información acerca del Ransomware, hasta el momento tenemos un PCAP con una petición HTTP que parece tener información binaria, una muestra del malware Original y nuestro malware del CTF.

Asi que con un poco de Google nos encontramos con un análisis de htnhan muy interesante y a bajo nivel sobre como funciona el malware.

En este análisis, estamos interesando en la sección C&C communication la cual explica como es la comunicación con el Command And Control el cual es un servidor controlado por el creador de malware para enviar las claves de cifrado del Ransomware y comunicarse con cada sistema infectado, entre otras cosas.

Al parecer el malware se comunica con su C&C usando peticiones HTTP POST pero con el contenido binario cifrado usando un algoritmo propio del malware.

El mismo es una combinación de “cifrado” XOR junto con operaciones AND y shift binario

Recuerdan el PCAP que tenemos? Bueno tal vez viene por ahi… el reto es entender el algoritmo de cifrado utilizado por el malware, implementarlo y descifrar esa información binaria del PCAP.

Asi que eso fue lo que hicimos, simplemente usamos el código del “blog post” para realizar el descifrado del payload en el PCAP, para esto necesitamos la key de cifrado que se llama K0 y la función client_decrypt

def _rol(val, bits, bit_size):
    return (val << bits % bit_size) & (2 ** bit_size - 1) | \
           ((val & (2 ** bit_size - 1)) >> (bit_size - (bits % bit_size)))

def _ror(val, bits, bit_size):
    return ((val & (2 ** bit_size - 1)) >> bits % bit_size) | \
           (val << (bit_size - (bits % bit_size)) & (2 ** bit_size - 1))

__ROR4__ = lambda val, bits: _ror(val, bits, 32)
__ROL4__ = lambda val, bits: _rol(val, bits, 32)

payload = bytearray(b"\x1f\xdd\x28\xc1\xdb\x96\x8f\x37\x44\x97\x6c\x3d\xd6\x9a\xf9\xaa\x8a\x15\x15\x86\xa8\xf6\xb3\x64\x23\xf5\x7a\xbc\x41\x4a\x5e\x14\x6f\x6a\xce\xc8\x51\x6b\x43\xde\x72\x27\x95\xa4\xbd\xd4\x72\xc5\x6d\x61\xfe\x8f\x79\x07\x92\x36\x9e\x10\xcf\x10\xb1\xc3\x58\x25\x3d\xea\xb2\xb1\x59\xf7\x4f\xd2\x3c\x7e\x76\x59\xaa\x46\x1e\xb4\xf8\xf9\x07\x25\xc0")

key =  0xCD43EF19
ctext = bytearray()

for i, v in enumerate(payload):
    n = ((__ROR4__(key, 0x05) - __ROL4__(i, 0x0D) & 0xFF) ^ v) & 0xFF
    ctext.append(n)
    tmp = __ROL4__(n, (i & 0xFF) & 0x1F) + __ROR4__(key, 0x1)
    key = tmp ^ (__ROR4__(i, 0x17) + 0x53702f68) & 0xFFFFFFFF

print(ctext)

Ahora lo ejecutamos

  • python3 ./decode-lucky.py
~ ❯❯❯ python3 ./decode-lucky.py
bytearray(b'g\x19\xc0\xd61\xa0\x7f\x9a\x11\xde\xe3\x81\xe3;\xe2\xfcJ\x0e\x13\x1e\xcf~<\xf8\x06\x1c\x85\xd8X|\x0eg\xbf\xff\x9f\xef\xb5\x9c\x9f)\x066\xf6\xce\xa3`c\x16o\x9b\x08\xef\xe2q\xe2\xd5\x84\xe6\xbf\xb3K\x05p\xea\xbdWZUu\x1c\x15^\xe97im\xdbETJ\xec\x08t\x87\xba')

No parecer ser texto en claro ni tener el flag que estamos buscando…

Luego de pensar un rato y analizar posibilidades, se nos ocurrió, que pasa si comparamos la muestra original que tenemos y la muestra modificada que nos proporciona el reto?

Para eso usamos los siguientes comandos con la idea de obtener una representación hexadecimal de cada binario, siendo sample_1.bin la muestra original y lucky la muestra del reto, para luego hacer una comparación de las mismas usando raddif2, la cual es una herramienta de la suite Radare, muy recomendada para hacer diff de binarios:

radiff2 sample_1.bin lucky

Cuando hacemos esto vemos que buscando por nuestro K0, recuerden que es la key de cifrado, encontramos que hay una diferencia:

0x0000680c 19ef43cd => 449f1c3d 0x0000680c

Nuestro K0 que conocemos gracias al análisis del “blog post” el cual es 0xCD43EF19, en el binario del reto parece haber sido cambiado por 0x3D1C9F44. Y mirando mas los cambios del binario del reto, nos encontramos con que otro valor ha sido cambiado, en este caso es el 0x53702f68

0x0000685e 682f7053 => 10101010 0x0000685e

El valor 0x53702f68 a sido cambiado en el binario del reto por 0x10101010. Asi que hagamos los cambios necesarios en nuestro código y veamos si obtenemos algo interesante!

[...]
key =  0xCD43EF19
key = 0x3D1C9F44 #Esta es la buena!
ctext = bytearray()

for i, v in enumerate(payload):
    n = ((__ROR4__(key, 0x05) - __ROL4__(i, 0x0D) & 0xFF) ^ v) & 0xFF
    ctext.append(n)
    tmp = __ROL4__(n, (i & 0xFF) & 0x1F) + __ROR4__(key, 0x1)
    key = tmp ^ (__ROR4__(i, 0x17) + 0x10101010) & 0xFFFFFFFF # Key anterior: 0x53702f68, esta es la buena!

Ejecutando…

Lucky-FLAG

Eso parece un FLAG! :)

Reto terminado!

En conclusión: Descargamos una muestra original del malware, luego realizamos un diff entre el original y el malware del reto para encontrar la claves de cifrado validas, usamos el código Python del “blog post” para realizar el descifrado del PCAP y LISTO!

Esperamos que les haya gustado el Writeup del reto. Cualquier consulta, tienen las redes sociales de contacto en el Home

Cuídense y Saludos! :)