Skip to content

Commit 2bf4601

Browse files
committed
Fetch: fixed heap buffer overflow in proxy URL credentials.
The destination buffers for the decoded user and password in ngx_js_parse_proxy_url() were a fixed 128 bytes, while the encoded input length was bounded only by the URL length. Since ngx_unescape_uri() writes at most one byte per input byte, raw credentials longer than 128 bytes overflowed the buffer; the length check ran only after the decode. The fix is to size the destination buffer based on the encoded input length. The bug appeared in dea8318 (0.9.4).
1 parent 0ffc96d commit 2bf4601

2 files changed

Lines changed: 18 additions & 11 deletions

File tree

nginx/ngx_js.c

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3549,37 +3549,35 @@ ngx_js_parse_proxy_url(ngx_pool_t *pool, ngx_log_t *log, ngx_str_t *url,
35493549
pass_start = colon + 1;
35503550
pass_len = at - pass_start;
35513551

3552-
decoded_user = ngx_pnalloc(pool, 128);
3552+
decoded_user = ngx_pnalloc(pool, user_len);
35533553
if (decoded_user == NULL) {
35543554
return NGX_ERROR;
35553555
}
35563556

3557-
decoded_pass = ngx_pnalloc(pool, 128);
3557+
decoded_pass = ngx_pnalloc(pool, pass_len);
35583558
if (decoded_pass == NULL) {
35593559
return NGX_ERROR;
35603560
}
35613561

35623562
p = user_start;
35633563
decoded_end = decoded_user;
35643564
ngx_unescape_uri(&decoded_end, &p, user_len, NGX_UNESCAPE_URI);
3565-
35663565
user_len = decoded_end - decoded_user;
3567-
if (user_len == 0 || user_len > 127) {
3566+
3567+
if (user_len == 0) {
35683568
ngx_log_error(NGX_LOG_ERR, log, 0,
3569-
"js_fetch_proxy username invalid or too long "
3570-
"(max 127 bytes after decoding)");
3569+
"js_fetch_proxy username is empty");
35713570
return NGX_ERROR;
35723571
}
35733572

35743573
p = pass_start;
35753574
decoded_end = decoded_pass;
35763575
ngx_unescape_uri(&decoded_end, &p, pass_len, NGX_UNESCAPE_URI);
3577-
35783576
pass_len = decoded_end - decoded_pass;
3579-
if (pass_len == 0 || pass_len > 127) {
3577+
3578+
if (pass_len == 0) {
35803579
ngx_log_error(NGX_LOG_ERR, log, 0,
3581-
"js_fetch_proxy password invalid or too long "
3582-
"(max 127 bytes after decoding)");
3580+
"js_fetch_proxy password is empty");
35833581
return NGX_ERROR;
35843582
}
35853583

nginx/t/js_fetch_proxy_variable.t

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ http {
5959
js_fetch_proxy $proxy_url;
6060
js_content test.http_fetch;
6161
}
62+
63+
location /dynamic_user_proxy {
64+
set $proxy_url "http://$arg_user:p@127.0.0.1:%%PORT_8081%%";
65+
js_fetch_proxy $proxy_url;
66+
js_content test.http_fetch;
67+
}
6268
}
6369
6470
server {
@@ -128,7 +134,7 @@ $t->write_file('test.js', <<EOF);
128134
129135
EOF
130136

131-
$t->try_run('no js_fetch_proxy')->plan(3);
137+
$t->try_run('no js_fetch_proxy')->plan(4);
132138

133139
###############################################################################
134140

@@ -138,5 +144,8 @@ like(http_get('/dynamic_proxy'), qr/PROXY:Basic\s+dGVzdHVzZXI6dGVzdHBhc3M=/,
138144
'dynamic proxy URL with auth');
139145
like(http_get('/dynamic_empty_proxy'), qr/ORIGIN:OK/,
140146
'dynamic empty proxy URL bypasses proxy');
147+
like(http_get('/dynamic_user_proxy?user=' . ('a' x 200)),
148+
qr/PROXY:BAD-AUTH/,
149+
'long user in dynamic proxy URL decoded without overflow');
141150

142151
###############################################################################

0 commit comments

Comments
 (0)