Monta tu propio laboratorio de Active Directory con Ludus y GOAD

Tabla de contenido

Monta tu propio laboratorio de Active Directory con Ludus y GOAD

Los Pro Labs de Hack The Box están muy bien, pero cuestan 49 €/mes y, sobre todo, no puedes publicar nada de lo que haces en ellos. Si lo que quieres es un dominio de Active Directory entero para romperlo, revertirlo y volver a empezar, y encima escribir sobre ello sin pedirle permiso a nadie, la respuesta es montártelo en casa.

En esta entrada monto un laboratorio de AD desde cero con Ludus , que automatiza todo el ciclo sobre Proxmox, y despliego encima GOAD (Game of Active Directory), el laboratorio vulnerable de Orange Cyberdefense. El resultado es un dominio completo que enciendes cuando quieres, rompes a gusto y devuelves a su estado original en veinte segundos con un snapshot.

Escribo esto también como nota para mí mismo: monté el mío hace meses y, cuando volví a él, ya no me acordaba ni de la mitad de los pasos.

🗺️ Qué vamos a montar

        Tu equipo (Kali)
              │
              │  WireGuard  (198.51.100.0/24)
              ▼
   ┌──────────────────────────────┐
   │   Servidor físico "ludus"    │   Debian 12 + Proxmox VE
   │   ────────────────────────   │
   │   Ludus  ──►  Packer         │   construye las plantillas
   │          ──►  Ansible        │   despliega y provisiona
   │                              │
   │   ┌────────────────────────┐ │
   │   │  Range  10.2.0.0/16    │ │
   │   │  router ─ DC01 ─ SRV01 │ │
   │   │           └─ LX01      │ │
   │   └────────────────────────┘ │
   └──────────────────────────────┘

Ludus es la pieza que lo hace llevadero. Instala Proxmox por ti, construye las plantillas de Windows y Linux con Packer, levanta las máquinas, las mete en VLANs, monta un router, te da un WireGuard para entrar desde tu Kali y gestiona los snapshots. Todo con una API y un cliente de línea de comandos.

GOAD pone el contenido: un dominio con usuarios, relaciones de confianza, ACLs mal puestas, delegaciones y servicios vulnerables. Trae varios laboratorios en el mismo repositorio, así que con la infraestructura montada una vez puedes ir cambiando de escenario.

🧰 Requisitos

Estas son las especificaciones de mi servidor, que es un equipo reciclado, para que veas que no hace falta nada del otro mundo:

PlacaMSI Z97 GAMING 5
CPUIntel i7-4790K (8 hilos) con VT-x
RAM19 GB
Disco218 GB SSD

Sobre eso corren ahora mismo 13 máquinas virtuales: 9 plantillas y 4 del laboratorio. Como referencia, mi laboratorio actual consume 12 GB de RAM con tres VMs encendidas.

⚠️ El disco se sufre más que la RAM. Cada plantilla de Windows reserva unos 250 GB de disco provisionado (aunque en la práctica gaste mucho menos) y yo voy al 74% de ocupación. Si andas justo, construye solo las plantillas que vayas a usar de verdad.

Necesitas además hardware dedicado: Ludus instala Proxmox, así que el equipo se convierte en un hipervisor y no vas a poder usarlo para otra cosa.

🔧 Fase 1: la BIOS

Este paso se olvida y luego las máquinas no arrancan o van a rastras. Los nombres son de una placa MSI, pero el equivalente está en cualquiera:

OpciónValorDónde
Intel Virtualization TechEnabledOC → CPU Features
Intel VT-D TechEnabledOC → CPU Features
Boot Mode SelectUEFISettings → Boot
Fast Boot / MSI Fast BootDisableSettings → Boot
Secure BootDisableAdvanced → Windows 8/8.1
SATA ModeAHCIAdvanced → Integrated Peripherals
Intel C-StateDisableAdvanced → Power Management

Las dos primeras son obligatorias: sin VT-x no hay virtualización. El resto evita sorpresas al arrancar.

🐧 Fase 2: Debian 12

Ludus se instala sobre Debian 12 limpio. Ni Ubuntu ni Debian 13: el instalador espera Bookworm.

Graba la ISO, instala el sistema base sin entorno gráfico y añade tu usuario al grupo sudo:

su -
apt update && apt install sudo -y
usermod -aG sudo TU_USUARIO

Aprovecha para fijar la IP del servidor en el router por MAC (DHCP binding), porque vas a apuntar ahí muchas cosas y no te interesa que cambie.

🚀 Fase 3: instalar Ludus

Ya desde SSH, con las dependencias mínimas:

