Logo
HackTheBox - LinkVortex Writeup

HackTheBox - LinkVortex Writeup

SH∆FIQ∆IM∆N SH∆FIQ∆IM∆N
April 28, 2025
1 min read
index

Nmap

Like always, I’m going to scan the IP Address by using nmap but I’m going to scan the full port first. Then, I’m going to scan the only open ports.

# Nmap 7.95 scan initiated Sun Feb 2 17:15:29 2025 as: /usr/lib/nmap/nmap -p22,80 -sCV -oN nmap/scripts.txt 10.10.11.47
Nmap scan report for 10.10.11.47
Host is up (0.060s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 3e:f8:b9:68:c8:eb:57:0f:cb:0b:47:b9:86:50:83:eb (ECDSA)
|_ 256 a2:ea:6e:e1:b6:d7:e7:c5:86:69:ce:ba:05:9e:38:13 (ED25519)
80/tcp open http Apache httpd
|_http-server-header: Apache
|_http-title: Did not follow redirect to http://linkvortex.htb/
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 at Sun Feb 2 17:15:38 2025 -- 1 IP address (1 host up) scanned in 8.42 seconds

The Nmap scan reveals that only two ports are open, SSH and HTTP. I decided to ignore port 22 since I don’t have any credentials.

Additionally, Nmap discovered the hostname linkvortex.htb, which I added to my /etc/hosts file.

add hostname to /etc/hosts

add hostname to /etc/hosts

Http: Linkvortex.htb

Navigating to http://linkvortex.htb, I found that the website is a blog about computer hardware. Scrolling to the footer, I noticed the text Powered by Ghost, indicating that the site uses the Ghost CMS.

website page

website page

website footer

website footer

Performing light enumeration, I reviewed all blog posts and discovered that they were created by a single user, which is admin.

user named admin

user named admin

Additionally, by inspecting the page source, I found the Ghost CMS version, which is 5.58.

view page source

view page source

I also attempted directory fuzzing on the website, which revealed multiple Ghost-related directories. However, the results were overwhelming and difficult to analyze. I also found the Ghost API, but authentication was required to interact with it.

Http: Dev.linkvortex.htb

Since I hadn’t found anything useful so far, I decided to perform virtual host enumeration using ffuf. Surprisingly, I discovered a new subdomain, dev.linkvortex.htb. I added this to my /etc/hosts file.

ffuf found dev vhost

ffuf found dev vhost

add dev to /etc/hosts

add dev to /etc/hosts

git-dumper

Upon visiting http://dev.linkvortex.htb, I found a simple “Launching Soon” page. I performed directory fuzzing and discovered that the .git directory was exposed.

website with launching soon page

website with launching soon page

I downloaded the entire .git directory using git-dumper. Then, I opened it in VSCode to analyze its Git history.
I found an interesting file, authentication.test.js, marked with M, indicating it had been Modified. Checking the changes, I found a password.

modified file

modified file

Ghost: Authenticated Arbitrary File Read

I previously identified the Ghost CMS version (5.58) but forgot to mention that I also searched for known vulnerabilities. I found a PoC exploit for CVE-2023-40028, which allows authenticated arbitrary file read.

In summary, this vulnerability allows authenticated users to upload symlinked files, enabling them to read arbitrary files on the system.

Summary

Ghost is an open source content management system. Versions prior to 5.59.1 are subject to a vulnerability which allows authenticated users to upload files that are symlinks.
This can be exploited to perform an arbitrary file read of any file on the host operating system. Site administrators can check for exploitation of this issue by looking for unknown symlinks within Ghost’s content/ folder.
Version 5.59.1 contains a fix for this issue. All users are advised to upgrade. There are no known workarounds for this vulnerability.

To exploit this, I needed valid credentials. Since I had found a password earlier, I assumed the username format was [email protected] and ran the PoC. It worked!

poc read /etc/passwd

poc read /etc/passwd

Next, I searched for the location of the Ghost CMS configuration file. Many sources pointed to /var/www/ghost, but I was unable to access it. Checking the .git directory again, I found a Dockerfile.ghost, which revealed that the configuration file is located at /var/lib/ghost/config.production.json.

location ghost config file

location ghost config file

Using the PoC, I successfully retrieved the Ghost config file, which contained another set of credentials belonging to bob.

poc read ghost config

poc read ghost config

User: Bob

I attempted to use Bob’s credentials to log in via SSH, and it worked! I now had access to the machine as bob. The first thing I checked was sudo permissions using sudo -l. I found that bob could run a script called clean_symlink.sh with sudo privileges.

ssh as bob

ssh as bob

Then I read the clean_symlink.sh file and found that it is a simple Bash script that checks if the provided argument ends with .png and verifies whether it is a symbolic link. If it is, the script moves it to quarantine.

Additionally, if CHECK_CONTENT is set to true, the script will read and display the file’s content. It also filters out links that contain either etc or root in their paths.

clean_symlink.sh
#!/bin/bash
QUAR_DIR="/var/quarantined"
if [ -z $CHECK_CONTENT ];then
CHECK_CONTENT=false
fi
LINK=$1
if ! [[ "$LINK" =~ \.png$ ); then
/usr/bin/echo "! First argument must be a png file !"
exit 2
fi
if /usr/bin/sudo /usr/bin/test -L $LINK;then
LINK_NAME=$(/usr/bin/basename $LINK)
LINK_TARGET=$(/usr/bin/readlink $LINK)
if /usr/bin/echo "$LINK_TARGET" | /usr/bin/grep -Eq '(etc|root)';then
/usr/bin/echo "! Trying to read critical files, removing link [ $LINK ] !"
/usr/bin/unlink $LINK
else
/usr/bin/echo "Link found [ $LINK ] , moving it to quarantine"
/usr/bin/mv $LINK $QUAR_DIR/
if $CHECK_CONTENT;then
/usr/bin/echo "Content:"
/usr/bin/cat $QUAR_DIR/$LINK_NAME 2>/dev/null
fi
fi
fi

Since the script filtered out symlinks pointing to etc or root, I bypassed the restriction using a chained symlink technique.

symlink chained

symlink chained

Finally, I read the root user’s SSH key using the same technique. I then used the key to SSH into the machine as root.

ssh as root

ssh as root