For the complete documentation index, see llms.txt. This page is also available as Markdown.
LINUXLINUX-PRIVESCSENSITIVE FILE DISCLOSURESSRF

Operation Coldstart

Wake up the staging server everyone left behind. - by l000gic & yathots

The following post by 0xb0b is licensed under CC BY 4.0


Scenario

Volt Labs, a small SaaS shop, suspects an old staging server has rotted into an exposed liability. Mara has assigned you the engagement. Find your way in and demonstrate full compromise.

Summary

Summary

In Operation Coldstart, we begin with external enumeration and discover FTP on port 21 with anonymous login enabled, alongside SSH on port 22 and a web server on port 80. Anonymous FTP access yields a backup.tar.gz containing the Flask source app.py, which reveals a URL Preview Service gated by a hostname allow-list of kestrel.thm and an /admin/ area restricted to loopback clients. Since kestrel.thm resolves to 127.0.0.1 via the server's internal /etc/hosts, the allow-list check passes while the request still hits localhost, enabling SSRF against the internal admin endpoint. We preview http://kestrel.thm/admin/notes through the /preview route, extract the webdev credentials stored in admin_notes.txt, and log in via SSH to retrieve the user flag at /home/webdev/user.txt.

For privilege escalation, we enumerate cron jobs and find /etc/cron.d/voltlabs-backup running tar czf with a wildcard * inside /opt/backups as root. Since webdev can write to that directory, we exploit tar's wildcard injection by creating files named --checkpoint=1 and --checkpoint-action=exec=sh shell.sh alongside a shell.sh that copies /bin/bash to /tmp and sets the SUID bit. When the cron job fires, tar interprets the crafted filenames as flags and executes our script as root, after which /tmp/bash -p drops us into a root shell and we read the final flag at /root/flag.txt.

Recon

We use rustscan -b 500 -a operation-coldstart.thm --top -- -sC -sV -Pn to enumerate all TCP ports on the target machine, piping the discovered results into Nmap which runs default NSE scripts -sC, service and version detection -sV, and treats the host as online without ICMP echo -Pn.

A batch size of 500 trades speed for stability, the default 1500 balances both, while much larger sizes increase throughput but risk missed responses and instability.

We have three ports open, FTP on port 21 which allows anonymous login, SSH on port 22 and a web server running on port 80.

We log in to the FTP server using anonymous and without a password, and find a backup in the public directory, which we download.

We unzip this backup and find a file named app.py. It's likely the source code for the site.

The site is a flask-based "URL Preview Service" for Volt Labs that fetches a user-supplied URL and renders its contents on the page, gated by a hostname allow-list kestrel.thm. It also exposes an /admin/ area restricted to loopback clients, including an /admin/notes endpoint that reads admin_notes.txt from disk.

From the comments, we can see that kestrel.thm resolves to 127.0.0.1 in the internal /etc/hosts file. And the admin endpoint is only accessible internally.

Shell as webdev

We open the page and see the URL preview in front of us.

We are unable to identify any additional directories using our Feroxbusters directory scan.

We first try to get a preview from localhost, but we get a message saying it's not on the allow list. As expected.

However, we can preview the page at http://kestrel.thm.

We also have access to the internal admin endpoint.

We preview the /admin/note endpoint and receive the credentials from the webdev user.

Next, we try to log in via SSH using the credentials and are successful. We are webdev and find the user flag at /home/webdev/user.txt.

Shell as root

While enumerating the targets, we came across the following cron job, which uses tar run by root.

The command allows a wildcard injection like depicted in hacktricks:

When root's cron runs tar czf out.tgz * in a directory you can write to, the shell expands * to whatever filenames exist there. So files named --checkpoint=1 and --checkpoint-action=exec=sh shell.sh get passed to tar as actual command-line flags. Tar's --checkpoint-action=exec= runs a shell command at each checkpoint, so it executes shell.sh as root.

With that we copy the bash binary to tmp and make it a SUID binary owned by root.

Once root runs e.g. tar -czf /root/backup.tgz *, shell.sh is executed as root.

After a short duration we have the SUID bash binary at tmp.

We run it in the context of root and get a root shell. The final flag is found at /root/flag.txt.

Last updated