
Rapport d'enquête sur les vulnérabilités Struts2 S2-045, S2-055 et Jackson CVE-2017-7525, CVE-2017-15095.
Un article de synthèse résumant les points essentiels et facile à lire a été publié. Recommandé à ceux qui veulent un aperçu ou qui manquent de temps.
Le 1er décembre 2017, une mise à jour de sécurité pour Struts2 a été publiée. Avant la publication, des discussions circulaient sur la liste de diffusion concernant le lien avec une vulnérabilité de Jackson (une bibliothèque JSON populaire en Java). L'auteur, qui utilise Jackson dans des systèmes et outils internes, s'intéressait également au contenu précis.
En réalité, les deux problèmes de sécurité suivants ont été corrigés dans la publication. Seule la vulnérabilité S2-055 est affectée par la vulnérabilité de jackson-databind, un composant de Jackson.
Dans le plugin REST, il semble que les gestionnaires utilisant JSON-lib et ceux utilisant Jackson étaient déjà intégrés, permettant à l'utilisateur de choisir. La correction dans son ensemble semble être : dans S2-054, le gestionnaire par défaut a été changé pour Jackson, et dans S2-055, la version obsolète de Jackson a été mise à jour vers la dernière.
Alors, quelle est exactement la vulnérabilité CVE-2017-7525 ? L'auteur, qui utilise habituellement Jackson pour le traitement JSON en Java, a passé le week-end des 2 et 3 décembre à étudier ce problème, ce qui fait l'objet de cet article.
Environnement de l'auteur utilisé pour la vérification du code d'exemple :
Une explication de CVE-2017-7525 a été publiée sur le blog d'Adam Caudill.
En résumé avec mes propres mots : jackson-databind fournit une fonctionnalité (classe ObjectMapper) pour mapper du JSON vers des objets Java.
En appelant ObjectMapper.enableDefaultTyping(), il devient possible de mapper en utilisant un nom de classe intégré arbitrairement dans le JSON.
Certains d'entre vous ont peut-être eu un mauvais pressentiment en lisant « possibilité de spécifier un nom de classe depuis le JSON d'entrée », et c'est exactement ce mauvais pressentiment qui s'est réalisé avec CVE-2017-7525.
Avant d'entrer dans l'explication de la vulnérabilité, je vais d'abord expliquer pourquoi une telle fonctionnalité a été implémentée.
ObjectMapper.enableDefaultTyping()Veuillez consulter le code d'exemple suivant pour l'utilisation de base de la désérialisation par jackson-databind. (Dans cet article, j'utilise Groovy pour les exemples de code Jackson. @Grab permet de changer facilement la version de jackson-databind.)
Dans le code d'exemple ci-dessus, la clé "animal" peut simplement être mappée directement à la classe Animal. Que se passe-t-il dans le cas suivant ?```java class Zoo { Animal animal; }
abstract class Animal { String name; protected Animal() { } }
class Dog extends Animal { double barkVolume; Dog() { } }
class Cat extends Animal { boolean likesCream; int lives; Cat() { } }
Dans cette configuration, il y a deux cas : le contenu de la clé "animal" fait référence à la classe Dog ou à la classe Cat. Par conséquent, des informations supplémentaires sont nécessaires pour savoir avec quelle classe mapper.
Pour résoudre ce problème, jackson-databind a intégré un traitement personnalisé permettant d'incorporer le nom de la classe à mapper dans le JSON.
Par exemple, comme ci-dessous, le contenu de la clé "animal" est transformé en tableau, et le premier élément spécifie le nom de la classe.```
{"animal":["Dog",{"name":"dog1","barkVolume":1.2}]}
Cela permet à ObjectMapper.readValue() de reconnaître que le contenu de la clé "animal" est de la classe Dog et d'effectuer le mappage en conséquence.
Bien sûr, il n'est pas possible de distinguer si le contenu de la clé "animal" était à l'origine un tableau ou s'il contient des informations de nom de classe propres à jackson-databind.
C'est la méthode ObjectMapper.enableDefaultTyping() qui permet de basculer entre ces deux cas.
Il existe également une autre méthode consistant à définir l'annotation @JsonTypeInfo sur la classe. Pour plus de détails, consultez la documentation Jackson suivante :
Voici un exemple de code utilisant réellement la méthode ObjectMapper.enableDefaultTyping().
Comme nous l'avons vu ci-dessus, en donnant un nom de classe suivi de ses propriétés en JSON, il est possible, bien qu'avec certaines limitations, de créer une instance de n'importe quelle classe avec des propriétés arbitraires.
C'est cette exploitation qui est à l'origine de la vulnérabilité CVE-2017-7525, et le rapport suivant en est probablement à l'origine :
Un rapport a signalé que des bibliothèques de sérialisation/désérialisation Java couramment utilisées, telles que Jackson, présentent un risque d'exécution de code arbitraire par manipulation des noms de classe, et une liste de noms de classes spécifiques potentiellement dangereuses a été dressée.
On ne sait pas si c'est en réaction à cela, mais peu de temps après le premier commit du dépôt ci-dessus, le problème suivant a été soumis sur jackson-databind et la correction a commencé :
À quoi ressemblent concrètement les données JSON et le code Java exploitant cette vulnérabilité ?
Le code de test de jackson-databind 2.8.9, qui corrige ce problème, fournit un indice :
Basé sur ce code de test, voici un exemple de code ajusté pour permettre une vérification fonctionnelle :