
CVE-2026-13233 (Drupal OpenAI Provider, SA-CONTRIB-2026-053): SSRF tramite response-URL / lettura di file locale. Upstream non attendibile, non il prompt. Reproducer sicuro + rilevamenti. Corretto nella 1.1.1/1.2.2.
Upstream non affidabile: il provider che ha recuperato l'URL indicato dalla propria risposta.
Drupal OpenAI Provider (ai_provider_openai) · SSRF (CWE-918) · CVE-2026-13233 · SA-CONTRIB-2026-053
Stato: Divulgazione coordinata completa (2026-07-10). Segnalato privatamente al Drupal Security Team, corretto e pubblicato come CVE-2026-13233 / advisory SA-CONTRIB-2026-053 (valutato Moderately critical). Autore accreditato come finder & remediation developer.
Uno studio di un classico difetto di autorizzazione / validazione degli input su un "punto di connessione" AI: un
provider di generazione immagini AI recuperava l'URL contenuto nella risposta API upstream usando una
funzione generica senza allowlist di schemi. Nelle distribuzioni in cui l'upstream configurato
non è il default fidato (bring-your-own-endpoint / proxy / gateway self-hosted / relay
compromesso), la risposta poteva puntare il server a file:// o a un indirizzo interno.
L'input non affidabile che raggiunge il sink è l'URL della risposta upstream — non il prompt dell'utente.
file://; l'analisi
interna dell'autore l'ha mappata anche su CWE-73 / CWE-441.3.1 / condizionale 5.3
(Medium); i precedenti 6.5 / 7.4 sono stati ritrattati, e la valutazione media lo ha confermato.ai_provider_openai) < 1.1.1 e 1.2.0–1.2.1 (verificata 1.2.1) su Drupal core 11.2Tutte le verifiche sono state eseguite in una sandbox di proprietà, isolata dalla rete, contro un upstream mock. La vera API OpenAI non è mai stata chiamata. Gli oracoli di leak erano esclusivamente sintetici.
Confermato (dimostrato dinamicamente):
settings.php confermata tramite byte-match sha256 (35.182 B) — contenuto mai stampato; le credenziali del DB
sono quindi in scope.Non verificato (esplicitamente non rivendicato):
Non interessato: api.openai.com predefinito su TLS. L'impostazione host è controllata da admin / config-sync / deploy
— un attaccante non-admin in esecuzione non può modificarla.
L'URL della risposta veniva passato a un fetch generico che onora ogni stream wrapper PHP registrato
(file, php, http, data, phar, …). response_format ha come default url, quindi il ramo vulnerabile
è il percorso predefinito. file:// è il filesystem wrapper predefinito di PHP e non è limitato da
allow_url_fopen (una precedente affermazione su "richiede allow_url_fopen" era sbagliata ed è stata ritrattata).
Correggi la primitiva, non solo il punto di chiamata:
http/https prima della richiesta; disabilita i redirect (un 30x può rimbalzare su file://).file://…, http://169.254.169.254/… e un redirect 302→file://
devono essere tutti rifiutati.Direzione completa: docs/fix-direction.md.
.
├── README.md # this file (English, canonical)
├── README_ja.md # Japanese mirror
├── SECURITY.md # reporting policy + responsible-disclosure disclaimer
├── .gitignore # blocks secrets / primary evidence from being committed
├── docs/
│ └── fix-direction.md # root-cause fix + variant hunt + minimal-diff sketch (EN)
├── reproducer/ # SAFE reproducer only (sandbox, mock upstream, benign oracle)
│ ├── README.md # how to stand up the network-isolated sandbox
│ ├── verdict.md # what "success" means: raw-byte match + 200-vs-403 + exit codes
│ ├── mock-openai-server.py # benign mock; default happy-path PNG, ssrf_demo → file:///etc/hostname only
│ └── docker-compose.yml # TEMPLATE: isolated Drupal sandbox + mock (you add the vulnerable target)
├── detections/
│ ├── README.md
│ ├── sigma/web-egress-to-internal-after-imagegen.yml
│ ├── sigma/php-sensitive-file-open.yml
│ ├── sentinel/imagegen-egress-correlation.kql
│ └── splunk/imagegen-content-type-mismatch.spl
└── timeline.md # disclosure timeline (single source of truth)
La documentazione è attualmente in giapponese (
.ja.md); le traduzioni canoniche in inglese sono un TODO. Non inclusi in questo repo: payload armati e qualsiasi PoC pronto all'uso di lettura file. Ilreproducer/pubblico dimostra il meccanismo del difetto contro un mock usando un singolo oracolo benigno (/etc/hostname) — nessuna lettura a percorso arbitrario, nessun recupero disettings.php, nessun IMDS. Non è un'arma contro i siti live. Aggiorna a 1.1.1 / 1.2.2.
Obiettivo: consentire a un ricercatore di confermare la classe del difetto senza fornire un exploit per target live o esporre segreti. Tutto viene eseguito offline.