# Request Smuggling in HTTP/2 Downgrades ## Origins 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**. ![](<../../.gitbook/assets/image (636) (1).png>) 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**. ## Examples ### 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: ![](<../../.gitbook/assets/image (639).png>) ### 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. ![](<../../.gitbook/assets/image (631).png>) ### 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: ![](<../../.gitbook/assets/image (662) (1) (1).png>) ### 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**. ![](<../../.gitbook/assets/image (648) (1) (1).png>) ### H2.TE via Header Name Injection HTTP/2 on some servers lets you put a **colon in the header name, and with a \r\n** you can inject a new header inside the header name like this: ![](<../../.gitbook/assets/image (632) (1).png>) Note that if you put just the new line characters sending a header without content, the request is going to be treated as **invalid**: ![](<../../.gitbook/assets/image (647) (1).png>) ### H2.TE via Request LIne Injection In this case the injection was performed inside the request line: ![](<../../.gitbook/assets/image (640) (1).png>) ### 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: ![](<../../.gitbook/assets/image (661) (1).png>) ### Request Line Injection via spaces ![](<../../.gitbook/assets/image (641).png>) ## 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). ![](<../../.gitbook/assets/image (646) (1) (1).png>) 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**: ![](<../../.gitbook/assets/image (652) (1).png>) ### Tunnel-vision Problem There could be another problem, if the **response** to the legit request **contains** a **Content-Length**, the **reverse prox**y 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. ![](<../../.gitbook/assets/image (628) (1).png>) ### 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: ![](<../../.gitbook/assets/image (656) (1) (1).png>) 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. {% hint style="info" %} 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** {% endhint %} ### 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. ![](<../../.gitbook/assets/image (659) (1).png>) ## 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` ## Tools * Burp extension: HTTP Request Smuggler * [https://github.com/neex/http2smugl](https://github.com/neex/http2smugl) ## References * This talk explains perfectly all the techniques indicated here: [https://www.youtube.com/watch?v=rHxVVeM9R-M](https://www.youtube.com/watch?v=rHxVVeM9R-M)