Layer 1.1 - Bypasseando las protecciones web
Entonces debemos inspeccionar este código, después de verificar, varias funciones necesarias para cargar la página, seguro que se nos ocurrirá echar un ojo a la función del tip givemeTheFirstPassword():
Layer 1.1.2 – La manera elegante
Se generará una alerta y se te redirigirá nuevamente al rick roll. Bueno, si revisas JS script.js no hay evidencia de un interval (así es como se llaman las tareas programadas de JS). Sin embargo, si suponemos que hay algo comprobando si la variable trollme ha cambiado, debe ser declarado en el HTML principal o en otro script importado del HTML principal.
Justo después de la importación del script.js podemos ver la declaración de otro script. Esto creará un interval para verificar si la variable trollme ha cambiado, podemos destruir este intervalo con el operador JS clearInterval(), eliminar esto y luego cambiar la variable recuperará el primer resultado del reto:
Layer 2 - Web fuzzing
Después de descifrar la primera capa con la primera contraseña, encontraremos la segunda capa del desafío (nota: en el nombre verá la capa 3, esto es un error de la parte del autor, pero decidió dejarlo, porque se dio cuenta mientras muchas personas lo intentaban y eso podía causar malentendidos):
No hay pistas de dónde está la contraseña en el archivo comprimido y, como dijimos en la introducción, este reto es investigar, no hay ninguna guía, pero si revisaste todo el script.js, deberías haber notado algo extraño. Justo antes de la función decodeBinary hay otra función llamada downloadWordList(). Si llamas a esta función, comenzará la descarga de una lista de palabras:
Lo primero que se te ocurrirá será bruteforcear la contraseña del archivo comprimido con hashcat o alguna otra herramienta. Pero primero debemos verificar cuál es el contenido de la lista de palabras. Si revisamos las palabras, veremos que el contenido del archivo es, de hecho, nombres de endpoints web, esto se puede ver, por ejemplo, en el nombre ".config", ".bash_history" nombres de archivos ocultos que generalmente se pueden encontrar en máquinas UNIX.
En resumen, esto nos está dando la pista de que se debe realizar un fuzzing de directorios en la página web para encontrar algo. Aquí al igual que en la primera capa tiene dos enfoques diferentes.
Layer 2.1: solución más rápida
Esta primera solución requerirá un poco de conocimiento de GitHub. Si revisas el dominio de la página web, verás que esta página está alojada en GitHub (https://mimorep.github.io/FeelTheM7/). Si tienes experiencia en alojar páginas web en GitHub, sabrás que esto alojará una página web dentro de un repositorio. Si el usuario no paga el premium de GitHub, el repositorio, será público para alojar la página. Para conocer la dirección del repositorio, debemos construir la URL teniendo en cuenta lo siguiente:
- https://github.com/<username>/<repositoryName>
- https://<username>.github.io/<repositoryName>/
Entonces teniendo esto en consideración la URL del repositorio, si el usuario no tiene una cuenta premium será:
- https://github.com/mimorep/FeelTheM7
Ahora que tenemos el repositorio, solo tenemos que ir a la carpeta de páginas web (tenga en cuenta que esta está presente en la lista de palabras) y veremos el código fuente de ambas páginas:
si inspeccionamos ambas páginas, veremos en el archivo surrender.html la contraseña de la segunda capa hardcodeada:
Pero en este caso, a pesar de ser una solución legítima, recomiendo más la solución normal, por ser más realista.
Layer 2.2 – Solución normal
Para esta solución, deberíamos realizar un "dir fuzzing" en la página web. En mi caso, usaré la herramienta dirbuster (puede usar su herramienta favorita en su lugar, esta si lo prefieres).
Si navegamos hasta el endpoint descubierto "web-pages/top-secret.html", se encontrará la siguiente página de destino:
Como sugiere el tip, cuando se crean endpoints sensibles, se debe tener en cuenta los diccionarios y evitar usar rutas conocidas, pero tened en cuenta que la seguridad por ocultación no es seguridad. Teniendo esta página si inspeccionamos el código, encontraremos un comentario bastante inusual:
Como podemos ver, el .html está comentado, pero si eliminamos el comentario, la frase resultará: “Never surrender.html”, así que coloquemos esa página en la URL:
Como puedes ver, se recupera la contraseña y se completa este reto.
Layer 3 - grep file
Probablemente, esta es el nivel más fácil de todo el reto. Después de descifrar la capa zip con la contraseña del ejercicio anterior, los siguientes archivos se encontrarán en la tercera capa:
Aquí habrá nuevamente otro archivo comprimido protegido por contraseña y, en este caso, un archivo de texto con el nombre GrepMeBBy.txt con el contenido:
Como bien sugiere el nombre, la solución de este reto es simplemente ejecutar comandos grep (u otras formas de encontrar cadenas en grandes archivos txt sin formato), para encontrar la contraseña correcta. Por ejemplo, si encontramos por “contraseña” encontraremos el primer bait del ejercicio:
Sí, una solución será encontrar cada frase que creamos que podría estar antes del token real, pero en este ejercicio, tenemos que ser más inteligentes que eso. Con el primer match podemos ver un patrón:
<nombre_clave> : {<token>}
Entonces, podemos hacer una búsqueda con expresiones regulares para obtener todas las cadenas coincidentes y obtener todo de una vez. En este caso, la expresión regular que usaremos es:
\:\{.*?\}
Eso devolverá todo carácter o número que esté entre paréntesis y antes de ellos dos puntos (esto será necesario, para ser más precisos con la búsqueda). Con esta expresión regular, se recuperarán todos los baits y la contraseña correcta (tened en cuenta que en lugar de grep se está usando VS Code, pero los resultados deberían ser los mismos):
Layer 4 – Stego Forensics
Después de descifrar la capa 3 con la contraseña, verás algo interesante. En este caso, no habrá más archivos comprimidos dentro, en su lugar, estará presente un archivo .wav familiar:
Lo primero que debemos hacer, como investigadores forenses, es verificar dos veces si el archivo es lo que dice (nunca ejecuteis archivos sin saber qué son). Para hacerlo, debemos buscar el número mágico del archivo (estos bytes son utilizados por el sistema operativo para identificar el binario que abrirá el archivo):
Como se puede ver en la imagen el archivo es lo que sugiere, un archivo WAV. Si reproducimos el archivo wav, se reproducirá un sonido familiar, sí, es un rickroll nuevamente. Pero si asumes que debes ser “rickrolled” y escuchar la canción, puedes notar que en algunas partes de la canción hay demasiado ruido.
Y otra cosa que puede venir a tu mente será, realmente 10 MB para una canción de solo 1 minuto en formato wav y con esta calidad de mierda, algo extraño está sucediendo aquí.
Ahora abramos el archivo .wav con "Audacity" para inspeccionar el sonido.
Si prestas atención, se pueden ver dos grandes crestas en la forma de onda, estas dos coinciden con el ruido que se escucha al reproducirlo (te recomiendo que reproduzcas esto por secciones), eso te dará una pista de que algo se esconde allí. con estenografía. Para ver lo que hay en las crestas, podemos usar la vista de espectrograma de Audacity, y se encontrarán algunos resultados interesantes:
Se iniciará una descarga automática y se colocará un programa de python en su máquina. El contenido de la herramienta no estará terminado y será tu trabajo terminar el script:
Una solución final del script puede ser como la siguiente (pero hay muchas opciones disponibles):
Con esto, podremos extraer el último archivo comprimido y descifrarlo con la contraseña.
Layer 5: parcheo y reversing.
Una vez que finalmente hayas recuperado el archivo comprimido, el contenido será:
Como puedes ver, este parece ser el último reto y de hecho lo es. Este es un binario simple que tendremos que reversear o parchear para obtener la solución final y la forma de reclamar tu lugar en el salón de la fama.
Si ejecutas el binario (no te preocupes, no es malware, pero si no me crees puedes pasar el hash a Virus Total o herramientas similares, se ha subido a VirusTotal, es posible que algunos proveedores detecten el binario como malware, pero como lo vas a reversearlo vas a ver eso no es malware en absoluto).
El programa está escrito en código C y compilado con Visual Studio Compiler, la decisión de crear el programa en código C es solo para hacer que el reversing sea un poco más complicado (C es conocido como uno de los códigos más difíciles de reversear, por ser uno de los lenguajes de programación de más bajo nivel).
El código fuente se muestra aquí, no será accesible durante el reto porque está precompilado, pero creo que es un buen punto para proporcionar el código para una mejor comprensión de su comportamiento.
Generalmente, los reversers utilizan entornos virtuales para probar el malware, por lo general, si el reverser no tiene suficiente experiencia, no cambiará la configuración de la máquina para que parezca un normal.
Considerando que las máquinas normales tienen cuatro o más núcleos de CPU, si la máquina tenía dos o más menos núcleos, podríamos suponer que es una máquina virtual, y no nos interesará dejar el código pasar por el flujo normal.
En una situación real, no informaremos al reverser que sabemos que es una máquina virtual, haremos las tareas normales para ocultar el payload real del malware.
Aquí también se realiza otra verificación, el "#" es un operador reservado en código C, que se usa para definir instrucciones precompiladas. En este caso, estamos revisando la distro del host, si no es WIN32 (host de Windows) también evitará la ejecución. Pero en este caso, no tiene sentido esta verificación, porque el binario está compilado puramente para Windows (tiene dependencias de WinAPI), esto se puede ver ejecutando un comando de archivo en el binario.
A veces, si solo queremos identificar rápidamente los IOC (indicadores de compromiso), lo más fácil es simplemente ejecutar strings en el binario. Strings es una herramienta nativa en la mayoría de las máquinas UNIX, pero para hosts de Windows deberemos descargarlo de sysinternals. Esta herramienta extrae todas las cadenas contenidas en el binario, y quizás podremos encontrar si no está bien protegido un beacon o alguna otra información útil.
Al ejecutar esto, recuperaremos información útil, pero el autor había hecho una técnica para evitar que esto sucediera.
- Hay una URL en alguna parte del binario (podemos ver el https://).
- El proceso de parcheo parece tener dos partes (hay una frase que nos dice "Algo falta para completar el desafío”).
- Podemos ver la sentencia anti VM.
- Parece que el resultado final estará en un archivo con el nombre FeelTheM7.txt creado en la misma ruta de ejecución (“./”).
Como se comentó antes, el operador “#” está reservado en C para definir operaciones de precompilación. En este caso, el programa está creando tres funciones, para ocultar el contenido de la cadena y luego en la precompilación del binario, esto se denomina técnicamente "macros".
Solo para resumir, la función HIDE_LETTER reemplazará la letra por otra basada en su código de operación, para luego ser reconstruida con UNHIDE_STRING. Un array con el resultado final contiene la cadena oculta durante el tiempo de ejecución, pero no mostraré el contenido de este array, al final el objetivo de este ejercicio es aprender, por lo que no se proporcionará el resultado final.
Pasemos ahora a la parte principal del programa. En esta parte encontraremos una declaración if que evita que ocurra la ejecución, esta será la tarea para parchear esta instrucción y permitir que el flujo del programa continúe:
Esto desbloqueará la parte final del reto. Ahora necesitaremmos revertir el código y comprender lo que está sucediendo aquí. El programa buscará un archivo específico llamado FeelTheM7.txt en caso de que esté en la misma ruta que el binario, mostrará la solución y la escribirá en este archivo.
Ahora centrémonos en cómo podemos analizar el binario con otras técnicas.
Layer 5.2: reversear y parchear el binario.
En esta sección partiremos del análisis de strings realizado en la sección anterior y tienen dos partes diferenciadas, la estática y la dinámica. Tened en cuenta que la solución se puede obtener con ambas técnicas, y podemos elegir lo que queramos.
Generalmente el análisis dinámico es más fácil, pero también más inseguro (al final estás ejecutando el binario), si me preguntas, un buen reverser siempre elegirá el estático, a menos que el tiempo vaya en contra (pero la solución perfecta será usar ambos).
Layer 5.2.1 – Análisis estático
Para el análisis estático, usaremos en este caso Ghidra, ¿por qué? Porque es gratis y cualquiera puede tenerlo. IDA por ejemplo, a pesar de ser para mi mejor, tiene una versión de pago que no es accesible para todos.
Después de crear el proyecto con el binario, simplemente arrastramos el binario a la carpeta y lo soltamos, Ghidra
detectará automáticamente la necesidad de su configuración:
Analiza el binario con la configuración predeterminada, espera hasta que finalice la ejecución y ahora estamos
listos para empezar.
Si recordamos el resultado de las strings, de la parte anterior, ya sabemos algunos de las cadenas clave.
Vayamos a "Felicidades, llegaste hasta aquí, ¿serías capaz de ¿Omitir esta declaración if? (presionamos la tecla "S" para abrir el indicador de búsqueda), porque parece ser la primera cadena mostrada (podemos verificar esto dos veces ejecutando el binario en un entorno seguro).
Después de buscar la cadena, debemos hacer doble clic en FUN_00401030:00401048, porque allí está la referencia donde se está utilizando la cadena.
En este punto, lo suyo es abrir la vista de gráfico (más rollo IDA), pero si te sientes más cómodo con la otra vista, usa la que más te guste. Para el flujo del binario, parece que estamos en la función principal del programa (todavía tenemos que verificar esto dos veces, pero la estructura del programa parece estar bien).
Para comprender mejor el flujo de ejecución, podemos reorganizar los cuadros (siempre se recomienda cambiar el nombre de algunos cuadros si conocemos su comportamiento, por ejemplo, aquí cambia el nombre del cuadro de salida a FIN), por ejemplo, para que parezca que la instrucción Next fuera una instrucción If que está saliendo de la ejecución del programa:
Podemos ver claramente que el cuadro superior izquierdo es el punto de entrada de la función principal, donde el programa nos felicita por haber llegado tan lejos, luego se detecta una declaración if clara (JNZ). Este comparará el valor de DAT_0041618 y dependiendo del valor saltará al “Parece que tú…” que luego saltará a la salida del programa.
Si prestamos la debida atención veremos que el recuadro inferior izquierdo tiene una cadena con el nombre “Lo estás haciendo bien” esta parece claramente la ruta por la que queremos enviar el programa, de lo contrario el terminará la ejecución.
El condicional del salto se realiza con un JNZ (Jump non Zero), por lo que para puentear y parchear esta parte tenemos dos opciones:
- Cambiar el JNZ por JMP, esto saltará siempre a la casilla que queramos.
- Cambiar el JNZ a JZ (Jump Zero), ya que sabemos que el binario nunca salta a esa parte, la inversión de la comparación de los valores también realizará el salto.
Una vez finalizado el bucle, la cadena de resultados se deofuscará y se imprimirá dentro del archivo “FeelTheM7.txt”, siendo esta la solución final del reto.
Conclusión
Feel the M7 es mi primer reto de ciberseguridad de M7, que se realiza solo con fines educativos y para preparar a las personas para escenarios del mundo real, donde esta tarea se convertirá en la base diaria de un ingeniero forense o reverser.
Si tienes alguna duda/sugerencia sobre el reto, puedes ponte en contacto con el autor a través de LinkedIn.
¡Recuerda permanecer siempre del lado de la luz y disfrutar!
Nos vemos en el salón de la fama.
~ El M7
Un placer poder contribuir a la comunidad, espero que disfrutéis del reto, tanto como yo he hecho mientras lo creaba y os animo a completarlo!
ResponderEliminar