Out-of-bounds read in the HTTP stream wrapper when following a redirect with an empty Location header

Kurzinfo

Metadaten

  • Original (extern): Link oeffnen
  • Veroeffentlicht: 2026-09-24 10:07 UTC
  • Importiert: 2026-10-07 06:38 UTC
  • CVSS: 6.3
  • Quelle-ID: php_sec
  • uid_hash: 00d1991eb47a4c4f5d2e3841da575bc83d18914eea91c0684ab52e4ecd1ae778

Inhalt

Summary

When the HTTP stream wrapper follows a redirect and the response carries a Location header with an empty value, the redirect code reads one byte past the end of the heap buffer holding the location. The value of that out-of-bounds byte decides which redirect target is built, so a malicious server controls whether the client is sent to the host root or to the current directory.

Details

Since the fix for GHSA-52jp-hrpf-2jff (CVE-2025-1861), php_stream_http_response_headers_parse() stores the Location value in a heap buffer of exactly location_len + 1 bytes:

https://github.com/php/php-src/blob/a66f30d78e10773048720b4b5f279136fdd0a433/ext/standard/http_fopen_wrapper.c#L295-L301

For an empty header value that is a single byte holding only the NUL terminator. The redirect handling in php_stream_url_wrap_http_ex() treats any location that does not start with / as a relative path and dereferences the second byte to decide whether to join it with the directory of the current request path:

https://github.com/php/php-src/blob/a66f30d78e10773048720b4b5f279136fdd0a433/ext/standard/http_fopen_wrapper.c#L1055-L1063

*(header_info.location+1) is one byte after the allocation.

Before CVE-2025-1861 was fixed, the location lived in a fixed-size char location[HTTP_HEADER_BLOCK_SIZE] stack buffer and the same read stayed in bounds. The bug therefore exists only in 8.1.32+, 8.2.28+, 8.3.18+, 8.4.5+ and all 8.5 releases.

The fix replaces strlen(header_info.location) with header_info.location_len and guards the second-byte read with the length, so an empty Location deterministically redirects to the host root. It was submitted and merged publicly, outside the security process, in https://github.com/php/php-src/pull/23467. A follow-up in https://github.com/php/php-src/pull/23521 corrected the guard from location_len > 0 to location_len > 1, restoring the long-standing resolution of a single-character relative Location.

PoC

A server answering HTTP/1.1 302 Found\r\nLocation:\r\nContent-Length: 0\r\n\r\n to this client:

 ['follow_location' => 1]]);
file_get_contents("http://host/a/b", false, $ctx);

The next request goes to / when the stray byte happens to be NUL and to /a// otherwise. Under Valgrind with USE_ZEND_ALLOC=0:

Invalid read of size 1
   at php_stream_url_wrap_http_ex (http_fopen_wrapper.c:1044)
   by php_stream_url_wrap_http (http_fopen_wrapper.c:1186)
   by _php_stream_open_wrapper_ex (streams.c:2206)
   by zif_file_get_contents (file.c:426)
 Address is 0 bytes after a block of size 1 alloc'd
   at malloc
   by _emalloc
   by php_stream_http_response_headers_parse (http_fopen_wrapper.c:268)

The regression tests are ext/standard/tests/http/http_empty_location_redirect.phpt and http_single_char_location_redirect.phpt.

Impact

An attacker who controls a server that a PHP application fetches from, or who can redirect such a request there, triggers a one-byte heap over-read. The byte is never returned to the attacker; it only selects between two redirect targets, so at most one bit, whether the byte is zero, is observable through the request path the server sees. Under the Zend memory manager the one-byte allocation sits in an 8-byte bin, so the read does not crash the process. The practical effect is a non-deterministic redirect target and undefined behaviour reported by ASan and Valgrind.

Verknuepfte CVEs

CVE-ID Severity (Quelle) CVSS (Quelle) Severity (CVE.org) CVSS (CVE.org) EPSS EPSS-Pctl Veroeffentlicht (CVE.org)

CVE-2025-1861

MEDIUM

6.3

MEDIUM

6.3

2025-03-30

CVE-2026-93682

MEDIUM

6.3

MEDIUM

5.8

2026-09-25

Quellen-Details

Bezeichnung Name Kategorie Tags Zielgruppe Sprache Feed-URL
PHP Security (php/php-src GHSA)

php_sec

vendor_advisory php, runtime - de https://github.com/php/php-src/security/advisories