CVE-2026-108360

mediumPriorität · verfolgen · 0OT-Risiko · erhöhtEPSS 0.002 · 13. PerzentilUS
grimoire priorityP3 · Track
Exploitable?
—
no known exploitation
Likely / severe?
0.22% (13th) EPSS
CVSS 6.4 · medium
Is it mine?
5 products
Prüfe deine Angriffsfläche
Who says so?
1 sources
osv

EPSS · bewertet 2026-10-27

PhpSpreadsheet is a pure PHP library for reading and writing spreadsheet files. Prior to 1.30.7, 2.1.19, 2.4.8, 3.10.8, and 5.10.0, Worksheet\Drawing::setPath validates only the initial external-image URL with setIsWhitelisted when setAllowExternalImages is enabled. The HTTP stream context permits PHP to follow a redirect without applying the callback to the redirected URL, so a permitted image host can redirect the server to an internal address and cause a blind GET request. The response body is not returned to the attacker because the reader validates the downloaded content as an image and removes the temporary file, but internal GET-driven endpoints, network reachability, and cloud metadata endpoints may be reached. This issue is fixed in versions 1.30.7, 2.1.19, 2.4.8, 3.10.8, and 5.10.0.

01

cvss

Technischer Schweregrad, 0–10. Eine Zeile je Quelle.

6.4
010

Basiswert 0–10 · höher = schwerwiegender · Vektor überfahren zum Auflösen

v3.16.4mediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:Nsecondary · security-advisories@github.comAusnutzbarkeit 3.1 · Auswirkung 2.7
v3.16.4mediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:Nsecondary · OSV
v3.15.9mediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N/E:Usecondary · CVSS-BT (t0sche)
02

schwächen

Die CWE-Schwächeklassen, zu denen dies gehört.

03

betroffene Produkte (5)

Betroffene Produkte und Versionsbereiche.

04

Advisories (1)