su -
apt update && apt install curl sudo git ca-certificates python3-debian -y
curl -s https://ludus.cloud/install | bash

⚠️ No hagas esta fase desde MobaXterm. Su terminal interfiere con el instalador. Usa un SSH normal y, a poder ser, dentro de tmux, porque el proceso reinicia el servidor un par de veces mientras instala Proxmox.

Cuando termine, comprueba el estado y apunta la clave de API de root, que es la que permite crear usuarios:

ludus-install-status
# Root API key: ROOT.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

A partir de aquí ya tienes Proxmox escuchando en https://IP_DEL_SERVIDOR:8006.

👤 Fase 4: tu usuario y las credenciales de Proxmox

Ludus es multiusuario: cada usuario tiene su propio range, su red y su WireGuard. Crea el tuyo con la clave de root:

export LUDUS_API_KEY='ROOT.xxxxxxxxxxxxxxxxxxxx'
ludus users add --name "Tu Nombre" --userid JO --admin --url https://127.0.0.1:8081
# te devuelve la API key del usuario: JO.xxxxxxxxxxxxxxxx

El --userid es importante: es la etiqueta que va a llevar todo lo tuyo (la red, las VMs, el peer de WireGuard). Yo uso JO, y por eso mi router se llama JO-router-debian11-x64.

Ahora cambia a la clave de tu usuario, que es la que vas a usar siempre:

export LUDUS_API_KEY='JO.xxxxxxxxxxxxxxxx'
ludus users list

Y saca las credenciales para entrar por web a Proxmox:

ludus users creds get

💡 Mete el export LUDUS_API_KEY en tu .bashrc. Sin esa variable el cliente responde [FATAL] No API key y no funciona ni un comando.

📦 Fase 5: las plantillas

Aquí es donde se va el tiempo. Ludus usa Packer para construir sus propias plantillas: descarga la ISO, hace una instalación desatendida, instala los guest tools y deja la imagen lista para clonar.

ludus templates list

La primera vez las verás todas a FALSE. Para construirlas:

ludus templates build                                  # todas
ludus templates build -n win2019-server-x64-template   # solo una
ludus templates logs -f                                # seguir el proceso

⏱️ Esto tarda horas, y es normal. Déjalo por la noche. Si algo se atasca, ludus templates abort corta el proceso, y si hay que ir a lo bruto, pkill packer como root.

Estas son las que tengo construidas:

+------------------------------------+-------+
|              TEMPLATE              | BUILT |
+------------------------------------+-------+
| debian-11-x64-server-template      | TRUE  |
| debian-12-x64-server-template      | TRUE  |
| kali-x64-desktop-template          | TRUE  |
| win11-22h2-x64-enterprise-template | TRUE  |
| win2022-server-x64-template        | TRUE  |
| ubuntu-24.04-x64-server-template   | TRUE  |
| win2019-server-x64-template        | TRUE  |
| win2025-server-x64-template        | TRUE  |
| win2025-server-x64-tpm-template    | TRUE  |
+------------------------------------+-------+

Añadir una plantilla propia

Las plantillas viven en el repositorio de Ludus y se pueden modificar. El caso que me tocó a mí: Windows Server 2025 sin TPM. Ludus trae la versión con TPM, pero para un laboratorio de AD el TPM sobra y complica los snapshots.

git clone https://gitlab.com/badsectorlabs/ludus.git
cd ludus/templates
cp -r win2025-server-x64-tpm win2025-server-x64
cd win2025-server-x64
mv win2025-server-x64-tpm.pkr.hcl win2025-server-x64.pkr.hcl

# renombrar la plantilla y la fuente dentro del .pkr.hcl
sed -i 's/win2025-server-x64-tpm-template/win2025-server-x64-template/' win2025-server-x64.pkr.hcl
sed -i 's/win2025-server-x64-tpm/win2025-server-x64/' win2025-server-x64.pkr.hcl

Al quitar el TPM hay que tocar también el Autounattend.xml, porque la instalación desatendida está preparada para un disco GPT con partición EFI. Sin TPM el esquema cambia: se deja una partición de sistema y otra de OS, y hay que corregir el PartitionID de destino, que pasa de la 3 a la 2.

Y se registra y se construye:

ludus templates add -d ~/ludus/templates/win2025-server-x64 --force
ludus templates build -n win2025-server-x64-template
ludus templates logs -f

🌐 Fase 6: WireGuard para tu máquina atacante

El laboratorio vive en una red aislada. Para atacarlo desde tu Kali, Ludus te da un túnel de WireGuard:

ludus users wireguard --user JO

