I had too much time on my hand a few weeks ago, so I decided to play around with Shodan to find some juicy stuff. Specifically, exposed directory listing that contain bash history, environment variables file, exposed version control directory and other configuration files stored in plain text format.
They mostly look like this:
Why did this happen?
Misconfigured virtual host (or server blocks as they call it in nginx)! These conditions must be satisfied:
- The default virtual host is also used to serve the application.
- Directory listing is enabled on the default virtual host.
If directory listing is disabled, it is still possible to access sensitive files, but you kind of have to brute force the file name (or use a dictionary).
What is the default virtual host?
The default virtual host is responsible as the “catch-all” virtual host. You can read this article to understand how nginx decides which server block to match the request to.
Steps to reproduce
For the sake of this article, I built nginx version 1.22.1 from source because I did not want to touch my default nginx installation. You should be able to replicate this issue by installing nginx using your package manager.
$ ./configure \
--prefix=/opt/nginx-1.22.1 \
--with-threads \
--with-http_ssl_module \
--with-stream \
--with-stream_ssl_module \
--with-http_secure_link_module \
--with-debug \
--with-ld-opt=-L/opt/homebrew/opt/openssl@3/lib
$ sudo make install
After running the commands above, nginx will be installed at /opt/nginx-1.22.1
For the sake of simplicity, this nginx installation will only serve static files. So, no need to configure PHP-FPM, etc.
# /opt/nginx-1.22.1/conf/nginx.conf
worker_processes 3;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
server_names_hash_bucket_size 64;
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
include sites_enabled/*.conf;
}
# /opt/nginx-1.22.1/conf/sites_available/default.conf
server {
listen 80 default_server;
server_name _;
location / {
root html; # sets /opt/nginx-1.22.1/html as the document root
autoindex on;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
# /etc/hosts
127.0.0.1 localhost
127.0.0.1 awesomewebsite.local
Then, /opt/nginx-1.22.1/conf/sites_available/default.conf
needs to be symlinked to /opt/nginx-1.22.1/conf/sites_enabled/default.conf
to enable the virtual host.
# ln -s /opt/nginx-1.22.1/conf/sites_available/default.conf /opt/nginx-1.22.1/conf/sites_enabled/default.conf
Note that awesomewebsite.local
does not have its own virtual host configuration.
Then, start nginx:
$ sudo /opt/nginx-1.22.1/sbin/nginx -t # test the configuration
$ sudo /opt/nginx-1.22.1/sbin/nginx
$ sudo /opt/nginx-1.22.1/sbin/nginx -s reload # to reload nginx after config changes
Now, send a HTTP request to the server using 3 methods:
- Direct to IP (127.0.0.1)
- localhost
- awesomewebsite.local
They all should return the same response.
What can I do with this information?
If the server that you found happens to be hosting a Laravel application:
- Check for
.env
file - Check for exposed version control folder (
.git
,.hg
) - Check whether it is vulnerable to
CVE-2017-9841
In general:
- Check for juicy information in
.bash_history
- Find database backup
- Find archive of source code
- Find database client
If you are lucky enough, you can find Azure configs, AWS configs, database credentials (sometimes they allow remote login) and a database client (Adminer/PHPMyAdmin).
I once found a server that allows SSH login using password AND the password is written in .bash_history
😀
If you can access the .git
directory, you can probably dump the source code using git-dumper.
Mitigation
Modify your NGINX configuration so that all request to dotfiles will be rejected:
# /opt/nginx-1.22.1/conf/sites_available/default.conf
server {
listen 80 default_server;
server_name _;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# add this
# blocks all request to anything that starts with a dot
location ~ /\. {
autoindex off;
deny all;
}
# for sensitive files that don't start with a dot
location = /super_sensitive_file.txt {
deny all;
return 403;
}
location / {
root html; # sets /opt/nginx-1.22.1/html as the document root
autoindex on;
}
}
In general, you should:
- Disable directory listing.
- Disable the default virtual host.
- Create specific virtual host or server block per website
chmod
sensitive files (wp-config.php, .env, etc) to 0400 (not completely related to this article but this will protect against symlink attacks)- Store your backups somewhere else.
- Do NOT upload/install a database client. You are screwed if your database creds are exposed.
- If you are deploying a Laravel app, do NOT use a shared hosting. Make sure you follow the deployment steps as described in the documentation.
nuice
hadirr
Nice article.