ECW 2018 - Web - Troll.JSP

L’ECW 2018 est un challenge Jeopardy français organisé par le Pôle d’Excellence Cyber en partenariat avec la Région Bretagne, Airbus et Thales. “Troll.JSP” est un challenge basé sur l’exploitation de la CVE-2017-5638 (Apache Struts) afin de changer la valeur d’une variable de session.

Du 06/10 au 21/10 2018, il sert de pré-sélection pour l’épreuve finale de type Capture The Flag qui débutera le 21 novembre 2018 lors de la conférence de l’European Cyber Week à Rennes.

Découverte

Pas vraiment d’énoncé pour ce challenge, puisque l’image suivante était affichée en guide d’énoncé. D’un côté, pas étonnant vu le nom du challenge.

ECW2018_troll_1

On arrive donc sur une page, très épurée, ne contenant quasiment pas d’informations.

ECW2018_troll_2

Recherche d’informations et identification de la vulnérabilité

Première chose, on file dans les sources de la page. On y trouve le fichier “debug.jsp”.

https://web125-trolljsp.challenge-ecw.fr/debug.jsp

ECW2018_troll_3

L’accès à cette page offre plus de fonctionnalités. On y trouve notamment un onglet “flag”, avec un cadeau…

ECW2018_troll_4

Inutile de préciser que connaissant l’essence de ce challenge, ce “flag” est un troll. Néanmoins, il permet d’avancer dans le challenge puisqu’une fois le hash MD5 cassé, on obtient l’information suivante : “swp”. Quelques recherches sur les extensions “.swp” nous amènent à penser qu’un fichier swap important pour le challenge est disponible. Il n’est pas rare que ces fichiers soient des fichiers cachés. Quelques tests sont donc réalisés et assez rapidement on trouve le fichier ”.flag.jsp.swp”.

https://web125-trolljsp.challenge-ecw.fr/.flag.jsp.swp

ECW2018_troll_5

Trois points intéressants ici :

  • le commentaire “TODO change the flag” ;
  • Un nouveau hash MD5 ;
  • Une variable de session, “flag” est affichée.

Premièrement, le hash MD5. S’il s’agit du même fonctionnement que pour le premier, il pourrait également nous donner des informations… Et en effet, une fois décodé, on obtient la chaine “equifax”. Quelques recherches au sujet de ce terme nous amènent à découvrir une histoire au sujet de l’entreprise Equifax qui s’est fait piraté en 2017. La cause ? Une vulnérabilité dans le framework Apache Struts (CVE-2017-5638). Il semble donc que nous sachions par quel biais nous allons résoudre ce challenge… Avant de partir dans l’exploitation, un rapide point sur les deux autres éléments.

Il semble que la page affiche la valeur de la variable “flag” si la valeur de session.flag n’est pas nulle. Sinon, on reçoit l’affichage de trollFlag, qu’on connaît déjà. Qui plus est, dans la mesure où ce fichier est un fichier swap et qu’il y a un commentaire “TODO change the flag” on peut supposer que dans le vrai fichier, le flag a été changé par le flag réel. Ainsi, on a une ligne directrice ! Il faut trouver un moyen de modifier la valeur de session.flag.

Exploication - CVE-2017-5638

Quelques recherches autour de cette vulnérabilité nous amènent sur les deux articles suivants, détaillant très bien l’ensemble :

Très brièvement, l’exploitation de cette dernière passe par l’utilisation du header “Content-Type” des requêtes HTTP.

  • Une requête HTTP de type “multipart” va être détectée par Struts ;
  • Celle ci sera envoyée au parseur Jakarta ;
  • Si une erreur survient lors du traitement (par exemple, un Content-Type invalide) le message d’erreur généré contiendra l’élément invalide ;
  • Afin d’afficher l’erreur à l’utilisateur, une fonction non sécurisée, findText() est utilisée. Cette dernière va évaluer la chaine qui lui est passée. Ainsi, si une charge malveillante est placée à cet endroit, elle sera exécutée.

Cette vulnérabilité permet en théorie de réaliser de l’exécution de commande directement le serveur. La charge malveillante utilisée dans beaucoup d’exemples sur Internet est la suivante :

Content-Type: %{(#_='multipart/form-data').(#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(@java.lang.Runtime@getRuntime().exec('curl localhost:8000'))}

Quelques tests ont permis de mettre en évidence le fait que ce type de charge ne fonctionnait pas sur le serveur. En effet, il semble que nous soyons face à un WAF (Web Application Firewall) qui va empêcher la requête de passer s’il détecte certains éléments (des parenthèses ou des points, par exemple). Ainsi, il faudra se passer de ces éléments.

Qui plus, dans notre cas, nous cherchons à modifier la valeur d’une variable de session. Pas besoin d’exécuter du code via @java.lang.Runtime@getRuntime().

On remarque que dans le payload, la première expression (#_='multipart/form-data') sert uniquement à passer la condition nécessaire au déclenchement de la vulnérabilité. Il s’agit de l’affectation d’une chaine à une variable. N’est ce pas exactement ce dont nous avons besoin ?

Quelques recherches et tests supplémentaires permettent de construire une charge utile de ce type :

Content-Type: %{#session["flag"]="multipart/form-data"}

De cette façon, on satisfait la condition côté serveur, indiquant que la chaine “multipart/form-data” doit être présente et on satisfait la condition côté application disant que la valeur de session.flag ne doit pas être nulle. De plus, aucun caractère interdit n’est utilisé, le pare-feu n’est donc pas déclenché.

Récupération du flag

Afin de faire passer notre exploit, on intercepte une requête classique vers la page /flag.jsp à l’aide de Burp, à laquelle on ajoute notre header malveillant.

ECW2018_troll_6

On observe que la requête est passée et que le flag retourné est différent… Challenge réussi ! ;)

Références

ECW 2018 - Web - SysIA ECW 2018 - Web - Intrusion (5 challenges)