Devuelve una configuración completa. Cópiala tal cual en tu Kali:

sudo nano /etc/wireguard/ludus.conf
[Interface]
PrivateKey = <la_tuya>
Address = 198.51.100.2/32

[Peer]
PublicKey = <la_del_servidor>
Endpoint = IP_DEL_SERVIDOR:51820
AllowedIPs = 10.2.0.0/16, 198.51.100.1/32
PersistentKeepalive = 25
sudo wg-quick up ludus     # conectar
sudo wg-quick down ludus   # desconectar

El fallo que me tuvo meses parcheando a mano

El túnel levantaba y yo llegaba al laboratorio sin problema, pero las máquinas del laboratorio no podían responderme: nada de reverse shells. El motivo está en el servidor, en /etc/wireguard/wg0.conf, donde tu peer queda así:

[Peer]
PublicKey = AD0s36X1DXCA...
AllowedIPs = 198.51.100.2/32

Solo con tu IP del túnel. Sin la red del laboratorio en esa línea, el servidor no sabe enrutar hacia ti el tráfico que sale de las VMs. Yo lo arreglaba en cada arranque con un wg set ... allowed-ips 198.51.100.2/32,10.2.0.0/16, sin caer en que ese comando no escribe en el fichero y se pierde al reiniciar.

La solución definitiva es editar el fichero y dejarlo así:

AllowedIPs = 198.51.100.2/32, 10.2.0.0/16

y aplicarlo en caliente, sin tirar el túnel:

sudo wg syncconf wg0 <(sudo wg-quick strip wg0)

⚠️ Ese bloque está marcado como # Ansible managed, así que si vuelves a ejecutar el instalador o recreas el usuario, te lo va a pisar. Anótalo.

🏗️ Fase 7: desplegar el laboratorio

Un range de Ludus se define con un YAML. Podrías escribirlo tú, pero lo interesante es que GOAD ya trae la configuración hecha para Ludus.

git clone https://github.com/Orange-Cyberdefense/GOAD.git
cd GOAD
./goad.sh          # la primera ejecución crea ~/.goad/

Configura GOAD para que hable con Ludus editando ~/.goad/goad.ini:

ludus_api_key = JO.xxxxxxxxxxxxxxxx
use_impersonation = no

⚠️ Si tu clave de API contiene un %, tienes que duplicarlo (%%) en este fichero. Es un ConfigParser de Python y el % es su carácter de interpolación. Si no lo haces, GOAD falla con un error que no dice absolutamente nada de la causa real.

Ahora coge la configuración del laboratorio que quieras. Yo uso DRACARYS:

cp ~/GOAD/ad/DRACARYS/providers/ludus/config.yml .
cp ~/GOAD/ad/DRACARYS/providers/ludus/inventory .

# quitar el prefijo de range_id de los nombres de VM
sed -i 's/{{ range_id }}-//g' config.yml

Ese config.yml es directamente el range de Ludus. Este es el mío:

network:
  external_default: ACCEPT        # internet para las VMs
  wireguard_vlan_default: ACCEPT  # tu Kali puede alcanzar las VMs

ludus:
  - vm_name: "DC01"
    hostname: "DC01"
    template: win2025-server-x64-template
    vlan: 10
    ip_last_octet: 10
    ram_gb: 4
    cpus: 2
    windows:
      sysprep: true
  - vm_name: "SRV01"
    hostname: "SRV01"
    template: win2025-server-x64-template
    vlan: 10
    ip_last_octet: 11
    ram_gb: 4
    cpus: 2
    windows:
      sysprep: true
  - vm_name: "LX01"
    hostname: "LX01"
    template: ubuntu-24.04-x64-server-template
    vlan: 10
    ip_last_octet: 12
    ram_gb: 4
    cpus: 2
    linux: true

Fíjate en cómo se forma la IP: el vlan: 10 y el ip_last_octet se combinan dentro de tu red 10.2.0.0/16, así que DC01 acaba siendo 10.2.10.10. Ludus levanta además un router propio en 10.2.10.254.

Y se despliega:

ludus range config set -f config.yml
ludus range deploy
ludus range logs -f      # seguir el aprovisionamiento
ludus range status       # hasta que ponga SUCCESS

Ludus crea las VMs y después Ansible las provisiona: promueve el dominio, crea los usuarios y mete las vulnerabilidades. Es largo. Si algo peta:

ludus range errors       # solo los errores relevantes de los logs
ludus range abort        # cortar el ansible en curso

Cuando termine deberías ver esto:

