Request Smuggling in HTTP/2 Downgrades


The main origin of this vulnerability is the fact that the reverse proxy is going to talk with the client using HTTP/2 but then it will transform that communication with the back-end server to HTTP/1.1.
The problem with this approach is that the user is going to be able to inject unnecessarily headers in the HTTP/2 communication that probably won't be checked by the proxy. But then, when those are injected blindly in the HTTP/1.1 communication, a request smuggling attack can be performed.


H2.CL Desync

The HTTP/2 specification indicates that the Content-Length header isn't needed but can be indicated. Therefore, the reverse proxy will treat all the content sent by the users as the request, but then, when downgrading to HTTP/1.1, this header is going to be injected in the request and therefore, the back-end will treat the request as 2 different requests as you can see in the image below:

H2.TE Desync URL Token Hijack

The HTTP/2 specification also indicates that any message containing connection-specific header fields MUST be treated as malformed... but if you don't follow this rule, you are vulnerable.
This technique was abused on AWS load balancer, so making sure that the users access a Host header pointing to a server controlled by the attacker will make them access that server.

H2.TE Desync Header Hijack

This is exactly the same technique as before, but checking the requests James noticed that clients were asking to send him their credentials, so he just modified his server to allow CORS to send him peoples credentials:

H2.TE via Request Header Injection

HTTP/2 also won't allow to put not permitted characters in headers, but if the server isn't respecting this rule, you can inject arbitrary headers when the communication is downgraded to HTTP/1.1.
In this case the header Transfer-Encoding was injected.

H2.TE via Header Name Injection

HTTP/2 on some servers lets you put a colon in the header name, and with a you can inject a new header inside the header name like this:
Note that if you put just the new line characters sending a header without content, the request is going to be treated as invalid:

H2.TE via Request LIne Injection

In this case the injection was performed inside the request line:

URL Prefix Injection

Inside the scheme of the HTTP/2 connection you might be able to send a full URL that will overwrite the one indicated in the path:

Request Line Injection via spaces

Frontend->backend connection reuse

Sometimes you will find that preforming a HTTP Request Smuggling attack you can only attack yourself. This could be because the reverse proxy has decided to use a different connection with the back-end server per IP.
Note that even with that restriction you still can perform attacks like authorization bypasses, internal headers leakage and cache deception and cache poisoning attacks.
Usually this restriction doesn't exist so you can smuggle request into the connection between the reverse proxy and the back end that other people are using, but it's even possible that the proxy doesn't even reuse a connection with connections from the same IP (pretty heavy restriction for this kind of attack).
In the heaviest restriction (no connection reuse) you will detect the vulnerability with the Time Based technique, but then testing it you will find that it's a "false positive".

Tunnelling Confirmation

A way to confirm if the endpoint is vulnerable but the connection is inside a "tunnel" is to smuggle 2 full requests into 1.
The problem with HTTP/1.1 is that if you receive 2 HTTP responses you don't know if the endpoint was vulnerable or it isn't and the "smuggled" request was just treated as a regular request.
However, this technique can be used in HTTP/2 because if the endpoint was vulnerable and you smuggled one request, you will see the headers of the response to the smuggled request in the response from the reverse proxy:

Tunnel-vision Problem

There could be another problem, if the response to the legit request contains a Content-Length, the reverse proxy is only going to read the bytes specified there and no more, so you won't be able to read the response from the smuggled request.
However, the HEAD request doesn't contain a body but it usually contains the Content-Length as if the request was a GET request. Therefore, sending a HEAD request instead of a POST request you can read the HEAD Content-Length bytes of the smuggled request response.

Leaking Internal Headers via Tunneling

If you find a POST parameter inside the application whose content is going to be reflected in the response, then you can try to inject HTTP/1.1 \r\n characters inside a HTTP/2 request header so the newly injected headers by the proxy are going to be appended in the POST parameter that will be reflected in the response:
Note that in this case the attacker just cares about the response to the first request, he doesn't need to read the request to the smuggled invalid second request.
Using this attack agains different parts of the web (method, path...) can lead to different back-ends being used and different sensitive information being leaked

Cache Poisoning via Tunneling

In this scenario a HEAD request to the URL whose cache is going to be poisoned is sent while smuggling a request whose response content will be containing the payload (maybe some XSS payload).
Due to the fact the the HEAD response contains the Content-Type: text/html and because the reverse proxy thinks that the whole response to the smuggled request is the body of the HEAD request, the XSS payload is going to be treated as HTML even if the page wasn't vulnerable to XSS.

Hidden HTTP/2

Usually servers advertise the support via ALPN field in TLS handshake, but some doesn't.
It can be easily detected using curl --http2 --http2-prior-knowledge