Описание
Out-of-bounds read in the HTTP stream wrapper when following a redirect with an empty Location header
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:
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:
*(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:
The next request goes to / when the stray byte happens to be NUL and to /a// otherwise. Under Valgrind with USE_ZEND_ALLOC=0:
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.
Пакеты
php
>=8.2.28, <8.2.34
8.2.34
php
>=8.3.18, <8.3.35
8.3.35
php
>=8.4.5, <8.4.26
8.4.26
php
>=8.5.0, <8.5.11
8.5.11
Связанные уязвимости
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.
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.
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.
Out-of-bounds read in the HTTP stream wrapper when following a redirect with an empty Location header
When the HTTP stream wrapper follows a redirect and the response carri ...