Compare commits
10 Commits
e799d1ddfe
...
29cd96f4b1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29cd96f4b1 | ||
|
|
3ce43447d9 | ||
|
|
9021ac06be | ||
|
|
0f0329de3e | ||
|
|
eb3e775bfa | ||
|
|
0614a9b80f | ||
|
|
82e7a81403 | ||
|
|
098fdacd23 | ||
|
|
c23bb006d9 | ||
|
|
b9c8f31338 |
52
CVE-2023-0056.patch
Normal file
52
CVE-2023-0056.patch
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
From 038a7e8aeb1c5b90c18c55d2bcfb3aaa476bce89 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>
|
||||||
|
(cherry picked from commit 2c681c6f30fb90adab4701e287ff7a7db669b2e7)
|
||||||
|
[cf: ctx adjustment]
|
||||||
|
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 f9daf8a7540..4611942a05c 100644
|
||||||
|
--- a/src/mux_h2.c
|
||||||
|
+++ b/src/mux_h2.c
|
||||||
|
@@ -4607,6 +4607,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;
|
||||||
|
+ }
|
||||||
|
/* Mark the end of message using EOM */
|
||||||
|
htx->flags |= HTX_FL_EOI; /* no more data are expected. Only EOM remains to add now */
|
||||||
|
if (!htx_add_endof(htx, HTX_BLK_EOM)) {
|
||||||
41
CVE-2023-0836.patch
Normal file
41
CVE-2023-0836.patch
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
From 2e6bf0a2722866ae0128a4392fa2375bd1f03ff8 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Youfu Zhang <zhangyoufu@gmail.com>
|
||||||
|
Date: Fri, 9 Dec 2022 19:15:48 +0800
|
||||||
|
Subject: [PATCH] BUG/MAJOR: fcgi: Fix uninitialized reserved bytes
|
||||||
|
|
||||||
|
The output buffer is not zero-initialized. If we don't clear reserved
|
||||||
|
bytes, fcgi requests sent to backend will leak sensitive data.
|
||||||
|
|
||||||
|
This patch must be backported as far as 2.2.
|
||||||
|
---
|
||||||
|
src/fcgi.c | 8 ++++++--
|
||||||
|
1 file changed, 6 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/fcgi.c b/src/fcgi.c
|
||||||
|
index dcf2db2..1d1a82b 100644
|
||||||
|
--- a/src/fcgi.c
|
||||||
|
+++ b/src/fcgi.c
|
||||||
|
@@ -47,7 +47,7 @@ int fcgi_encode_record_hdr(struct buffer *out, const struct fcgi_header *h)
|
||||||
|
out->area[len++] = ((h->len >> 8) & 0xff);
|
||||||
|
out->area[len++] = (h->len & 0xff);
|
||||||
|
out->area[len++] = h->padding;
|
||||||
|
- len++; /* rsv */
|
||||||
|
+ out->area[len++] = 0; /* rsv */
|
||||||
|
|
||||||
|
out->data = len;
|
||||||
|
return 1;
|
||||||
|
@@ -94,7 +94,11 @@ int fcgi_encode_begin_request(struct buffer *out, const struct fcgi_begin_reques
|
||||||
|
out->area[len++] = ((r->role >> 8) & 0xff);
|
||||||
|
out->area[len++] = (r->role & 0xff);
|
||||||
|
out->area[len++] = r->flags;
|
||||||
|
- len += 5; /* rsv */
|
||||||
|
+ out->area[len++] = 0; /* rsv */
|
||||||
|
+ out->area[len++] = 0;
|
||||||
|
+ out->area[len++] = 0;
|
||||||
|
+ out->area[len++] = 0;
|
||||||
|
+ out->area[len++] = 0;
|
||||||
|
|
||||||
|
out->data = len;
|
||||||
|
return 1;
|
||||||
|
--
|
||||||
|
1.7.10.4
|
||||||
145
CVE-2023-25725.patch
Normal file
145
CVE-2023-25725.patch
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
From a6c7ac9d51248a641f456906549120d3f6387049 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Willy Tarreau <w@1wt.eu>
|
||||||
|
Date: Thu, 9 Feb 2023 21:36:54 +0100
|
||||||
|
Subject: 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.
|
||||||
|
---
|
||||||
|
src/h1.c | 4 ++++
|
||||||
|
src/hpack-dec.c | 9 +++++++++
|
||||||
|
src/qpack-dec.c | 9 +++++++++
|
||||||
|
3 files changed, 22 insertions(+)
|
||||||
|
|
||||||
|
--- a/src/h1.c
|
||||||
|
+++ b/src/h1.c
|
||||||
|
@@ -706,6 +706,10 @@ int h1_headers_to_hdr_list(char *start,
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
--- a/src/hpack-dec.c
|
||||||
|
+++ b/src/hpack-dec.c
|
||||||
|
@@ -419,6 +419,15 @@ int hpack_decode_frame(struct hpack_dht
|
||||||
|
/* <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
|
||||||
276
CVE-2023-40225.patch
Normal file
276
CVE-2023-40225.patch
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
From e8ba5e106444fc78558f4ff26e9ce946f89216f4 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Willy Tarreau <w@1wt.eu>
|
||||||
|
Date: Wed, 9 Aug 2023 08:32:48 +0200
|
||||||
|
Subject: [PATCH] BUG/MAJOR: http: reject any empty content-length header value
|
||||||
|
|
||||||
|
Origin: https://github.com/FireBurn/haproxy/commit/e8ba5e106444fc78558f4ff26e9ce946f89216f4
|
||||||
|
|
||||||
|
The content-length header parser has its dedicated function, in order
|
||||||
|
to take extreme care about invalid, unparsable, or conflicting values.
|
||||||
|
But there's a corner case in it, by which it stops comparing values
|
||||||
|
when reaching the end of the header. This has for a side effect that
|
||||||
|
an empty value or a value that ends with a comma does not deserve
|
||||||
|
further analysis, and it acts as if the header was absent.
|
||||||
|
|
||||||
|
While this is not necessarily a problem for the value ending with a
|
||||||
|
comma as it will be cause a header folding and will disappear, it is a
|
||||||
|
problem for the first isolated empty header because this one will not
|
||||||
|
be recontructed when next ones are seen, and will be passed as-is to the
|
||||||
|
backend server. A vulnerable HTTP/1 server hosted behind haproxy that
|
||||||
|
would just use this first value as "0" and ignore the valid one would
|
||||||
|
then not be protected by haproxy and could be attacked this way, taking
|
||||||
|
the payload for an extra request.
|
||||||
|
|
||||||
|
In field the risk depends on the server. Most commonly used servers
|
||||||
|
already have safe content-length parsers, but users relying on haproxy
|
||||||
|
to protect a known-vulnerable server might be at risk (and the risk of
|
||||||
|
a bug even in a reputable server should never be dismissed).
|
||||||
|
|
||||||
|
A configuration-based work-around consists in adding the following rule
|
||||||
|
in the frontend, to explicitly reject requests featuring an empty
|
||||||
|
content-length header that would have not be folded into an existing
|
||||||
|
one:
|
||||||
|
|
||||||
|
http-request deny if { hdr_len(content-length) 0 }
|
||||||
|
|
||||||
|
The real fix consists in adjusting the parser so that it always expects a
|
||||||
|
value at the beginning of the header or after a comma. It will now reject
|
||||||
|
requests and responses having empty values anywhere in the C-L header.
|
||||||
|
|
||||||
|
This needs to be backported to all supported versions. Note that the
|
||||||
|
modification was made to functions h1_parse_cont_len_header() and
|
||||||
|
http_parse_cont_len_header(). Prior to 2.8 the latter was in
|
||||||
|
h2_parse_cont_len_header(). One day the two should be refused but the
|
||||||
|
former is also used by Lua.
|
||||||
|
|
||||||
|
The HTTP messaging reg-tests were completed to test these cases.
|
||||||
|
|
||||||
|
Thanks to Ben Kallus of Dartmouth College and Narf Industries for
|
||||||
|
reporting this! (this is in GH #2237).
|
||||||
|
|
||||||
|
(cherry picked from commit 6492f1f29d738457ea9f382aca54537f35f9d856)
|
||||||
|
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
|
||||||
|
(cherry picked from commit a32f99f6f991d123ea3e307bf8aa63220836d365)
|
||||||
|
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
|
||||||
|
(cherry picked from commit 65921ee12d88e9fb1fa9f6cd8198fd64b3a3f37f)
|
||||||
|
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
|
||||||
|
(cherry picked from commit d17c50010d591d1c070e1cb0567a06032d8869e9)
|
||||||
|
[wt: applied to h2_parse_cont_len_header() in src/h2.c instead]
|
||||||
|
Signed-off-by: Willy Tarreau <w@1wt.eu>
|
||||||
|
(cherry picked from commit ba9afd2774c03e434165475b537d0462801f49bb)
|
||||||
|
Signed-off-by: Willy Tarreau <w@1wt.eu>
|
||||||
|
---
|
||||||
|
reg-tests/http-messaging/h1_to_h1.vtc | 26 ++++++++++++
|
||||||
|
reg-tests/http-messaging/h2_to_h1.vtc | 60 +++++++++++++++++++++++++++
|
||||||
|
src/h1.c | 20 +++++++--
|
||||||
|
src/h2.c | 20 +++++++--
|
||||||
|
4 files changed, 120 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/reg-tests/http-messaging/h1_to_h1.vtc b/reg-tests/http-messaging/h1_to_h1.vtc
|
||||||
|
index 5b02f172433b..39708a246f6d 100644
|
||||||
|
--- a/reg-tests/http-messaging/h1_to_h1.vtc
|
||||||
|
+++ b/reg-tests/http-messaging/h1_to_h1.vtc
|
||||||
|
@@ -269,3 +269,29 @@ client c3h1 -connect ${h1_feh1_sock} {
|
||||||
|
# arrive here.
|
||||||
|
expect_close
|
||||||
|
} -run
|
||||||
|
+
|
||||||
|
+client c4h1 -connect ${h1_feh1_sock} {
|
||||||
|
+ # this request is invalid and advertises an invalid C-L ending with an
|
||||||
|
+ # empty value, which results in a stream error.
|
||||||
|
+ txreq \
|
||||||
|
+ -req "GET" \
|
||||||
|
+ -url "/test31.html" \
|
||||||
|
+ -hdr "content-length: 0," \
|
||||||
|
+ -hdr "connection: close"
|
||||||
|
+ rxresp
|
||||||
|
+ expect resp.status == 400
|
||||||
|
+ expect_close
|
||||||
|
+} -run
|
||||||
|
+
|
||||||
|
+client c5h1 -connect ${h1_feh1_sock} {
|
||||||
|
+ # this request is invalid and advertises an empty C-L, which results
|
||||||
|
+ # in a stream error.
|
||||||
|
+ txreq \
|
||||||
|
+ -req "GET" \
|
||||||
|
+ -url "/test41.html" \
|
||||||
|
+ -hdr "content-length:" \
|
||||||
|
+ -hdr "connection: close"
|
||||||
|
+ rxresp
|
||||||
|
+ expect resp.status == 400
|
||||||
|
+ expect_close
|
||||||
|
+} -run
|
||||||
|
diff --git a/reg-tests/http-messaging/h2_to_h1.vtc b/reg-tests/http-messaging/h2_to_h1.vtc
|
||||||
|
index 481aded12aee..3ed3751902e1 100644
|
||||||
|
--- a/reg-tests/http-messaging/h2_to_h1.vtc
|
||||||
|
+++ b/reg-tests/http-messaging/h2_to_h1.vtc
|
||||||
|
@@ -10,6 +10,8 @@ barrier b1 cond 2 -cyclic
|
||||||
|
barrier b2 cond 2 -cyclic
|
||||||
|
barrier b3 cond 2 -cyclic
|
||||||
|
barrier b4 cond 2 -cyclic
|
||||||
|
+barrier b5 cond 2 -cyclic
|
||||||
|
+barrier b6 cond 2 -cyclic
|
||||||
|
|
||||||
|
server s1 {
|
||||||
|
rxreq
|
||||||
|
@@ -31,6 +33,12 @@ server s1 {
|
||||||
|
|
||||||
|
barrier b4 sync
|
||||||
|
# the next request is never received
|
||||||
|
+
|
||||||
|
+ barrier b5 sync
|
||||||
|
+ # the next request is never received
|
||||||
|
+
|
||||||
|
+ barrier b6 sync
|
||||||
|
+ # the next request is never received
|
||||||
|
} -repeat 2 -start
|
||||||
|
|
||||||
|
haproxy h1 -conf {
|
||||||
|
@@ -115,6 +123,32 @@ client c1h2 -connect ${h1_feh2_sock} {
|
||||||
|
txdata -data "this is sent and ignored"
|
||||||
|
rxrst
|
||||||
|
} -run
|
||||||
|
+
|
||||||
|
+ # fifth request is invalid and advertises an invalid C-L ending with an
|
||||||
|
+ # empty value, which results in a stream error.
|
||||||
|
+ stream 9 {
|
||||||
|
+ barrier b5 sync
|
||||||
|
+ txreq \
|
||||||
|
+ -req "GET" \
|
||||||
|
+ -scheme "https" \
|
||||||
|
+ -url "/test5.html" \
|
||||||
|
+ -hdr "content-length" "0," \
|
||||||
|
+ -nostrend
|
||||||
|
+ rxrst
|
||||||
|
+ } -run
|
||||||
|
+
|
||||||
|
+ # sixth request is invalid and advertises an empty C-L, which results
|
||||||
|
+ # in a stream error.
|
||||||
|
+ stream 11 {
|
||||||
|
+ barrier b6 sync
|
||||||
|
+ txreq \
|
||||||
|
+ -req "GET" \
|
||||||
|
+ -scheme "https" \
|
||||||
|
+ -url "/test6.html" \
|
||||||
|
+ -hdr "content-length" "" \
|
||||||
|
+ -nostrend
|
||||||
|
+ rxrst
|
||||||
|
+ } -run
|
||||||
|
} -run
|
||||||
|
|
||||||
|
# HEAD requests : don't work well yet
|
||||||
|
@@ -257,4 +291,30 @@ client c3h2 -connect ${h1_feh2_sock} {
|
||||||
|
txdata -data "this is sent and ignored"
|
||||||
|
rxrst
|
||||||
|
} -run
|
||||||
|
+
|
||||||
|
+ # fifth request is invalid and advertises invalid C-L ending with an
|
||||||
|
+ # empty value, which results in a stream error.
|
||||||
|
+ stream 9 {
|
||||||
|
+ barrier b5 sync
|
||||||
|
+ txreq \
|
||||||
|
+ -req "POST" \
|
||||||
|
+ -scheme "https" \
|
||||||
|
+ -url "/test25.html" \
|
||||||
|
+ -hdr "content-length" "0," \
|
||||||
|
+ -nostrend
|
||||||
|
+ rxrst
|
||||||
|
+ } -run
|
||||||
|
+
|
||||||
|
+ # sixth request is invalid and advertises an empty C-L, which results
|
||||||
|
+ # in a stream error.
|
||||||
|
+ stream 11 {
|
||||||
|
+ barrier b6 sync
|
||||||
|
+ txreq \
|
||||||
|
+ -req "POST" \
|
||||||
|
+ -scheme "https" \
|
||||||
|
+ -url "/test26.html" \
|
||||||
|
+ -hdr "content-length" "" \
|
||||||
|
+ -nostrend
|
||||||
|
+ rxrst
|
||||||
|
+ } -run
|
||||||
|
} -run
|
||||||
|
diff --git a/src/h1.c b/src/h1.c
|
||||||
|
index 83912343d2c1..f351af8f7125 100644
|
||||||
|
--- a/src/h1.c
|
||||||
|
+++ b/src/h1.c
|
||||||
|
@@ -29,13 +29,20 @@ int h1_parse_cont_len_header(struct h1m *h1m, struct ist *value)
|
||||||
|
int not_first = !!(h1m->flags & H1_MF_CLEN);
|
||||||
|
struct ist word;
|
||||||
|
|
||||||
|
- word.ptr = value->ptr - 1; // -1 for next loop's pre-increment
|
||||||
|
+ word.ptr = value->ptr;
|
||||||
|
e = value->ptr + value->len;
|
||||||
|
|
||||||
|
- while (++word.ptr < e) {
|
||||||
|
+ while (1) {
|
||||||
|
+ if (word.ptr >= e) {
|
||||||
|
+ /* empty header or empty value */
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* skip leading delimiter and blanks */
|
||||||
|
- if (unlikely(HTTP_IS_LWS(*word.ptr)))
|
||||||
|
+ if (unlikely(HTTP_IS_LWS(*word.ptr))) {
|
||||||
|
+ word.ptr++;
|
||||||
|
continue;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
/* digits only now */
|
||||||
|
for (cl = 0, n = word.ptr; n < e; n++) {
|
||||||
|
@@ -74,6 +81,13 @@ int h1_parse_cont_len_header(struct h1m *h1m, struct ist *value)
|
||||||
|
h1m->flags |= H1_MF_CLEN;
|
||||||
|
h1m->curr_len = h1m->body_len = cl;
|
||||||
|
*value = word;
|
||||||
|
+
|
||||||
|
+ /* Now either n==e and we're done, or n points to the comma,
|
||||||
|
+ * and we skip it and continue.
|
||||||
|
+ */
|
||||||
|
+ if (n++ == e)
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
word.ptr = n;
|
||||||
|
}
|
||||||
|
/* here we've reached the end with a single value or a series of
|
||||||
|
diff --git a/src/h2.c b/src/h2.c
|
||||||
|
index bda4b05c7e22..b25aee10a51b 100644
|
||||||
|
--- a/src/h2.c
|
||||||
|
+++ b/src/h2.c
|
||||||
|
@@ -79,13 +79,20 @@ int h2_parse_cont_len_header(unsigned int *msgf, struct ist *value, unsigned lon
|
||||||
|
int not_first = !!(*msgf & H2_MSGF_BODY_CL);
|
||||||
|
struct ist word;
|
||||||
|
|
||||||
|
- word.ptr = value->ptr - 1; // -1 for next loop's pre-increment
|
||||||
|
+ word.ptr = value->ptr;
|
||||||
|
e = value->ptr + value->len;
|
||||||
|
|
||||||
|
- while (++word.ptr < e) {
|
||||||
|
+ while (1) {
|
||||||
|
+ if (word.ptr >= e) {
|
||||||
|
+ /* empty header or empty value */
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* skip leading delimiter and blanks */
|
||||||
|
- if (unlikely(HTTP_IS_LWS(*word.ptr)))
|
||||||
|
+ if (unlikely(HTTP_IS_LWS(*word.ptr))) {
|
||||||
|
+ word.ptr++;
|
||||||
|
continue;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
/* digits only now */
|
||||||
|
for (cl = 0, n = word.ptr; n < e; n++) {
|
||||||
|
@@ -124,6 +131,13 @@ int h2_parse_cont_len_header(unsigned int *msgf, struct ist *value, unsigned lon
|
||||||
|
*msgf |= H2_MSGF_BODY_CL;
|
||||||
|
*body_len = cl;
|
||||||
|
*value = word;
|
||||||
|
+
|
||||||
|
+ /* Now either n==e and we're done, or n points to the comma,
|
||||||
|
+ * and we skip it and continue.
|
||||||
|
+ */
|
||||||
|
+ if (n++ == e)
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
word.ptr = n;
|
||||||
|
}
|
||||||
|
/* here we've reached the end with a single value or a series of
|
||||||
107
CVE-2023-45539.patch
Normal file
107
CVE-2023-45539.patch
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
From 2eab6d354322932cfec2ed54de261e4347eca9a6 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Willy Tarreau <w@1wt.eu>
|
||||||
|
Date: Tue, 8 Aug 2023 16:17:22 +0200
|
||||||
|
Subject: [PATCH] BUG/MINOR: h1: do not accept '#' as part of the URI component
|
||||||
|
|
||||||
|
Seth Manesse and Paul Plasil reported that the "path" sample fetch
|
||||||
|
function incorrectly accepts '#' as part of the path component. This
|
||||||
|
can in some cases lead to misrouted requests for rules that would apply
|
||||||
|
on the suffix:
|
||||||
|
|
||||||
|
use_backend static if { path_end .png .jpg .gif .css .js }
|
||||||
|
|
||||||
|
Note that this behavior can be selectively configured using
|
||||||
|
"normalize-uri fragment-encode" and "normalize-uri fragment-strip".
|
||||||
|
|
||||||
|
The problem is that while the RFC says that this '#' must never be
|
||||||
|
emitted, as often it doesn't suggest how servers should handle it. A
|
||||||
|
diminishing number of servers still do accept it and trim it silently,
|
||||||
|
while others are rejecting it, as indicated in the conversation below
|
||||||
|
with other implementers:
|
||||||
|
|
||||||
|
https://lists.w3.org/Archives/Public/ietf-http-wg/2023JulSep/0070.html
|
||||||
|
|
||||||
|
Looking at logs from publicly exposed servers, such requests appear at
|
||||||
|
a rate of roughly 1 per million and only come from attacks or poorly
|
||||||
|
written web crawlers incorrectly following links found on various pages.
|
||||||
|
|
||||||
|
Thus it looks like the best solution to this problem is to simply reject
|
||||||
|
such ambiguous requests by default, and include this in the list of
|
||||||
|
controls that can be disabled using "option accept-invalid-http-request".
|
||||||
|
|
||||||
|
We're already rejecting URIs containing any control char anyway, so we
|
||||||
|
should also reject '#'.
|
||||||
|
|
||||||
|
In the H1 parser for the H1_MSG_RQURI state, there is an accelerated
|
||||||
|
parser for bytes 0x21..0x7e that has been tightened to 0x24..0x7e (it
|
||||||
|
should not impact perf since 0x21..0x23 are not supposed to appear in
|
||||||
|
a URI anyway). This way '#' falls through the fine-grained filter and
|
||||||
|
we can add the special case for it also conditionned by a check on the
|
||||||
|
proxy's option "accept-invalid-http-request", with no overhead for the
|
||||||
|
vast majority of valid URIs. Here this information is available through
|
||||||
|
h1m->err_pos that's set to -2 when the option is here (so we don't need
|
||||||
|
to change the API to expose the proxy). Example with a trivial GET
|
||||||
|
through netcat:
|
||||||
|
|
||||||
|
[08/Aug/2023:16:16:52.651] frontend layer1 (#2): invalid request
|
||||||
|
backend <NONE> (#-1), server <NONE> (#-1), event #0, src 127.0.0.1:50812
|
||||||
|
buffer starts at 0 (including 0 out), 16361 free,
|
||||||
|
len 23, wraps at 16336, error at position 7
|
||||||
|
H1 connection flags 0x00000000, H1 stream flags 0x00000810
|
||||||
|
H1 msg state MSG_RQURI(4), H1 msg flags 0x00001400
|
||||||
|
H1 chunk len 0 bytes, H1 body len 0 bytes :
|
||||||
|
|
||||||
|
00000 GET /aa#bb HTTP/1.0\r\n
|
||||||
|
00021 \r\n
|
||||||
|
|
||||||
|
This should be progressively backported to all stable versions along with
|
||||||
|
the following patch:
|
||||||
|
|
||||||
|
REGTESTS: http-rules: add accept-invalid-http-request for normalize-uri tests
|
||||||
|
|
||||||
|
Similar fixes for h2 and h3 will come in followup patches.
|
||||||
|
|
||||||
|
Thanks to Seth Manesse and Paul Plasil for reporting this problem with
|
||||||
|
detailed explanations.
|
||||||
|
---
|
||||||
|
src/h1.c | 15 +++++++++++----
|
||||||
|
1 file changed, 11 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/h1.c b/src/h1.c
|
||||||
|
index 88a54c4a593d..4e224f895422 100644
|
||||||
|
--- a/src/h1.c
|
||||||
|
+++ b/src/h1.c
|
||||||
|
@@ -551,13 +551,13 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
||||||
|
case H1_MSG_RQURI:
|
||||||
|
http_msg_rquri:
|
||||||
|
#ifdef HA_UNALIGNED_LE
|
||||||
|
- /* speedup: skip bytes not between 0x21 and 0x7e inclusive */
|
||||||
|
+ /* speedup: skip bytes not between 0x24 and 0x7e inclusive */
|
||||||
|
while (ptr <= end - sizeof(int)) {
|
||||||
|
- int x = *(int *)ptr - 0x21212121;
|
||||||
|
+ int x = *(int *)ptr - 0x24242424;
|
||||||
|
if (x & 0x80808080)
|
||||||
|
break;
|
||||||
|
|
||||||
|
- x -= 0x5e5e5e5e;
|
||||||
|
+ x -= 0x5b5b5b5b;
|
||||||
|
if (!(x & 0x80808080))
|
||||||
|
break;
|
||||||
|
|
||||||
|
@@ -569,8 +569,15 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
||||||
|
goto http_msg_ood;
|
||||||
|
}
|
||||||
|
http_msg_rquri2:
|
||||||
|
- if (likely((unsigned char)(*ptr - 33) <= 93)) /* 33 to 126 included */
|
||||||
|
+ if (likely((unsigned char)(*ptr - 33) <= 93)) { /* 33 to 126 included */
|
||||||
|
+ if (*ptr == '#') {
|
||||||
|
+ if (h1m->err_pos < -1) /* PR_O2_REQBUG_OK not set */
|
||||||
|
+ goto invalid_char;
|
||||||
|
+ if (h1m->err_pos == -1) /* PR_O2_REQBUG_OK set: just log */
|
||||||
|
+ h1m->err_pos = ptr - start + skip;
|
||||||
|
+ }
|
||||||
|
EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rquri2, http_msg_ood, state, H1_MSG_RQURI);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if (likely(HTTP_IS_SPHT(*ptr))) {
|
||||||
|
sl.rq.u.len = ptr - sl.rq.u.ptr;
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
From 0013757152ef499539377943e556a7f96acf605c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Aurelien DARRAGON <adarragon@haproxy.com>
|
||||||
|
Date: Tue, 26 Mar 2024 10:42:48 +0100
|
||||||
|
Subject: [PATCH] BUG/MINOR: server: 'source' interface ignored from
|
||||||
|
'default-server' directive
|
||||||
|
|
||||||
|
Sebastien Gross reported that 'interface' keyword ('source' subargument)
|
||||||
|
is silently ignored when used from 'default-server' directive despite the
|
||||||
|
documentation implicitly stating that the keyword should be supported
|
||||||
|
there.
|
||||||
|
|
||||||
|
When support for 'source' keyword was added to 'default-server' directive
|
||||||
|
in dba97077 ("MINOR: server: Make 'default-server' support 'source'
|
||||||
|
keyword."), we properly duplicated the conn iface_name from the default-
|
||||||
|
server but we forgot to copy the conn iface_len which must be set as well
|
||||||
|
since it is used as setsockopt()'s 'optlen' argument in
|
||||||
|
tcp_connect_server().
|
||||||
|
|
||||||
|
It should be backported to all stable versions.
|
||||||
|
|
||||||
|
(cherry picked from commit bd98db50785b6cef946d38715b48f72e7ca73a59)
|
||||||
|
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
|
||||||
|
(cherry picked from commit ada8c0e37df568c58e3a328c171d6f27bcfbe652)
|
||||||
|
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
|
||||||
|
(cherry picked from commit 92b935e99aef7573e658ff53858619bca737aeaf)
|
||||||
|
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
|
||||||
|
(cherry picked from commit 8acf8e51f8a0cbeea778f2c392dad7a7e068a075)
|
||||||
|
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
|
||||||
|
(cherry picked from commit b7ff822695e72695dfd753be23ff11fc97696fb3)
|
||||||
|
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
|
||||||
|
(cherry picked from commit e34253add4973de6082795706cd105f2f5d8247e)
|
||||||
|
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
|
||||||
|
|
||||||
|
Conflict: NA
|
||||||
|
Reference: https://git.haproxy.org/?p=haproxy-2.2.git;a=commit;h=0013757152ef499539377943e556a7f96acf605c
|
||||||
|
---
|
||||||
|
src/server.c | 4 +++-
|
||||||
|
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/src/server.c b/src/server.c
|
||||||
|
index 2b9340734cff0..d011d397aecff 100644
|
||||||
|
--- a/src/server.c
|
||||||
|
+++ b/src/server.c
|
||||||
|
@@ -1539,8 +1539,10 @@ static void srv_conn_src_cpy(struct server *srv, const struct server *src)
|
||||||
|
srv->conn_src.bind_hdr_occ = src->conn_src.bind_hdr_occ;
|
||||||
|
srv->conn_src.tproxy_addr = src->conn_src.tproxy_addr;
|
||||||
|
#endif
|
||||||
|
- if (src->conn_src.iface_name != NULL)
|
||||||
|
+ if (src->conn_src.iface_name != NULL) {
|
||||||
|
srv->conn_src.iface_name = strdup(src->conn_src.iface_name);
|
||||||
|
+ srv->conn_src.iface_len = src->conn_src.iface_len;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
27
haproxy.spec
27
haproxy.spec
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
Name: haproxy
|
Name: haproxy
|
||||||
Version: 2.2.16
|
Version: 2.2.16
|
||||||
Release: 3
|
Release: 8
|
||||||
Summary: The Reliable, High Performance TCP/HTTP Load Balancer
|
Summary: The Reliable, High Performance TCP/HTTP Load Balancer
|
||||||
|
|
||||||
License: GPLv2+
|
License: GPLv2+
|
||||||
@ -18,6 +18,13 @@ Source4: %{name}.sysconfig
|
|||||||
|
|
||||||
Patch0001: CVE-2021-40346.patch
|
Patch0001: CVE-2021-40346.patch
|
||||||
Patch0002: CVE-2022-0711.patch
|
Patch0002: CVE-2022-0711.patch
|
||||||
|
Patch0003: CVE-2023-25725.patch
|
||||||
|
Patch0004: CVE-2023-0056.patch
|
||||||
|
Patch0005: CVE-2023-40225.patch
|
||||||
|
Patch0006: CVE-2023-0836.patch
|
||||||
|
# https://github.com/haproxy/haproxy/commit/2eab6d354322932cfec2ed54de261e4347eca9a6
|
||||||
|
Patch0007: CVE-2023-45539.patch
|
||||||
|
Patch0008: backport-BUG-MINOR-server-source-interface-ignored-from-defau.patch
|
||||||
|
|
||||||
BuildRequires: gcc lua-devel pcre-devel zlib-devel openssl-devel systemd-devel systemd-units libatomic
|
BuildRequires: gcc lua-devel pcre-devel zlib-devel openssl-devel systemd-devel systemd-units libatomic
|
||||||
Requires: %{name}-help = %{version}-%{release}
|
Requires: %{name}-help = %{version}-%{release}
|
||||||
@ -125,6 +132,24 @@ exit 0
|
|||||||
%{_mandir}/man1/*
|
%{_mandir}/man1/*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Mon Jun 24 2024 xinghe <xinghe2@h-partners.com> - 2.2.16-8
|
||||||
|
- Type:bugfix
|
||||||
|
- CVE:NA
|
||||||
|
- SUG:NA
|
||||||
|
- DESC:server: 'source' interface ignored from 'default-server' directive
|
||||||
|
|
||||||
|
* Wed Dec 06 2023 yaoxin <yao_xin001@hoperun.com> - 2.2.16-7
|
||||||
|
- Fix CVE-2023-45539
|
||||||
|
|
||||||
|
* Fri Dec 1 2023 liningjie <liningjie@xfusion.com> - 2.2.16-6
|
||||||
|
- Fix CVE-2023-0836
|
||||||
|
|
||||||
|
* Mon Aug 21 2023 wangkai <wang_kai001@hoperun.com> - 2.2.16-5
|
||||||
|
- Fix CVE-2023-40225
|
||||||
|
|
||||||
|
* Sat Feb 25 2023 yaoxin <yaoxin30@h-partners.com> - 2.2.16-4
|
||||||
|
- Fix CVE-2023-25725 and CVE-2023-0056
|
||||||
|
|
||||||
* Fri Mar 11 2022 yaoxin <yaoxin30@huawei.com> - 2.2.16-3
|
* Fri Mar 11 2022 yaoxin <yaoxin30@huawei.com> - 2.2.16-3
|
||||||
- Fix CVE-2022-0711
|
- Fix CVE-2022-0711
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user