Nmap
# 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.
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.
Performing light enumeration, I reviewed all blog posts and discovered that they were created by a single user, which is admin
.
Additionally, by inspecting the page source, I found the Ghost CMS version, which is 5.58
.
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.
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.
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
.
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.
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!
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
.
Using the PoC, I successfully retrieved the Ghost config file, which contained another set of credentials belonging to bob
.
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.
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.
#!/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
Chained symlink
Since the script filtered out symlinks pointing to etc
or root
, I bypassed the restriction using a chained symlink
technique.
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
.