Setup
- I’m hosting multiple docker containers on a shared web server.
The requests are passed through an nginx Proxy Manager instance that redirects them based on the sub domain to their destination container. Examples:- alex-luebeck.de and www.alex-luebeck.de get redirected to the main web site container
- blog.alex-luebeck.de gets redirects to the container that hosts the blog
- This setup has an advantage. Every request passes the nginx reverse proxy container, which is able to log all the requests. So, what can we find in the logs?
Finding the log files
The log files are located at /data/logs/ within the container. Using a volumes in Docker for that path allows keeping even after a container is destroyed, e.g. because of image updates.
The folder contains multiple files:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42default-host_access.log proxy-host-1_error.log.1.gz proxy-host-4_access.log.1.gz proxy-host-6_error.log.5.gz
default-host_access.log.1.gz proxy-host-1_error.log.2.gz proxy-host-4_access.log.2.gz proxy-host-6_error.log.6.gz
default-host_access.log.2.gz proxy-host-1_error.log.3.gz proxy-host-4_access.log.3.gz proxy-host-6_error.log.7.gz
default-host_access.log.3.gz proxy-host-1_error.log.4.gz proxy-host-4_access.log.4.gz proxy-host-6_error.log.8.gz
default-host_access.log.4.gz proxy-host-1_error.log.5.gz proxy-host-4_error.log proxy-host-6_error.log.9.gz
default-host_error.log proxy-host-1_error.log.6.gz proxy-host-4_error.log.10.gz proxy-host-7_access.log
default-host_error.log.1.gz proxy-host-1_error.log.7.gz proxy-host-4_error.log.1.gz proxy-host-7_access.log.1.gz
default-host_error.log.2.gz proxy-host-1_error.log.8.gz proxy-host-4_error.log.2.gz proxy-host-7_access.log.2.gz
default-host_error.log.3.gz proxy-host-1_error.log.9.gz proxy-host-4_error.log.3.gz proxy-host-7_access.log.3.gz
fallback_access.log proxy-host-2_access.log proxy-host-4_error.log.4.gz proxy-host-7_access.log.4.gz
fallback_access.log.1.gz proxy-host-2_access.log.1.gz proxy-host-4_error.log.5.gz proxy-host-7_error.log
fallback_access.log.2.gz proxy-host-2_access.log.2.gz proxy-host-4_error.log.6.gz proxy-host-7_error.log.10.gz
fallback_access.log.3.gz proxy-host-2_access.log.3.gz proxy-host-4_error.log.7.gz proxy-host-7_error.log.1.gz
fallback_access.log.4.gz proxy-host-2_access.log.4.gz proxy-host-4_error.log.8.gz proxy-host-7_error.log.2.gz
fallback_error.log proxy-host-2_error.log proxy-host-4_error.log.9.gz proxy-host-7_error.log.3.gz
fallback_error.log.10.gz proxy-host-2_error.log.10.gz proxy-host-5_access.log proxy-host-7_error.log.4.gz
fallback_error.log.1.gz proxy-host-2_error.log.1.gz proxy-host-5_access.log.1.gz proxy-host-7_error.log.5.gz
fallback_error.log.2.gz proxy-host-2_error.log.2.gz proxy-host-5_access.log.2.gz proxy-host-7_error.log.6.gz
fallback_error.log.3.gz proxy-host-2_error.log.3.gz proxy-host-5_access.log.3.gz proxy-host-7_error.log.7.gz
fallback_error.log.4.gz proxy-host-2_error.log.4.gz proxy-host-5_access.log.4.gz proxy-host-7_error.log.8.gz
fallback_error.log.5.gz proxy-host-2_error.log.5.gz proxy-host-5_error.log proxy-host-7_error.log.9.gz
fallback_error.log.6.gz proxy-host-2_error.log.6.gz proxy-host-5_error.log.10.gz proxy-host-8_access.log
fallback_error.log.7.gz proxy-host-2_error.log.7.gz proxy-host-5_error.log.1.gz proxy-host-8_access.log.1.gz
fallback_error.log.8.gz proxy-host-2_error.log.8.gz proxy-host-5_error.log.2.gz proxy-host-8_error.log
fallback_error.log.9.gz proxy-host-2_error.log.9.gz proxy-host-5_error.log.3.gz proxy-host-8_error.log.1.gz
letsencrypt-requests_access.log proxy-host-3_access.log proxy-host-5_error.log.4.gz proxy-host-9_access.log
letsencrypt-requests_access.log.1.gz proxy-host-3_access.log.1.gz proxy-host-5_error.log.5.gz proxy-host-9_access.log.1.gz
letsencrypt-requests_access.log.2.gz proxy-host-3_access.log.2.gz proxy-host-5_error.log.6.gz proxy-host-9_access.log.2.gz
letsencrypt-requests_access.log.3.gz proxy-host-3_access.log.3.gz proxy-host-5_error.log.7.gz proxy-host-9_access.log.3.gz
letsencrypt-requests_access.log.4.gz proxy-host-3_access.log.4.gz proxy-host-5_error.log.8.gz proxy-host-9_access.log.4.gz
letsencrypt-requests_error.log proxy-host-3_error.log proxy-host-5_error.log.9.gz proxy-host-9_error.log
proxy-host-10_access.log proxy-host-3_error.log.10.gz proxy-host-6_access.log proxy-host-9_error.log.10.gz
proxy-host-10_error.log proxy-host-3_error.log.1.gz proxy-host-6_access.log.1.gz proxy-host-9_error.log.1.gz
proxy-host-11_access.log proxy-host-3_error.log.2.gz proxy-host-6_access.log.2.gz proxy-host-9_error.log.2.gz
proxy-host-11_error.log proxy-host-3_error.log.3.gz proxy-host-6_access.log.3.gz proxy-host-9_error.log.3.gz
proxy-host-1_access.log proxy-host-3_error.log.4.gz proxy-host-6_access.log.4.gz proxy-host-9_error.log.4.gz
proxy-host-1_access.log.1.gz proxy-host-3_error.log.5.gz proxy-host-6_error.log proxy-host-9_error.log.5.gz
proxy-host-1_access.log.2.gz proxy-host-3_error.log.6.gz proxy-host-6_error.log.10.gz proxy-host-9_error.log.6.gz
proxy-host-1_access.log.3.gz proxy-host-3_error.log.7.gz proxy-host-6_error.log.1.gz proxy-host-9_error.log.7.gz
proxy-host-1_access.log.4.gz proxy-host-3_error.log.8.gz proxy-host-6_error.log.2.gz proxy-host-9_error.log.8.gz
proxy-host-1_error.log proxy-host-3_error.log.9.gz proxy-host-6_error.log.3.gz proxy-host-9_error.log.9.gz
proxy-host-1_error.log.10.gz proxy-host-4_access.log proxy-host-6_error.log.4.gzWe’ve got:
- default-host_access -> Records all access requests to the default host.
- proxy-host-1_access.log, proxy-host-2_access.log, etc.: These files record access requests for specific proxy hosts (host 1, host 2, etc.).
- fallback_access.log: Records access requests that fall back to a default configuration.
- letsencrypt-requests_access.log: Records access requests related to Let’s Encrypt certificate issuance.
- for each of these types of files, there is also an error file in case of any major problems
The default host log file lines usually look like this:
1
<client IP> - - [24/Jul/2025:13:58:34 +0000] "GET / HTTP/1.1" 404 183 "-" "<user agent>"
The proxy log file entries usually look like this:
1
[24/Jul/2025:05:51:06 +0000] - 200 200 - GET http <target hostname> "/" [Client <client IP>] [Length 7689] [Gzip 4.06] [Sent-to website] "<user agent>" "-"
Preparation
- Let’s prepare the data and let’s do that in a reproducible way to be able to repeat these steps in the future.
- We create a copy of the whole logs folder in case something goes wrong.
- We unzip the gz files and throw away away the original gz files:
1
2
3
4for file in *.gz; do
gunzip -k "$file"
done
rm *.gz - We remove most of the log entries that were caused by myself to be able to focuss on those from others.
1
2
3
4for file in *; do
sed -i '/^42\.42\.42\.42/d' "$file"
sed -i '/\[Client 42\.42\.42\.42\]/d' "$file"
done - We separate access logs and error logs…
1
2
3
4mkdir access
mkdir error
mv *_access.log* access
mv *_error.log* error
Let’s go
total entries
- There are 22,823 log entries in total.
- The oldes one is from June 15th 2025, the newest one is from July 25th 2025.
filtering by words in the URL
- One way to filter words in the URLs of the logs files (in this case, the word “admin”)
1
2
3
4
5
6
7
8
9
10
11grep -oP '\"[A-Z]+\s+(/[^ ]*admin[^ ]*)\s+HTTP\/[0-9\.]+\"' default* | awk '{print $2}' > ../admin.log
grep -oP '\"(/admin[^"]*)\"' proxy* | awk -F'"' '{print $2}' >> ../admin.log
sort -o ../admin.log ../admin.log
grep -oP '\"[A-Z]+\s+(/[^ ]*wordpress[^ ]*)\s+HTTP\/[0-9\.]+\"' default* | awk '{print $2}' > ../wordpress.log
grep -oP '\"(/wordpress[^"]*)\"' proxy* | awk -F'"' '{print $2}' >> ../wordpress.log
sort -o ../wordpress.log ../wordpress.log
grep -oP '\"[A-Z]+\s+(/[^ ]*)\s+HTTP\/[0-9\.]+\"' default* | awk '{print $2}' > ../all.log
grep -oP '\"(/[^"]*)\"' proxy* | awk -F'"' '{print $2}' >> ../all.log
sort -o ../all.log ../all.log
admin
- “admin” is a nice word to start looking for in the logs, because an admin interface would usually be a good target.
- There are 1,728 entries containing “admin”. 722 or them are unique.
- starting with /admin (Regex:
^/admin) (x418) - hierarchy of /admin (Regex:
^/admin/.) (x232) - /wp-admin (x88)
- /phpmyadmin/… (x27)
- /symfony/… (PHP web framework) (x5)
- /spring/… (x5)
- /nextjs/… (x5)
- /laravel/… (x5)
- /flask/… (x5)
- /fastapi/… (x5)
- /express/… (x5)
- /django/… (x5)
- /codeigniter/… (x5)
- /boaform/… (x5)
- starting with /admin (Regex:
- The top 20 unique URLS containing “admin” (found out via Text Power Tools in VsCode (“Count line occurrences”)):
- 87 /admin/.env
- 52 /admin/assets/css/jquery-ui.css
- 52 /boaform/admin/formLogin
- 50 /admin/assets/js/views/login.js
- 49 /admin/assets/js/pbxlib.js
- 48 /admin/config.php
- 29 /admin/configs.php
- 21 /admin/server_info.php
- 20 /admin/config
- 17 /admin/
- 15 /admin/index.html
- 15 /admin/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
- 13 //admin/login.asp
- 13 /boaform/admin/formLogin?username=admin&psd=admin
- 12 /admin-app/.env
- 12 /admin/controllers/merchant.js
- 12 /admin/controllers/partner.js
- 12 /admin/phpinfo.php
- 11 /controller/admin/post.js
- 10 /admin.php
JSON, PHP, PY, YAML and YML
- It seems that there was one attacker that tried out the cross product of different web frameworks, different potential filenames and 5 extensions.
- The requests were:
- /<provider>/<filename>.json
- /<provider>/<filename>.php
- /<provider>/<filename>.py
- /<provider>/<filename>.yaml
- /<provider>/<filename>.yml
- The different web frameworks: codeigniter, django, express, fastapi, flask, laravel, nextjs, nuxt, sprint, symfony, yii
- The filenames were admin, app, application, auth, config, env, main, settings
- Even this already results in
5 * 8 * 11 = 444requests - The user agent stayed constant during all the requests.
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36looks quite normal by the way. - The IP address stayed the same, too.
- Alltogether, there were even more requests from that source.
- Total: 1,552
- Between 30/Jun/2025:21:46:04 and 30/Jun/2025:21:46:31
- That’s probably something, a good firewall would have blocked.
.env
Another quite popular target seem to be .env files, which is not surprising, because they might contain credentials or other information you usually don’t want to expose.
The top 30 unique URLs containing “.env” (found out via Text Power Tools in VSCode (“Count line occurrences”)):
- 925 /.env
- 73 /.env.prod
- 68 /.env.example
- 62 /.env.old
- 57 /.env.bak
- 57 /.env.production
- 52 /.env.local
- 49 /api/.env
- 44 /backend/.env
- 43 /admin/.env
- 39 /.env.stage
- 37 /.env.save
- 35 /.env.development
- 33 /.env.dev
- 33 /.env.test
- 31 /app/.env
- 29 /.env.backup
- 28 /.env.production.local
- 27 /.env_sample
- 25 /core/.env
- 25 /public/.env
- 24 /laravel/.env
- 22 /docker/.env
- 21 /crm/.env
- 21 /dev/.env
- 20 /local/.env
- 20 /web/.env
- 19 /prod/.env
- 18 /apps/.env
- 18 /mail/.env
The top subfolders to search .env in:
- I used regex
(^/[^/]*/)?(.*), replaced with$1, removed empty lines and use Text Power Tools in VsCode (“Count line occurrences”) for this. - 182 /api/
- 104 /test/
- 103 /app/
- 87 /backend/
- 83 /admin/
- 77 /config/
- 76 /core/
- 74 /examples/
- 69 /dev/
- 61 /docker/
- 59 /laravel/
- 58 /new/
- 57 /resources/
- 56 /server/
- 53 /mail/
- 52 /src/
- 50 /staging/
- 47 /tmp/
- 42 /auth/
- 42 /database/
- 42 /download/
- 41 /dashboard/
- 41 /docs/
- 40 /panel/
- 40 /services/
- 40 /upload/
- 38 //
- 37 /tests/
- 36 /console/
- 30 /env/
- I used regex
There are 6,496 requests containing .env in total.
.git
One can try to get the content of .env files, but getting source code (especially the one of the website or web app) is also interesting. Let’s find “.git” in the logs…
The top 15 unique URLs containing “.git” (found out via Text Power Tools in VsCode (“Count line occurrences”)):
- 442 /.git/config
- 127 /.git/HEAD
- 25 /.gitignore
- 24 /.git/index
- 23 /.gitlab-ci.yml
- 21 /.git/logs/HEAD
- 13 /.git-credentials
- 13 /.github/workflows/
- 13 /.gitlab-ci/.env
- 9 /.gitattributes
- 9 /.git/refs/
- 9 /.git/refs/heads/
- 9 /.git/refs/tags/
- 8 /.git/
- 8 /.git/info/exclude
The top subfolders to search .git in:
- I used regex
(^/[^/]*/)?(.*), replaced with$1, removed empty lines and use Text Power Tools in VsCode (“Count line occurrences”) for this. - 737 /.git/
- 22 /.github/
- 13 /.gitlab-ci/
- 12 /vendor/
- 9 /wp-content/
- 5 /api/
- 2 /admin/
- 2 /.gitlab/
- 2 /package/
- 1 /blog/
- I used regex
There are 904 requests containing .git in total.
the top URLs
- What are the most often called URLs (I filtered out legit one, but kept robots.txt in)?
- 925 /.env
- 442 /.git/config
- 353 /robots.txt
- 158 /wp-admin/setup-config.php
- 137 /wordpress/wp-admin/setup-config.php
- 127 /.git/HEAD
- 119 /api/.env
- 110 /login
- 96 /backend/.env
- 96 /_profiler/phpinfo
- 94 /.aws/credentials
- 91 /config.json
- 87 /admin/.env
- 82 /phpinfo.php
- 73 /.env.prod
- 70 /sitemap.xml
- 68 /.env.example
- 68 /info.php
- 63 /phpinfo
- 62 /1.php
- 62 /.env.old
- 60 /.aws/config
- 58 /core/.env
- 58 /form.html
- 58 /geoip/
- 58 /password.php
- 58 /systembc/password.php
- 58 /t4
- 58 /upl.php
- 57 /.env.bak
- 57 /.env.production
- 56 /apps/.env
- 55 /app/.env
- 52 /boaform/admin/formLogin
- 52 /.env.local
- 50 /admin/assets/js/views/login.js
- 49 /admin/assets/js/pbxlib.js
- 49 /docker-compose.yml
- 49 /settings.py
- 48 /admin/config.php
- 46 /aws/credentials
- 45 /.well-known/security.txt
- 44 /wiki
- 43 /application.properties
- 43 /dev/.env
- 42 /prod/.env
- 41 /geoserver/web/
- 40 /main/.env
- 39 /config/.env
- 39 /config.yaml
the top folders
What are the most often folders (I filtered out legit one, but kept robots.txt in)?
- 1455 /assets/
- 966 /config/
- 875 /.well-known/
- 774 /admin/
- 736 /.git/
- 551 /wp-content/
- 500 /wp-admin/
- 406 /wp-includes/
- 367 /app/
- 309 /backend/
- 264 /src/
- 251 /test/
- 227 /laravel/
- 227 /wordpress/
- 205 /apps/
- 204 /public/
- 201 /server/
- 200 /.aws/
- 188 /backup/
- 188 /dev/
- 183 /cgi-bin/
- 183 /vendor/
- 169 /core/
MISC
sql
- 24 /database.sql
- 11 /locales/locale.json?locale=../../config/&namespace=database
- 11 /server/config/database.js
- 10 /database_backup.sql
- 9 /database/.env
- 8 /database.yml
- 5 /config/database.php
- 5 /database.json
- 5 /database.php
- 5 /locales/locale.json?locale=../../../pterodactyl&namespace=config/database
- 4 /config/database.yml
- 3 /config/database.json
- 3 /docker/database/.env
- 2 /database.env
- 2 /database.ini
- 2 /database.zip
backup
- 27 /backup.zip
- 25 /backup.tar.gz
- 18 /backup
- 16 /.env.backup
- 15 /backup/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
- 14 /backup/.env
- 13 /env.backup
- 12 /backup.sql
- 5 /backup/config.php
- 4 /backup/
- 4 /backup/sendgrid.json
- 4 /wp-config.php.backup
- 3 /backup/sendgrid_config
- 3 /backup/sendgrid_keys
- 3 /backup/sendgrid_keys.json
- 3 /db_backup.zip
- 3 /.env_backup
- 3 /var/backup/.env
database
Here, we can see some attempts of path treversal:
- 3 /db.sql
- 3 /dump.sql
- 3 /mysql/
- 3 /resources/docker/mysql/.env
- 2 /backup.sql
- 2 /database.sql
- 2 /db_backup.sql
- 2 /mysql.sql
- 2 /mysql.zip
- 2 /sqlite.zip
- 2 /sqllite.zip
- 2 /sql.zip
- 1 /?action=../../../../etc/mysql/my.cnf
- 1 /adminer-4.2.5-mysql-en.php
- 1 /adminer.sql
- 1 /admin/sqladmin.php
- 1 /admin/sql.conf
- 1 /app/config/database.yml.pgsql
- 1 /app/config/database.yml.sqlite3
- 1 /?asset=../../../../etc/mysql/my.cnf
- 1 /?callback=../../../../etc/mysql/my.cnf
- 1 /?config=../../../../etc/mysql/my.cnf
- 1 /config.sql
- 1 /?content=../../../../etc/mysql/my.cnf
- 1 /database_backup.sql
- 1 /?data=../../../../etc/mysql/my.cnf
- 1 /db/websql/index.php?lang=en
- 1 /debug.sql
- 1 /debug.sql.tar.gz
- 1 /debug.sql.zip
- 1 /delete.sql
- 1 /?dir=../../../../etc/mysql/my.cnf
- 1 /?doc=../../../../etc/mysql/my.cnf
- 1 /?document=../../../../etc/mysql/my.cnf
- 1 /etc/mysql/my.cnf
- 1 /etc/postgresql/14/main/pg_hba.conf
- 1 /etc/postgresql/14/main/postgresql.conf
- 1 /export.sql
- 1 /?file=../../../../etc/mysql/my.cnf
- 1 /?folder=../../../../etc/mysql/my.cnf
- 1 /?inc=../../../../etc/mysql/my.cnf
- 1 /?include=../../../../etc/mysql/my.cnf
- 1 /install.sql
- 1 /?lang=../../../../etc/mysql/my.cnf
- 1 /?layout=../../../../etc/mysql/my.cnf
- 1 /?load=../../../../etc/mysql/my.cnf
- 1 /?module=../../../../etc/mysql/my.cnf
- 1 /module/info/include/mysql/phpcms_info.sql
- 1 /mysql-admin/
- 1 /mysqladmin/
- 1 /mysqladmin.php
- 1 /mysql-admin/scripts/setup.php
- 1 /mysqladmin/scripts/setup.php
- 1 /mysql_backup
- 1 /mysql_backup/
- 1 /mysql_debug.sql
- 1 /mysql.dump
- 1 /.mysql.env
- 1 /mysqlmanager/
- 1 /mysql/scripts/setup.php
- 1 /?navigation=../../../../etc/mysql/my.cnf
- 1 /?page=../../../../etc/mysql/my.cnf
- 1 /?path=../../../../etc/mysql/my.cnf
- 1 /pgsql.dump
- 1 /pgsql.sql
- 1 /?read=../../../../etc/mysql/my.cnf
- 1 /?section=../../../../etc/mysql/my.cnf
- 1 /site.sql
- 1 /?source=../../../../etc/mysql/my.cnf
- 1 /sqladmin.php
- 1 /sql_backup
- 1 /sql_backup/
- 1 /sql.bak
- 1 /sql_dump.tar.gz
- 1 /sqlite/main.php
- 1 /sqlitemanager/main.php
- 1 /sqlmanager/
- 1 /sqlmanager/scripts/setup.php
- 1 /sql.php
- 1 /sql/phpmyadmin3/index.php?lang=en
- 1 /sql/phpmyadmin5/index.php?lang=en
- 1 /sql/sql/index.php?lang=en
- 1 /sqlweb/
- 1 /sqlweb/scripts/setup.php
- 1 /?template=../../../../etc/mysql/my.cnf
- 1 /?template_file=../../../../etc/mysql/my.cnf
- 1 /test/sqlite/SQLiteManager-1.2.0/SQLiteManager-1.2.0/main.php
- 1 /?theme=../../../../etc/mysql/my.cnf
- 1 /update.sql
- 1 /?view=../../../../etc/mysql/my.cnf
- 1 /websql/
- 1 /wordpress.sql
- 1 /wp-backup.sql
- 1 /wp-content/backup/database.sql
- 1 /wp-content/backup/data.sql
- 1 /wp-content/backup/db_backup.sql
- 1 /wp-content/backup/dbdump.sql
- 1 /wp-content/backup/db.sql
- 1 /wp-content/backups/database.sql
- 1 /wp-content/backups/data.sql
- 1 /wp-content/backups/dbdump.sql
- 1 /wp-content/backups/db.sql
- 1 /wp-content/backups/wordpress.sql
- 1 /wp-content/data.sql
- 1 /wp-content/db_backup.sql
- 1 /wp-content/dbdump.sql
- 1 /wp-content/db.sql
- 1 /wp-content/dump.sql
- 1 /wp-content/fullbackup.sql
- 1 /wp-content/fullwebsite.sql
- 1 /wp-content/mysql.sql
- 1 /wp-content/uploads/data.sql
- 1 /wp-content/uploads/db_backup.sql
- 1 /wp-content/uploads/dbdump.sql
- 1 /wp-content/uploads/db.sql
- 1 /wp-content/uploads/mysql.sql
- 1 /wp-content/wordpress.sql
Wordpress
- 137 /wordpress/wp-admin/setup-config.php
- 22 /wordpress
- 16 /wordpress/wp-admin/setup-config.ph%70?step=1&language=en_EN
- 13 /wordpress/
- 12 /wordpress/wp-includes/wlwmanifest.xml
- 8 /wordpress/ALFA_DATA/alfacgiapi/
- 6 /wordpress/wp-admin/includes/
- 6 /wordpress/wp-admin/includes/wp-admin/js/
- 6 /wordpress/wp-admin/setup-config.php?step=1
- 6 /wordpress/wp-content/uploads/
- 6 /wordpress/wp-includes/
- 3 /wordpress/wp-admin/includes/class-ftp.php
- 3 /wordpress/wp-admin/includes/class-wp-filesystem-base.php
- 2 /wordpress/.env