Skip to content

Commit acb0353

Browse files
covenerxrmx
authored andcommitted
apache2/mod_proxy_uwsgi: stricter backend HTTP response parsing/validation
HTTP Response Smuggling vulnerability in Apache HTTP Server via mod_proxy_uwsgi. Special characters in the origin response header can truncate/split the response forwarded to the client. Fix #2538 origin: https://github.com/apache/httpd/commit/d753ea76b5972a85349b68c31b59d04c60014f2d.patch bug-cve: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-27522
1 parent 130e727 commit acb0353

File tree

1 file changed

+35
-13
lines changed

1 file changed

+35
-13
lines changed

apache2/mod_proxy_uwsgi.c

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -315,27 +315,25 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec *backend, proxy_server_
315315
apr_bucket_brigade *pass_bb = apr_brigade_create(r->pool, c->bucket_alloc);
316316

317317
len = ap_getline(buffer, sizeof(buffer), rp, 1);
318-
319318
if (len <= 0) {
320-
// oops
319+
/* invalid or empty */
321320
return HTTP_INTERNAL_SERVER_ERROR;
322321
}
323-
324322
backend->worker->s->read += len;
325-
326-
if (len >= sizeof(buffer)-1) {
327-
// oops
323+
if ((apr_size_t)len >= sizeof(buffer)) {
324+
/* too long */
328325
return HTTP_INTERNAL_SERVER_ERROR;
329326
}
327+
330328
/* Position of http status code */
331329
int status_start;
332330
if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
333331
status_start = 9;
334332
} else if (apr_date_checkmask(buffer, "HTTP/# ###*")) {
335333
status_start = 7;
336334
} else {
337-
// oops
338-
return HTTP_INTERNAL_SERVER_ERROR;
335+
/* not HTTP */
336+
return HTTP_BAD_GATEWAY;
339337
}
340338
int status_end = status_start + 3;
341339

@@ -354,17 +352,41 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec *backend, proxy_server_
354352
}
355353
r->status_line = apr_pstrdup(r->pool, &buffer[status_start]);
356354

357-
// start parsing headers;
355+
/* parse headers */
358356
while ((len = ap_getline(buffer, sizeof(buffer), rp, 1)) > 0) {
357+
if ((apr_size_t)len >= sizeof(buffer)) {
358+
/* too long */
359+
len = -1;
360+
break;
361+
}
359362
value = strchr(buffer, ':');
360-
// invalid header skip
361-
if (!value) continue;
362-
*value = '\0';
363-
++value;
363+
if (!value) {
364+
/* invalid header */
365+
len = -1;
366+
break;
367+
}
368+
*value++ = '\0';
369+
if (*ap_scan_http_token(buffer)) {
370+
/* invalid name */
371+
len = -1;
372+
break;
373+
}
364374
while (apr_isspace(*value)) ++value;
365375
for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end) *end = '\0';
376+
if (*ap_scan_http_field_content(value)) {
377+
/* invalid value */
378+
len = -1;
379+
break;
380+
}
366381
apr_table_add(r->headers_out, buffer, value);
367382
}
383+
if (len < 0) {
384+
/* Reset headers, but not to NULL because things below the chain expect
385+
* this to be non NULL e.g. the ap_content_length_filter.
386+
*/
387+
r->headers_out = apr_table_make(r->pool, 1);
388+
return HTTP_BAD_GATEWAY;
389+
}
368390

369391
if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
370392
ap_set_content_type(r, apr_pstrdup(r->pool, buf));

0 commit comments

Comments
 (0)