
Mis experimentos en el armamento de Nim (https://nim-lang.org/)
Mis experimentos en armamento de Nim para desarrollo de implantes y operaciones ofensivas generales.
DllMain exportado
mingw y pasar una sola bandera al compilador de Nim.| Archivo | Descripción |
|---|---|
| amsi_patch_2_bin.nim | Parchea AMSI fuera del proceso actual usando un método diferente (WIP, se agradece ayuda) |
| excel_4_com_bin.nim |
Este repositorio no proporciona binarios, tendrás que compilarlos tú mismo. Este repositorio fue configurado para compilar de forma cruzada los archivos fuente Nim de ejemplo a Windows desde Linux o MacOS.
Usa VSCode Devcontainers para configurar automáticamente un entorno de desarrollo para ti (Consulta la sección Configurando un Entorno de Desarrollo). Una vez hecho eso, simplemente ejecuta make.
Instala Nim usando el gestor de paquetes de tu sistema (para Windows usa el instalador en el sitio web oficial)
brew install nimapt install nimchoco install nim(Nim también proporciona una imagen Docker en Dockerhub)
Ahora deberías tener los comandos nim y nimble disponibles, el primero es el compilador de Nim y el segundo es el gestor de paquetes de Nim.
Instala el toolchain Mingw necesario para la compilación cruzada a Windows (No es necesario si estás compilando en Windows):
apt-get install mingw-w64brew install mingw-w64Finalmente, instala la magnífica biblioteca Winim, junto con zippy y nimcrypto
nimble install winim zippy nimcryptoLuego navega a la raíz de este repositorio y ejecuta make.
Deberías encontrar los binarios y dlls en el directorio bin/
Consulta la sección de compilación cruzada en la guía de uso del compilador de Nim, para muchos más detalles.
La compilación cruzada a Windows desde MacOS/*nix requiere el toolchain mingw, normalmente solo es cuestión de brew install mingw-w64 o apt install mingw-w64.
Luego solo tienes que pasar la bandera -d=mingw al compilador de Nim.
Ej. nim c -d=mingw --app=console --cpu=amd64 source.nim
Consulta la sección FFI increíble en el manual de Nim.
Si estás familiarizado con P/Invoke de C#, es esencialmente el mismo concepto aunque se ve un poco más feo:
Ejemplo de llamada a `MessageBox````nim type HANDLE* = int HWND* = HANDLE UINT* = int32 LPCSTR* = cstring
proc MessageBox*(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT): int32 {.discardable, stdcall, dynlib: "user32", importc: "MessageBoxA".}
MessageBox(0, "Hello, world !", "Nim is Powerful", 0)
Para cualquier llamada compleja a la API de Windows utiliza la [biblioteca Winim](https://github.com/khchen/winim), ahorra una cantidad increíble de tiempo y no añade demasiado al tamaño del ejecutable (ver más abajo) dependiendo de cómo la importes.
¡Incluso tiene soporte COM!
## Creación de DLLs de Windows con un `DllMain` exportado
Muchas gracias a la persona que publicó [esto](https://forum.nim-lang.org/t/1973) en el foro de Nim.
El compilador de Nim intenta crear automáticamente una función `DllMain` durante la compilación cuando le indicas que cree una DLL de Windows, sin embargo, por alguna razón no la exporta. Para tener un `DllMain` exportado necesitas pasar `--nomain` y definir tú mismo una función `DllMain` con las pragmas adecuadas (`stdcall, exportc, dynlib`).
También necesitas llamar a `NimMain` desde tu `DllMain` para inicializar el recolector de basura de Nim. (Muy importante, de lo contrario tu computadora explotará literalmente).
Ejemplo:
```nim
proc DllMain(hinst: HINSTANCE, reason: DWORD, reserved: LPVOID): BOOL {.stdcall, exportc, dynlib.} =
if reason == DLL_PROCESS_ATTACH:
NimMain() # Initialize Nim's garbage collector
# Your initialization code here
elif reason == DLL_PROCESS_DETACH:
# Finalization code here
return true
``````nim
import winim/lean
proc NimMain() {.cdecl, importc.}
proc DllMain(hinstDLL: HINSTANCE, fdwReason: DWORD, lpvReserved: LPVOID) : BOOL {.stdcall, exportc, dynlib.} =
NimMain()
if fdwReason == DLL_PROCESS_ATTACH:
MessageBox(0, "Hello, world !", "Nim is Powerful", 0)
return true
Para compilar:``` nim c -d=mingw --app=lib --nomain --cpu=amd64 mynim.dll
### Creación de XLLs
Puedes crear un XLL (un DLL de Excel, imagínate) con una función de auto-apertura que puede usarse para la entrega de payloads. El siguiente código crea un simple XLL que tiene una función de auto-apertura y todo el código boilerplate necesario para compilar como una biblioteca de enlace. El POC se compila como un DLL, luego puedes cambiar la extensión a .xll y se abrirá en Excel y ejecutará el payload al hacer doble clic:```nim
#[
Compile:
nim c -d=mingw --app=lib --nomain --cpu=amd64 nim_xll.nim
Will compile as a DLL, you can then just change the extension to .xll
]#
import winim/lean
proc xlAutoOpen() {.stdcall, exportc, dynlib.} =
MessageBox(0, "Hello, world !", "Nim is Powerful", 0)
proc NimMain() {.cdecl, importc.}
proc DllMain(hinstDLL: HINSTANCE, fdwReason: DWORD, lpvReserved: LPVOID) : BOOL {.stdcall, exportc, dynlib.} =
NimMain()
return true
Hay muchas otras cosas astutas que se pueden hacer con XLL. Vea más ejemplos de técnicas XLL aquí.
Tomado de la página de preguntas frecuentes de Nim
Para la mayor reducción de tamaño, use las siguientes banderas -d:danger -d:strip --opt:size
Adicionalmente, he descubierto que puedes reducir unos pocos bytes más pasando --passc=-flto --passl=-flto al compilador. También eche un vistazo al Makefile en este repositorio.
Estas banderas reducen los tamaños drásticamente: el ejemplo de inyección de shellcode pasa de 484.3 KB a 46.5 KB cuando se compila de forma cruzada desde MacOSX!
Un enorme agradecimiento a @Shitsecure por descubrir esto!
Por defecto, Nim no genera PE con una tabla de reubicación, la cual es necesaria para la mayoría de herramientas que cargan EXE de forma reflectiva.
Para generar un ejecutable de Nim con una sección de reubicación, necesitas pasar algunas banderas adicionales al enlazador.
Específicamente: --passL:-Wl,--dynamicbase
Comando de ejemplo completo:``` nim c --passL:-Wl,--dynamicbase my_awesome_malwarez.nim
## Diferencia de tamaño del ejecutable al usar la biblioteca Winim vs sin ella
Increíblemente, la diferencia de tamaño es bastante insignificante. Especialmente cuando se aplican las optimizaciones de tamaño descritas anteriormente.
Los dos ejemplos `pop_bin.nim` y `pop_winim_bin.nim` fueron creados para este propósito.
El primero define la llamada `MessageBox` de WinAPI manualmente y el segundo utiliza la biblioteca Winim (específicamente `winim/lean` que es solo el SDK central, ver [aquí](https://github.com/khchen/winim#usage)), resultados:```
byt3bl33d3r@ecl1ps3 OffensiveNim % ls -lah bin
-rwxr-xr-x 1 byt3bl33d3r 25K Nov 20 18:32 pop_bin_32.exe
-rwxr-xr-x 1 byt3bl33d3r 32K Nov 20 18:32 pop_bin_64.exe
-rwxr-xr-x 1 byt3bl33d3r 26K Nov 20 18:33 pop_winim_bin_32.exe
-rwxr-xr-x 1 byt3bl33d3r 34K Nov 20 18:32 pop_winim_bin_64.exe
Si importas toda la biblioteca Winim con import winim/com, solo añade alrededor de ~20ish KB que, considerando la cantidad de funcionalidad que abstrae, vale 100% la pena ese tamaño extra.```
byt3bl33d3r@ecl1ps3 OffensiveNim % ls -lah bin
-rwxr-xr-x 1 byt3bl33d3r 42K Nov 20 19:20 pop_winim_bin_32.exe
-rwxr-xr-x 1 byt3bl33d3r 53K Nov 20 19:20 pop_winim_bin_64.exe
## Consideraciones de Opsec
Debido a cómo Nim resuelve las DLLs dinámicamente usando `LoadLibrary` mediante su FFI, ninguna de tus funciones importadas externamente aparecerá en las importaciones estáticas del ejecutable (mira [esta publicación de blog](https://web.archive.org/web/20210117002945/https://secbytes.net/implant-roulette-part-1:-nimplant/) para más información):

Si compilas código fuente de Nim a una DLL, parece que siempre tendrás un `NimMain` exportado, sin importar si especificas tu propio `DllMain` o no (??). Esto podría usarse potencialmente como una firma, no sé cuántas empresas están realmente usando Nim en su pila de desarrollo. Definitivamente destaca.

## Escribir Nim sin el Runtime de Nim
Dado que Nim es muy marcado por las soluciones antivirus, una forma de evitarlo es escribir programas Nim sin el runtime de Nim. [Writing Nim-less Nim](https://www.youtube.com/watch?v=EXX3HmCG3pw) es una charla que describe los pasos para escribir código Nim sin el runtime de Nim y C; el código fuente de esta charla está disponible [aquí](https://github.com/m4ul3r/writing_nimless). La charla sigue y amplía el trabajo de [zimawhit3](https://github.com/zimawhit3) con [Bitmancer](https://github.com/zimawhit3/Bitmancer/tree/main).
La premisa es confiar en las definiciones de tipos de `winim` y aprovechar la escritura de código que no resulte en que se use el runtime de Nim.
## Convertir código C a Nim
https://github.com/nim-lang/c2nim
Lo usé para traducir un montón de fragmentos pequeños de C, no he probado nada grande.
## Puentes de Lenguaje
- Integración con Python https://github.com/yglukhov/nimpy
* Esto es realmente muy interesante, [especialmente esta parte](https://github.com/yglukhov/nimpy/blob/master/nimpy/py_lib.nim#L330). ¿Con alguna modificación podría esto cargar PythonxXX.dll desde la memoria?
- Integración con la máquina virtual de Java: https://github.com/yglukhov/jnim
## Depuración
Usa la función `repr()` en combinación con `echo`, soporta casi todos (??) tipos de datos, ¡incluso estructuras!
Mira [esta publicación del blog para más información](https://nim-lang.org/blog/2017/10/02/documenting-profiling-and-debugging-nim-code.html)
## Configurar un entorno de desarrollo
Este repositorio admite [Devcontainers de VSCode](https://code.visualstudio.com/docs/remote/create-dev-container) que te permite desarrollar en un contenedor Docker. Esto automatiza la configuración de un entorno de desarrollo para ti.
1. Instala VSCode y Docker Desktop
2. Clona este repositorio y ábrelo en VSCode
3. Instala la extensión `Visual Studio Code Remote - Containers`
4. Abre la paleta de comandos y selecciona el comando `Remote-Containers: Reopen in Container`
VSCode ahora construirá la imagen Docker (tomará un poco) y te colocará directamente en tu entorno de desarrollo Nim preconstruido.
## Errores en los que me encontré cayendo
- Al llamar a winapi con Winim y tratar de pasar un valor nulo, asegúrate de pasar el valor `NULL` (definido dentro de la librería Winim) en lugar del valor `nil` incorporado de Nim. (Ugh)
- Para obtener el identificador del sistema operativo del archivo creado después de llamar a `open()` en Windows, necesitas llamar a `f.getOsFileHandle()` **no** a `f.getFileHandle()` por razones.
- El compilador de Nim sí acepta argumentos en la forma `-a=value` o `--arg=value` aunque si miras el uso solo tiene argumentos pasados como `-a:value` o `--arg:value`. (Importante para Makefiles)
- Al definir un arreglo de bytes, también necesitas indicar al menos en el primer valor que es un arreglo de bytes, un poco raro pero ok (https://forum.nim-lang.org/t/4322)
Arreglo de bytes en C#:```csharp
byte[] buf = new byte[5] {0xfc,0x48,0x81,0xe4,0xf0,0xff}
let byteArray = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64]
``````nim
var buf: array[5, byte] = [byte 0xfc,0x48,0x81,0xe4,0xf0,0xff]
Un abrazo virtual para todos los que contribuyeron ❤️
| Archivo | Descripción |
|---|
| pop_bin.nim | Llamar a MessageBox de WinApi sin usar la biblioteca Winim |
| pop_winim_bin.nim | Llamar a MessageBox con la biblioteca Winim |
| pop_winim_lib.nim | Ejemplo de creación de una DLL de Windows con un DllMain exportado |
| execute_assembly_bin.nim | Aloja el CLR, ejecuta reflexivamente ensamblados .NET desde memoria |
| clr_host_cpp_embed_bin.nim | Aloja el CLR incrustando directamente código C++, ejecuta un ensamblado .NET desde disco |
| scshell_c_embed_bin.nim | Muestra cómo armar rápidamente código C existente incrustando SCShell (C) directamente dentro de Nim |
| fltmc_bin.nim | Enumera todos los controladores Minifilter |
| blockdlls_acg_ppid_spoof_bin.nim | Crea un proceso suspendido que falsifica su PPID a explorer.exe, también habilita BlockDLLs y ACG |
| named_pipe_client_bin.nim | Cliente de Tubería Nombrada |
| named_pipe_server_bin.nim | Servidor de Tubería Nombrada |
| embed_rsrc_bin.nim | Incrusta un recurso (archivo zip) en tiempo de compilación y extrae contenidos en tiempo de ejecución |
| self_delete_bin.nim | Una forma de eliminar un ejecutable bloqueado o en ejecución actual en disco. Método descubierto por @jonasLyk |
| encrypt_decrypt_bin.nim | Cifrado/Descifrado usando AES256 (Modo CTR) usando la biblioteca Nimcrypto |
| amsi_patch_bin.nim | Parchea AMSI fuera del proceso actual |
| amsi_providerpatch_bin.nim | Parchea la DLL del Proveedor AMSI (en este caso MpOav.dll) para evadir AMSI. Publicado aquí |
| etw_patch_bin.nim | Parchea ETW fuera del proceso actual (Contribuido por ) |
| wmiquery_bin.nim | Consulta procesos en ejecución y antivirus instalados usando WMI |
| out_compressed_dll_bin.nim | Comprime, codifica en Base-64 y genera código PowerShell para cargar una dll administrada en memoria. Puerto del script original de PowerSploit a Nim. |
| dynamic_shellcode_local_inject_bin.nim | POC para inyectar shellcode localmente recuperado dinámicamente en lugar de codificarlo en un array. |
| shellcode_callback_bin.nim | Ejecuta shellcode usando funciones de callback |
| shellcode_bin.nim | Crea un proceso suspendido e inyecta shellcode con VirtualAllocEx/CreateRemoteThread. También demuestra el uso de definiciones en tiempo de compilación para detectar arquitectura, SO, etc. |
| shellcode_fiber.nim | Ejecución de shellcode mediante fibras |
| shellcode_inline_asm_bin.nim | Ejecuta shellcode usando ensamblador en línea |
| ssdt_dump.nim | Recuperación simple de SSDT usando tabla de funciones en tiempo de ejecución desde el directorio de excepciones. Técnica inspirada en el artículo de MDSEC |
| syscalls_bin.nim | Muestra cómo hacer llamadas directas al sistema |
| execute_powershell_bin.nim | Aloja el CLR y ejecuta PowerShell a través de un espacio de ejecución no administrado |
| passfilter_lib.nim | Registra cambios de contraseña en un archivo (ab)usando un filtro de complejidad de contraseñas |
| minidump_bin.nim | Crea un volcado de memoria de lsass usando MiniDumpWriteDump |
| http_request_bin.nim | Demuestra un par de formas de hacer solicitudes HTTP |
| execute_sct_bin.nim | Ejecución de archivo .sct mediante GetObject() |
| scriptcontrol_bin.nim | Ejecutar dinámicamente VBScript y JScript usando el objeto COM MSScriptControl |
| excel_com_bin.nim | Inyecta shellcode usando el objeto COM de Excel y Macros |
| keylogger_bin.nim | Keylogger usando SetWindowsHookEx |
| memfd_python_interpreter_bin.nim | Usar la syscall memfd_create para cargar un binario en un archivo anónimo y ejecutarlo con la syscall execve. |
| uuid_exec_bin.nim | Planta shellcode desde un array UUID en el espacio heap y usa el callback EnumSystemLocalesA para ejecutar el shellcode. |
| unhookc.nim | Desengancha ntdll.dll para evadir ganchos de EDR/AV (incrusta la plantilla de código C de ired.team) |
| unhook.nim | Desengancha ntdll.dll para evadir ganchos de EDR/AV (implementación pura en Nim) |
| taskbar_ewmi_bin.nim | Usa inyección de memoria de ventana extra mediante la propiedad de Aplicación en Ejecución de la Barra de Tareas para ejecutar el shellcode. |
| fork_dump_bin.nim | (ab)usa la implementación de Windows de fork() y adquiere un handle a un proceso remoto usando el derecho de acceso PROCESS_CREATE_PROCESS. Luego intenta volcar la memoria del proceso bifurcado usando MiniDumpWriteDump() |
| ldap_query_bin.nim | Realizar consultas LDAP vía COM usando el proveedor ADSI de ADO |
| sandbox_process_bin.nim | Esto aísla un proceso estableciendo su nivel de integridad a No Confiable y elimina tokens importantes. Esto puede usarse para "deshabilitar silenciosamente" un proceso PPL (ej. AV/EDR) |
| list_remote_shares.nim | Usar NetShareEnum para listar los recursos compartidos accesibles por el usuario actual |
| chrome_dump_bin.nim | Leer y descifrar cookies de la base de datos sqlite de Chrome |
| suspended_thread_injection.nim | Ejecución de shellcode mediante inyección de hilo suspendido |
| dns_exfiltrate.nim | Exfiltración simple de DNS mediante consultas de registros TXT |
| rsrc_section_shellcode.nim | Ejecutar shellcode incrustado en la sección .rsrc del binario |
| token_steal_cmd.nim | Robar un token/suplantar y luego ejecutar un comando |
| anti_analysis_isdebuggerpresent.nim | Anti-análisis simple que verifica si hay un depurador |
| sandbox_domain_check.nim | Técnica simple de evasión de sandbox, que verifica si la computadora está conectada al dominio o no |
| Hook.nim | Ejemplo de Hook ofensivo para MessageBoxA |
| anti_debug.nim | Mostrando dos técnicas anti-depuración |
| anti_debug_via_tls.nim | Anti-depuración mediante TLS |
| local_pe_execution.nim | Ejecutar archivos exe y dll en memoria |
| stack_string_allocation.nim | Asignar cadenas c y anchas en la pila usando arrays |
| hardware_breakpoints.nim | Funciones Hook usando puntos de interrupción de hardware |
| Inyecta shellcode usando el objeto COM de Excel y Macros Excel 4 (WIP) |