#2472 closed defect (invalid)

Invalid request caused by '.' in the first URI component

Reported by: Weitian LI Owned by:
Priority: minor Milestone:
Component: nginx-core Version: 1.22.x
Keywords: Cc: Weitian LI
uname -a: Linux xxx 6.1.6-arch1-3 #1 SMP PREEMPT_DYNAMIC Mon, 16 Jan 2023 12:51:23 +0000 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.22.1
built with OpenSSL 3.0.7 1 Nov 2022
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --conf-path=/etc/nginx/nginx.conf --sbin-path=/usr/bin/nginx --pid-path=/run/nginx.pid --lock-path=/run/lock/nginx.lock --user=http --group=http --http-log-path=/var/log/nginx/access.log --error-log-path=stderr --http-client-body-temp-path=/var/lib/nginx/client-body --http-proxy-temp-path=/var/lib/nginx/proxy --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-cc-opt='-march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -flto=auto' --with-ld-opt='-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now -flto=auto' --with-compat --with-debug --with-file-aio --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_degradation_module --with-http_flv_module --with-http_geoip_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-pcre-jit --with-stream --with-stream_geoip_module --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-threads

Description (last modified by Weitian LI)

Hi,

I found a request to /% 2e% 2e/x caused Nginx to response 400 with error:

2023/03/21 17:06:55 [info] 1552400#1552400: *6 client sent invalid request while reading client request line, client: 127.0.0.1, server: _, request: "GET /% 2e% 2e/x HTTP/1.1"

Similarly, requests of /% 2e% 2e, /.% 2e, and /% 2e. all give the same 400 result.

However, requests of /x/% 2e% 2e, /x/.% 2e, and /x/% 2e. all work as expected. Meanwhile, request of /.. is also OK (so .. and % 2e% 2e behave differently).

I traced the issue and found it was raised by https://github.com/nginx/nginx/blame/master/src/http/ngx_http_parse.c#L1466, but I couldn't yet find the root cause.

N.B. please ignore the space between % and 2e; otherwise, trac auto replace it with ..

Thank you.

Change History (5)

comment:1 by Weitian LI, 13 months ago

Description: modified (diff)

comment:2 by Maxim Dounin, 13 months ago

Requests to /.. are also rejected by nginx:

$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /.. HTTP/1.0
HTTP/1.1 400 Bad Request
Server: nginx/1.22.1
Date: Tue, 21 Mar 2023 17:48:03 GMT
Content-Type: text/html
Content-Length: 157
Connection: close

<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<hr><center>nginx/1.22.1</center>
</body>
</html>
Connection closed by foreign host.

With the corresponding log message:

2023/03/21 20:48:03 [info] 93113#100064: *1 client sent invalid request while reading client request line, client: 127.0.0.1, server: , request: "GET /.. HTTP/1.0"

This matches handling of various escaped variants, including /%2e%2e, /.%2e, and /%2e., as well as /../x and /%2e%2e/x.

Could you please clarify how do you test so you see different handling for /..?

Just in case it's not clear, all these are rejected since these are different forms to escape from root. In particular, RFC 2396 used to consider such references to be in error:

      g) If the resulting buffer string still begins with one or more
         complete path segments of "..", then the reference is
         considered to be in error.  Implementations may handle this
         error by retaining these components in the resolved path (i.e.,
         treating them as part of the final URI), by removing them from
         the resolved path (i.e., discarding relative levels above the
         root), or by avoiding traversal of the reference.

While the the suggested pseudocode in RFC 3986 replaces such invalid dot segments with just / (that is, discards relative levels above the root), nginx prefers conservative approach here, and rejects such URIs.

Note well that URIs in HTTP requests are not expected to be relative, and therefore should not contain any dot segments. Such URIs used to work though, and therefore nginx tries to normalize them unless there are obvious errors.

in reply to:  2 comment:3 by Weitian LI, 13 months ago

Replying to Maxim Dounin:

Could you please clarify how do you test so you see different handling for /..?

I was testing with curl, but failed to notice curl auto normalized the /.. part. So I was actually requesting / instead of /... So %2e%2e and .. are essentially the same. My bad. Sorry.

Just in case it's not clear, all these are rejected since these are different forms to escape from root. In particular, RFC 2396 used to consider such references to be in error:

      g) If the resulting buffer string still begins with one or more
         complete path segments of "..", then the reference is
         considered to be in error.  Implementations may handle this
         error by retaining these components in the resolved path (i.e.,
         treating them as part of the final URI), by removing them from
         the resolved path (i.e., discarding relative levels above the
         root), or by avoiding traversal of the reference.

While the the suggested pseudocode in RFC 3986 replaces such invalid dot segments with just / (that is, discards relative levels above the root), nginx prefers conservative approach here, and rejects such URIs.

Note well that URIs in HTTP requests are not expected to be relative, and therefore should not contain any dot segments. Such URIs used to work though, and therefore nginx tries to normalize them unless there are obvious errors.

This makes it crystal clear. Thank you for the prompt and detailed response.

I'm sorry for the noise.

comment:4 by Weitian LI, 13 months ago

It seems I couldn't close this ticket. Please close it. Thanks.

comment:5 by Maxim Dounin, 13 months ago

Resolution: invalid
Status: newclosed

Thanks for the details, closing this.

Note: See TracTickets for help on using tickets.