Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
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.

··Feeds·Contact·Privacy·© 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

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
1 month agoNot yet reviewed

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

root@kitploit:~
.
├── 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.

  • Isolation: docker compose with the app and a mock upstream on an isolated network (--network none for the language-primitive checks). No credentials mounted. The mock never talks to the real API.
  • Faithful-untrusted-upstream model: the mock plays "a host pointing at an untrusted proxy/self-hosted gateway." It is armed out-of-band (not via the prompt) to return a synthetic target URL — this encodes the corrected attacker model (the upstream chooses the URL, the user only triggers).
  • Benign oracles only:
    • file:///etc/hostname and a per-run unique marker written inside the app container only → proves the app read an upstream-chosen path (marker absent on the mock).
    • settings.php verified by sha256 byte-match, content never emitted → proves "DB creds in scope" without disclosing them.
    • An internal HTTP service with no public port → proves internal SSRF response retrieval with a synthetic token.
  • Verdict is raw-byte, not HTTP 200: success = served body byte-equals the expected synthetic value, plus a 200-vs-403 authorization contrast. Exit codes are strict (0 exploitable / 1 precondition / 2 not-reproduced / cleanup-failed / error). See .

See reproducer/README.md.


Disclosure Timeline

See timeline.md. Summary:


Detection Guidance

Practitioner detections live in detections/; rationale in docs/detection-engineering.md. In short:

  • Correlate who triggered image generation (app audit) with anomalous egress (proxy / EDR) to 169.254.169.254 / loopback / RFC1918 in a tight time window after the API call. Do not alert on egress alone.
  • file:// reads never hit the network — detect sensitive-file opens by web processes via host auditing (auditd/EDR), baselined so only off-pattern reads within an image-gen request fire.
  • Retrospectively hunt image/*-declared bytes whose magic bytes aren't an image.
  • Most AI integrations don't log which user fetched which URL — add a fetch audit record {request_id, actor, target_url, scheme, resolved_ip, content_type}. Standard logs alone won't catch this.

References

  • OWASP Top 10 A10:2021 — Server-Side Request Forgery
  • CWE-918 SSRF · CWE-73 external control of file name/path · CWE-441 unintended proxy/relay · CWE-610 externally controlled reference
  • PHP stream wrappers (file:// is the default filesystem wrapper; not gated by allow_url_fopen)
  • Drupal Security Team — coordinated disclosure via the project's "Report a security vulnerability"
  • Advisory: Drupal SA-CONTRIB-2026-053 — https://www.drupal.org/sa-contrib-2026-053
  • CVE: CVE-2026-13233 (CNA: Drupal.org)

Security Notice

  • Disclosure is complete (CVE-2026-13233 / SA-CONTRIB-2026-053), so product/version/code details are public. Update to 1.1.1 / 1.2.2 first — unpatched sites remain in the wild.
  • The reproducer/ is sandbox-only and uses synthetic oracles. It is not a live-target exploit and contains no secrets. Do not run it against systems you do not own and are not authorized to test.
  • No working file-read PoC and no primary evidence containing secrets are included here — deliberately, even post-fix, while unpatched deployments exist.

Responsible Disclosure Disclaimer

All research was performed by the author in a self-owned, network-isolated sandbox, against a mock upstream — the real vendor API was never contacted, and no third-party system was ever tested. The finding was reported privately to the Drupal Security Team first and published only in sync with the fix and advisory (SA-CONTRIB-2026-053, CVE-2026-13233); the vendor's rating is Moderately critical. The author's CVSS figures are a provisional self-assessment, not an official score, and were deliberately kept conservative (no CVSS Critical/High) — consistent with the vendor's mid-band rating. This material is for defensive and educational purposes. See SECURITY.md.

Download Tool
3
99
reproducer/verdict.md
  • Transparency note: any environment normalization (e.g. correcting a sandbox files-directory owner that a prior root-run left unwritable) is documented and affects only the save/serve leg, not the file-read sink itself. This is disclosed so reviewers don't misread it as a rigged precondition.
  • DateEvent
    2026-06-10Static review locates the sink; fixed-path file:// read dynamically confirmed
    2026-06-11Arbitrary-path read + settings.php sha256 match + internal HTTP SSRF confirmed
    2026-06-11Production-consumer reachability (automator internal-path; FWA legitimate-HTTP exfil)
    2026-06-11Independent peer review corrects over-claims (attacker model, CVSS, allow_url_fopen); submission put on HOLD
    2026-06/07HOLD items cleared; reported privately to the Drupal Security Team; base-class twin flagged to drupal/ai
    2026-07-10Fixed releases 1.1.1 / 1.2.2; advisory SA-CONTRIB-2026-053 and CVE-2026-13233 published; author credited as finder & remediation developer