parent
99f94569de
commit
760348d361
@ -1 +1 @@
|
|||||||
SOURCES/haproxy-2.4.17.tar.gz
|
SOURCES/haproxy-2.4.22.tar.gz
|
||||||
|
@ -1 +1 @@
|
|||||||
28a0b8de9a6a4095406d190b83a024a11d7aedf6 SOURCES/haproxy-2.4.17.tar.gz
|
d0654cbab48039d998fca2459ce9251c6dbf2ae8 SOURCES/haproxy-2.4.22.tar.gz
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
From 2c681c6f30fb90adab4701e287ff7a7db669b2e7 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Christopher Faulet <cfaulet@haproxy.com>
|
|
||||||
Date: Thu, 22 Dec 2022 09:47:01 +0100
|
|
||||||
Subject: [PATCH] BUG/MEDIUM: mux-h2: Refuse interim responses with end-stream
|
|
||||||
flag set
|
|
||||||
|
|
||||||
As state in RFC9113#8.1, HEADERS frame with the ES flag set that carries an
|
|
||||||
informational status code is malformed. However, there is no test on this
|
|
||||||
condition.
|
|
||||||
|
|
||||||
On 2.4 and higher, it is hard to predict consequences of this bug because
|
|
||||||
end of the message is only reported with a flag. But on 2.2 and lower, it
|
|
||||||
leads to a crash because there is an unexpected extra EOM block at the end
|
|
||||||
of an interim response.
|
|
||||||
|
|
||||||
Now, when a ES flag is detected on a HEADERS frame for an interim message, a
|
|
||||||
stream error is sent (RST_STREAM/PROTOCOL_ERROR).
|
|
||||||
|
|
||||||
This patch should solve the issue #1972. It should be backported as far as
|
|
||||||
2.0.
|
|
||||||
|
|
||||||
(cherry picked from commit 827a6299e6995c5c3ba620d8b7cbacdaef67f2c4)
|
|
||||||
Signed-off-by: Willy Tarreau <w@1wt.eu>
|
|
||||||
(cherry picked from commit ebfae006c6b5de1d1fe0cdd51847ec1e39d5cf59)
|
|
||||||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
|
|
||||||
(cherry picked from commit 84f5cba24f59b1c8339bb38323fcb01f434ba8e5)
|
|
||||||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
|
|
||||||
(cherry picked from commit f5748a98c34bc889cae9386ca4f7073ab3f4c6b1)
|
|
||||||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
|
|
||||||
---
|
|
||||||
src/mux_h2.c | 5 +++++
|
|
||||||
1 file changed, 5 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/src/mux_h2.c b/src/mux_h2.c
|
|
||||||
index 7d23e5abd..7dbbfefec 100644
|
|
||||||
--- a/src/mux_h2.c
|
|
||||||
+++ b/src/mux_h2.c
|
|
||||||
@@ -4942,6 +4942,11 @@ static int h2c_decode_headers(struct h2c *h2c, struct buffer *rxbuf, uint32_t *f
|
|
||||||
*flags |= H2_SF_HEADERS_RCVD;
|
|
||||||
|
|
||||||
if (h2c->dff & H2_F_HEADERS_END_STREAM) {
|
|
||||||
+ if (msgf & H2_MSGF_RSP_1XX) {
|
|
||||||
+ /* RFC9113#8.1 : HEADERS frame with the ES flag set that carries an informational status code is malformed */
|
|
||||||
+ TRACE_STATE("invalid interim response with ES flag!", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_H2C_ERR|H2_EV_PROTO_ERR, h2c->conn);
|
|
||||||
+ goto fail;
|
|
||||||
+ }
|
|
||||||
/* no more data are expected for this message */
|
|
||||||
htx->flags |= HTX_FL_EOM;
|
|
||||||
}
|
|
||||||
--
|
|
||||||
2.37.3
|
|
||||||
|
|
@ -1,164 +0,0 @@
|
|||||||
From 486cd730485c8a119ef65b3f792134b56e7941b4 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Willy Tarreau <w@1wt.eu>
|
|
||||||
Date: Thu, 9 Feb 2023 21:36:54 +0100
|
|
||||||
Subject: [PATCH] BUG/CRITICAL: http: properly reject empty http header field
|
|
||||||
names
|
|
||||||
|
|
||||||
The HTTP header parsers surprizingly accepts empty header field names,
|
|
||||||
and this is a leftover from the original code that was agnostic to this.
|
|
||||||
|
|
||||||
When muxes were introduced, for H2 first, the HPACK decompressor needed
|
|
||||||
to feed headers lists, and since empty header names were strictly
|
|
||||||
forbidden by the protocol, the lists of headers were purposely designed
|
|
||||||
to be terminated by an empty header field name (a principle that is
|
|
||||||
similar to H1's empty line termination). This principle was preserved
|
|
||||||
and generalized to other protocols migrated to muxes (H1/FCGI/H3 etc)
|
|
||||||
without anyone ever noticing that the H1 parser was still able to deliver
|
|
||||||
empty header field names to this list. In addition to this it turns out
|
|
||||||
that the HPACK decompressor, despite a comment in the code, may
|
|
||||||
successfully decompress an empty header field name, and this mistake
|
|
||||||
was propagated to the QPACK decompressor as well.
|
|
||||||
|
|
||||||
The impact is that an empty header field name may be used to truncate
|
|
||||||
the list of headers and thus make some headers disappear. While for
|
|
||||||
H2/H3 the impact is limited as haproxy sees a request with missing
|
|
||||||
headers, and headers are not used to delimit messages, in the case of
|
|
||||||
HTTP/1, the impact is significant because the presence (and sometimes
|
|
||||||
contents) of certain sensitive headers is detected during the parsing.
|
|
||||||
Thus, some of these headers may be seen, marked as present, their value
|
|
||||||
extracted, but never delivered to upper layers and obviously not
|
|
||||||
forwarded to the other side either. This can have for consequence that
|
|
||||||
certain important header fields such as Connection, Upgrade, Host,
|
|
||||||
Content-length, Transfer-Encoding etc are possibly seen as different
|
|
||||||
between what haproxy uses to parse/forward/route and what is observed
|
|
||||||
in http-request rules and of course, forwarded. One direct consequence
|
|
||||||
is that it is possible to exploit this property in HTTP/1 to make
|
|
||||||
affected versions of haproxy forward more data than is advertised on
|
|
||||||
the other side, and bypass some access controls or routing rules by
|
|
||||||
crafting extraneous requests. Note, however, that responses to such
|
|
||||||
requests will normally not be passed back to the client, but this can
|
|
||||||
still cause some harm.
|
|
||||||
|
|
||||||
This specific risk can be mostly worked around in configuration using
|
|
||||||
the following rule that will rely on the bug's impact to precisely
|
|
||||||
detect the inconsistency between the known body size and the one
|
|
||||||
expected to be advertised to the server (the rule works from 2.0 to
|
|
||||||
2.8-dev):
|
|
||||||
|
|
||||||
http-request deny if { fc_http_major 1 } !{ req.body_size 0 } !{ req.hdr(content-length) -m found } !{ req.hdr(transfer-encoding) -m found } !{ method CONNECT }
|
|
||||||
|
|
||||||
This will exclusively block such carefully crafted requests delivered
|
|
||||||
over HTTP/1. HTTP/2 and HTTP/3 do not need content-length, and a body
|
|
||||||
that arrives without being announced with a content-length will be
|
|
||||||
forwarded using transfer-encoding, hence will not cause discrepancies.
|
|
||||||
In HAProxy 2.0 in legacy mode ("no option http-use-htx"), this rule will
|
|
||||||
simply have no effect but will not cause trouble either.
|
|
||||||
|
|
||||||
A clean solution would consist in modifying the loops iterating over
|
|
||||||
these headers lists to check the header name's pointer instead of its
|
|
||||||
length (since both are zero at the end of the list), but this requires
|
|
||||||
to touch tens of places and it's very easy to miss one. Functions such
|
|
||||||
as htx_add_header(), htx_add_trailer(), htx_add_all_headers() would be
|
|
||||||
good starting points for such a possible future change.
|
|
||||||
|
|
||||||
Instead the current fix focuses on blocking empty headers where they
|
|
||||||
are first inserted, hence in the H1/HPACK/QPACK decoders. One benefit
|
|
||||||
of the current solution (for H1) is that it allows "show errors" to
|
|
||||||
report a precise diagnostic when facing such invalid HTTP/1 requests,
|
|
||||||
with the exact location of the problem and the originating address:
|
|
||||||
|
|
||||||
$ printf "GET / HTTP/1.1\r\nHost: localhost\r\n:empty header\r\n\r\n" | nc 0 8001
|
|
||||||
HTTP/1.1 400 Bad request
|
|
||||||
Content-length: 90
|
|
||||||
Cache-Control: no-cache
|
|
||||||
Connection: close
|
|
||||||
Content-Type: text/html
|
|
||||||
|
|
||||||
<html><body><h1>400 Bad request</h1>
|
|
||||||
Your browser sent an invalid request.
|
|
||||||
</body></html>
|
|
||||||
|
|
||||||
$ socat /var/run/haproxy.stat <<< "show errors"
|
|
||||||
Total events captured on [10/Feb/2023:16:29:37.530] : 1
|
|
||||||
|
|
||||||
[10/Feb/2023:16:29:34.155] frontend decrypt (#2): invalid request
|
|
||||||
backend <NONE> (#-1), server <NONE> (#-1), event #0, src 127.0.0.1:31092
|
|
||||||
buffer starts at 0 (including 0 out), 16334 free,
|
|
||||||
len 50, wraps at 16336, error at position 33
|
|
||||||
H1 connection flags 0x00000000, H1 stream flags 0x00000810
|
|
||||||
H1 msg state MSG_HDR_NAME(17), H1 msg flags 0x00001410
|
|
||||||
H1 chunk len 0 bytes, H1 body len 0 bytes :
|
|
||||||
|
|
||||||
00000 GET / HTTP/1.1\r\n
|
|
||||||
00016 Host: localhost\r\n
|
|
||||||
00033 :empty header\r\n
|
|
||||||
00048 \r\n
|
|
||||||
|
|
||||||
I want to address sincere and warm thanks for their great work to the
|
|
||||||
team composed of the following security researchers who found the issue
|
|
||||||
together and reported it: Bahruz Jabiyev, Anthony Gavazzi, and Engin
|
|
||||||
Kirda from Northeastern University, Kaan Onarlioglu from Akamai
|
|
||||||
Technologies, Adi Peleg and Harvey Tuch from Google. And kudos to Amaury
|
|
||||||
Denoyelle from HAProxy Technologies for spotting that the HPACK and
|
|
||||||
QPACK decoders would let this pass despite the comment explicitly
|
|
||||||
saying otherwise.
|
|
||||||
|
|
||||||
This fix must be backported as far as 2.0. The QPACK changes can be
|
|
||||||
dropped before 2.6. In 2.0 there is also the equivalent code for legacy
|
|
||||||
mode, which doesn't suffer from the list truncation, but it would better
|
|
||||||
be fixed regardless.
|
|
||||||
|
|
||||||
CVE-2023-25725 was assigned to this issue.
|
|
||||||
|
|
||||||
(cherry picked from commit a8598a2eb11b6c989e81f0dbf10be361782e8d32)
|
|
||||||
Signed-off-by: Willy Tarreau <w@1wt.eu>
|
|
||||||
(cherry picked from commit a0e561ad7f29ed50c473f5a9da664267b60d1112)
|
|
||||||
Signed-off-by: Willy Tarreau <w@1wt.eu>
|
|
||||||
(cherry picked from commit 73be199c4f5f1ed468161a4c5e10ca77cd5989d8)
|
|
||||||
[wt: dropped QPACK changes for 2.5]
|
|
||||||
Signed-off-by: Willy Tarreau <w@1wt.eu>
|
|
||||||
(cherry picked from commit f8b2b88aeae15dc3b261cd3749277ae75caf9db8)
|
|
||||||
Signed-off-by: Willy Tarreau <w@1wt.eu>
|
|
||||||
---
|
|
||||||
src/h1.c | 4 ++++
|
|
||||||
src/hpack-dec.c | 9 +++++++++
|
|
||||||
2 files changed, 13 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/src/h1.c b/src/h1.c
|
|
||||||
index 4c2e234c5..73de48be0 100644
|
|
||||||
--- a/src/h1.c
|
|
||||||
+++ b/src/h1.c
|
|
||||||
@@ -750,6 +750,10 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
|
||||||
|
|
||||||
if (likely(*ptr == ':')) {
|
|
||||||
col = ptr - start;
|
|
||||||
+ if (col <= sol) {
|
|
||||||
+ state = H1_MSG_HDR_NAME;
|
|
||||||
+ goto http_msg_invalid;
|
|
||||||
+ }
|
|
||||||
EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_l1_sp, http_msg_ood, state, H1_MSG_HDR_L1_SP);
|
|
||||||
}
|
|
||||||
|
|
||||||
diff --git a/src/hpack-dec.c b/src/hpack-dec.c
|
|
||||||
index 04f3d9ffa..ed39007d1 100644
|
|
||||||
--- a/src/hpack-dec.c
|
|
||||||
+++ b/src/hpack-dec.c
|
|
||||||
@@ -420,6 +420,15 @@ int hpack_decode_frame(struct hpack_dht *dht, const uint8_t *raw, uint32_t len,
|
|
||||||
/* <name> and <value> are correctly filled here */
|
|
||||||
}
|
|
||||||
|
|
||||||
+ /* We must not accept empty header names (forbidden by the spec and used
|
|
||||||
+ * as a list termination).
|
|
||||||
+ */
|
|
||||||
+ if (!name.len) {
|
|
||||||
+ hpack_debug_printf("##ERR@%d##\n", __LINE__);
|
|
||||||
+ ret = -HPACK_ERR_INVALID_ARGUMENT;
|
|
||||||
+ goto leave;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
/* here's what we have here :
|
|
||||||
* - name.len > 0
|
|
||||||
* - value is filled with either const data or data allocated from tmp
|
|
||||||
--
|
|
||||||
2.37.3
|
|
||||||
|
|
Loading…
Reference in new issue