Are there CVE numbers coming for these vulnerabilities? On Tue, Aug 17, 2021 at 8:14 AM Willy Tarreau <w...@1wt.eu> wrote:
> Hi everyone, > > HAProxy is affected by 4 vulnerabilities in its HTTP/2 implementation in > recent versions (starting with 2.0). Three of them are considered as having > a moderate impact as they only affect the interpretation of the authority > (Host header field) in H2->H2 communications in versions 2.2 and above. > One only affects a risk of misinterpretation from lenient HTTP/1 backend > servers, and affects version 2.0 and above, though at the time of writing > we're not aware of any such vulnerable server among the mainstream ones > that are commonly found behind HAProxy (Apache, NGINX, Varnish, etc). > > Background: on Aug 05 a research article was published about flaws > affecting some HTTP/2 to HTTP/1 proxies or gateways: > > https://portswigger.net/research/http2 > > A first analysis of the article compared to some selected pieces of code > concluded that haproxy was safe. This was actually wrong as only older > versions are safe (2.0 in "legacy" mode and 1.8). Tim Düsterhus conducted > some additional tests and found some problems, which after digging for a > few days, revealed to be significantly more embarrassing. In practice, > version 2.0 in the default "HTX" mode and all later versions are affected > to some degrees. > > > 1) Spaces in the ":method" field > > The first problem is based on the ":method" field. By passing a space in > the method, it is possible to build an invalid HTTP/1 request on the > backend side, which some lenient servers might possibly interpret as valid, > resulting in a different request between the one seen by haproxy and by the > server. This might be abused to circumvent some switching rules for > example, > and get a request to be routed to a wrong server. Example: > > H2 request > :method: "GET /admin? HTTP/1.1" > :path: "/static/images" > > HAProxy would route all "/static" requests to the static server farm, > but once the request is reassembled it would become this: > > GET /admin? HTTP/1.1 /static/images HTTP/1.1 > > This is not valid but if a server fails to properly validate this input, > it might be fooled into thinking this is a request for /admin. > > Please note that HTTP/2 backend servers are not affect as the request is > sent as a new ":method" field there. Additionally, dangerous characters > like CR, LF or NUL are always blocked on input so is is not possible to > perform a request smuggling attack, and the risks are limited to HTTP/1 > servers which fail to properly parse the request line (note that all > major server implementations are safe against this). > > A workaround for this issue for those having to rely on possibly unsafe > servers is to reject invalid characters in the method by placing such a > filtering rule on the request path either in the frontend or the backend: > > http-request reject if { method -m reg [^A-Z0-9] } > > A second workaround that may only be used on version 2.0 consists in > disabling the HTX internal representation in the affected backends and > the frontends that route to them: > > no option http-use-htx > > This will have for effect to transform the HTTP/2 requests to HTTP/1 that > will then be submitted to the internal HTTP/1 parser which will reject > the poorly formatted request. This older representation called "legacy" > is not available any more in version 2.1 and above, and is not compatible > with HTTP/2 nor FastCGI backend servers. > > This issue affects all versions from 2.0 and above, in HTX mode, with > HTTP/1 on the server side. > > > 2) Domain parts in ":scheme" and ":path" > > The ":scheme" HTTP/2 header field contains the scheme that prefixes the > request URI, typically "http" or "https". ":path" contains the part that > is local to the target host, and that usually starts with the "/". By > appending a part of a request to ":scheme" or by prepending a part of a > domain name to ":path", it is possible to make haproxy and a backend > server see a different authority or URL prefix. Please note that this > only affects HTTP/2 servers on versions 2.2 and above. These versions > are indeed capable of passing an absolute URI from end to end, while > earlier versions were limited to origin URIs. In addition, HTTP/2 > requests are always forwarded in origin form to HTTP/1 backend servers > (i.e. they do not have a scheme nor authority parts). As such HTTP/1 > servers are safe and only HTTP/2 servers are exposed. > > What happens is that on the entry point, the :scheme, :authority and :path > fields are concatenated to rebuild a full URI that is then passed along the > chain, but the Host header is set from :authority before this concatenation > is performed. As such, the Host header field used internally may not always > match the authority part of the recomposed URI. Examples: > > H2 request > :method: "GET" > :scheme: "http://localhost/?orig=" > :authority "example.org" > :path: "/" > > or: > > H2 request > :method: "GET" > :scheme: "http" > :authority "example.org" > :path: ".local/" > > An internal Host header will be build with "example.org" then the complete > URI will become "http://localhost/?orig=example.org/" in the first > example, > or "http://example.org.local/" in the second example, and this URI will be > used to build the HTTP/2 request on the server side, dropping the unneeded > Host header field. In HTTP/1 there is no such issue as the URI is dropped > and the Host is kept. Thus if the configuration contains some routing rules > based on the Host header field, a target HTTP/2 server might receive a > different :authority than the one that was expected to be routed there. > > A workaround consists in rewriting the URI as itself before processing the > Host header field, which will have for effect to resynchronize the Host > header field with the recomposed URI, making sure both haproxy and the > backend server will always see the same value: > > http-request set-uri %[url] > > > 3) Mismatch between ":authority" and "Host" > > The HTTP/2 specification (RFC7540) implicitly allows the "Host" header > and the ":authority" header field to differ and further mentions that the > contents of ":authority" may be used to build "Host" if this one is > missing. > This results in an ambiguous situation analogue to the one above, because > rules built based on the "Host" field will match against a possibly > different "Host" header field that will be dropped when the request is > forwarded to an HTTP/2 backend server. An HTTP/1 server will not be > affected since HTTP/2 requests are forwarded to HTTP/1 in origin form, > i.e. without the authority part. Example: > > H2 request > :method: "GET" > :scheme: "http" > :authority "victim.com" > :path: "/" > Host: "example.org" > > Internal switching rules using the "Host" header field will see " > example.org" > but when the request is passed to an H2 server, "Host" will be dropped and > "victim.com" will be used by this server to fill the missing "Host" > header. > > The new H2 specification in progress ("http2bis") addresses this issue by > proposing that "Host" is always ignored on input in favor of ":authority" > which remains more consistent with what is done along the chain. This is > the solution adopted by the fix here. > > A workaround consists in using the same rule as for the previous issue, > before the Host header field is used by any switching rule (typically in > the frontend), which will have for effect to rewrite the "Host" part > according to the contents of the ":authority" field: > > http-request set-uri %[url] > > > 4) Affected versions > > - versions 1.7 do not support H2 and are not affected > - versions 1.8 only support H2 legacy mode are not affected > - versions 2.0 prior to 2.0.24 are affected by the :method bug > - versions 2.2 prior to 2.2.16 are affected by all 4 bugs > - versions 2.3 prior to 2.3.13 are affected by all 4 bugs > - versions 2.4 prior to 2.4.3 are affected by all 4 bugs > - versions 2.5 prior to 2.5-dev4 are affected by all 4 bugs > > > 5) Instant remediation > > Several solutions are usable against all of these issues in affected > versions before upgrading: > > - disabling HTTP/2 communication with servers by removing "proto h2" from > "server" lines is sufficient to address the ":authority", ":scheme", and > ":path" issues if the servers are known *not* to be vulnerable to the > issue described in the ":method" attack above. This probably is the > easiest solution when using trusted mainstream backend servers such as > Apache, NGINX or Varnish, especially since very few configurations make > use of H2 to communicate with servers. > > - placing the two following rules at the beginning of every HTTP frontend: > > http-request reject if { method -m reg [^A-Z0-9] } > http-request set-uri %[url] > > - in version 2.0, disabling HTX processing will force the request to be > reprocessed by the internal HTTP/1 parser (but this is not compatible > with H2 servers nor FastCGI servers): > > no option http-use-htx > > - commenting out "alpn h2" advertisement on all "bind" lines in frontends, > and disabling H2 processing entirely by placing the following line in > the global section: > > tune.h2.max-concurrent-streams 0 > > - in versions 2.2 and above it is possible to refine filtering per frontend > by disabling "alpn h2" per bind line and by disabling HTTP/1 to HTTP/2 > upgrade by placing this option in the respective frontends: > > option disable-h2-upgrade > > > Many thanks to Tim for helping getting these issues resolved! > Willy > > -- James Brown Engineer