HTTP/2 become the standard defacto for the modern web and causes new application security risks. The HTTP2 request smuggling is one of a few HTTP/2 vulnerabilities with the high severity that raised last year. In this post, we will describe it in detail and suggest an open-source tool http2smugl that detects such kinds of vulnerabilities.
HTTP/2 is the thing that already took an Internet. According to the Wikipedia:
"The standardization effort was supported by Chrome, Opera, Firefox, Internet Explorer 11, Safari, Amazon Silk, and Edge browsers. Most major browsers had added HTTP/2 support by the end of 2015. About 97% of web browsers used have the capability, while according to W3Techs, as of March 2021, 50.2% of the top 10 million websites supported HTTP/2.".Wikipedia
In a fact, it means, that half of the most popular Internet resources are now HTTP/2 and more coming. At the same time, this new protocol carries cyber-security risks, especially caused by the conversion of HTTP/2 first-tier edge and the HTTP/1.1 cache or backend. We already wrote about one of these issues in a post Cloudflare fixed an HTTP/2 smuggling vulnerability last October.
The same researcher Emil Lerner who reported that the Cloudflare bug continued to discover more and more HTTP/2 security flaws and released the tool
http2smugl to help AppSec, DevSecOps, and NOC folks to check their infrastructures.
What's the Request Smuggling?
Request smuggling is the vulnerability caused by different request parsing processes that may be interfering if the client's request was forwarded from one server to another. Let's say, you have to transfer the greeting voice message to your friend in another city, but you can't call them and have to tell the message to another friend who is going to that city in a few days. You carefully say the greeting message, and the other friend remembered it in full. Unfortunately, the travel friend has some specific accent and can't pronounce the greeting message as you said it, and the receiver friend doesn't know it. As a result, the receiver friend heard about some "Comrades" instead of "Congrats". That's exactly what happens if smuggling, but with servers instead of friends. Actually, since all the servers are our friends anyways, the definition above is very strict.
Technically, HTTP Request Smuggling is a type of web vulnerability that may arise clients' requests forwards from one server (frontend, caching server, or load balancer) to another server (backend). It is also known as reverse-proxy architecture. Usually, the frontend uses HTTP/1.1 with Keep-Alive connections to communicate with the backend server.
The request smuggling exploitation can cause security risk when an attacker can make servers parse a malicious request differently. Usually, the attacker wants to make the backend think that the request ends earlier than it actually does and parse the tail as a separate request.
The impact of this type of vulnerability varies a lot. It causes authentication bypass, information leakage, XSS, CRLF, and a lot of other things. For instance, if the attacker can make the server save provided information (e.g. by editing her profile), she can exfiltrate HTTP requests of other users, including cookies (thus achieving account takeover). In other cases, the attacker can poison cache on the frontend server, making the impact equivalent of such of reflected or stored XSS.
HTTP2 to HTTP1 conversion flow
As stated earlier, HTTP/2 is a binary (unlike plain/text HTTP/1.1) protocol that works over TLS. TLS and HTTP/2 are usually terminated on the frontend servers — meaning that the request is converted to HTTP/1.1 and forwarded to the backend this way.
As HTTP/2 headers are not delimited by any kind of special characters, like it was with \r\n in the case of HTTP/1.1, they can contain newlines (both names and values). However, such headers may corrupt the forwarded request — newline in HTTP/1.1 terminates header line, thus the content after the new line will be interpreted as a new header.
Playing this way, attackers can send headers like
"fake:header\\ntransfer-encoding:chunked". It will override transfer encoding for the backend server — thus allowing attacker to control where the request finishes and where new request starts. The latter one is fully under attacker's control.
The difference between smuggling and h2c attack
There's another HTTP/2 related attack that is trending: h2c smuggling (https://labs.bishopfox.com/tech-blog/h2c-smuggling-request-smuggling-via-http/2-cleartext-h2c). That technique aims to bypass endpoints restrictions at the frontend (e.g. "don't allow access to
/api/private/ endpoint") by upgrading HTTP/1.1 connection to HTTP/2, and talking directly to the backend. This is an interesting technique that was named the top web hacking technique of 2020, but it's different.
In this article, we're addressing only those security issues and vulnerabilities that cause by specifically request smuggling, not h2c techniques. Smuggling attacks' goal is not to bypass some frontend URL routes, but to achieve HTTP Connection Desync by exploiting possible weaknesses of HTTP/2 → HTTP/1.1 conversion made by frontends.
In other words, when doing "h2c smuggling" one connects to the frontend via HTTP/1.1 and then upgrades the connection to HTTP/2 in a way that is "invisible" by the frontend. When using techniques described in this article, one connects to the frontend via HTTP/2 and sends semi-invalid headers in order to achieve HTTP Desync in the frontend <-> backend connection.
Sending HTTP/2 requests with special characters in headers may be a struggle: no "usual" HTTP tools like curl, Burp, or wget allow this. That's why we introduce a new special tool that allows sending malformed HTTP2 requests with some HTTP/1.1 headers hidden inside — http2smugl.
To install the tool, you need a modern version of the Go programming language. To download it, visit https://golang.org/dl/ and follow the instructions. Once it is installed, just type
go get -u http://github.com/neex/http2smugl in your shell, and the binary will appear in your
To make a request, run the tool in the following way:
Unlike usual HTTP clients, i.e. curl, you can freely use special characters in the headers. Moreover, you can provide them in the escaped form: e.g. you can use
\r\n instead of a newline.
The most powerful feature of http2smugl is vulnerability detection.
It allows to run several detection techniques against a target and tell if the target is suspected to be vulnerable. The tools try to detect if the target server parses the request it should not parse by providing several variations of it and trying to distinguish received answers. More information about the detection algorithm is available in the tool's readme (https://github.com/neex/http2smugl/blob/master/README.md).
Here is an example of usage:
emil@moshka:~$ go get -u github.com/neex/http2smugl emil@moshka:~$ http2smugl detect https://⬛⬛⬛⬛⬛⬛.com/ 2>/dev/null target=https://⬛⬛⬛⬛⬛⬛.com/, http_method=POST, detect_method=detect chunked body validation, smuggling_method=header smuggling via unicode lowercase/uppercase, smuggling_variant=replace k by K (\u212a) in header value, padding_method=no padding headers: distinguishable not by timing target=https://⬛⬛⬛⬛⬛⬛.com/, http_method=OPTIONS, detect_method=detect chunked body validation, smuggling_method=header smuggling via unicode lowercase/uppercase, smuggling_variant=replace k by K (\u212a) in header value, padding_method=no padding headers: distinguishable not by timing target=https://⬛⬛⬛⬛⬛⬛.com/, http_method=GET, detect_method=detect chunked body validation, smuggling_method=header smuggling via unicode lowercase/uppercase, smuggling_variant=replace k by K (\u212a) in header value, padding_method=no padding headers: distinguishable not by timing ^C emil@moshka:~$ http2smugl request https://⬛⬛⬛⬛⬛⬛.com/ "transfer-encoding:chunKed" --auto-content-length --body-str "0\r\n\r\n" :status: 200 <snip> emil@moshka:~$ http2smugl request https://⬛⬛⬛⬛⬛⬛.com/ "transfer-encoding:chunKed" --auto-content-length --body-str "100\r\n\r\n " Error: read tcp 192.168.1.182:40456->184.108.40.206:443: i/o timeout emil@moshka:~$ # smuggling confirmed
In this example, the following happens:
- We install
- We run
http2smugl detecton a domain that says it can distinguish the results between valid and invalid chunked bodies when sends
transfer-encoding:chuKedheader. Note the big K! It is not just uppercase, but a unicode symbol (\u212a).
- Then we confirm that smuggling happens using the request subcommand. We show that depending on the request body, it either processes the request or timeouts. It means we can send a request body like
0 GET /private/endpoint HTTP/1.1 Host: ⬛⬛⬛⬛⬛⬛.com ...and smuggle the request into frontend/backend connection.
Made with mass-scanning for bugbounty in mind, http2smugl tries to check multiple targets at the same time as easy as possible. In this sections, we describe how to run the tool for a number of domains and how to interpret the results.
Prepare domains list
First, you will need to prepare a list of target. While there're exceptions, usually all URLs for one domain are forwarded to the same backend. Thus, you need to filter similar URLs from your list before providing it to the tool.
Run the tool
To run the tool, execute `http2smugl detect ---targets <path-to-target-list> --csv-log <result file>.csv. You can control the number of threads using --threads option. The tool will not daemonize, so be sure to run it in a screen-like environment if you're scanning a lot of URLs.
The tool will output a CSV file with all the variations it tried. It will mark the targets it suspects to be vulnerable by distingushable keyword — either by timing or not. I suggest to focus on investing the timing-based detections as they are much more reliable.
The first thing to do is to confirm the possible injection manually, that is to replicate the requests using
http2smugl request subcommand and confirm that timeout happens depending on the data sent.
After you confirmed the presence of HTTP Request Smuggling, there're some options of how to exploit it. Here we describe only one of them: the one leading to steal other user's request.
First, you need to find a form on the site such that you will be able to "save" something on the site using this form. It could be a profile page or something similar — the form must "edit" something that you will be able to retrieve later.
After that, you will need to "smuggle" the saving request into the frontent<->backend connection using the concrete method you discovered earlier. The value of
Content-Length needs to be increased by some small value, e.g. 300.
When another user's request comes in the same connection, it will be appended to your form, thus it will be saved into your profile and you can later retrieve it. If the request line does not contains ampersand symbols (
&), you will able to see cookies of another user!
http2smugl tool is mostly in PoC state now. Here are the lines of future improvements can be made:
- More variations of HTTP/2-related smuggling techniques can be found. For example, while the current tool tests only for a simpliest version of the header injection via newline, there might be variants like body injection after newline and so on.
- There are some sophisticated techniques of request smuggling that are present in "HTTP Request Smugling" Burp plugin that are not implemented in
http2smugl. For example,
http2smuglcurrently does not try values similar to
CONNECTmethod is handled differently in HTTP/2 (see RFC 8441), which might open a door to new loopholes in HTTP/2 implementations. The same is true for "Upgrade" mechanics, which is prohibited by the RFC — but who knows how different implementation handle it?
- The first character in HTTP/2 headers is special — it can contain colon, which is not usually allowed in header names. There might be interesting bugs around that.
If you have any ideas on what to add, you can fill an issue at http://github.com/neex/http2smugl/issues. Feel free to do so, even if it is just an idea!
HTTP/2 become a standard de-facto of the Internet, therefore, its security is very actual. The HTTP Request Smuggling technique explained in this article is one of the most dangerous attack vectors for HTTP/2. Because of this research and an open-source tool http2smug, engineers, DevOps, and security teams now can check their load balancers for such kind of vulnerability for free. On the other hand, we believe, that the tool will be useful for bug bounty and further vulnerability research.
Thanks for reading, and stay secure!