# SCAN: stable-diffusion-webui date: 2026-02-13 | program: stable-diffusion-webui | repo: https://github.com/AUTOMATIC1111/stable-diffusion-webui | bounty: $0-$1500 ## summary raw_findings: 12 | real: 3 | high_conf: 1 | med_conf: 2 | low_conf: 0 | false_pos: 9 scan_method: manual_review ## findings ### F1: SSRF via redirect bypass in decode_base64_to_image severity: high | confidence: medium | type: SSRF | cwe: CWE-918 file: modules/api/api.py:58-86 | tool: manual The verify_url function at line 58 resolves the domain and checks IPs are global, but requests.get() at line 86 follows HTTP redirects by default. An attacker-controlled URL resolving to a public IP can return a 302 redirect to http://169.254.169.254/ (cloud metadata) or http://127.0.0.1/. Additionally, DNS rebinding is possible since the domain is resolved twice (once for check, once for fetch). attack_vector: POST /sdapi/v1/txt2img or /sdapi/v1/img2img with init_images containing a URL to attacker server that 302-redirects to internal resources. impact: Read internal network resources, cloud metadata (AWS/GCP credentials). Requires --api flag and api_enable_requests=True (both common in production). recommendation: INVESTIGATE --- ### F2: SSRF in refresh_available_extensions via urllib.request.urlopen severity: high | confidence: high | type: SSRF | cwe: CWE-918 file: modules/ui_extensions.py:407-414 | tool: manual The url parameter comes from Gradio UI text input with no SSRF protection. urllib.request.urlopen(url) supports file://, http://, ftp:// protocols. No check_access() gate on this function (unlike install/update operations). Any Gradio user can trigger this. The json.loads() on the response will fail for non-JSON content but the request is still made. attack_vector: Extensions > Available tab, change URL to file:///etc/passwd or http://169.254.169.254/latest/meta-data/ and click Load. impact: Full SSRF -- local file read via file://, internal network probe, cloud metadata access. Error messages from json.loads failure may leak response content. recommendation: REPORT --- ### F3: Arbitrary Python running via Custom Code script severity: critical | confidence: medium | type: RCE | cwe: CWE-94 file: scripts/custom_code.py:31-85 | tool: manual User-supplied Python code is compiled and run via the exec builtin. Gated by --allow-code CLI flag (defaults to False). When enabled, any Gradio/API user gets full RCE. attack_vector: With --allow-code enabled, select "Custom code" script, enter Python code. impact: Full server compromise. recommendation: SKIP -- by-design, gated behind explicit CLI flag. --- ## skipped | file:line | rule | reason | |---|---|---| | modules/safe.py:194 | torch.load override | RestrictedUnpickler well-implemented | | modules/sd_models.py:323 | torch.load | Goes through safe.load() | | modules/hypernetworks/hypernetwork.py:248 | torch.load | Same safe.load() wrapper | | modules/textual_inversion/textual_inversion.py:178 | torch.load | Same safe.load() wrapper | | extensions-builtin/LDSR/*.py | torch.load | Same safe.load() wrapper | | modules/sd_vae_taesd.py:70 | torch.load | Same wrapper | | modules/gitpython_hack.py:18 | subprocess | Hardcoded args | | modules/launch_utils.py:* | subprocess | Startup code, not API-reachable | | modules/ui_extra_networks.py:97 | fetch_file | Has dir+ext allowlist | | modules/textual_inversion/textual_inversion.py:275 | create_embedding | Name sanitized | | modules/hypernetworks/hypernetwork.py:442 | create_hypernetwork | Name sanitized | | modules/ui_extensions.py:344 | install dirname traversal | Gated by check_access() |