Warum Reverse Shells fehlschlagen: meistens sind es Netzwerk, Quoting oder Kontext
Wie man tote Reverse-Shell-Callbacks systematisch debuggt: Routing, Egress-Filter, Listener, fehlende Runtimes und Quoting.
Wenn eine Reverse Shell nicht zurückkommt, wechseln viele sofort die Payload. Das ist meistens Aktionismus. Der Callback muss durch DNS, Routing, NAT, Egress-Filter, lokale Firewall, Prozessausführung, Quoting und manchmal durch ein EDR, das den Prozess beendet und nur ein dünnes Log hinterlässt.
Ändere eine Variable nach der anderen. Sonst debuggt niemand, du würfelst nur.
Der Listener hört nicht dort, wo du denkst
Der erste Fehler ist fast immer banal: falsche Schnittstelle, falscher Host, falscher Port. nc -lvnp 4444 hört normalerweise auf allen Interfaces, aber Wrapper und Alternativen können anders arbeiten. Manche binden nur an localhost. Manche laufen in Containern und veröffentlichen keinen Port zum Host.
Prüfe es:
ss -lntp | grep 4444
In Docker bedeutet 0.0.0.0:4444 im Container nicht, dass das Ziel den Port erreicht. Du brauchst Port-Publishing, passende Host-Firewall-Regeln und eine routebare Adresse. Kubernetes legt noch eine Schicht darüber: Pod-IP, Service, NodePort, Ingress, Network Policy. Tote Reverse Shells zeigen gnadenlos, wenn das eigene Netzwerkmodell nicht stimmt.
Egress-Filter blockieren genau solche Tests
Viele Unternehmensnetze erlauben keinen beliebigen ausgehenden TCP-Verkehr. Manche lassen nur 80 und 443 über einen Proxy zu, inspizieren TLS oder blockieren direkte IP-Verbindungen. Ein Lab, das nur LHOST=10.0.0.5 auf Port 4444 testet, bereitet dich darauf schlecht vor.
Schau auf Pakete, bevor du die Payload beschuldigst:
tcpdump -ni any host TARGET_IP and port 4444
Wenn nichts rausgeht, wurde der Befehl vielleicht nie ausgeführt oder lokal blockiert. Wenn Traffic rausgeht, aber nicht ankommt, ist der Pfad kaputt. Wenn er ankommt und zurückgesetzt wird, sind Listener, lokale Firewall oder netcat-Variante die nächsten Verdächtigen.
Runtimes und Quoting brechen leise
Python-Payload auf einem System ohne Python. Bash-Syntax unter /bin/sh. PowerShell im Constrained Language Mode. PHP mit disable_functions, die Prozessstart verhindert. Das sind keine exotischen Edge Cases. Das ist Alltag.
Quoting ist noch unangenehmer. Ein Befehl kann durch ein Webformular, JSON, YAML, CI-Variablen, einen Shell-Wrapper und erst dann in den Zielprozess laufen. Jede Schicht kann ein Quote schlucken oder ein Sonderzeichen neu interpretieren.
Reduziere das Problem. Teste einen harmlosen Callback über denselben Ausführungspfad. Sammle stderr, wenn das Lab es erlaubt. Prüfe Prozessstart-Telemetrie. "Es wurde nie ein Prozess gestartet" und "der Prozess lief 50 ms" sind völlig unterschiedliche Befunde.
Verbindung heißt nicht Sitzung
Manchmal öffnet sich der Socket und stirbt sofort. stdin kann geschlossen sein, ein Modul fehlen, ein Binary nicht existieren, Job Control fehlen oder ein Sicherheitsagent beendet Kindprozesse.
Eine Reverse Shell ist fragil, weil sie zu viele Annahmen in eine Zeile presst. Ein guter Generator macht diese Annahmen sichtbar. Ein guter Operator prüft Route, Listener, Runtime und Ausführungskontext, bevor er wahllos Varianten tauscht.