Blog.Volema

Some cases of insecure NGINX configurations

Mon 02 June 2014

Nginx is very nice web server with flexilble configuration options and secure-enough defaults.

But, sometimes administrators can make mistakes cooking it.

Case 1: try_files

Nginx has a nice feature try_files which "checks the existence of files in the specified order and uses the first found file for request processing ...", "if none of the files were found, an internal redirect to the uri specified in the last parameter is made."

Here is an example of misconfiguration of django project but the same config can be used with any other framework (Flask etc). A lot of django projects have similar structure:

$ tree /your/django/project/root
|
+-- media
+---- some_static.css
+-- djangoproject
+---- __init__.py
+---- settings.py
+---- urls.py
+---- wsgi.py
+-- manage.py

So, administrators decide to serve static files with nginx and use this configuration:

root /your/django/project/root;

location / {
    try_files $uri @django;
}

location @django {
    proxy_pass http://django_backend;
}

What's wrong with it?

Anyone can access manage.py and all of the project sources (including djangoproject/settings.py which will lead to RCE with some Django versions) because nginx will first try to serve static file from root, and only if it does not exists pass the request to @django location.

http://yourserver.com/manage.py

Case 2: rewrite with $uri ($document_uri)

Nginx has internal variables. One of these variables is $uri - "normalized" URI of the request.

According to nginx docs - "normalization" is "decoding the text encoded in the '%XX' form, resolving references to the relative path components '.' and '..', and possible compression of two or more adjacent slashes into a single slash.".

What can go wrong?

Some administrators use this (simplified) configuration for HTTP->HTTPS redirect:

location / {
    rewrite ^ https://$host/$uri;
}

or

location / {
    return 302 https://$host$uri;
}

If someone access the server with urlencoded CRLF (%0d%0a) - it'll be decoded and passed to rewrite. Rewrite will answer with Location header, and will include CRLF there.

Example request:

GET /test%0d%0aSet-Cookie:%20malicious%3d1 HTTP/1.0
Host: yourserver.com

Servers response:

HTTP/1.1 302 Moved Temporarily
Server: nginx
Date: Mon, 02 Jun 2014 13:08:09 GMT
Content-Type: text/html
Content-Length: 154
Connection: close
Location: https://yourserver.com/test
Set-Cookie: malicious=1

...

As we can see cookie "malicious=1" will be set to the client.

We have decided that this is an issue in nginx behaviour (CRLF should not be sent with Location header) and reported it to nginx team, however they claim it as just a misconfiguration. So - $uri ($document_uri) should not be used with rewrites.

Also, you should check if you don't use any untrusted variables set by 3rdparty modules or vulnerable backend with your rewrite config.

Case 3: nginx as caching proxy

Nginx is a reverse proxy, but sometimes it's used almost like a forward proxy (in pair with secure_link module for example).

Example configuration:

location /cache/ {
    proxy_pass $proto://$arg_link;
}

What can go wrong?

Malicious backend can

  1. Use Set-Cookies header and set custom cookies to your clients. This can be mitigated with

    proxy_hide_header "Set-Cookie";

  2. It can answer with Nginx's internal headers (X-Accel-Redirect etc).

    Nginx can be configured to ignore them with proxy_ignore_headers directive.

  3. Leak Referrer or Cookies. Don't forget to use

    proxy_set_header Referer ""; proxy_set_header Cookie "";

etc.

Try to avoid these types of configurations!

Comments !