Hersteller- und Ökosystem-Meldungen dazu.

  • GHSA-2phh-27fw-q4gcPhpSpreadsheet: SSRF whitelist bypass via unre-validated HTTP redirect in Drawing::setPath external image loading2026-10-27ghsa · osvUS

    ## Summary PhpSpreadsheet's `Worksheet\Drawing::setPath()` validates an external-image URL against an operator-supplied `isWhitelisted` callback **once**, on the initial URL only, then fetches it with `file_get_contents()` using a stream context that does **not** set `follow_location => 0`. A whitelisted host that returns an HTTP `302 Location:` redirect to an internal target is therefore followed by PHP's HTTP wrapper (default `follow_location=1`, `max_redirects=20`) with **no re-validation of the redirect hop** — defeating the whitelist and yielding a blind SSRF from the server. This is an unfixed variant of CVE-2026-59931 (GHSA-6hq5-7373-42rg, the WEBSERVICE redirect-whitelist-bypass): the `follow_location => 0` fix (commit `7ef7b25e`) was applied ONLY to `Calculation/Web/Service.php`; `Worksheet/Drawing.php` was never touched. ## Root Cause `src/PhpSpreadsheet/Worksheet/Drawing.php`: - Scheme allowlist at `:125` (`/^(http|https|file|ftp|s3):/`) permits `http`/`https` (the redirect-following schemes) and runs on the initial URL only. - Opt-in gate `if (!$allowExternal)` at `:128` (requires `setAllowExternalImages(true)`; default `false`, `BaseReader.php:55`). - Whitelist check `if ($isWhitelisted !== null && !$isWhitelisted($path))` at `:131` — validates the INITIAL URL only; never re-applied after a redirect. - Stream context built at `:140-152` sets `user_agent` + `header` (+ `ssl.crypto_method` for https) but **omits `follow_location`**. `git grep follow_location` on tag 5.9.0 yields a single hit — `Service.php:45` (the CVE-2026-59931 fix) — confirming it was never added to `Drawing.php`. - Sink `file_get_contents($path, false, $ctx)` at `:154`. ## Impact Blind SSRF from the server: reach and trigger internal GET-driven endpoints, coarse internal reachability/port probing, and reach cloud-metadata endpoints. The fetched body is written to a temp file, checked with `isImage()` (mime `image/*`, or `application/octet-stream` with `bin`/`emf` extension), only image dimensions are stored, and the temp file is `unlink`ed — so the response body is **not** exfiltrated (non-image responses fail `isImage()`). This is a blind GET primitive, not full response read. ## Proof of Concept 1. Attacker server `https://trusted-whitelisted.example/redir.png` returns `302 Location: http://169.254.169.254/latest/meta-data/`. 2. `evil.xlsx` with `xl/drawings/_rels/drawing1.xml.rels`: `<Relationship ... Type=".../image" Target="https://trusted-whitelisted.example/redir.png" TargetMode="External"/>`. 3. Victim: `$r = new Xlsx(); $r->setAllowExternalImages(true)->setIsWhitelisted(fn($p)=>str_contains($p,'trusted-whitelisted.example')); $r->load('evil.xlsx');` -> the server-side GET is forced to the internal target despite the whitelist. HTML-reader variant: `<img src="https://trusted-whitelisted.example/redir.png">` loaded via `Reader\Html` (`Html.php:1237`) with the same reader settings. ## Attack Chain 1. Entry: crafted `evil.xlsx`, `xl/drawings/_rels/*.rels` `TargetMode="External"`, `Target="https://trusted.example/redir.png"` (whitelisted host). Guard: none (legal external-image relationship). Bypass proof: Target stored verbatim -> `$url` at `Reader/Xlsx.php:1544`. 2. Config: victim loads with `setAllowExternalImages(true)->setIsWhitelisted($cb)` where `$cb` allows `trusted.example`. Guard: `Drawing.php:128` opt-in (satisfied) + `:131` whitelist. Bypass proof: initial URL is whitelisted -> returns true; check runs once on `$path`. 3. Fetch/redirect: `file_get_contents` (`:154`) GETs the URL; whitelisted host returns `302 Location: http://169.254.169.254/...`. Guard: stream context (`:140-152`). Bypass proof: no `follow_location` key -> PHP follows the redirect (default `follow_location=1`, `max_redirects=20`) with no re-validation. 4. Impact: server issues a blind GET to the internal target (body temp-filed, `isImage()`-checked, dimensions stored, temp file unlinked — body not exfiltrated). ## Bypass Evidence - `git grep follow_location 5.9.0 -- src/` -> single hit at `Service.php:45`; `Drawing.php` has none. - CVE-2026-59931's own regression harness (`ServerTest.php` + `redirect.php`: a whitelisted host 302-redirects to `direct.php`; only after `follow_location => 0` does the fetch stop following it) demonstrates the exact behavior. That fix is present in `Service.php` and absent in `Drawing.php`. - `git grep isWhitelisted 5.9.0 -- src/` confirms the callback is applied only at `Drawing.php:131` (initial `$path`), never after the redirect. ## Affected Versions `>= 5.5.0, <= 5.9.0`. The `isWhitelisted` control being bypassed was introduced in 5.5.0 (commit `50a7552f`); present and unfixed on the latest release (5.9.0) and on HEAD (`05e99ebf`). No post-release fix to `Drawing.php`. ## Suggested Fix Add `'follow_location' => 0` (and/or `'max_redirects' => 0`) to `$ctxArray['http']` in `Drawing::setPath()` (`:140-148`), mirroring the CVE-2026-59931 fix at `Service.php:45`; alternatively follow redirects manually and re-run `$isWhitelisted` on each hop. --- Reported by **zx (Jace)** — GitHub: @manus-use

05

quellen

Welche Quellen diesen Datensatz belegen und wann.

veröffentlicht 2026-10-22geändert 2026-10-23
07

Referenzen (11) nvd

Externe Belege — Patches, Meldungen, Analysen.

scry

Angreifer nutzen Schwachstellen wie diese gegen im Internet exponierte Dienste aus. Sehen Sie mit einem kostenlosen Passiv-Scan, was exponiert ist.

Kostenloser Scan