Fetch the Flag CTF 2022: solución de Pay Attention
Assaf Ben Josef
10 de noviembre de 2022
0 minutos de lectura¡Gracias por jugar Fetch con nosotros! Felicitaciones a los miles de jugadores que participaron en Fetch the Flag CTF. ¡Y muchas gracias a los Snykers que crearon, probaron y documentaron los desafíos!
En esta publicación, veremos cómo nuestro equipo resolvió el desafío pay-attention de Fetch the Flag CTF 2022 de Snyk. Este desafío simula un caso en el que probablemente se haya secuestrado un paquete popular y se lo haya convertido en malicioso; ¡asumiremos el papel del investigador de seguridad que analiza el problema!
Guía paso a paso
Al comenzar, encontramos un archivo descargable pytest-7.1.3.tar.gz y la pista «¿Has estado prestando atención últimamente?».
Lo primero que notamos es el nombre del archivo. Pytest es un framework de pruebas de Python muy popular y, al momento de escribir esto, la versión 7.1.3 es la más reciente. Así que parece que este archivo debería ser un archivo comprimido de la última versión de pytest.
Claro que no puede ser una copia exacta de pytest 7.1.3, porque, de lo contrario, no tendríamos ninguna pista. Para comprobarlo, iremos al repositorio de pytest en GitHub y descargaremos el código fuente de la versión 7.1.3. Si inspeccionamos las propiedades de estos archivos comprimidos, veremos que la versión de pytest del CTF es apenas un poco más grande que la versión original de pytest.
Para encontrar exactamente dónde está la diferencia entre estos dos archivos comprimidos, podemos extraer las carpetas y compararlas con el comando diff -rq de la terminal, que recorrerá recursivamente el contenido de las carpetas y buscará diferencias entre los archivos, así:

Ahora encontramos algunas diferencias menores en varios archivos de metadatos. Pero también, y más importante aún, uno de los archivos de código fuente reales, fixtures.py, es diferente en estas dos carpetas. ¡Ahí debe estar!
Prestar atención al código sospechoso
Al inspeccionar el archivo con nuestro IDE favorito, desplazándonos un poco por el contenido (o al recibir una alerta del IDE por una sangría inesperada), encontraremos esta línea de código sospechosa:
A primera vista, parece que esta línea importa algunas bibliotecas y luego invoca algunas llamadas de función interesantes, como b64decode y exec. Sin duda es sospechoso, pero todavía no podemos saber qué está pasando, así que vamos a decodificarlo.
Empezaremos con las cadenas más cortas que comparten un patrón similar, como \x65\x78\x65\x63.
Es posible que algunos sepan que Python 3 usa Unicode para representar cadenas y, si profundizamos en la documentación de Python 3, encontraremos que este patrón es simplemente una representación hexadecimal de dos dígitos de puntos de código Unicode.
Pero, en realidad, no necesitamos saber todo eso. Al observar la línea de código, vemos que estas cadenas se pasan tal cual a funciones integradas de Python, como __import__(), así que podemos suponer que el intérprete de Python ya sabe cómo procesarlas. Entonces, si iniciamos una sesión REPL de Python, podemos llamar a la función print() con cualquiera de estas cadenas para que imprima su representación Unicode legible. Por ejemplo, print(‘\x65\x78\x65\x63’) nos daría la cadena exec. ¡Genial!
Después de resolver todo esto, obtendremos el siguiente resultado:
Parece que este script decodifica una cadena en base64, la compila y luego la ejecuta. Ahora queremos saber qué fragmento de código se está ejecutando, así que decodifiquemos esa cadena en base64 con cualquier herramienta de decodificación en línea. Esto es lo que obtenemos:
Esta vez, parece que el código crea un archivo temporal, escribe un script corto en él y luego lo ejecuta. El script de ese archivo temporal descarga y ejecuta un fragmento de código de un recurso en línea (en el dominio del CTF de Snyk).
Queremos seguir las pistas y averiguar finalmente qué código se ejecuta al final de esta cadena, pero nos encontramos con un obstáculo bastante difícil. ¡El código que se descarga está totalmente ofuscado! La única parte comprensible está justo al comienzo, pero nos indica el nombre y el repositorio de GitHub de la herramienta que se usó para ofuscar el código: Hyperion.
Nuestro primer impulso podría ser pensar que, como tenemos el script ofuscado y el código fuente de la herramienta de ofuscación, deberíamos ponernos el sombrero de ingeniería inversa y empezar a trabajar. Sin embargo, como el script parece estar bien ofuscado y el archivo README del repositorio de GitHub de la herramienta dice que actualmente no hay ningún desofuscador disponible, decidimos agotar primero todas las demás opciones.
Con esa idea, primero descargamos el script ofuscado en un archivo local y configuramos un entorno de depuración para ver si podíamos encontrar algo interesante que ocurriera durante la ejecución. Con nuestro IDE favorito, podemos depurar y ejecutar el código línea por línea para observar qué está pasando.
Aviso de seguridad
Si se tratara de una situación real y estuviéramos trabajando con un script diseñado para causar daño, haríamos todo esto en un entorno de pruebas aislado y seguro.
Y, por suerte, al final nuestro ojo avizor logra encontrar la bandera escondida entre las variables declaradas (después de quitar las barras invertidas):

¡Presta atención!
En resumen, teníamos:
Un paquete aparentemente inofensivo con una línea de código sospechosa inyectada en uno de sus archivos
La línea de código compila y ejecuta un script codificado en base64
Ese script crea y ejecuta un archivo temporal
Ese archivo accede a un recurso remoto para descargar un script ofuscado y luego lo ejecuta
Al inspeccionar y depurar ese script ofuscado, finalmente pudimos encontrar la bandera oculta entre sus declaraciones.
Este tipo de ofuscación de varias capas es muy común en las inyecciones de scripts maliciosos. ¡Qué aventura!
¿Quieres saber cómo encontramos todas las demás banderas? Visita nuestra página de soluciones de Fetch the Flag para descubrir cómo lo hicimos.



