SESION_ACTIVA :: OPERACION_RED_TEAM

Weaponizing Nim: Guia Avanzada de Desarrollo de Malware y Evasion para Red Teamers

Guia avanzada para usar Nim en desarrollo ofensivo: syscalls, memoria, opsec y ejecucion sigilosa.

Weaponizing Nim: Guia Avanzada de Desarrollo de Malware y Evasion para Red Teamers

Weaponizing Nim: Guia Avanzada de Desarrollo de Malware y Evasion para Red Teamers

Si has estado haciendo pentesting o Red Teaming ultimamente, habras notado un cambio en el ecosistema de herramientas. Durante anos, C# y PowerShell fueron los reyes, pero las firmas y el logging (AMSI, Script Block Logging) han hecho que sea cada vez mas dificil operar sin ser detectado. Aqui es donde entra Nim.

Recientemente, Nim se ha convertido en la opcion preferida para muchos desarrolladores de malware. Por que? Porque tiene una sintaxis amigable similar a Python, pero compila a ejecutables o DLLs nativos de Windows muy facilmente, sin depender de una maquina virtual pesada como lo hace Java o incluso Golang.

Este articulo no es una introduccion basica. Vamos a profundizar en como usar Nim para interactuar directamente con la API nativa de Windows (syscalls), como manejar la memoria de forma ofensiva y como evitar los errores comunes que cometen los novatos al intentar portar codigo de C a Nim.


Configuracion y Compilacion para OpSec

Lo primero es olvidar Visual Studio. No necesitas ese IDE pesado. Un simple editor como VS Code y el compilador de Nim son suficientes. Una de las mayores ventajas de Nim es su capacidad de compilacion cruzada. Puedes compilar un binario de Windows desde Linux simplemente instalando el toolchain de MinGW y pasando un flag al compilador.

Flags de Compilacion Criticos

Para operaciones ofensivas, no puedes usar la configuracion por defecto. Necesitamos reducir el tamano y eliminar informacion de depuracion que los analistas de AV podrian usar para crear firmas. Segun la documentacion de Nim y la experiencia en campo, esta es la configuracion ganadora:

nim c -d:danger -d:strip --opt:size payload.nim
  • -d:danger: Elimina todas las comprobaciones de tiempo de ejecucion (como limites de arrays). Es arriesgado, pero necesario para malware optimizado.
  • -d:strip: Elimina simbolos.
  • --opt:size: Optimiza el tamano del binario.

De Win32 a Syscalls: Evasion de Hooks

La mayoria del malware inyecta shellcode en memoria usando una secuencia de 4 metodos bien conocidos:

  1. OpenProcess: Obtener un handle.
  2. VirtualAllocEx: Reservar memoria (RWX o RW).
  3. WriteProcessMemory: Escribir el payload.
  4. CreateRemoteThread: Ejecutar.

El problema es que los EDRs y AVs colocan hooks (ganchos) en estas funciones a nivel de usuario (en ntdll.dll o kernel32.dll) para inspeccionar el flujo y redirigirlo a su motor de analisis.

Para evitar esto, usamos syscalls. Esto nos permite llamar a la Native API directamente, saltandonos los hooks del AV. En Nim, esto implica mapear nuestras llamadas a sus equivalentes nativos:

  • Windows API -> Native API
  • OpenProcess -> NtOpenProcess
  • VirtualAllocEx -> NtAllocateVirtualMemory
  • WriteProcessMemory -> NtWriteVirtualMemory
  • CreateRemoteThread -> NtCreateThreadEx

Al implementar un runner usando estos syscalls, he observado que soluciones como Windows Defender suelen ignorar el binario, especialmente si el payload no esta embebido en el ejecutable sino que se descarga por la red.


Nim a Bajo Nivel: Punteros, Memoria y Trampas

Aqui es donde la mayoria se atasca. Nim es un lenguaje de alto nivel, pero para malware necesitamos tocar memoria cruda.

Shellcode: Array o Secuencia?

