# DevOps Nightmare: una cadena DFIR pensada para leer compromiso, persistencia y exfiltración
Table of Contents
Máquina creada por: Oscar
Plataforma: The Hackers Labs
Sistema operativo: Linux
Dificultad: media
Sobre este CTF
DevOps Nightmare es una máquina que diseñé para trabajar una idea muy concreta: cómo reconstruir una intrusión a partir de evidencias dispersas, correlacionando autenticación, actividad de sistema, tráfico de red y rastros de exfiltración. No la planteé como una colección de pruebas aisladas, sino como una cadena coherente en la que cada artefacto responde a una fase del ataque.
La intención del laboratorio no es premiar a quien memoriza comandos, sino obligar a leer el incidente con criterio. Primero aparece un acceso inicial identificable en auth.log, después una descarga sospechosa en syslog, más tarde un canal de persistencia visible en red y en logs, y finalmente movimiento lateral y exfiltración sobre un activo interno. Esa secuencia reproduce errores que no son exclusivos de un CTF: reutilización o exposición de credenciales, ejecución de payloads desde infraestructura externa, listeners no autorizados y transferencias internas que terminan saliendo del entorno.
Por eso el valor de la máquina no está solo en responder preguntas del solucionario. Está en entender por qué la cadena tiene sentido, qué evidencia deja cada fase y cómo una investigación razonable puede reconstruirla sin depender de una única fuente.
Información técnica
| Campo | Valor |
|---|---|
| Nombre | DevOps Nightmare |
| IP objetivo | No especificada en las notas |
| Servicios | SSH, tráfico HTTP saliente, servicio persistente en puerto alto, comunicación interna hacia servidor de base de datos |
| Cadena principal | Acceso inicial por credenciales comprometidas → descarga de payload → persistencia mediante listener → movimiento lateral hacia activo interno → exfiltración |
| Dificultad | Media |
Descubrimiento de la evidencia
El punto de partida del laboratorio es una colección de artefactos que obliga a combinar fuentes distintas. No quise que la investigación dependiera de una sola pista evidente, sino de la relación entre logs de autenticación, logs del sistema, captura de red y un artefacto de base de datos presente como contexto.
mkdir investigaciontar -xzvf DevOps_Nightmare.tar.gz -C investigacion/cd investigacionls -lEn las notas aparecen estos ficheros:
auth.logsyslognetwork.pcapapp_database.dbDesde el diseño, esta fase busca introducir una mentalidad básica de DFIR: antes de responder qué ha pasado, hay que entender qué fuentes existen y qué parte de la historia puede aportar cada una. auth.log sirve para fijar el acceso inicial, syslog para ver actividad local, network.pcap para validar comportamiento y temporalidad, y app_database.db queda como artefacto contextual, no necesariamente como pieza central de la resolución.
Acceso inicial
La primera parte de la cadena está en auth.log. Aquí la intención era enseñar una lectura sencilla pero útil: un patrón de fallo seguido de éxito sobre el mismo usuario y desde la misma IP en una ventana muy corta suele ser suficiente para orientar la investigación, incluso sin disponer de telemetría más rica.
Las notas muestran primero mucho ruido de accesos válidos por clave pública:
Nov 15 23:55:49 ubuntu sshd[3258]: Accepted publickey for admin from 10.0.0.62 port 36536Nov 15 23:55:54 ubuntu sshd[9827]: Accepted publickey for jenkins from 10.0.0.254 port 62422Nov 15 23:56:25 ubuntu sshd[4264]: Accepted publickey for developer1 from 10.0.0.165 port 48274...Ese ruido está puesto a propósito. En una investigación real rara vez se trabaja sobre un log limpio. Hay eventos legítimos, automatizaciones y accesos esperables que no ayudan a explicar el incidente. La pista relevante aparece al apartar ese tráfico normal:
grep -v "Accepted publickey" auth.logNov 15 03:27:45 ubuntu sshd[10612]: Failed password for developer1 from 45.63.89.234 port 39654Nov 15 03:28:15 ubuntu sshd[10612]: Accepted password for developer1 from 45.63.89.234 port 39654Técnicamente, la conclusión es clara: la IP 45.63.89.234 consigue autenticarse como developer1 tras un fallo previo. No necesitaba una fuerza bruta masiva para que la idea funcionase; bastaba con una secuencia mínima que obligara a detectar la relación temporal y contextual.
Lo que quise enseñar aquí es que el acceso inicial no siempre se presenta como una firma espectacular. A veces es solo una transición muy breve entre falló y entró, y la capacidad de verla depende más de filtrar bien el ruido que de buscar indicadores complejos.
Descarga del payload y dominio de mando y control
Una vez fijado el acceso inicial, el siguiente paso del laboratorio lleva al sistema. Quería que la investigación no se quedara en hubo un login sospechoso, sino que avanzara hacia lo que el intruso hizo después de entrar. Para eso, syslog contiene una evidencia explícita de descarga, pero codificada de forma lo bastante simple como para exigir una validación manual.
La búsqueda se concentra alrededor de la hora del compromiso:
grep "wget" syslogNov 15 03:28:30 ubuntu wget[3225]: Download completed: aHR0cDovL2Nkbi11cGRhdGUuZGV2b3BzLnRlY2gvdXBkYXRlX2FnZW50LnNoLa cadena en base64 se decodifica así:
echo "aHR0cDovL2Nkbi11cGRhdGUuZGV2b3BzLnRlY2gvdXBkYXRlX2FnZW50LnNo" | base64 -dhttp://cdn-update.devops.tech/update_agent.shA nivel técnico, esta fase identifica dos piezas: el fichero descargado, update_agent.sh, y el dominio usado para servirlo, cdn-update.devops.tech.
A nivel de diseño, esta parte representa algo muy habitual: tras obtener acceso, el atacante no necesita ejecutar un implante sofisticado si puede traer un script desde infraestructura externa y apoyarse en utilidades nativas del sistema. La codificación en base64 no pretende ser malware avanzado; pretende enseñar una lección más útil: muchos indicadores no están ocultos, solo empaquetados lo justo para que una revisión superficial los pase por alto.
Persistencia y canal de control
La fase siguiente está pensada para forzar correlación entre red y sistema. No quise dejar la persistencia resuelta en un único log inequívoco. Preferí que primero apareciera como anomalía de tráfico y solo después pudiera confirmarse en syslog.
Según las notas, el análisis del PCAP parte de un resumen de conversaciones TCP:
tshark -r network.pcap -q -z conv,tcp > trafico.txtcat trafico.txt | grep -v ":80"La observación relevante es una conexión asociada al puerto 54321, fuera del patrón esperado. La validación posterior en los logs del sistema cierra la hipótesis:
grep "54321" syslogStarted listener on port 54321Con eso, la persistencia queda asociada a un listener local en un puerto alto no estándar.
Lo importante aquí no es el número de puerto en sí, sino el razonamiento. En muchos entornos reales, la persistencia no se descubre por una firma inequívoca, sino por la combinación de dos hechos discretos: un patrón de red extraño y un rastro local que explica quién abrió ese canal. Diseñé esta parte para enseñar esa lectura cruzada. También buscaba reflejar un error operativo común: procesos no autorizados escuchando en puertos altos que terminan normalizándose porque nadie revisa esos detalles mientras el servicio principal sigue funcionando.
Movimiento lateral hacia el segundo servidor
Una vez comprometido el host inicial, la máquina empuja la investigación hacia el interior de la red. La idea era evitar que toda la historia girase alrededor del atacante externo y mostrar que, tras consolidar acceso, el siguiente paso razonable es explorar activos internos de más valor.
Las notas proponen identificar conexiones salientes desde la víctima hacia otros sistemas internos:
tshark -r network.pcap -Y "ip.src == 192.168.1.100 and ip.dst != 45.63.89.234 and tcp.flags.syn==1"A partir de ahí aparece una IP interna relevante: 192.168.1.200.
La correlación con syslog revela el hostname:
grep "192.168.1.200" syslogfirewall: dbmaster01 (192.168.1.200) added to authorized whitelistLa pista clave es dbmaster01.
Desde el punto de vista del diseño, esta fase existe para enseñar dos cosas. La primera es que el movimiento lateral no siempre deja nombres claros en la captura; a menudo el nombre útil aparece en otra fuente, como logs de firewall, inventario o sistema. La segunda es que un atacante que ya dispone de un punto de apoyo raramente se queda en la máquina comprometida si detecta un servidor de base de datos accesible en red interna. Por eso el salto a dbmaster01 no es gratuito: representa una progresión lógica hacia un activo con mayor impacto.
Exfiltración y nombre del archivo robado
La última fase está construida para cerrar la cadena completa: no basta con ver intrusión y persistencia; hay que identificar qué salió del entorno. Aquí introduje una evidencia pequeña, pero suficiente, en el payload TCP.
Las notas extraen el contenido hexadecimal de una transferencia desde el servidor interno hacia la IP atacante:
tshark -r network.pcap \ -Y "ip.src == 192.168.1.200 and ip.dst == 45.63.89.234" \ -T fields -e tcp.payload | head -17af9d12b5f636f6e666964656e7469616c5f64756d702e73716c2e677aAl reconstruirlo desde hexadecimal:
echo "7af9d12b5f636f6e666964656e7469616c5f64756d702e73716c2e677a" | xxd -r -p7af9d12b_confidential_dump.sql.gzSi además se extraen solo los caracteres imprimibles, el nombre legible del archivo queda todavía más claro:
echo "7af9d12b5f636f6e666964656e7469616c5f64756d702e73716c2e677a" | xxd -r -p | stringsconfidential_dump.sql.gzAquí conviene limpiar una inconsistencia presente en las notas. En el solucionario aparece como respuesta a la pregunta sobre los primeros 8 caracteres del hash MD5 la cadena 7af9d12b_confidential_dump.sql.gz, pero la parte que realmente responde a esa pregunta es 7af9d12b. El nombre del archivo exfiltrado, por otro lado, es confidential_dump.sql.gz, o bien la cadena completa observada en el payload: 7af9d12b_confidential_dump.sql.gz.
Esta fase la diseñé para que el participante entendiera que la exfiltración no siempre exige reconstruir un archivo completo. A veces basta con recuperar fragmentos de naming, prefijos hash o metadatos embebidos en el flujo para saber qué salió y desde dónde. Es una lección útil fuera del laboratorio, sobre todo cuando se trabaja con capturas parciales o con tráfico incompleto.
Qué quería enseñar al diseñar DevOps Nightmare
DevOps Nightmare está pensado como una cadena de fallos y evidencias, no como una sucesión de adivinanzas. Quería que el laboratorio obligara a relacionar acceso inicial, ejecución posterior, persistencia, movimiento lateral y exfiltración bajo una lógica reconocible en un incidente real.
La máquina enseña que una investigación sólida no depende de encontrar una bala de plata en un solo archivo. Depende de saber leer contexto, tiempos y relaciones entre artefactos. También quería que cada fase representara un error operativo concreto: una cuenta expuesta, un sistema capaz de descargar y ejecutar contenido externo, un listener persistente sin control, una red interna alcanzable y un activo sensible finalmente exfiltrado.
| Fase | Qué enseña | Error representado |
|---|---|---|
| Acceso inicial | Detectar compromiso correlando fallo y éxito de autenticación | Credenciales comprometidas o débiles en cuenta con acceso SSH |
| Descarga de payload | Identificar ejecución posterior al login y rastrear infraestructura externa | Capacidad de descargar y ejecutar contenido externo sin control efectivo |
| Persistencia | Validar una anomalía de red con evidencia del sistema | Listener persistente no autorizado en puerto alto |
| Movimiento lateral | Relacionar tráfico interno con metadatos del entorno | Segmentación insuficiente o confianza excesiva entre sistemas internos |
| Exfiltración | Extraer indicadores del archivo robado desde payload parcial | Falta de visibilidad o control sobre salidas de datos sensibles |
En conjunto, la cadena busca dejar una idea sencilla: cuando varias pequeñas debilidades conviven en el mismo entorno, el impacto no lo marca cada fallo por separado, sino su combinación.
Recursos y referencias
grep,cat,awkpara filtrado y lectura inicial de evidencias.tsharko Wireshark para análisis de conversaciones y payloads.base64para decodificar artefactos simples ofuscados.xxdystringspara reconstrucción e interpretación de contenido hexadecimal.- Revisión cruzada entre
auth.log,syslogynetwork.pcapcomo método base de correlación.
Resumen operativo de hallazgos
| Pregunta | Respuesta |
|---|---|
| IP del atacante inicial | 45.63.89.234 |
| Usuario comprometido primero | developer1 |
| Fichero malicioso descargado | update_agent.sh |
| Dominio usado para C2 o distribución | cdn-update.devops.tech |
| Puerto usado para persistencia | 54321 |
| Hostname del segundo servidor comprometido | dbmaster01 |
| Primeros 8 caracteres observados del identificador/hash | 7af9d12b |
| Nombre legible del archivo robado | confidential_dump.sql.gz |