+------------+------------------------+-------+-------------+
| PROXMOX ID |        VM NAME         | POWER |     IP      |
+------------+------------------------+-------+-------------+
|    105     | JO-router-debian11-x64 |  On   | 10.2.10.254 |
|    106     | DC01                   |  On   | 10.2.10.10  |
|    107     | SRV01                  |  On   | 10.2.10.11  |
|    108     | LX01                   |  On   | 10.2.10.12  |
+------------+------------------------+-------+-------------+

📸 Fase 8: los snapshots

No empieces a jugar sin hacer un snapshot. Es lo que separa un laboratorio de un dolor de cabeza: puedes reventar el dominio entero y volver al estado inicial en segundos.

ludus power off -n all
ludus snapshots create dracarys-ok -d "DRACARYS OK" --noRAM
ludus power on -n all

Consultar y revertir:

ludus snapshots list
ludus snapshots revert dracarys-ok
VM 106 (DC01)
└── dracarys-ok 2026-03-13 16:20:47 (DRACARYS OK)
    └── current  (You are here!)

Yo hago siempre dos: uno antes de que GOAD provisione, por si el aprovisionamiento se tuerce y no quiero reconstruir las VMs desde cero, y otro después, con el laboratorio ya montado y funcionando.

🎮 El día a día

Una vez montado, la rutina se queda en esto:

export LUDUS_API_KEY='JO.xxxxxxxxxxxx'

ludus power on -n all                 # encender
sudo wg-quick up ludus                # (en la Kali) levantar el túnel
# ... a jugar ...
ludus snapshots revert dracarys-ok    # dejarlo como estaba
ludus power off -n all                # apagar

Y hay tres comandos que no salen en ningún tutorial y que ahorran mucho tiempo:

ludus range etc-hosts    # genera el /etc/hosts con todos los hosts del range
ludus range rdp          # zip con los .rdp de todas las máquinas Windows
ludus range inventory    # el inventario de Ansible del range

El primero es oro puro: lo pegas en el /etc/hosts de tu Kali y ya te refieres a las máquinas por su nombre.

🧪 Comprobar que está vivo

Un barrido rápido te dice si el dominio está en pie de verdad:

nmap -Pn -p 88,135,389,445,3389,5985 10.2.10.10-12

Un controlador de dominio sano responde en 88 (Kerberos), 389 (LDAP), 445 (SMB) y 5985 (WinRM). Y el DNS del dominio tiene que resolver:

dig +short @10.2.10.10 dracarys.lab
10.2.10.10

🧯 Problemas con los que me he encontrado

  • Las VMs no salen a internet. El router del range hace de NAT, pero según cómo quede la red se puede quedar corto. Se arregla en el propio router (ssh debian@10.2.10.254, contraseña debian) con un MASQUERADE para tu red y, si el DNS no tira, un DNAT del puerto 53 hacia 8.8.8.8.
  • El % de la API key en goad.ini. Ya lo he dicho arriba, pero es que con esto se pierde media tarde.
  • MobaXterm durante la instalación. Rompe el instalador de Ludus. Y como bonus, luego se te olvida que la contraseña del servidor la tienes guardada solo ahí.
  • Las plantillas de Windows con TPM complican los snapshots. Si no vas a probar cosas de TPM o Credential Guard, hazte la plantilla sin él.
  • Ojo con ludus range rm. Destruye todas las VMs del range sin preguntar. Las plantillas se conservan, que es lo que cuesta horas, pero el laboratorio hay que volver a desplegarlo entero.

🎯 Qué más puedes montar

Con la infraestructura hecha, cambiar de laboratorio es cambiar un fichero de configuración. En el repositorio de GOAD tienes:

LaboratorioQué es
GOADEl completo: 5 máquinas, dos bosques y tres dominios
GOAD-LightLa versión reducida, 3 máquinas
GOAD-MiniAún más pequeño, para ir justo de recursos
MINILABUn dominio sencillo, ideal para empezar
NHANinja Hacker Academy
SCCMEnfocado a atacar SCCM/MECM
DRACARYSEl que uso en esta entrada

Y como todo esto corre en tu hardware, no hay reglas de publicación, ni suscripción, ni máquinas que se retiran. Puedes documentar hasta el último paso.

📝 Resumen

FaseQué hacesTiempo
1BIOS: VT-x y VT-D5 min
2Debian 12 limpio + sudo20 min
3Instalar Ludus30 min
4Crear usuario y sacar credenciales5 min
5Construir plantillashoras
6WireGuard en la Kali10 min
7ludus range config set y deploy1-2 h
8Snapshot2 min

La parte cara es la fase 5, y solo se paga una vez. A partir de ahí, montar un laboratorio nuevo son diez minutos de configuración y un deploy.