Por qué fallan los reverse shells: casi siempre es red, quoting o contexto
Cómo depurar callbacks que no llegan por problemas de rutas, egreso, listeners, runtimes ausentes y comandos mal escapados.
Cuando un reverse shell no conecta, mucha gente cambia de payload inmediatamente. Casi siempre es ruido. El callback tiene que pasar por DNS, rutas, NAT, filtrado de egreso, firewall local, ejecución del proceso, quoting y a veces un EDR que decide terminar el proceso sin explicar demasiado.
Cambia una variable cada vez. Si no, solo estás agitando el problema.
El listener no está donde crees
El primer error es escuchar en la interfaz equivocada. nc -lvnp 4444 normalmente escucha en todas, pero no todos los wrappers hacen lo mismo. Algunos se quedan en localhost. Otros escuchan dentro de un contenedor y nunca publican el puerto hacia el host.
Compruébalo:
ss -lntp | grep 4444
En Docker, 0.0.0.0:4444 dentro del contenedor no significa que la máquina objetivo pueda conectarse. Necesitas publicar el puerto, abrir el firewall del host y usar una dirección enrutable. En Kubernetes la historia se complica con IP de pod, service, node port, ingress y network policy. Los reverse shells fallidos son buenos detectores de modelos mentales de red obsoletos.
El filtrado de egreso existe por una razón
Muchas redes corporativas bloquean TCP saliente arbitrario. A veces permiten 80 y 443 mediante proxy, inspeccionan TLS o bloquean conexiones directas a IPs. Un laboratorio que solo prueba LHOST=10.0.0.5 en el puerto 4444 no se parece mucho a producción.
Mira paquetes antes de culpar al comando:
tcpdump -ni any host TARGET_IP and port 4444
Si no sale nada, quizá el comando no se ejecutó o la política local lo bloqueó. Si sale pero no llega, la ruta está rota. Si llega y se resetea, sospecha del listener, del firewall local o de una variante rara de netcat.
Runtimes y quoting fallan en silencio
Payload Python en una máquina sin Python. Sintaxis Bash ejecutada por /bin/sh. PowerShell en constrained language mode. PHP con disable_functions bloqueando ejecución de procesos. No son casos raros. Son casos normales.
El quoting es peor. El comando puede atravesar un formulario web, un campo JSON, un runner YAML, una variable CI y un wrapper shell antes de ejecutarse. Cada capa puede tragarse una comilla o reinterpretar un carácter especial.
Reduce el problema. Prueba un callback inofensivo por el mismo camino de ejecución. Captura stderr cuando el laboratorio te lo permita. Mira eventos de creación de procesos si los tienes. No es lo mismo "nunca arrancó" que "arrancó y salió en 50 ms".
Conectar no significa tener sesión
A veces el socket se abre y muere de inmediato. Puede ser stdin cerrado, un módulo ausente, un binario que no existe, un shell sin job control o un control de seguridad terminando hijos del proceso.
Un reverse shell es frágil porque mete demasiadas suposiciones en una línea. Un buen generador las hace visibles. Un buen operador verifica ruta, listener, runtime y contexto antes de cambiar payloads sin método.