Skip to main content

Krampus apporte une vulnérabilité Struts en fin d’année

Écrit par
feature snyk honeycomb

2 janvier 2024

0 minutes de lecture

Note de la rédaction : 16 janvier 2024

Un exemple de code dans cet article a été mis à jour afin de renforcer la sécurité du correctif.

Le 20 décembre 2023, le NIST a mis à jour une CVE pour signaler une nouvelle vulnérabilité de traversée de chemin dans struts-core. Il s’agit de CVE-2023-50164, également répertoriée dans la Snyk Vulnerability Database, avec un score CVSS de gravité critique de 9,8. Si vous travaillez dans la cybersécurité depuis suffisamment longtemps, vous vous souvenez de la fuite de données d’Equifax en 2017, elle aussi due à une vulnérabilité Struts non corrigée.

Dans cet article, je présente le problème, explique sa gravité, vous guide à travers un exploit de preuve de concept et vous donne des conseils pour y remédier. Et si vous utilisez Snyk (inscrivez-vous gratuitement ici), vous pouvez facilement recevoir des notifications sur cette vulnérabilité et d’autres, ainsi que des conseils pour corriger le code vulnérable !

Commençons par les conseils de correction. Il vous suffit de mettre à niveau votre version de Struts vers la 2.5.33 ou la 6.3.0.2 (ou une version ultérieure), selon la version de base que vous utilisez.

Quelle est la gravité de la vulnérabilité de traversée de chemin CVE-2023-50164 dans Struts ?

Nous avons déjà vu le pire scénario d’une vulnérabilité dans Struts pouvant entraîner l’exécution de code arbitraire. Cette vulnérabilité a provoqué en 2017 la célèbre fuite de données d’Equifax. L’exploitation consistait à envoyer un en-tête Content-Type malformé et à tirer parti de l’exception qui en résultait. C’était le pire type de vulnérabilité : l’exploit pouvait être exécuté à distance et aucun contrôle dans le code ne permettait de s’en protéger à l’époque.

Cette nouvelle vulnérabilité est grave : vous devez veiller à mettre à jour votre version de Struts. Elle est néanmoins moins grave que celle de 2017, car son exploitation nécessite à la fois du code non sécurisé et une version vulnérable de Struts.

Cette vulnérabilité permet une traversée de chemin lors du téléversement d’un fichier. Autrement dit, vous pouvez téléverser un fichier et « sortir » du dossier de téléversement prévu en indiquant un chemin relatif. Cela peut entraîner l’exécution de code à distance, car le chemin relatif peut pointer vers les dossiers servis par l’application.

Preuve de concept d’un exploit de traversée de chemin pour Struts CVE-2023-50164

Tout le code présenté dans cet article est disponible sur GitHub.

Il s’agit d’un projet Java qui utilise Maven pour les builds. Le projet comporte deux profils : l’un s’appelle vuln (c’est le profil par défaut) et l’autre no-vuln. Si vous ne connaissez pas Maven ou ses profils, pas d’inquiétude ! L’exécution est très simple et le profil par défaut est la version vulnérable. La seule condition requise est un environnement d’exécution Java, version 17 ou ultérieure. 

Accédez au dossier du projet que vous avez cloné, puis exécutez la commande suivante :

./mvnw clean jetty:run

Vous pouvez accéder au serveur à l’adresse http://localhost:9999/struts-vuln-poc. Vous y trouverez une interface très sommaire permettant de téléverser des fichiers. Mais ce n’est pas là que nous allons passer notre temps. Pour prouver que la démo ne repose sur aucun tour de passe-passe, accédez à http://localhost:9999/struts-vuln-poc/rogue.jsp. Vous devriez voir une page 404 comme celle-ci :

Page d’erreur HTTP 404 indiquant que le fichier JSP /rogue.jsp est introuvable, propulsée par Jetty 9.4.46

Nous voulons exploiter cette vulnérabilité. Ouvrez donc un terminal, accédez au dossier du projet et préparez-vous à utiliser le client HTTP de votre choix. Dans mon exemple, j’utilise curl, mais vous pouvez utiliser n’importe quel client HTTP prenant en charge le téléversement de fichiers. Exécutez la commande suivante :

curl \
http://localhost:9999/struts-vuln-poc/upload.action \
-F "Upload=@./payload/rogue.jsp" \
-F "uploadFileName=../src/main/webapp/rogue.jsp"

Accédez maintenant à http://localhost:9999/struts-vuln-poc/rogue.jsp. Le message suivant devrait alors s’afficher : Ya been PWNED!.

Maintenant que nous avons exploité la vulnérabilité en déposant un fichier malveillant dans un dossier servi par notre application, examinons comment nous en sommes arrivés là.

J’ai indiqué plus haut que l’exploitation nécessitait à la fois une version vulnérable de la bibliothèque Struts et du code non sécurisé.

