Nuclei es un escáner de vulnerabilidades de ProjectDiscovery que funciona con plantillas YAML: cada plantilla describe una petición y las condiciones que indican un fallo (un CVE, un panel expuesto, una cabecera de seguridad ausente). La comunidad mantiene miles de plantillas y tú puedes escribir las tuyas en pocos minutos. En este tutorial instalarás Nuclei en Ubuntu 24.04, lanzarás escaneos filtrados sobre tus propios sistemas, crearás y probarás una plantilla personalizada contra un servidor de laboratorio y guardarás los resultados en JSON.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS (x86_64 o arm64), por ejemplo un VPS de CubePath.
  • Un usuario no root con privilegios sudo.
  • 1 GB de RAM como mínimo; los escaneos con miles de plantillas usan bastante memoria.
  • Un objetivo propio para practicar, que en esta guía se llama your_domain.

Paso 1: Instalar el binario de Nuclei

Nuclei se distribuye como un binario estático en las releases de GitHub. Es la forma más sencilla de tener la última versión sin instalar Go.

Instala las herramientas necesarias:

sudo apt update
sudo apt install curl unzip jq

Obtén el número de la última versión y la arquitectura del servidor:

VERSION=$(curl -s https://api.github.com/repos/projectdiscovery/nuclei/releases/latest | jq -r .tag_name | sed 's/^v//')
ARCH=$(dpkg --print-architecture)
echo "$VERSION $ARCH"
3.x.x amd64

Descarga el binario y el fichero de sumas de comprobación, y verifica la integridad:

cd /tmp
curl -fsSLO "https://github.com/projectdiscovery/nuclei/releases/download/v${VERSION}/nuclei_${VERSION}_linux_${ARCH}.zip"
curl -fsSLO "https://github.com/projectdiscovery/nuclei/releases/download/v${VERSION}/nuclei_${VERSION}_checksums.txt"
sha256sum --check --ignore-missing "nuclei_${VERSION}_checksums.txt"
nuclei_3.x.x_linux_amd64.zip: OK

Descomprime e instala el binario en /usr/local/bin:

unzip -o "nuclei_${VERSION}_linux_${ARCH}.zip" nuclei
sudo install -m 0755 nuclei /usr/local/bin/nuclei
nuclei -version
[INF] Nuclei Engine Version: v3.x.x

Para actualizar el motor más adelante basta con nuclei -update.

Paso 2: Descargar las plantillas

Las plantillas de la comunidad se guardan en ~/nuclei-templates del usuario que ejecuta Nuclei. Descárgalas o actualízalas con:

nuclei -update-templates

Comprueba cuántas hay y cómo están organizadas:

ls ~/nuclei-templates
nuclei -tl | wc -l
cloud  code  dast  dns  file  headless  http  javascript  network  ssl  workflows ...

Las plantillas se agrupan por protocolo (http/, dns/, ssl/, network/) y dentro por tipo (http/cves/, http/exposed-panels/, http/misconfiguration/...). Además, cada una lleva etiquetas (cve, panel, tech, misconfig) y una severidad, que son la forma más práctica de filtrar.

Paso 3: Lanzar tu primer escaneo

Un escaneo sin filtros ejecuta miles de plantillas y envía miles de peticiones. Empieza por algo acotado: detectar las tecnologías del objetivo, que es rápido y no intrusivo.

nuclei -u https://your_domain -tags tech -rl 20

-rl 20 limita el escaneo a 20 peticiones por segundo (el valor por defecto es 150), algo recomendable contra servidores en producción. La salida muestra una línea por hallazgo:

[nginx-version] [http] [info] https://your_domain ["nginx/1.24.0"]
[tech-detect:nginx] [http] [info] https://your_domain
[http-missing-security-headers:content-security-policy] [http] [info] https://your_domain

El formato es [id-plantilla:matcher] [protocolo] [severidad] URL [datos extraídos].

Filtrar por severidad y etiqueta

Busca solo problemas de severidad media o superior:

nuclei -u https://your_domain -s medium,high,critical -rl 20

Combina etiquetas para centrarte en paneles expuestos, configuraciones incorrectas y ficheros sensibles publicados:

nuclei -u https://your_domain -tags panel,misconfig,exposure -rl 20

Escanear varios objetivos

Pon un objetivo por línea en un fichero:

nano ~/objetivos.txt
https://your_domain
https://api.your_domain
https://staging.your_domain
nuclei -l ~/objetivos.txt -s high,critical -rl 20 -c 10

-c fija cuántas plantillas se ejecutan en paralelo. Bájalo junto con -rl si el servidor objetivo es pequeño.

Paso 4: Guardar los resultados

Para revisar o procesar los hallazgos después, guárdalos en formato JSON Lines, un objeto JSON por línea:

nuclei -u https://your_domain -s medium,high,critical -rl 20 -jsonl -o ~/nuclei-$(date +%F).jsonl

Con jq puedes extraer un resumen tabulado:

jq -r '[.info.severity, .["template-id"], .["matched-at"]] | @tsv' ~/nuclei-$(date +%F).jsonl | sort
high    git-config    https://your_domain/.git/config
medium  phpinfo-files https://your_domain/info.php

Si prefieres un informe legible, -me (-markdown-export) genera un fichero Markdown por hallazgo en el directorio que indiques.

Paso 5: Preparar un laboratorio para plantillas propias

Para probar una plantilla necesitas un objetivo donde el fallo exista de verdad. Crea uno local con el servidor HTTP de Python, que expone un repositorio Git publicado por error, un fallo real y habitual:

mkdir -p ~/lab/.git
printf '[core]\n\trepositoryformatversion = 0\n\tbare = false\n' > ~/lab/.git/config
python3 -m http.server 8000 --bind 127.0.0.1 --directory ~/lab

Deja esa terminal abierta y abre otra para los pasos siguientes. Comprueba que el fichero se sirve:

curl -s http://127.0.0.1:8000/.git/config
[core]
	repositoryformatversion = 0
	bare = false

Paso 6: Escribir una plantilla personalizada

Guarda tus plantillas fuera de ~/nuclei-templates para que las actualizaciones no las toquen:

mkdir -p ~/nuclei-custom
nano ~/nuclei-custom/git-config-expuesto.yaml
id: git-config-expuesto

info:
  name: Repositorio Git expuesto
  author: your_user
  severity: high
  description: El fichero .git/config es accesible por HTTP, lo que permite descargar el código fuente.
  tags: exposure,git,custom

http:
  - method: GET
    path:
      - "{{BaseURL}}/.git/config"

    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200

      - type: word
        part: body
        words:
          - "[core]"
          - "repositoryformatversion"
        condition: and

      - type: word
        part: header
        words:
          - "text/html"
        negative: true

La plantilla solo da positivo si se cumplen los tres matchers (matchers-condition: and): código 200, las dos cadenas típicas de un .git/config en el cuerpo, y que la respuesta no sea una página HTML. Este último matcher negativo evita falsos positivos en sitios que devuelven su página de inicio con código 200 para cualquier ruta.

Valida la sintaxis:

nuclei -validate -t ~/nuclei-custom/git-config-expuesto.yaml
[INF] All templates validated successfully

Ejecútala contra el laboratorio:

nuclei -u http://127.0.0.1:8000 -t ~/nuclei-custom/git-config-expuesto.yaml
[WRN] Loading 1 unsigned templates for scan. Use with caution.
[git-config-expuesto] [http] [high] http://127.0.0.1:8000/.git/config

El aviso sobre plantillas sin firmar es normal en plantillas propias de tipo HTTP. Para confirmar que no da falsos positivos, borra el fichero y repite el escaneo: no debe aparecer ningún resultado.

rm ~/lab/.git/config
nuclei -u http://127.0.0.1:8000 -t ~/nuclei-custom/git-config-expuesto.yaml

Extraer datos de la respuesta

Los extractores añaden información útil al hallazgo. Esta segunda plantilla informa de la cabecera Server si revela una versión:

nano ~/nuclei-custom/server-version.yaml
id: server-version

info:
  name: Versión del servidor en la cabecera Server
  author: your_user
  severity: info
  tags: tech,custom

http:
  - method: GET
    path:
      - "{{BaseURL}}/"

    matchers:
      - type: regex
        part: header
        regex:
          - "(?i)server: [a-z-]+/[0-9.]+"

    extractors:
      - type: kval
        kval:
          - server
nuclei -u https://your_domain -t ~/nuclei-custom/server-version.yaml
[server-version] [http] [info] https://your_domain/ ["nginx/1.24.0"]

Para ejecutar todas tus plantillas de una vez, pasa el directorio: nuclei -u https://your_domain -t ~/nuclei-custom/. Cuando termines, detén el servidor del laboratorio con Ctrl+C.

Solución de problemas

El escaneo tarda mucho o el objetivo empieza a dar errores 429 o 503. Baja -rl y -c, y acota con -tags o -s. Añade -stats para ver el progreso cada pocos segundos.

Un hallazgo parece un falso positivo. Repite solo esa plantilla con -debug para ver la petición y la respuesta completas: nuclei -u https://your_domain -id nombre-plantilla -debug. Si lo confirmas, exclúyela con -exclude-id nombre-plantilla.

Muchos errores de conexión o timeout. Sube el tiempo de espera con -timeout 15 y revisa que un WAF o un rate limit (por ejemplo de Cloudflare o CrowdSec) no esté bloqueando la IP desde la que escaneas.

Conclusión

Has instalado Nuclei verificando su suma de comprobación, has lanzado escaneos filtrados por etiqueta y severidad, has exportado los resultados a JSON y has escrito y probado dos plantillas propias. Como siguientes pasos, programa un escaneo semanal de tus dominios con un temporizador de systemd, integra la plantilla de severidad alta en tu pipeline de CI para que falle ante hallazgos críticos, y revisa las plantillas de ~/nuclei-templates/http/ para aprender los patrones que usa la comunidad.