diff options
-rw-r--r-- | debian/changelog | 17 | ||||
-rw-r--r-- | debian/patches/0052-CVE-2023-27522-HTTP-Response-Smuggling-mod_proxy_uws.patch | 120 | ||||
-rw-r--r-- | debian/patches/0053-CVE-2023-25690-1.patch | 170 | ||||
-rw-r--r-- | debian/patches/0054-CVE-2023-25690-2.patch | 35 | ||||
-rw-r--r-- | debian/patches/0055-CVE-2023-25690-Regression-1.patch | 131 | ||||
-rw-r--r-- | debian/patches/0056-CVE-2023-25690-Regression-2.patch | 138 | ||||
-rw-r--r-- | debian/patches/0057-CVE-2023-25690-Regression-3.patch | 24 | ||||
-rw-r--r-- | debian/patches/series | 8 | ||||
-rw-r--r-- | debian/perl-framework/t/modules/buffer.t | 9 | ||||
-rw-r--r-- | debian/salsa-ci.yml | 13 | ||||
-rw-r--r-- | debian/tests/CVE-2023-25690 | 110 | ||||
-rw-r--r-- | debian/tests/control | 8 | ||||
-rw-r--r-- | debian/tests/uwsgi | 142 |
13 files changed, 917 insertions, 8 deletions
diff --git a/debian/changelog b/debian/changelog index c5cbe51..ee0857b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,20 @@ +apache2 (2.4.38-3+deb10u10) buster-security; urgency=medium + + * Non-maintainer upload by the LTS Team. + * CVE-2023-27522: HTTP Response Smuggling in mod_proxy_uwsgi + (Closes: #1032476) + * CVE-2023-25690: Some mod_proxy configurations allow a HTTP + Request Smuggling attack. Configurations are affected + when mod_proxy is enabled along with some form of RewriteRule + or ProxyPassMatch in which a non-specific pattern matches + some portion of the user-supplied request-target (URL) + data and is then re-inserted into the proxied request-target + using variable substitution. (Closes: #1032476) + * Backport perl-framework testsuite from sid + * Backport regression fix for CVE-2023-25690 + + -- Bastien Roucariès <rouca@debian.org> Fri, 21 Apr 2023 22:01:00 +0000 + apache2 (2.4.38-3+deb10u9) buster-security; urgency=medium * Non-maintainer upload by the LTS Team. diff --git a/debian/patches/0052-CVE-2023-27522-HTTP-Response-Smuggling-mod_proxy_uws.patch b/debian/patches/0052-CVE-2023-27522-HTTP-Response-Smuggling-mod_proxy_uws.patch new file mode 100644 index 0000000..f39fa72 --- /dev/null +++ b/debian/patches/0052-CVE-2023-27522-HTTP-Response-Smuggling-mod_proxy_uws.patch @@ -0,0 +1,120 @@ +From: Eric Covener <covener@apache.org> +Date: Sun, 5 Mar 2023 20:22:52 +0000 +Subject: CVE-2023-27522: HTTP Response Smuggling mod_proxy_uwsgi + +HTTP Response Smuggling vulnerability in Apache HTTP Server via mod_proxy_uwsgi. +This issue affects Apache HTTP Server: from 2.4.30 through 2.4.55. +Special characters in the origin response header can truncate/split the response forwarded to the client. + +mod_proxy_uwsgi: Stricter backend HTTP response parsing/validation + +Reviewed By: ylavic, covener, gbechis, rpluem + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1908094 13f79535-47bb-0310-9956-ffa450edef68 +origin: https://github.com/apache/httpd/commit/d753ea76b5972a85349b68c31b59d04c60014f2d.patch +bug-debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1032476 +bug-debian-security: https://security-tracker.debian.org/tracker/CVE-2023-27522 +bug-cve: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-27522 +--- + .../proxy_uwsgi_response_validation.txt | 2 + + modules/proxy/mod_proxy_uwsgi.c | 49 +++++++++++++++------- + 2 files changed, 37 insertions(+), 14 deletions(-) + create mode 100644 changes-entries/proxy_uwsgi_response_validation.txt + +diff --git a/changes-entries/proxy_uwsgi_response_validation.txt b/changes-entries/proxy_uwsgi_response_validation.txt +new file mode 100644 +index 0000000..2cdb6c6 +--- /dev/null ++++ b/changes-entries/proxy_uwsgi_response_validation.txt +@@ -0,0 +1,2 @@ ++ *) mod_proxy_uwsgi: Stricter backend HTTP response parsing/validation. ++ [Yann Ylavic] +diff --git a/modules/proxy/mod_proxy_uwsgi.c b/modules/proxy/mod_proxy_uwsgi.c +index ebe16e8..9ba10b9 100644 +--- a/modules/proxy/mod_proxy_uwsgi.c ++++ b/modules/proxy/mod_proxy_uwsgi.c +@@ -303,18 +303,16 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend, + pass_bb = apr_brigade_create(r->pool, c->bucket_alloc); + + len = ap_getline(buffer, sizeof(buffer), rp, 1); +- + if (len <= 0) { +- /* oops */ ++ /* invalid or empty */ + return HTTP_INTERNAL_SERVER_ERROR; + } +- + backend->worker->s->read += len; +- +- if (len >= sizeof(buffer) - 1) { +- /* oops */ ++ if ((apr_size_t)len >= sizeof(buffer)) { ++ /* too long */ + return HTTP_INTERNAL_SERVER_ERROR; + } ++ + /* Position of http status code */ + if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) { + status_start = 9; +@@ -323,8 +321,8 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend, + status_start = 7; + } + else { +- /* oops */ +- return HTTP_INTERNAL_SERVER_ERROR; ++ /* not HTTP */ ++ return HTTP_BAD_GATEWAY; + } + status_end = status_start + 3; + +@@ -344,21 +342,44 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend, + } + r->status_line = apr_pstrdup(r->pool, &buffer[status_start]); + +- /* start parsing headers */ ++ /* parse headers */ + while ((len = ap_getline(buffer, sizeof(buffer), rp, 1)) > 0) { ++ if ((apr_size_t)len >= sizeof(buffer)) { ++ /* too long */ ++ len = -1; ++ break; ++ } + value = strchr(buffer, ':'); +- /* invalid header skip */ +- if (!value) +- continue; +- *value = '\0'; +- ++value; ++ if (!value) { ++ /* invalid header */ ++ len = -1; ++ break; ++ } ++ *value++ = '\0'; ++ if (*ap_scan_http_token(buffer)) { ++ /* invalid name */ ++ len = -1; ++ break; ++ } + while (apr_isspace(*value)) + ++value; + for (end = &value[strlen(value) - 1]; + end > value && apr_isspace(*end); --end) + *end = '\0'; ++ if (*ap_scan_http_field_content(value)) { ++ /* invalid value */ ++ len = -1; ++ break; ++ } + apr_table_add(r->headers_out, buffer, value); + } ++ if (len < 0) { ++ /* Reset headers, but not to NULL because things below the chain expect ++ * this to be non NULL e.g. the ap_content_length_filter. ++ */ ++ r->headers_out = apr_table_make(r->pool, 1); ++ return HTTP_BAD_GATEWAY; ++ } + + if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { + ap_set_content_type(r, apr_pstrdup(r->pool, buf)); diff --git a/debian/patches/0053-CVE-2023-25690-1.patch b/debian/patches/0053-CVE-2023-25690-1.patch new file mode 100644 index 0000000..a7370c7 --- /dev/null +++ b/debian/patches/0053-CVE-2023-25690-1.patch @@ -0,0 +1,170 @@ +From 8789f6bb926fa4c33b4231a8444340515c82bdff Mon Sep 17 00:00:00 2001 +From: Eric Covener <covener@apache.org> +Date: Sun, 5 Mar 2023 20:28:43 +0000 +Subject: [PATCH] [1/2] Fix CVE-2023-25690: HTTP Request Smuggling in mod_proxy* + + don't forward invalid query strings + + Submitted by: rpluem + +Reviewed By: covener, fielding, rpluem, gbechis +bug: https://httpd.apache.org/security/vulnerabilities_24.html#CVE-2023-25690 +bug-debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1032476 +bug-debian-security: https://security-tracker.debian.org/tracker/CVE-2023-25690 +origin: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1908096 13f79535-47bb-0310-9956-ffa450edef68 +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1908096 13f79535-47bb-0310-9956-ffa450edef68 +--- + modules/http2/mod_proxy_http2.c | 10 ++++++++++ + modules/mappers/mod_rewrite.c | 22 ++++++++++++++++++++++ + modules/proxy/mod_proxy_ajp.c | 10 ++++++++++ + modules/proxy/mod_proxy_balancer.c | 10 ++++++++++ + modules/proxy/mod_proxy_http.c | 10 ++++++++++ + modules/proxy/mod_proxy_wstunnel.c | 10 ++++++++++ + 6 files changed, 72 insertions(+) + +diff --git a/modules/http2/mod_proxy_http2.c b/modules/http2/mod_proxy_http2.c +index 3faf03472bb..aa299b937a5 100644 +--- a/modules/http2/mod_proxy_http2.c ++++ b/modules/http2/mod_proxy_http2.c +@@ -158,6 +158,16 @@ static int proxy_http2_canon(request_rec *r, char *url) + path = ap_proxy_canonenc(r->pool, url, (int)strlen(url), + enc_path, 0, r->proxyreq); + search = r->args; ++ if (search && *(ap_scan_vchar_obstext(search))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO() ++ "To be forwarded query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } + } + break; + case PROXYREQ_PROXY: +diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c +index 943996560e5..f6398f19386 100644 +--- a/modules/mappers/mod_rewrite.c ++++ b/modules/mappers/mod_rewrite.c +@@ -4729,6 +4729,17 @@ static int hook_uri2file(request_rec *r) + unsigned skip; + apr_size_t flen; + ++ if (r->args && *(ap_scan_vchar_obstext(r->args))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10410) ++ "Rewritten query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } ++ + if (ACTION_STATUS == rulestatus) { + int n = r->status; + +@@ -5013,6 +5024,17 @@ static int hook_fixup(request_rec *r) + if (rulestatus) { + unsigned skip; + ++ if (r->args && *(ap_scan_vchar_obstext(r->args))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10411) ++ "Rewritten query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } ++ + if (ACTION_STATUS == rulestatus) { + int n = r->status; + +diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c +index 1449acad733..e46bd903a36 100644 +--- a/modules/proxy/mod_proxy_ajp.c ++++ b/modules/proxy/mod_proxy_ajp.c +@@ -69,6 +69,16 @@ static int proxy_ajp_canon(request_rec *r, char *url) + path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, + r->proxyreq); + search = r->args; ++ if (search && *(ap_scan_vchar_obstext(search))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10406) ++ "To be forwarded query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } + } + if (path == NULL) + return HTTP_BAD_REQUEST; +diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c +index f6fb6345ae3..7f990084336 100644 +--- a/modules/proxy/mod_proxy_balancer.c ++++ b/modules/proxy/mod_proxy_balancer.c +@@ -106,6 +106,16 @@ static int proxy_balancer_canon(request_rec *r, char *url) + path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, + r->proxyreq); + search = r->args; ++ if (search && *(ap_scan_vchar_obstext(search))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10407) ++ "To be forwarded query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } + } + if (path == NULL) + return HTTP_BAD_REQUEST; +diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c +index ec4e7fb06b5..51d19a0a21b 100644 +--- a/modules/proxy/mod_proxy_http.c ++++ b/modules/proxy/mod_proxy_http.c +@@ -125,6 +125,16 @@ static int proxy_http_canon(request_rec *r, char *url) + path = ap_proxy_canonenc(r->pool, url, strlen(url), + enc_path, 0, r->proxyreq); + search = r->args; ++ if (search && *(ap_scan_vchar_obstext(search))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10408) ++ "To be forwarded query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } + } + break; + case PROXYREQ_PROXY: +diff --git a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunnel.c +index bcbba42f9a4..88f86a49dbb 100644 +--- a/modules/proxy/mod_proxy_wstunnel.c ++++ b/modules/proxy/mod_proxy_wstunnel.c +@@ -114,6 +114,16 @@ static int proxy_wstunnel_canon(request_rec *r, char *url) + path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, + r->proxyreq); + search = r->args; ++ if (search && *(ap_scan_vchar_obstext(search))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10409) ++ "To be forwarded query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } + } + if (path == NULL) + return HTTP_BAD_REQUEST; + diff --git a/debian/patches/0054-CVE-2023-25690-2.patch b/debian/patches/0054-CVE-2023-25690-2.patch new file mode 100644 index 0000000..978be78 --- /dev/null +++ b/debian/patches/0054-CVE-2023-25690-2.patch @@ -0,0 +1,35 @@ +From 8b93a6512f14f5f68887ddfe677e91233ed79fb0 Mon Sep 17 00:00:00 2001 +From: Ruediger Pluem <rpluem@apache.org> +Date: Mon, 6 Mar 2023 10:00:09 +0000 +Subject: [PATCH] [2/2] Fix CVE-2023-25690: HTTP Request Smuggling in mod_proxy* + +* modules/http2/mod_proxy_http2.c: Fix missing APLOGNO. + +Submitted by: jorton +Reviewed by: rpluem + +Note: mod_proxy_http2 is CTR on 2.4.x. + +bug: https://httpd.apache.org/security/vulnerabilities_24.html#CVE-2023-25690 +bug-debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1032476 +bug-debian-security: https://security-tracker.debian.org/tracker/CVE-2023-25690 +origin: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1908118 13f79535-47bb-0310-9956-ffa450edef68 +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1908118 13f79535-47bb-0310-9956-ffa450edef68 +--- + modules/http2/mod_proxy_http2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/modules/http2/mod_proxy_http2.c b/modules/http2/mod_proxy_http2.c +index aa299b937a5..2a9967e5d57 100644 +--- a/modules/http2/mod_proxy_http2.c ++++ b/modules/http2/mod_proxy_http2.c +@@ -163,7 +163,7 @@ static int proxy_http2_canon(request_rec *r, char *url) + * We have a raw control character or a ' ' in r->args. + * Correct encoding was missed. + */ +- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO() ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10412) + "To be forwarded query string contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + diff --git a/debian/patches/0055-CVE-2023-25690-Regression-1.patch b/debian/patches/0055-CVE-2023-25690-Regression-1.patch new file mode 100644 index 0000000..d57a71c --- /dev/null +++ b/debian/patches/0055-CVE-2023-25690-Regression-1.patch @@ -0,0 +1,131 @@ +From 815cf05bb2d506f44a35b65e93de393d5410c779 Mon Sep 17 00:00:00 2001 +From: Yann Ylavic <ylavic@apache.org> +Date: Tue, 1 Mar 2022 13:26:03 +0000 +Subject: [PATCH] mod_rewrite: URI-to-filename rewrites to transparently handle + proxy mappings. + +Since mod_rewrite works on r->filename and mod_proxy's mapping=servlet|decoded +sets its "proxy:" URL there at pre_translate_name stage (i.e. before +mod_rewrite's translate_name hook), users have to match the full proxy URL in +their RewriteRules to handle proxy mappings, which is not very friendly nor +consistent with how proxy non-mapping requests have to be matched. + +Let's use r->filename = r->uri in hook_uri2file() for pre_trans'ed reverse +proxy requests, and restore r->filename to its original value if the request +was finally DECLINED (like in hook_fixup). + +But if a proxy mapping gets rewritten to a non-proxy request, clear any +proxy specific r->proxyreq or r->handler so that processing continues +accordingly. + + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1898509 13f79535-47bb-0310-9956-ffa450edef68 +--- + changes-entries/rewrite_vs_proxy_mapping.txt | 2 ++ + modules/mappers/mod_rewrite.c | 38 +++++++++++++++----- + 2 files changed, 32 insertions(+), 8 deletions(-) + create mode 100644 changes-entries/rewrite_vs_proxy_mapping.txt + +Index: apache2/changes-entries/rewrite_vs_proxy_mapping.txt +=================================================================== +--- /dev/null ++++ apache2/changes-entries/rewrite_vs_proxy_mapping.txt +@@ -0,0 +1,2 @@ ++ *) mod_rewrite: Make URI-to-filename rewrites work transparently with ++ proxy early mappings (mapping=servlet/decoded). [Yann Ylavic] +\ No newline at end of file +Index: apache2/modules/mappers/mod_rewrite.c +=================================================================== +--- apache2.orig/modules/mappers/mod_rewrite.c ++++ apache2/modules/mappers/mod_rewrite.c +@@ -4575,6 +4575,7 @@ static int hook_uri2file(request_rec *r) + unsigned int port; + int rulestatus; + void *skipdata; ++ char *ofilename; + const char *oargs; + + /* +@@ -4628,7 +4629,10 @@ static int hook_uri2file(request_rec *r) + /* + * remember the original query string for later check, since we don't + * want to apply URL-escaping when no substitution has changed it. ++ * also, we'll restore original r->filename if we decline this ++ * request. + */ ++ ofilename = r->filename; + oargs = r->args; + + /* +@@ -4671,13 +4675,15 @@ static int hook_uri2file(request_rec *r) + apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URI, var); + + if (!(saved_rulestatus = apr_table_get(r->notes,"mod_rewrite_rewritten"))) { +- /* if filename was not initially set, +- * we start with the requested URI ++ /* If r->filename was not initially set or if it's a pre_trans reverse ++ * "proxy:" scheme, we start with the requested URI. + */ +- if (r->filename == NULL) { ++ if (r->filename == NULL || (r->proxyreq == PROXYREQ_REVERSE && ++ strncmp(r->filename, "proxy:", 6) == 0)) { + r->filename = apr_pstrdup(r->pool, r->uri); +- rewritelog((r, 2, NULL, "init rewrite engine with requested uri %s", +- r->filename)); ++ rewritelog((r, 2, NULL, "init rewrite engine with requested uri " ++ "%s. Original filename = %s", r->filename, ++ ((ofilename) ? ofilename : "n/a"))); + } + else { + rewritelog((r, 2, NULL, "init rewrite engine with passed filename " +@@ -4701,6 +4707,7 @@ static int hook_uri2file(request_rec *r) + if (rulestatus) { + unsigned skip; + apr_size_t flen; ++ int to_proxyreq; + + if (r->args && *(ap_scan_vchar_obstext(r->args))) { + /* +@@ -4721,7 +4728,19 @@ static int hook_uri2file(request_rec *r) + } + + flen = r->filename ? strlen(r->filename) : 0; +- if (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0) { ++ to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0); ++ ++ /* If a pre_trans reverse "proxy:" filename gets rewritten to ++ * a non-proxy one this is not a proxy request anymore. ++ */ ++ if (r->proxyreq == PROXYREQ_REVERSE && !to_proxyreq) { ++ if (r->handler && strcmp(r->handler, "proxy-server") == 0) { ++ r->handler = NULL; ++ } ++ r->proxyreq = PROXYREQ_NONE; ++ } ++ ++ if (to_proxyreq) { + /* it should be go on as an internal proxy request */ + + /* check if the proxy module is enabled, so +@@ -4888,7 +4907,9 @@ static int hook_uri2file(request_rec *r) + } + } + else { +- rewritelog((r, 1, NULL, "pass through %s", r->filename)); ++ rewritelog((r, 1, NULL, "pass through %s, filename %s", ++ r->filename, ((ofilename) ? ofilename : "n/a"))); ++ r->filename = ofilename; + return DECLINED; + } + } +@@ -5234,7 +5255,8 @@ static int hook_fixup(request_rec *r) + } + } + else { +- rewritelog((r, 1, dconf->directory, "pass through %s", r->filename)); ++ rewritelog((r, 1, dconf->directory, "pass through %s, filename %s", ++ r->filename, ((ofilename) ? ofilename : "n/a"))); + r->filename = ofilename; + return DECLINED; + } diff --git a/debian/patches/0056-CVE-2023-25690-Regression-2.patch b/debian/patches/0056-CVE-2023-25690-Regression-2.patch new file mode 100644 index 0000000..55eaa6b --- /dev/null +++ b/debian/patches/0056-CVE-2023-25690-Regression-2.patch @@ -0,0 +1,138 @@ +From 07b802c934b841d376733a3e2ecfa55f6b0ee994 Mon Sep 17 00:00:00 2001 +From: Eric Covener <covener@apache.org> +Date: Sat, 11 Mar 2023 20:57:52 +0000 +Subject: [PATCH] allow decoded chars when they will be escaped + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1908296 13f79535-47bb-0310-9956-ffa450edef68 +--- + changes-entries/rewrite-escape.diff | 3 ++ + modules/mappers/mod_rewrite.c | 45 +++++++++++++++++------------ + 2 files changed, 30 insertions(+), 18 deletions(-) + create mode 100644 changes-entries/rewrite-escape.diff + +Index: apache2/changes-entries/rewrite-escape.diff +=================================================================== +--- /dev/null ++++ apache2/changes-entries/rewrite-escape.diff +@@ -0,0 +1,3 @@ ++ *) mod_rewrite: Re-allow some proxy and redirect substitutions flagged as ++ 403 errors in 2.4.56. [Eric Covener] ++ +Index: apache2/modules/mappers/mod_rewrite.c +=================================================================== +--- apache2.orig/modules/mappers/mod_rewrite.c ++++ apache2/modules/mappers/mod_rewrite.c +@@ -4705,14 +4705,19 @@ static int hook_uri2file(request_rec *r) + } + + if (rulestatus) { +- unsigned skip; +- apr_size_t flen; +- int to_proxyreq; +- +- if (r->args && *(ap_scan_vchar_obstext(r->args))) { ++ unsigned skip_absolute = is_absolute_uri(r->filename, NULL); ++ apr_size_t flen = r->filename ? strlen(r->filename) : 0; ++ int to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0); ++ int will_escape = (to_proxyreq || skip_absolute) ++ && (rulestatus != ACTION_NOESCAPE); ++ ++ if (r->args ++ && !will_escape ++ && *(ap_scan_vchar_obstext(r->args))) { + /* + * We have a raw control character or a ' ' in r->args. +- * Correct encoding was missed. ++ * Correct encoding was missed and we're not going to escape ++ * it before returning. + */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10410) + "Rewritten query string contains control " +@@ -4727,9 +4732,6 @@ static int hook_uri2file(request_rec *r) + return n; + } + +- flen = r->filename ? strlen(r->filename) : 0; +- to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0); +- + /* If a pre_trans reverse "proxy:" filename gets rewritten to + * a non-proxy one this is not a proxy request anymore. + */ +@@ -4782,15 +4784,15 @@ static int hook_uri2file(request_rec *r) + r->filename)); + return OK; + } +- else if ((skip = is_absolute_uri(r->filename, NULL)) > 0) { ++ else if (skip_absolute > 0) { + int n; + + /* it was finally rewritten to a remote URL */ + + if (rulestatus != ACTION_NOESCAPE) { + rewritelog((r, 1, NULL, "escaping %s for redirect", +- r->filename)); +- r->filename = escape_absolute_uri(r->pool, r->filename, skip); ++ r->filename)); ++ r->filename = escape_absolute_uri(r->pool, r->filename, skip_absolute); + } + + /* append the QUERY_STRING part */ +@@ -5016,9 +5018,17 @@ static int hook_fixup(request_rec *r) + */ + rulestatus = apply_rewrite_list(r, dconf->rewriterules, dconf->directory); + if (rulestatus) { +- unsigned skip; ++ unsigned skip_absolute = is_absolute_uri(r->filename, NULL); ++ int to_proxyreq = 0; ++ int will_escape = 0; + +- if (r->args && *(ap_scan_vchar_obstext(r->args))) { ++ l = strlen(r->filename); ++ to_proxyreq = l > 6 && strncmp(r->filename, "proxy:", 6) == 0; ++ will_escape = skip_absolute && (rulestatus != ACTION_NOESCAPE); ++ ++ if (r->args ++ && !will_escape ++ && *(ap_scan_vchar_obstext(r->args))) { + /* + * We have a raw control character or a ' ' in r->args. + * Correct encoding was missed. +@@ -5036,8 +5046,7 @@ static int hook_fixup(request_rec *r) + return n; + } + +- l = strlen(r->filename); +- if (l > 6 && strncmp(r->filename, "proxy:", 6) == 0) { ++ if (to_proxyreq) { + /* it should go on as an internal proxy request */ + + /* make sure the QUERY_STRING and +@@ -5061,7 +5070,7 @@ static int hook_fixup(request_rec *r) + "%s [OK]", r->filename)); + return OK; + } +- else if ((skip = is_absolute_uri(r->filename, NULL)) > 0) { ++ else if (skip_absolute > 0) { + /* it was finally rewritten to a remote URL */ + + /* because we are in a per-dir context +@@ -5070,7 +5079,7 @@ static int hook_fixup(request_rec *r) + */ + if (dconf->baseurl != NULL) { + /* skip 'scheme://' */ +- cp = r->filename + skip; ++ cp = r->filename + skip_absolute; + + if ((cp = ap_strchr(cp, '/')) != NULL && *(++cp)) { + rewritelog((r, 2, dconf->directory, +@@ -5114,8 +5123,8 @@ static int hook_fixup(request_rec *r) + /* now prepare the redirect... */ + if (rulestatus != ACTION_NOESCAPE) { + rewritelog((r, 1, dconf->directory, "escaping %s for redirect", +- r->filename)); +- r->filename = escape_absolute_uri(r->pool, r->filename, skip); ++ r->filename)); ++ r->filename = escape_absolute_uri(r->pool, r->filename, skip_absolute); + } + + /* append the QUERY_STRING part */ diff --git a/debian/patches/0057-CVE-2023-25690-Regression-3.patch b/debian/patches/0057-CVE-2023-25690-Regression-3.patch new file mode 100644 index 0000000..431f145 --- /dev/null +++ b/debian/patches/0057-CVE-2023-25690-Regression-3.patch @@ -0,0 +1,24 @@ +From 1a4aac3d209f4314bcb511d73cf12f8c25c8c984 Mon Sep 17 00:00:00 2001 +From: Eric Covener <covener@apache.org> +Date: Sat, 11 Mar 2023 21:29:11 +0000 +Subject: [PATCH] followup to r1908296: only for redirects + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1908299 13f79535-47bb-0310-9956-ffa450edef68 +--- + modules/mappers/mod_rewrite.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +Index: apache2/modules/mappers/mod_rewrite.c +=================================================================== +--- apache2.orig/modules/mappers/mod_rewrite.c ++++ apache2/modules/mappers/mod_rewrite.c +@@ -4708,8 +4708,7 @@ static int hook_uri2file(request_rec *r) + unsigned skip_absolute = is_absolute_uri(r->filename, NULL); + apr_size_t flen = r->filename ? strlen(r->filename) : 0; + int to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0); +- int will_escape = (to_proxyreq || skip_absolute) +- && (rulestatus != ACTION_NOESCAPE); ++ int will_escape = skip_absolute && (rulestatus != ACTION_NOESCAPE); + + if (r->args + && !will_escape diff --git a/debian/patches/series b/debian/patches/series index c430013..839511c 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -53,3 +53,11 @@ CVE-2006-20001.patch CVE-2022-36760.patch CVE-2022-37436.patch CVE-2021-33193.patch +0052-CVE-2023-27522-HTTP-Response-Smuggling-mod_proxy_uws.patch +0053-CVE-2023-25690-1.patch +0054-CVE-2023-25690-2.patch +0055-CVE-2023-25690-Regression-1.patch +0056-CVE-2023-25690-Regression-2.patch +0057-CVE-2023-25690-Regression-3.patch + + diff --git a/debian/perl-framework/t/modules/buffer.t b/debian/perl-framework/t/modules/buffer.t index 84cc5bc..e508f37 100644 --- a/debian/perl-framework/t/modules/buffer.t +++ b/debian/perl-framework/t/modules/buffer.t @@ -24,10 +24,15 @@ foreach my $t (@testcases) { ## Big query ## # 'foo' is 3 bytes, so 'foo' x 1000000 is ~3M, which is way over the default 'BufferSize' - $r = POST($t->[0], content => $t->[1] x 1000000); + ### FIXME - testing with to x 10000 is confusing LWP's full-duplex + ### handling: https://github.com/libwww-perl/libwww-perl/issues/299 + ### throttled down to a size which seems to work reliably for now + my $bigsize = 100000; + + $r = POST($t->[0], content => $t->[1] x $bigsize); # Checking for return code ok t_cmp($r->code, 200, "Checking return code is '200'"); # Checking for content - ok t_cmp($r->content, $t->[1] x 1000000, $r->content . ' equals ' . $t->[1]); + ok t_is_equal($r->content, $t->[1] x $bigsize); } diff --git a/debian/salsa-ci.yml b/debian/salsa-ci.yml index f1f5897..e90a48f 100644 --- a/debian/salsa-ci.yml +++ b/debian/salsa-ci.yml @@ -1,7 +1,8 @@ ---- -variables: - RELEASE: 'buster' - include: - - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml - - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml +- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml + +variables: + RELEASE: 'buster' + SALSA_CI_COMPONENTS: 'main contrib non-free' + SALSA_CI_DISABLE_REPROTEST: 1 + SALSA_CI_DISABLE_LINTIAN: 1 diff --git a/debian/tests/CVE-2023-25690 b/debian/tests/CVE-2023-25690 new file mode 100644 index 0000000..2aa916f --- /dev/null +++ b/debian/tests/CVE-2023-25690 @@ -0,0 +1,110 @@ +#!/bin/bash + +# test CVE-2023-25690 +set -eux + +RC=0 +fail () { + echo "FAIL: $@" >&2 + RC=1 +} + + +function exit_handler() +{ + # fix cp: cannot access '/tmp/autopkgtest-lxc.x06nhp9r/downtmp/CVE-2023-25690-artifacts/apache2': Permission denied + chmod -R a+rwX "$AUTOPKGTEST_ARTIFACTS/apache2" || true + systemctl status apache2.service || true + systemctl stop apache2 || true + cat $AUTOPKGTEST_ARTIFACTS/apache2/error.log || true + cat $AUTOPKGTEST_ARTIFACTS/apache2/access.log || true + cat $AUTOPKGTEST_ARTIFACTS/apache2/error.8080.log || true + cat $AUTOPKGTEST_ARTIFACTS/apache2/access.8080.log || true +} +trap exit_handler EXIT + + +a2enmod proxy +a2enmod proxy_http +a2enmod rewrite + +rsync -a /var/log/apache2 "$AUTOPKGTEST_ARTIFACTS" +rm /var/log/apache2/* +mount -o bind "$AUTOPKGTEST_ARTIFACTS/apache2" /var/log/apache2 + +tee /etc/apache2/ports.conf <<'EOF' +Listen 80 +Listen 8080 +EOF + + +tee /etc/apache2/sites-available/000-default.conf <<'EOF' +<VirtualHost *:8080> + # The ServerName directive sets the request scheme, hostname and port that + # the server uses to identify itself. This is used when creating + # redirection URLs. In the context of virtual hosts, the ServerName + # specifies what hostname must appear in the request's Host: header to + # match this virtual host. For the default virtual host (this file) this + # value is not decisive as it is used as a last resort host regardless. + # However, you must set it for any further virtual host explicitly. + #ServerName www.example.com + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + + # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, + # error, crit, alert, emerg. + # It is also possible to configure the loglevel for particular + # modules, e.g. + #LogLevel info ssl:warn + + ErrorLog ${APACHE_LOG_DIR}/error.8080.log + CustomLog ${APACHE_LOG_DIR}/access.8080.log combined + + # For most configuration files from conf-available/, which are + # enabled or disabled at a global level, it is possible to + # include a line for only one particular virtual host. For example the + # following line enables the CGI configuration for this host only + # after it has been globally disabled with "a2disconf". + #Include conf-available/serve-cgi-bin.conf +</VirtualHost> +<VirtualHost *:80> + # The ServerName directive sets the request scheme, hostname and port that + # the server uses to identify itself. This is used when creating + # redirection URLs. In the context of virtual hosts, the ServerName + # specifies what hostname must appear in the request's Host: header to + # match this virtual host. For the default virtual host (this file) this + # value is not decisive as it is used as a last resort host regardless. + # However, you must set it for any further virtual host explicitly. + #ServerName www.example.com + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + + # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, + # error, crit, alert, emerg. + # It is also possible to configure the loglevel for particular + # modules, e.g. + #LogLevel info ssl:warn + LogLevel alert rewrite:trace6 + LogLevel error proxy:trace6 + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + RewriteEngine on + RewriteRule "^/here/(.*)" "http://localhost:8080/index.html?$1" [P] + ProxyPassReverse "/here/" "http://localhost:8080/" +</VirtualHost> +EOF + +systemctl restart apache2 + +CHOKEURL="http://localhost/here/index.html%20HTTP/1.1%0d%0aHost:%20localhost%0d%0aConnection:%20keep-alive%0d%0a%0d%0aGET%20/BAD.html%20HTTP/1.1%0d%0aFoo:%20bar HTTP/1.1" +wget -S -q --output-document - "$CHOKEURL" || true +(wget -S -q --output-document /dev/null "$CHOKEURL" 2>&1 || true) +(wget -S -q --output-document /dev/null "$CHOKEURL" 2>&1 || true) | grep -e '^[[:space:]]*HTTP/1.1 4[[:digit:]][[:digit:]] ' + +cat $AUTOPKGTEST_ARTIFACTS/apache2/access.8080.log | grep '] "GET /BAD.html HTTP/1.1"' && exit 1 + +exit 0 + diff --git a/debian/tests/control b/debian/tests/control index cb45689..e2d3875 100644 --- a/debian/tests/control +++ b/debian/tests/control @@ -27,3 +27,11 @@ Tests: chroot Features: no-build-needed Restrictions: needs-root allow-stderr breaks-testbed Depends: apache2, wget, dpkg-dev + +Tests: uwsgi +Restrictions: allow-stderr, needs-root +Depends: apache2, libapache2-mod-proxy-uwsgi, uwsgi, wget, uwsgi-plugin-python, rsync, netcat-openbsd | netcat-traditional + +Tests: CVE-2023-25690 +Restrictions: allow-stderr, needs-root +Depends: apache2, rsync, curl, wget diff --git a/debian/tests/uwsgi b/debian/tests/uwsgi new file mode 100644 index 0000000..fd51b82 --- /dev/null +++ b/debian/tests/uwsgi @@ -0,0 +1,142 @@ +#!/bin/bash +set -eux + +RC=0 +fail () { + echo "FAIL: $@" >&2 + RC=1 +} + + +function exit_handler() +{ + systemctl stop apache2 || true + if test -f /run/uwsgi/uwsgi.pid; then + kill -TERM $(cat /run/uwsgi/uwsgi.pid) + fi + cat $AUTOPKGTEST_ARTIFACTS/apache2/error.log || true + cat $AUTOPKGTEST_ARTIFACTS/apache2/access.log || true + cat $AUTOPKGTEST_ARTIFACTS/apache2/uwsgi.log || true + cat $AUTOPKGTEST_ARTIFACTS/apache2/uwsgi.error.log || true +} +trap exit_handler EXIT + + +a2enmod proxy +a2enmod proxy_uwsgi + +rsync -a /var/log/apache2 "$AUTOPKGTEST_ARTIFACTS" +rm /var/log/apache2/* +mount -o bind "$AUTOPKGTEST_ARTIFACTS/apache2" /var/log/apache2 + +tee /etc/apache2/sites-available/000-default.conf <<'EOF' +<VirtualHost *:80> + # The ServerName directive sets the request scheme, hostname and port that + # the server uses to identify itself. This is used when creating + # redirection URLs. In the context of virtual hosts, the ServerName + # specifies what hostname must appear in the request's Host: header to + # match this virtual host. For the default virtual host (this file) this + # value is not decisive as it is used as a last resort host regardless. + # However, you must set it for any further virtual host explicitly. + #ServerName www.example.com + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + + # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, + # error, crit, alert, emerg. + # It is also possible to configure the loglevel for particular + # modules, e.g. + #LogLevel info ssl:warn + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + # For most configuration files from conf-available/, which are + # enabled or disabled at a global level, it is possible to + # include a line for only one particular virtual host. For example the + # following line enables the CGI configuration for this host only + # after it has been globally disabled with "a2disconf". + #Include conf-available/serve-cgi-bin.conf + ProxyPass "/uwsgi" "unix:/run/uwsgi/test.socket|uwsgi://localhost" +</VirtualHost> +EOF + +systemctl restart apache2 + +test -d /etc/uwsgi/ || mkdir /etc/uwsgi + + + +tee /etc/systemd/system/uwsgi-app@.socket <<EOF +[Unit] +Description=Socket for uWSGI app %i + +[Socket] +ListenStream=/run/uwsgi/%i.socket +SocketUser=www-%i +SocketGroup=www-data +SocketMode=0660 + +[Install] +WantedBy=sockets.target +EOF + +tee /etc/systemd/system/uwsgi-app@.service <<EOF +[Unit] +Description=%i uWSGI app +After=syslog.target + +[Service] +ExecStart=/usr/bin/uwsgi \ + --ini /etc/uwsgi/apps-available/%i.ini \ + --socket /run/uwsgi/%i.socket +User=www-%i +Group=www-data +Restart=on-failure +KillSignal=SIGQUIT +Type=notify +StandardError=file:/var/log/apache2/uwsgi.error.log +StandardOutput=file:/var/log/apache2/uwsgi.log +NotifyAccess=all +EOF + +systemctl daemon-reload + +useradd uwsgi_test +useradd www-test + +tee /etc/uwsgi/apps-available/test.ini <<EOF +[uwsgi] +chdir=/tmp +master=True +cheap=True +die-on-idle=True +manage-script-name=True +plugin=python +wsgi-file=/tmp/uwsgi.py +EOF + + +tee /tmp/uwsgi.py <<'EOF' +import wsgiref.headers as h +def application(env, start_response): + buggy_header=('buggy','buggy#\r\nbuggy2:buggy2') + start_response('200 OK', [('Content-Type','text/html'),buggy_header]) + ret = b"Hello World Headers {}".format(env) + return [ret] +EOF +chown www-test.www-test /tmp/uwsgi.py +chmod +x /tmp/uwsgi.py + +systemctl enable uwsgi-app@test.socket +systemctl enable uwsgi-app@test.service +systemctl start uwsgi-app@test.socket +systemctl restart apache2 + + +wget -S -q --output-document - http://localhost/uwsgi +wget -q --output-document - http://localhost/uwsgi | grep "^Hello World" + +exit $RC +- |