summaryrefslogtreecommitdiffstats
path: root/debian/patches/0052-CVE-2023-27522-HTTP-Response-Smuggling-mod_proxy_uws.patch
blob: f39fa72f7e72b9c0aae23ed77a1af190a4b36fa8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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));