HTTP/2 is a completely different than HTTP/1.1. HTTP/1.1 is a plaintext protocol where headers are differentiated via newlines, while HTTP/2 is binary protocol. In HTTP/2, each frame has a built in length measurement while HTTP/1.1 has a length per request being sent. HTTP/2 has streams that allow for sending data over the same socket instead of multiple connections; this allows for data to be sent back out of order and it can still be found, since there is a stream id.
The first principle of this attack is HTTP/2 downgrades. Although the original connection may be over HTTP/2, the connection to the backend is being downgraded to HTTP/1.1 when being proxied to the backend. As a result, all of the security protections of HTTP/2 have vanished. By doing this, the backend may not agree with how the frontend thinks is a single request with the transfer-encoding or content-length header, just as HTTP/1.1 smuggling.
The rest of the presentation is mostly case studies on real world targets. On Netflix, the frontend would understand the Content-Length header in the HTTP/2 request and use this to forward the request to HTTP/1. The problem is that the Content-Length could be invalid, with other data attached to the request. This translation would result in a request being smuggled in that was not anticipated and an extra response being added.
Exploiting these bugs is complicated in practice since every situation is so unique. To exploit the bugs above, the author started redirecting users to his site, hoping to see sensitive information including authentication tokens. In on case, the victim server was checking to see if they were allowed to send the credentials to his server! At this point, they modified their own website to send the Access-Control headers to send them over.
The next example has to do when connection information is included in the request. In HTTP/2, any connection specific header needs to be labeled as malformed. In the case of AWS ALB and Incapsula WAF, this was not the case. Even though the header transfer-encoding: chunked should have been rejected, it was still appended onto the request. The Content-Length is written out but the backend server prioritizes the transfer-encoding: chunked header, resulting in another Desync.
In another attack against Firefox, they found a header injection vulnerability by putting the transfer-encoding header into another header but adding a newline inside the header. When this transfer between protocols occurred, there was another desync. Against Jira, this header injection worked to desync the response from the request, resulting in the wrong requests being sent back. There are a plethora of other ways to attack the HTTP/2 downgrade as well.
The exploitation path depends on the connection reuse. Depending on how much connection reuse is done, things such as internal header theft, header spoofing, cache poisoning, response queue poisoning and cross-user attacks may be possible. When there is no connection reuse, the attack does not work very well since we cannot affect the other users. How do we know that it is still vulnerable?
When sending two requests (when one is smuggled), this seems like a problem to detect. However, if we truly do smuggle a request, HTTP/1.1 headers will be returned in the response, even though the request should be HTTP/2 only. Sometimes, this is blind though and only the first response is sent back. Using a HEAD or OPTIONS request for this may result in headers being returned, such as the Content-Length, to make it obviously vulnerable. What can we do if we cannot attack other users though?
Once we have a request tunneling issue, we want to see or use the internal headers. Commonly, internal headers are used for sensitive operations, such as who the user is. By using tunneling, we can bypass rejection of the internal headers and use them anyway. Finding these can be hard, as it may require a bunch of brute forcing.
Another option for this attack is to leak internal headers. On BitBucket, the author noticed they could inject newlines into headers. As a result, the frontend and backend were confused on where the body of the request starts and ends. If the parameter that we are using gets reflected back in the request, the added internal headers will be appended to this! Now, we have leaked the headers. The headers may even have secret information in them as well.
If the stars truly align, web cache poisoning may be possible with request tunneling as well. Using the poisoning, we can create reflected XSS or hijack pages entirely.
The end of the article/talk gives a large list of other potential HTTP/2 smuggling issues:
- Duplicating the path, method and scheme between different HTTP servers.
- authority header replaces the HOST header. But, servers support both, which could cause issues.
- The
scheme of an HTTP/2 request that is meant to be HTTP/HTTPs. If this is not verified, we can put a full URL inside of it and confuse how servers are used. The author found SSRF using this exact issue.
- Some servers do not allow newlines into the fields. However, they do allow for colons, which could cause issues.
- Anything that is an input in HTTP/2 is potentially in danger. Especially those being converted to HTTP/1.1 can that are not in the HTTP/2 specification.
What else could there possible be? Essential knowledge! Lots of servers support HTTP/2 but forget to advertise it. Burp Request Smuggler now has a way to detect this. Additionally, some requests can corrupt each other; be careful when testing this, as you may corrupt your owns things.
The tooling is real interesting sparse right now but is going:
- Turbo Intruder has custom H2 stack written by the author of this. Some rewrites were done to put things like newlines into places where they should not be at.
- http2smugl is a patched GoLang client.
- Burp Suite has a repeater and the normal proxy, which can be used for this.
- YouTube talk and similar research.