Commençons par examiner le code non sécurisé. L’action Upload possède trois propriétés :

private File upload;
private String uploadFileName;
private String uploadContentType;

En raison du fonctionnement de Struts, ces propriétés sont renseignées à partir de la requête envoyée par un client HTTP (qu’il s’agisse de votre navigateur ou d’un utilitaire en ligne de commande comme curl).

La méthode execute traite la requête une fois les propriétés définies.

Voici le cœur de la méthode execute :

String uploadDirectory = System.getProperty("user.dir") + "/uploads/";
File destFile = new File(uploadDirectory, uploadFileName);
FileUtils.copyFile(upload, destFile);

Vous repérez le problème ? Plutôt que de nous fier à notre capacité à repérer les bugs, nous pouvons utiliser Snyk pour identifier le problème dans ce code. Snyk propose une interface en ligne de commande (CLI), des intégrations IDE et une interface web ; vous pouvez donc l’utiliser où que vous travailliez. 

Voici le résultat de l’exécution de snyk code test dans la CLI, qui utilise les capacités de test de sécurité des applications statique (SAST) de Snyk :

Terminal montrant l’analyse statique de Snyk détectant une vulnérabilité critique de traversée de chemin dans Upload.java, à la ligne 23.

Ce résultat indique le numéro de la ligne précise où se trouve une vulnérabilité, ainsi que son type : Path Traversal. Il indique également la gravité du problème, qui est High.

Voici le résultat de l’exécution de snyk test dans la CLI, qui utilise les capacités d’analyse de la composition logicielle (SCA) de Snyk :

Sortie du terminal de Snyk test montrant une vulnérabilité critique d’exécution de code à distance dans Apache Struts et recommandant une mise à niveau de la version 6.3.0.1 vers la version 6.3.0.2

Ce résultat indique que j’ai une dépendance présentant une vulnérabilité connue : ma version de struts-core, la version 6.3.0.1. Il me recommande également de la mettre à jour vers la version 6.3.0.2.

Ces deux résultats sont très utiles et permettent d’agir. Mais l’intégration à l’IDE offre une vue encore plus pertinente et complète.

J’ai chargé le projet dans IntelliJ IDEA, l’IDE que j’utilise pour développer en Java. Voici les résultats de l’analyse Snyk du projet :

L’extension Snyk pour IntelliJ IDEA détecte une vulnérabilité d’exécution de code à distance dans un composant Struts.

Dans le volet de gauche, je vois les résultats SAST et SCA. Dans celui de droite, je vois les conseils de correction spécifiques.

Encore plus utile : lorsque je clique sur le problème dans le code, un volet affiche les corrections apportées à ce même problème dans trois autres projets open source :

L’extension Snyk pour IntelliJ IDEA détecte une vulnérabilité de traversée de chemin dans Struts.

Examinons l’exemple google/j2objc. En faisant défiler la page, je peux voir les lignes de code modifiées pour corriger cette vulnérabilité, présentées au format diff :

Analyse de sécurité Snyk montrant une vulnérabilité critique dans Apache Struts et un diff de code ajoutant une vérification de validation du chemin canonique

Cela me donne une longueur d’avance considérable pour corriger le code, surtout si je ne connais pas très bien la vulnérabilité.

Correction de la vulnérabilité de traversée de chemin

Grâce aux informations fournies par les outils d’analyse de Snyk, je peux maintenant corriger à la fois mon code personnalisé et mes dépendances.

Commençons par le code. Exécutez la commande suivante avec curl ou un autre client HTTP :

curl \
http://localhost:9999/struts-vuln-poc/upload-no-vuln.action \
-F "Upload=@./payload/rogue.jsp" \
-F "uploadFileName=../src/main/webapp/rogue.jsp"

Un message d’erreur s’affiche alors : Attempted path traversal attack. En suivant les indications de l’analyse Snyk Code, j’ai modifié le code comme suit :

File uploadDirectory = new File(USER_DIR + "/uploads/");
File destFile = new File(uploadDirectory, uploadFileName);

if (
  !destFile.getCanonicalPath().startsWith(uploadDirectory.getCanonicalPath() + File.separator)
  !upload.getCanonicalPath().startsWith(USER_DIR)
) {
  throw new SecurityException("Attempted path traversal attack");
}

FileUtils.copyFile(upload, destFile);

L’instruction if vérifie que destFile utilise le chemin canonique du répertoire uploads défini. Elle vérifie également que le fichier téléversé se trouve dans les dossiers définis par l’application, et non dans un emplacement malveillant. Lors du téléversement, Struts place automatiquement le fichier dans un dossier temporaire de son chemin de classes. Si ces vérifications échouent, une SecurityException est levée.

