Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
drupal-openai-provider-ssrf-cve-2026-13233 — CVE-2026-13233 (Drupal OpenAI Provider, SA-CONTRIB-2026-053): response-URL SSRF / local file read. Untrusted upstream, not the prompt. Safe reproducer + detections. Fixed in 1.1.1/1.2.2. | Kitploit
Tools/GitHubGitHub/kuninogu/drupal-openai-provider-ssrf-cve-2026-13233
Vulnerability AnalysisWeb SecurityLearning & Education
GitHubkuninogu/drupal-openai-provider-ssrf-cve-2026-13233

drupal-openai-provider-ssrf-cve-2026-13233

CVE-2026-13233 (Drupal OpenAI Provider, SA-CONTRIB-2026-053): response-URL SSRF / local file read. Untrusted upstream, not the prompt. Safe reproducer + detections. Fixed in 1.1.1/1.2.2.

View Repository
243 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Drupal OpenAI Provider — SSRF / local file read via response URL (CVE-2026-13233)

Untrusted upstream: the provider that fetched the URL its response told it to. Drupal OpenAI Provider (ai_provider_openai) · SSRF (CWE-918) · CVE-2026-13233 · SA-CONTRIB-2026-053

Status: Coordinated disclosure complete (2026-07-10). Reported privately to the Drupal Security Team, fixed, and published as CVE-2026-13233 / advisory SA-CONTRIB-2026-053 (rated Moderately critical). Author credited as finder & remediation developer.

A study of a classic authorization / input-validation defect on an AI "connection point": an AI image-generation provider fetched the URL contained in the upstream API response using a general-purpose function with no scheme allowlist. On deployments where the configured upstream is not the trusted default (bring-your-own-endpoint / proxy / self-hosted gateway / compromised relay), the response could point the server at file:// or an internal address.

The untrusted input that reaches the sink is the upstream response URL — not the user's prompt.

  • Class (published): SSRF (CWE-918). The SSRF included a file:// local file read; the author's internal analysis also mapped it to CWE-73 / CWE-441.
  • Severity: Drupal Moderately critical (SA-CONTRIB-2026-053) — a mid band on Drupal's scale ≈ CVSS Medium; not CVSS Critical/High. Author's pre-disclosure self-assessment: floor 3.1 / conditional 5.3 (Medium); prior 6.5 / 7.4 retracted, and the mid rating bore that out.
  • Affected: Drupal OpenAI Provider (ai_provider_openai) < 1.1.1 and 1.2.0–1.2.1 (audited 1.2.1) on Drupal core 11.2
  • Fixed in: 1.1.1 / 1.2.2 · CVE: CVE-2026-13233 · Advisory: https://www.drupal.org/sa-contrib-2026-053

What was demonstrated (confirmed) vs. not (unverified)

All verification ran in an owned, network-isolated sandbox against a mock upstream. The real OpenAI API was never called. Leak oracles were synthetic only.

Confirmed (dynamically demonstrated):

  • Non-admin trigger → server-side read of an upstream-chosen local file (arbitrary path proven with a per-run unique marker; served body raw-byte-equals the marker; marker present only in the app container, absent on the mock — ruling out "the mock returned its own file").
  • settings.php read confirmed by sha256 byte-match (35,182 B) — content never printed; DB credentials are therefore in scope.
  • Internal HTTP SSRF response retrieval from a service with no public port (internal token raw-byte match).
  • Authorization boundary: the same account gets 403 on the admin config route (200-vs-403 contrast).
  • Legitimate-HTTP reachability through a realistic admin-configured field-widget "Generate" flow, by an editor with no AI permissions.

Unverified (explicitly not claimed):

  • Cloud metadata (IMDS) credential theft — mechanism-plausible from the same sink, not attempted.
  • Base-class twin dynamic reachability — overridden on this provider (dead code for this target); recorded as a same-root-cause variant to port the fix, not a separate claim.
  • Passive "save-only" automator firing — tested, negative. "Merely saving" does not trigger it.
  • Stored-XSS (content-type confusion) and speech-to-text concurrency race — separate leads, unproven.

Not affected: default api.openai.com over TLS. The host setting is admin / config-sync / deploy controlled — a runtime non-admin attacker cannot change it.


Root cause & fix direction (summary)

The response URL was passed to a general-purpose fetch that honors every registered PHP stream wrapper (file, php, http, data, phar, …). response_format defaults to url, so the vulnerable branch is the default path. file:// is PHP's default filesystem wrapper and is not gated by allow_url_fopen (an earlier "requires allow_url_fopen" claim was wrong and is retracted).

Fix the primitive, not just the call site:

  1. Stop using a general-purpose fetch for remote retrieval; use the injected HTTP-only client.
  2. Validate scheme is http/https before the request; disable redirects (a 30x can bounce to file://).
  3. Defense in depth: reject resolved private/link-local/loopback hosts, or constrain to the configured image-CDN host(s); validate size / content-type / image-decode.
  4. Port the same fix to the base class so sibling providers inherit it (incomplete-fix otherwise).
  5. Regression tests are deny-case first: file://…, http://169.254.169.254/…, and a 302→file:// redirect must all be refused.

Full direction: docs/fix-direction.md.


Directory structure

.
├── 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)

Docs are currently Japanese (.ja.md); English canonical translations are a TODO. Withheld from this repo: arming payloads and any turnkey file-read PoC. The public reproducer/ demonstrates the defect mechanism against a mock using a single benign oracle (/etc/hostname) — no arbitrary-path read, no settings.php grab, no IMDS. It is not a weapon against live sites. Update to 1.1.1 / 1.2.2.


Safe Reproducer

Goal: let a researcher confirm the defect class without providing a live-target exploit or exposing secrets. Everything runs offline.

Download Tool