Browse Source

[core] thwart h2c smuggling when Upgrade enabled

Existing behavior: mod_proxy *does not* forward Upgrade header
unless explicitly enabled in lighttpd.conf (default: not enabled)
  (proxy.header += ("upgrade" => "enable"))

mod_cgi previously used to forward Upgrade request header, but would
remove Upgrade response header if cgi.upgrade was not explicitly enabled
  (cgi.upgrade = "enable")

This patch thwarts h2c smuggling when lighttpd.conf has also been
explicitly configured to pass "Upgrade" request header

x-ref:
  "h2c Smuggling: Request Smuggling Via HTTP/2 Cleartext (h2c)"
  https://labs.bishopfox.com/tech-blog/h2c-smuggling-request-smuggling-via-http/2-cleartext-h2c
Glenn Strauss 1 month ago
parent
commit
0ff9d5deb0
  1. 2
      src/connections.c
  2. 1
      src/h2.c
  3. 11
      src/mod_cgi.c
  4. 2
      src/mod_proxy.c
  5. 7
      src/request.c

2
src/connections.c

@ -779,7 +779,7 @@ static int connection_handle_read_state(connection * const con) {
connection_set_state(r, CON_STATE_REQUEST_END);
if (light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE)
&& !con->is_ssl_sock && r->conf.h2proto && 0 == r->http_status
&& 0 == r->http_status
&& h2_check_con_upgrade_h2c(r)) {
/*(Upgrade: h2c over cleartext does not have SNI; no COMP_HTTP_HOST)*/
r->conditional_is_valid = (1 << COMP_SERVER_SOCKET)

1
src/h2.c

@ -2810,6 +2810,7 @@ h2_check_con_upgrade_h2c (request_st * const r)
buffer * const b = r->tmp_buf;
buffer_clear(b);
if (r->conf.h2proto > 1/*(must be enabled with server.h2c feature)*/
&& !r->con->is_ssl_sock /*(disallow h2c over TLS socket)*/
&&
http_header_str_contains_token(BUF_PTR_LEN(http_connection),
CONST_STR_LEN("HTTP2-Settings"))

11
src/mod_cgi.c

@ -980,10 +980,13 @@ URIHANDLER_FUNC(cgi_is_handled) {
hctx->plugin_data = p;
hctx->cgi_handler = &ds->value;
memcpy(&hctx->conf, &p->conf, sizeof(plugin_config));
hctx->conf.upgrade =
hctx->conf.upgrade
&& r->http_version == HTTP_VERSION_1_1
&& light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE);
if (!light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE))
hctx->conf.upgrade = 0;
else if (!hctx->conf.upgrade || r->http_version != HTTP_VERSION_1_1) {
hctx->conf.upgrade = 0;
http_header_request_unset(r, HTTP_HEADER_UPGRADE,
CONST_STR_LEN("Upgrade"));
}
hctx->opts.max_per_read =
!(r->conf.stream_response_body /*(if not streaming response body)*/
& (FDEVENT_STREAM_RESPONSE|FDEVENT_STREAM_RESPONSE_BUFMIN))

2
src/mod_proxy.c

@ -930,7 +930,7 @@ static handler_t proxy_create_env(gw_handler_ctx *gwhctx) {
te = &ds->value; /*("trailers")*/
break;
case HTTP_HEADER_UPGRADE:
if (hctx->conf.header.force_http10 || r->http_version == HTTP_VERSION_1_0) continue;
if (hctx->conf.header.force_http10 || r->http_version != HTTP_VERSION_1_1) continue;
if (!hctx->conf.header.upgrade) continue;
if (!buffer_is_blank(&ds->value)) upgrade = &ds->value;
break;

7
src/request.c

@ -1167,6 +1167,13 @@ http_request_parse (request_st * const restrict r, const int scheme_port)
return http_request_header_line_invalid(r, 400, "HTTP/1.1 but Host missing -> 400");
}
if (HTTP_VERSION_1_1 != r->http_version
&& (r->rqst_htags
& (light_bshift(HTTP_HEADER_UPGRADE)
|light_bshift(HTTP_HEADER_HTTP2_SETTINGS)))) {
return http_request_header_line_invalid(r, 400, "invalid hop-by-hop header w/o HTTP/1.1 -> 400");
}
if (0 == r->reqbody_length) {
/* POST requires Content-Length (or Transfer-Encoding)
* (-1 == r->reqbody_length when Transfer-Encoding: chunked)*/

Loading…
Cancel
Save