Cette modification renforce considérablement la protection contre les traversées de chemin par rapport au code d’origine.

Un détail mérite d’être souligné. Remarquez que la vérification ajoute une barre oblique finale (/) à l’aide de l’appel File.separator. C’est très important : sans cela, un utilisateur malveillant pourrait toujours sortir du dossier uploads au moyen d’une attaque exploitant un chemin partiel. En effet, getCanonicalPath() renvoie toujours une valeur sans barre oblique finale. Jonathan Leitschuh l’explique très bien dans sa présentation à DefCon Jonathan Leitschuh. Une correction encore plus robuste utilise getCanonicalFile().toPath() ; elle est incluse dans le dépôt associé à cet article. 

L’essentiel à retenir, c’est que la mise à jour vers la dernière version de Struts est la meilleure façon de résoudre durablement le problème de traversée de chemin. Vous n’aurez pas à compter sur des vérifications manuelles dans le code, car les chemins relatifs sont automatiquement supprimés.

Nous pouvons renforcer encore notre posture de sécurité en appliquant les autres recommandations de l’analyse Snyk. Autrement dit, nous pouvons mettre à niveau struts-core, de la version 6.3.0.1 à la version 6.3.0.2.

Le projet du dépôt que vous avez cloné précédemment comporte un profil utilisant la version mise à jour de struts-core. Arrêtez l’application en cours d’exécution et supprimez le fichier src/main/webapp/rogue.jsp téléversé précédemment. Relancez l’application avec la commande suivante :

./mvnw clean jetty:run -P no-vuln

Exécutez ensuite la commande curl initiale :

curl \
http://localhost:9999/struts-vuln-poc/upload.action \
-F "Upload=@./payload/rogue.jsp" \
-F "uploadFileName=../src/main/webapp/rogue.jsp"

Cette fois, un message de réussite s’affiche, par exemple : File uploaded successfully to /CVE-2023-50164-POC/uploads/rogue.jsp

Remarquez que le chemin relatif fourni a été ignoré. Vous vous demandez peut-être pourquoi nous n’avons pas obtenu le message d’erreur affiché précédemment. C’est parce que la version mise à jour de struts-core assainit automatiquement les chemins fournis en entrée. 

Si vous définissez un point d’arrêt à la ligne 23 de la classe Upload.java, voici ce que vous verrez en envoyant la requête curl :

Vue du débogueur montrant uploadFileName défini sur « rogue.jsp », ainsi que le répertoire de téléversement et les variables de fichier de destination.

Le chemin relatif que j’ai fourni a été supprimé de uploadFileName.

Gardez une longueur d’avance sur les vulnérabilités avec Snyk

J’espère que cette démonstration de la vulnérabilité de traversée de chemin dans les anciennes versions de struts-core vous a été utile. Tout le code présenté dans cet article est disponible sur GitHub.

Les outils d’analyse de Snyk ont détecté le problème à la fois dans la déclaration des dépendances du projet et dans le code personnalisé.

Vous pouvez encore gagner en productivité en utilisant Snyk pour surveiller activement votre projet et recevoir automatiquement des notifications lorsque de nouvelles vulnérabilités sont découvertes. Snyk peut même créer automatiquement une pull request (PR) en votre nom lorsqu’une nouvelle vulnérabilité est détectée. Il ne vous reste plus qu’à la vérifier et à la fusionner !

Au lieu de passer au crible les CVE et de vérifier manuellement si votre code présente un problème, concentrez-vous sur ce que vous faites le mieux : coder ! Essayez Snyk gratuitement sur : https://app.snyk.io

Autres ressources pour gérer Apache Struts :

Lancez-vous dans le Capture the Flag

Découvrez comment résoudre des défis Capture the Flag en regardant à la demande notre atelier virtuel d’initiation.

Lire la suite

Blog

Les modèles de pointe ont trouvé les vulnérabilités. Seul l’attaquant a trouvé les chaînes d’exploitation.

L’analyse statique a détecté les failles, mais seuls des tests d’attaque en conditions réelles ont prouvé comment elles pouvaient être enchaînées pour provoquer des compromissions. Comparaison d’Evo COS, de Claude Security et de Claude Code Security.

feature insights context
Blog

Les attaques autonomes sont déjà là. La défense doit suivre leur rythme.

Les attaquants autonomes réduisent la fenêtre de défense. Découvrez comment la découverte, la correction, la validation et la prévention continues peuvent aider les équipes de sécurité à suivre le rythme.

Blog

Pourquoi les agents de codage IA créent-ils sans cesse des failles de contrôle d’accès ?

Les agents de codage IA peuvent générer une logique d’autorisation qui compile et passe la revue, tout en exposant les données d’un tenant à un autre. Découvrez pourquoi les failles de contrôle d’accès sont difficiles à détecter et comment les prévenir.