El lenguaje de programación Nim se ha vuelto cada vez más atractivo para los desarrolladores de malware debido a su robusto compilador y su capacidad para trabajar fácilmente con otros lenguajes. El compilador de Nim puede compilar Nim en JavaScript, C, C++ y Objective-C, y realizar una compilación cruzada para los principales sistemas operativos como Windows, Linux, macOS, Android e iOS. Además, Nim admite la importación de funciones y símbolos de los lenguajes mencionados anteriormente y la importación desde bibliotecas vinculadas dinámicamente para Windows y bibliotecas compartidas para Linux. También están disponibles módulos contenedores de Nim, como Winim, que hacen que la interacción con el sistema operativo sea sencilla. Todas estas capacidades permiten una fácil integración de Nim en los procesos de desarrollo utilizando estos lenguajes e impulsan el desarrollo de nuevas herramientas, tanto benignas como maliciosas. No sorprende, entonces, que ESET Research haya observado un uso continuo de malware desarrollado en Nim en estado salvaje. Ya en 2019, se detectó que Sednit utilizaba un descargador malicioso escrito en Nim. Otro grupo notorio que juega el juego Nim, y el impulso para desarrollar Nimfilt, es el grupo Mustang Panda APT. ESET Research registró que Mustang Panda utilizó Nim en su conjunto de herramientas por primera vez en una campaña contra una organización gubernamental en Eslovaquia en agosto de 2023. La DLL maliciosa detectada, y utilizada como parte del clásico cargador tridente Korplug del grupo, estaba escrita en Nim. Para los investigadores encargados de realizar ingeniería inversa en dichos binarios, Nimfilt es una herramienta poderosa para acelerar el análisis. Si bien Nimfilt se puede ejecutar como un script de Python tanto en la línea de comandos (con un subconjunto de su funcionalidad) como en el programa IDA de Hex-Rays, aquí se presentará principalmente como un complemento de Python para IDA. Inicializando Nimfilt en IDA Cuando IDA se abre por primera vez, carga e inicializa todos los complementos en el directorio de complementos de IDA. Durante la inicialización de Nimfilt, el complemento utiliza heurística básica para determinar si el binario desensamblado se compiló con el compilador Nim. Si se pasa una de las siguientes comprobaciones, Nimfilt determina que se utilizó este compilador: El binario contiene las siguientes cadenas: El binario contiene cualquiera de los siguientes nombres de funciones de Nim conocidos: NimMain NimMainInner NimMainModule El binario contiene al menos dos de las siguientes cadenas de mensajes de error: @valor fuera de rango @división por cero @desbordamiento excesivo o insuficiente @índice fuera de límites Se proporcionan reglas YARA junto con Nimfilt que realizan comprobaciones similares para determinar si un archivo ELF o PE se ha compilado con Nim. En conjunto, estas comprobaciones son mucho más sólidas que el enfoque adoptado por otras herramientas, como Detect It Easy, que actualmente solo comprueba la sección .rdata de los archivos PE en busca de la cadena io.nim o fatal.nim. Como paso final de inicialización, si el indicador AUTO_RUN de Nimfilt está configurado en verdadero, el complemento se ejecuta inmediatamente. De lo contrario, Nimfilt se puede ejecutar como de costumbre desde el menú de complementos de IDA, como se muestra en la Figura 1. Figura 1. Inicialización y ejecución del complemento Nimfilt en IDA Demangling con Nimfilt Nim utiliza un esquema de manipulación de nombres personalizado que Nimfilt puede decodificar. Durante una ejecución, Nimfilt itera a través de cada nombre de función en el binario, verificando si el nombre es un paquete Nim o un nombre de función. Los nombres descubiertos cambian de nombre a sus formas demandadas. Curiosamente, estos nombres pueden filtrar información sobre el entorno del desarrollador, de forma muy similar a las rutas PDB. Esto se debe a que el compilador de Nim agrega la ruta del archivo al nombre durante la manipulación; Nimfilt revela la ruta al realizar la manipulación. Por ejemplo, los nombres de funciones de paquetes de terceros se almacenan como rutas absolutas durante el proceso de manipulación. La Figura 2 muestra el nombre de una función que se almacena como una ruta absoluta que revela la versión y la suma de verificación del paquete nimSHA2 utilizado, junto con la ruta de instalación del desarrollador para nimble, el administrador de paquetes predeterminado de Nim. Python nimfilt.py GET_UINT32_BE__6758Z85sersZ85serOnameZOnimbleZpkgs50Znim837265504548O49O494554555453d57a4852c515056c5452eb5354b51fa5748f5253545748 505752cc56fdZnim83726550_u68 C:/Users/User.name/.nimble/pkgs2/nimSHA2-0.1.1-6765d9a04c328c64eb56b3fa90f45690294cc8fd/nimSHA2::GET_UINT32_BE u68 Figura 2. el nombre de una función de un paquete de terceros Por el contrario, La Figura 3 muestra el nombre de una función de un paquete Nim estándar almacenado como una ruta relativa (es decir, relativa a la ruta de instalación de Nim). python nimfilt.py toHex__pureZstrutils_u2067 pure/strutils::toHex u2067 Figura 3. Exigiendo el nombre de una función de un paquete Nim estándar Sin embargo, los nombres no siempre se modifican de la misma manera. La Figura 4 muestra que el mismo nombre de función anterior del paquete nimSHA2 se almacena en Linux como una ruta relativa. python nimfilt.py GET_UINT32_BE__OOZOOZOOZhomeZalexZOnimbleZpkgs50Znim837265504548O49O494554555453d57a4852c515056c5452eb5354b51fa5748f5253545748505752cc 56fdZnim83726550_u49 ../../../home/alex/.nimble/pkgs2/nimSHA2-0.1.1-6765d9a04c328c64eb56b3fa90f45690294cc8fd/nimSHA2::GET_UINT32_BE u49 Figura 4. Demangling el nombre de un función de un tercero paquete en Linux Las funciones de inicialización del paquete se modifican de una manera completamente diferente: el nombre del paquete se almacena como una ruta de archivo (incluida la extensión del archivo) colocada antes del nombre de la función y se utiliza un esquema de escape para representar ciertos caracteres como barras diagonales, guiones, y puntos. Al realizar la manipulación, Nimfilt limpia el nombre del paquete eliminando la extensión del archivo .nim, como se muestra en la Figura 5. python nimfilt.py atmdotdotatsdotdotatsdotnimbleatspkgsatswinimminus3dot9dot1atswinimatsincatswinbasedotnim_DatInit000 ../../.nimble/pkgs/winim-3.9.1/ winim/inc/winbase ::DatInit000 Figura 5. Cómo controlar el nombre de una función de inicialización de un paquete de terceros La Figura 6 muestra cómo los nombres de las funciones de inicialización de paquetes nativos se almacenan como rutas absolutas. python nimfilt.py atmCatcatstoolsatsNimatsnimminus2dot0dot0atslibatssystemdotnim_Init000 C:/tools/Nim/nim-2.0.0/lib/system::Init000 Figura 6. Control del nombre de una función de inicialización desde un paquete nativo En IDA, el proceso de control de nombres de Nimfilt es seguido por la creación de directorios en la ventana Funciones para organizar funciones según su nombre de paquete o ruta, como se muestra en la Figura 7. Figura 7. La ventana Funciones IDA antes (izquierda) y después (derecha) de Nimfilt organiza los nombres de funciones por paquete o ruta Aplicación de estructuras a Cadenas de Nim La última acción realizada durante una ejecución de Nimfilt es aplicar estructuras de estilo C a las cadenas de Nim. Así como las cadenas en algunos otros lenguajes de programación son objetos en lugar de secuencias de bytes terminadas en nulo, también lo son las cadenas en Nim. La Figura 8 muestra cómo aparece la cadena ABCDEF en IDA antes y después de ejecutar Nimfilt. Tenga en cuenta que, en forma desensamblada, un binario compilado con Nim utiliza el prefijo _TM como parte del nombre temporal de algunas variables; Suelen ser cadenas de Nim. Figura 8. Una cadena de Nim antes (izquierda) y después (derecha) de ejecutar Nimfilt Nimfilt itera a través de cada dirección en el segmento .rdata o .rodata, y en cualquier otro segmento de datos de solo lectura, buscando cadenas de Nim. Las estructuras se aplican a cualquier cadena descubierta; la estructura contiene un campo de longitud y un puntero a la carga útil que consta de los caracteres de la cadena. Resumen En su camino hacia la compilación como ejecutable, el código fuente de Nim normalmente se traduce a C o C++; sin embargo, este proceso no elimina por completo todos los rastros de Nim. Al realizar un recorrido por el código fuente del compilador de Nim, hemos desentrañado algunos de los caminos tomados en el proceso de compilación y, por lo tanto, pudimos construir Nimfilt como una herramienta de Python y un complemento IDA para ayudar en este desenredado. En resumen, ya sea que sea nuevo en Nim o no, recurrir a Nimfilt hará que su trabajo de ingeniería inversa con binarios compilados por Nim sea casi instantáneamente más fácil y más enfocado. Sin embargo, el desarrollo de Nimfilt no está paralizado en ningún caso; Estamos trabajando en funciones adicionales para manejar la doble manipulación y mejorar el formato de los nombres solicitados y la agrupación de nombres de paquetes. El código fuente y la documentación de Nimfilt están disponibles en un repositorio alojado en la organización GitHub de ESET en https://github.com/eset/nimfilt.