Hack The Box: Late


Jump Ahead: EnumUserRootResources

TL;DR;

To solve this machine, we begin by enumerating open ports using nmap – finding ports 22 and 80 open. From the webserver, we find an FQDN, which we use to enumerate an additional vHost. Going to the vHost, we find it is hosting an application to convert images to text using the Flask framework. Exploiting an SSTI (Server-Side Template Injection) RCE (Remote Code Execution) vulnerability in the application, we are able to get the SSH key of the svc_acc user, and login and read user.txt. Enumerating the machine further, we find a file that stands out. After further analysis, we see that we are able to append to the file. After triggering an SSH event, root will execute the file. This allows us to get a reverse shell as root – gaining access to root.txt.

Enumeration

Like all machines, we begin by enumerating open ports using nmap. From our scans, we find ports 22 and 80 open.

$ sudo nmap -v -p- --min-rate 3000 $RHOST
[...]
$ sudo nmap -v -n -Pn -sV -A -p 22,80 -oA enum/nmap/tcp-scripts $RHOST
# Nmap 7.92 scan initiated Wed May  4 08:40:25 2022 as: nmap -v -n -Pn -sV -A -p 22,80 -oA enum/nmap/tcp-scripts 10.129.112.17
Nmap scan report for 10.129.112.17
Host is up (0.064s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 02:5e:29:0e:a3:af:4e:72:9d:a4:fe:0d:cb:5d:83:07 (RSA)
|   256 41:e1:fe:03:a5:c7:97:c4:d5:16:77:f3:41:0c:e9:fb (ECDSA)
|_  256 28:39:46:98:17:1e:46:1a:1e:a1:ab:3b:9a:57:70:48 (ED25519)
80/tcp open  http    nginx 1.14.0 (Ubuntu)
|_http-title: Late - Best online image tools
|_http-favicon: Unknown favicon MD5: 1575FDF0E164C3DB0739CF05D9315BDF
|_http-server-header: nginx/1.14.0 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET HEAD
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 4.15 - 5.6 (95%), Linux 5.3 - 5.4 (95%), Linux 2.6.32 (95%), Linux 5.0 - 5.3 (95%), Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%), Linux 5.0 (93%)
No exact OS matches for host (test conditions non-ideal).
Uptime guess: 16.668 days (since Sun Apr 17 16:38:32 2022)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=263 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE
HOP RTT      ADDRESS
1   64.22 ms 10.129.112.17

Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed May  4 08:40:38 2022 -- 1 IP address (1 host up) scanned in 14.00 seconds

Since we have a webserver running on port 80, we do further enumeration using nikto and gobuster. These tools did not return any useful information. Since the TCP port count was low, we launch a basic UDP scan, however, this did not return any common ports. Going to the web server in the browser, we see the page is pretty simple.

At the bottom of the page, we find the FQDN late.htb at the bottom of the page.

After adding the FQDN to our /etc/hosts file, we start to enumerate it. Going late.htb in the browser returns the same page as accessing the site via IP address. Next, we use gobuster to enumerate vHosts, and find the vHost images.late.htb.

Going to the vHost in the browser, we learn it’s an application that converts images to text using Flask. Based on this, we know the application is likely written using Python, and makes use of the Jinja templating engine.

Running tools like nikto and gobuster on the vHost, unfortunately, do not yield any additional useful information.

Getting User

As we suspect the server is using Jinja, and image upload is all we can do on the website, we decide to test for Server-Side Template Injection (SSTI). Using BurpSuite to manipulate our request, we change the magic bytes of an image we upload, and see that we are able to leak the path on the system.

When we try to fuzz the filename parameter with code, we do not see any results.

Lastly, we try something simpler by putting the injection code in an image we upload. This time, it works, and we get the results in the response.

As we now have a working proof-of-concept, we check for Remote Code Execution (RCE) by seeing if we are able to run the id command on the system. Doing so, we see the application is running as the svc_acc account.

Next, we want to try to get a shell on the machine. As we know SSH is running on the machine, we can attempt to get svc_acc‘s SSH key. For this, we have to play around with the image settings, as the software is really finicky depending on the payload size. The settings we are able to use are “Bitstream Vera Sans Mono, 14pt” font, 1000×100 image size, and adjusting the kerning to 1.5. After uploading the payload, we get the user’s SSH key in the response, and login as svc_acc.

We are now able to read user.txt.

Getting Root

Having gained a shell on the machine as svc_acc, we begin enumeration using LinPEAS. From the output, we see that port 3000 is listening locally, however, when we look into it, we see that it’s the flask application we used to gain access to the system. Next, we see /usr/local/sbin/ssh-alert.sh listed as an interesting file modified within the past 5 minutes, and decide to look into it.

Looking at the file, we see that it sends the root user a system mail every time a user logs in.

To get addition context, and to look for a route to exploitation, we upload and execute pspy. We then start another SSH session. From pspy, we see the script get executed as root, then shortly after, we see cron jobs copy a fresh copy of the script, and set the script to be owned by svc_acc.

After some additional time, there appears to be a cron job that makes the script only appendable. As we will eventually own the file, we should be able to append a reverse shell and get access as root when the SSH event triggers the file execution. After starting a socket handler using netcat, we append the reverse shell after svc_acc is set as the owner, and execute the logout command. Doing so, we get a reverse shell as root, and can now read root.txt.

Thank you for taking the time to read my write-up. I am interested in other ways this machine has been solved. Feel free to reach out to me and we can discuss it. Thanks!

Resources