RootMe: Nginx - Alias Misconfiguration


Introduction

Hello everyone. This time I bring you a writeup for a Root-Me web challenge in which we have to audit the new intranet of a company before it goes live in production. The challenge revolves around a classic but still very common vulnerability: a misconfigured alias directive in nginx that allows path traversal outside the intended directory, giving us access to files that should never have been exposed.

Reconnaissance and Enumeration

The first thing I do is send a request to the site’s index to see what we’re dealing with:

The server returns a very simple login page. Two details stand out: the nginx/1.27.2 banner in the Server header, and an HTML comment left behind by the developer: <!--TODO: Patch /assets/ -->. That note acts as a huge hint: something related to /assets/ is still pending a fix.

Following the hint, if we list /assets/, nginx returns the directory autoindex, but it’s completely empty:

Apparently there’s nothing interesting there. However, when we request the same resource without the trailing slash, i.e. /assets, the behavior changes completely:

nginx responds with a 301 Moved Permanently pointing to http://challenge01.root-me.org:8000/assets/. This reveals very valuable information: the service we’re querying is actually a reverse proxy that delegates to a backend listening on port 8000. But the most important thing is what the difference in behavior between /assets and /assets/ implies: it strongly suggests a configuration like location /assets { alias ...; } without the trailing slash on the location block, which is exactly the condition needed for the bug we’re looking for.

Exploitation
Alias Misconfiguration

When nginx is configured with something like:

location /assets {
    alias /var/www/site/assets/;
}

the server takes any request whose path starts with /assets, strips that prefix and concatenates the rest directly to the alias value. The problem is that, since the location doesn’t end with /, a request like /assets../ also matches the block. nginx then removes /assets and concatenates ../ to the alias, resolving /var/www/site/assets/ + ../ = /var/www/site/. In other words: we escape one level above the directory the developer intended to expose.

To test this, we simply request /assets../ with curl:

And there it is: the autoindex of the parent directory, where we can see the backend’s actual folders (assets/, static/) and, most importantly, a flag.txt file waiting to be read. All that’s left is to request it:

~Grepmam