Prompt Injection
Andrea Hauser
This is how HTTP/2 Request Smuggling works
HTTP/2 is a binary protocol. HTTP/2 messages are sent as one or more frames and each frame has an explicit length that tells the server how many bytes to read. The length of an HTTP/2 message is calculated accordingly by adding up the length of all frames. This length can not be manipulated. Now the question arises how request smuggling can still occur in HTTP/2 if the length of the frames cannot be manipulated, since this manipulation of the length was the prerequisite for request smuggling in HTTP/1.1 requests. Basically, HTTP/2 request smuggling only occurs when the front-end supports HTTP/2 but the back-end does not. Then the front-end must convert the HTTP/2 request for the back-end into an HTTP/1.1 request. This creates new possibilities for manipulation.
Since HTTP/2 is a binary protocol, abstracted and simplified representations are used here to show the concepts. The individual frames are not shown. In HTTP/2 there are so-called pseudo headers. These are defined for the rest of the article as follows and marked with a colon in front of the name
Pseudo Header | Description |
---|---|
:method | Corresponds to the request method. |
:path | Corresponds to the request path including possible parameters. |
:authority | Corresponds approximately to the host header. |
:scheme | Corresponds to the request scheme and is normally http or https. |
:status | Corresponds to the response status code and is not used in requests. |
It is easiest to see these headers in use compared to the same request in HTTP/1.1 and HTTP/2.
HTTP/1.1 Request
POST /test HTTP/1.1\r\n Host: example.com\r\n User-Agent: test\r\n Content-Length: 3\r\n \r\n x=1
HTTP/2 Request
:method POST :path /test :authority example.com :scheme https user-agent test x=1
With the basics covered, HTTP/2 request smuggling vulnerabilities can now be addressed.
This vulnerability works in principle similar to the TE.CL vulnerability from the last article, but the front-end uses HTTP/2, ignores the Content-Length Header and passes it unchanged to the back-end. The back-end uses HTTP/1.1 and therefore interprets the Content-Length Header. In an example, the attack looks like this.
:method POST :path /test :authority example.com content-type application/x-www-form-urlencoded content-length 0 Injected
Since the back-end finishes processing the request earlier than what the front-end sent due to the content length of 0, the rest of the request remains in the pipeline between the front-end and the back-end. As soon as a next request arrives, the remainder of the previous request that is still in the pipeline is added before this new request. So a new request would look like this in the back-end.
InjectedGET /user-requested HTTP/1.1 Host: example.com
Similar to the vulnerability described above, the front-end uses HTTP/2, ignores the Transfer-Encoding Header and passes it unchanged to the back-end. The back-end uses HTTP/1.1 and therefore interprets the Transfer-Encoding Header. In an example, the attack looks like this.
:method POST :path /test :authority example.com content-type application/x-www-form-urlencoded transfer-encoding chunked 0 GET /admin HTTP/1.1 Host: example.com Foo: Injected
Since the back-end finishes processing the request earlier than what the front-end sent due to the transfer encoding of 0, the rest of the request remains in the pipeline between the front-end and the back-end. As soon as a next request arrives, the remainder of the previous request still in the pipeline is added before this new request. So a new request would look like this in the back-end.
POST /test HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded Transfer-Encoding: chunked 0 --> Request one ends here GET /admin HTTP/1.1 Host: example.com Foo: InjectedGET /user-requested HTTP/1.1
Since HTTP/2 and HTTP/1.1 are not interpreted on the same basis, as HTTP/2 is a binary protocol and HTTP/1.1 is string based, these differences can be exploited. In an HTTP/2 header, for example, line breaks can be used without any problems, as they are not interpreted in a special way. This means that a header interpreted as one header in the front-end can become several headers in the back-end. For example
Testheader: Test\n\rInjected
one header in the front-end, would look like this in the back-end
Testheader: Test Injected
Due to the excellent research by James Kettel, the attacks described above can be simulated in the Web Security Academy.
The easiest way to prevent this vulnerability, as already indicated at the very beginning, is to use HTTP/2 throughout. If this is not possible, care must be taken that no headers such as content length or transfer encoding and no special characters such as \r\n and : are included in the conversion to an HTTP/1.1 request.
When switching to HTTP/2 in the front-end server, an extensive vulnerability can be unknowingly introduced if the HTTP/2 and HTTP/1.1 speaking servers do not agree on where the limit of the request is. The effects of request smuggling can include the unauthorised reading of responses from other users, the insertion of arbitrary JavaScript into arbitrary responses from users, and the complete disruption of the normal operation of a website. The fix is comparatively easy if the back-end server can also be converted to work over HTTP/2. Otherwise, it requires careful conversion of the requests to avoid introducing vulnerabilities.
Our experts will get in contact with you!
Andrea Hauser
Andrea Hauser
Andrea Hauser
Andrea Hauser
Our experts will get in contact with you!