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
|
||||
Version: 2.2.16
|
||||
Release: 3
|
||||
Release: 8
|
||||
Summary: The Reliable, High Performance TCP/HTTP Load Balancer
|
||||
|
||||
License: GPLv2+
|
||||
@ -18,6 +18,13 @@ Source4: %{name}.sysconfig
|
||||
|
||||
Patch0001: CVE-2021-40346.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
|
||||
Requires: %{name}-help = %{version}-%{release}
|
||||
@ -125,6 +132,24 @@ exit 0
|
||||
%{_mandir}/man1/*
|
||||
|
||||
%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
|
||||
- Fix CVE-2022-0711
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user