Nginx

Theory

NGINX is open source software for web serving, reverse proxying, caching, load balancing, media streaming, and more. But there is some common Nginx misconfigurations that, if left unchecked, leave the web site vulnerable to attack.

Misconfigurations

Tools

There are several tools available, such as Gixy and Nginxpwner, which can automate the process of identifying misconfigurations in Nginx.

Gixy is a tool to analyze Nginx configuration. The main goal of Gixy is to prevent security misconfiguration and automate flaw detection. This is a static files analyzer.

#Static analyze
gixy /etc/nginx/nginx.conf

Missing Root Location

The root directive specifies the root folder for Nginx. In the following example, the root folder is /etc/nginx which means that we can reach files within that folder.

server {
        root /etc/nginx;

        location /hello.txt {
                try_files $uri $uri/ =404;
                proxy_pass http://127.0.0.1:8080/;
        }
}

In the above example, the root folder is /etc/nginx which means that we can reach files within that folder. The configuration does not have a location for / (location / {...})

Off-By-Slash Misconfiguration

With the Off-by-slash misconfiguration, it is possible to traverse one step up the path due to a missing slash. Inside the Nginx configuration look the "location" statements, if someone looks like:

#Missing slash with alias directive
location /imgs { 
    alias /path/images/;
}

#Missing slash with proxy_pass directive
location /api {
    proxy_pass http://apiserver/v1/;
}

Unsafe Variable Use

Some frameworks, scripts and Nginx configurations unsafely use the variables stored by Nginx. This can lead to issues such as XSS, bypassing HttpOnly-protection, information disclosure and in some cases even RCE.

With a configuration such as the following:

location / {
  return 302 https://example.com$uri;
}

The misconfiguration related is to use $uri or $document_uri instead of $request_uri which results in a CRLF injection. This is because this two variables contain the normalized URI whereas the normalization in Nginx includes URL decoding.

Raw Backend Response Reading

In Nginx, proxy_pass you can intercept errors and HTTP headers created by the backend. This is very useful if you want to hide internal error messages. But if client sends an invalid HTTP request, it will be forwarded as-is to the backend, the backend will answer with its raw content, and then Nginx won’t understand the invalid HTTP response and just forward it to the client.

With a configuration such as the following:

http {
   error_page 500 /html/error.html;
   proxy_intercept_errors on;
   proxy_hide_header Secret-Header;
}

proxy_intercept_errors will serve a custom response if the backend has a response status greater than 300. In our uWSGI application above, we will send a 500 Error which would be intercepted by Nginx. proxy_hide_header is pretty much self explanatory; it will hide any specified HTTP header from the client.

Merge_slashes Set To Off

The merge_slashes directive is set to on by default which is a mechanism to compress two or more forward slashes into one, so /// would become /. If Nginx is used as a reverse-proxy and the application that’s being proxied is vulnerable to local file inclusion, using extra slashes in the request could leave room for exploit it. This is described in detail by Danny Robinson and Rotem Bar

Proxy_pass Misconfigurations

The proxy_pass directive can be used to redirect internally requests to other servers internal or external. The use of this directive isn't a vulnerability but you should check how it's configured.

HTTP Splitting

If the regular expressions used in that directive are weak, they allow HTTP splitting to happen.

With a configuration such as the following:

location ~ /docs/([^/]*/[^/]*)? {
    proxy_pass https://bucket.s3.amazonaws.com/docs-website/$1.html;
}

the problem with this regular expressions is that it also allows newlines per default. In this case, the [^/]* part actually also includes encoded newlines.

Controlling proxied host

In some setups, a matching path is used as part of the hostname to proxy to.

location ~ /static/(.*)/(.*) {
    proxy_pass   http://$1-example.s3.amazonaws.com/$2;
}

Since the bucket is attacker controlled (part of the URI path) this leads to XSS but also has further implications.

We could make proxy_pass connect to a local unix socket as it supports proxying requests to local unix sockets. What might be surprising is that the URI given to proxy_pass can be prefixed with http:// or as a UNIX-domain socket path specified after the word unix and enclosed in colons:

proxy_pass http://unix:/tmp/backend.sock:/uri/;

Resources

Last updated