Es recomendable usar seq[byte] (secuencia) en lugar de un array fijo. Las secuencias son dinamicas, lo que permite cargar payloads de tamano variable desde un archivo o red.

let shellcode: seq[byte] = @[byte 0x41, 0x41, 0x41]

El problema de “addr” vs “unsafeAddr”

Este es el punto mas critico. En C, obtener la direccion de un array es trivial. En Nim, no puedes usar simplemente la palabra clave addr para obtener la direccion directa de una secuencia seq[byte]. Debes usar unsafeAddr e indexar el primer elemento de la secuencia.

Si intentas pasar la secuencia directamente a una API de Windows como VirtualAllocEx, fallara. La forma correcta es:

VirtualAllocEx(..., unsafeAddr shellcode[0], ...)

Es vital indexar la variable (shellcode[0]) para obtener el puntero al inicio de los datos en memoria.

Casteo de Tipos y Structs

Windows requiere tipos especificos (HANDLE, DWORD, etc.). En Nim, puedes definir estos structs como objetos. Un detalle importante es el uso del pragma {.pure.} al definir el objeto para asegurar la compatibilidad de memoria. Ademas, al pasar structs inicializados a funciones (como NtOpenProcess), a veces es necesario pasarlos usando unsafeAddr en lugar de addr, ya que addr puede no comportarse como esperas con objetos gestionados por Nim.

Ejemplo de casteo rapido de un entero a DWORD:

let pid: int = 1337
let dPid: DWORD = cast[DWORD](pid)

Weaponizacion Real: Un Runner para C2 (Sliver/Cobalt Strike)

Pongamos esto en practica. Un runner moderno no deberia llevar la shellcode dentro. Deberia descargarla.

He experimentado creando un modulo principal que recibe parametros (HTTP o SMB), descarga el payload, elimina el Vector de Inicializacion (IV) si viene de Sliver (los primeros 16 bytes), lo descifra (AES128 CBC) y lo inyecta.

Logica de Descifrado y Limpieza

Si usas Sliver, el payload suele venir con un IV prepuesto. El codigo en Nim debe limpiar esto antes de descifrar:

# Si es Sliver, remover el IV de los primeros 16 bytes
if $paramStr(1) == "sliver":
  for i in 16 ..< shellcode.len:
    actual.add(shellcode[i])
  shellcode = decrypt(actual, key, iv)

Inyeccion Segura

Un consejo de OpSec: inyectar en procesos como Notepad.exe es una bandera roja masiva para Defender. Si haces eso, tu sesion morira rapido. Es mejor dejar que los syscalls creen su propio proceso o hilo, o inyectar en un proceso que tu mismo hayas generado y suspendido, evitando cruzar limites de procesos de sistema sensibles.


Capacidades Avanzadas: El Repositorio “OffensiveNim”

Si quieres ir mas alla, el repositorio OffensiveNim de Byt3bl33d3r es la referencia obligada. Contiene snippets para casi cualquier tecnica moderna:

  • Bypassing AMSI: Parchear la memoria del proceso para deshabilitar el escaneo de scripts.
  • Bypassing ETW: Deshabilitar Event Tracing for Windows para cegar la telemetria.
  • Unhooking ntdll: Recargar una copia limpia de ntdll desde el disco para eliminar los hooks del EDR.
  • Shellcode Injection: Varios metodos, incluyendo inyeccion clasica y syscalls directos.
  • CLR Hosting: Ejecutar assemblies de .NET directamente desde memoria sin tocar disco.

Conclusion

Nim ofrece la elegancia de un lenguaje de scripting con la potencia de bajo nivel de C. Te permite construir herramientas ofensivas personalizadas rapidamente, manipular memoria con precision usando punteros (con cuidado con unsafeAddr) y compilar binarios que, por ahora, evaden muchas soluciones de seguridad por el simple hecho de no ser C# o PowerShell.

Si estas buscando actualizar tu arsenal de Red Team, portar tus cargadores y herramientas a Nim es el siguiente paso logico.