apps/ssrf/labs/webhook.py · view on GitHub
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | """SSRF lab: webhook — INTENTIONALLY VULNERABLE. POST a JSON payload to a user-controlled webhook URL, then echo back the HTTP status, response length, content-type, and any X-* headers from the response. The body itself is never returned. A "semi-blind" SSRF: partial metadata is enough to fingerprint internal services, and any flag leaked through a custom header is exfiltrable. """ from __future__ import annotations from pathlib import Path import requests from flask import Blueprint, render_template, request bp = Blueprint("webhook", __name__, url_prefix="/webhook") META = { "slug": "webhook", "title": "Semi-blind SSRF via a webhook poster", "summary": "POSTs your URL with a JSON body and echoes status, length, content-type, X-* headers.", "hint": ( "The body is hidden but headers aren't. The internal service at " "http://127.0.0.1:8089/webhook-callback returns the flag in a " "response header." ), "sink": "requests.post", "source_path": str(Path(__file__).resolve()), "vulnerable": True, } @bp.route("/", methods=["GET", "POST"]) def lab(): url = (request.values.get("url") or "").strip() payload = request.values.get("payload") or '{"event":"test"}' body = error = None if url: try: r = requests.post( url, data=payload.encode("utf-8"), headers={"Content-Type": "application/json", "User-Agent": "vulnlab-webhook/1.0"}, timeout=5, allow_redirects=False, ) x_headers = {k: v for k, v in r.headers.items() if k.lower().startswith("x-")} lines = [ f"status: {r.status_code}", f"content-length: {len(r.content)}", f"content-type: {r.headers.get('Content-Type', '(none)')}", "x-headers:", ] if x_headers: lines.extend(f" {k}: {v}" for k, v in x_headers.items()) else: lines.append(" (none)") lines.append("\n(body intentionally hidden — this lab leaks only metadata)") body = "\n".join(lines) except Exception as e: error = f"{type(e).__name__}: {e}" return render_template("lab.html", meta=META, url=url, body=body, error=error) |