La vulnérabilité de ce composant a été exposée il y a longtemps, mais je l'ai rencontré à nouveau au travail récemment. J'ai récemment regardé des trucs de désérialisation Java, j'ai donc décidé de le retirer et de l'analyser. encore une fois, et je l'ai également rencontré pendant le processus. Quelques questions étranges.
La plupart des articles d'analyse sur Internet ajoutent manuellement la dépendance de commons-collections4-4.0 afin d'utiliser la charge utile CommonsCollections2 générée par ysoserial. Cependant, la situation que j'ai rencontrée est que l'utilisation de CommonsBeanutils1 peut directement réussir. pas répéter l’analyse de CommonsCollections2 en ligne.
Environnement de débogage :
JDK 1.8.0_72
Tomcat 8.0.30
Nous clonons d'abord le code source de shiro et passons à la branche problématique.
git clone https://github.com/apache/shiro.git shiro-rootcd shiro-root git checkout 1.2.0
Afin de faire fonctionner les exemples fournis avec Shiro, nous devons apporter quelques modifications au fichier samples/web/pom.xml. Nous devons changer la version jstl en 1.2 et supprimer le champ scope de servlet-api. . Mettez jstl-1.2.jar dans le répertoire WEB-INF/lib. Ensuite, vous devriez pouvoir exécuter et déboguer.
Nous définissons le point d'arrêt sur la méthode solvePrincipals dans org.apache.shiro.mgt.DefaultSecurityManager et envoyons une requête avec RememberMe Cookie, et elle devrait pouvoir se briser.
Nous continuons à suivre la méthode getRememberedIdentity :
Continuons à suivre la méthode getRememberedSerializedIdentity :
Dans cette méthode, le cookie que nous avons transmis est lu et décodé en base64 :
Connect Ensuite, Shiro appellera convertBytesToPrincipals et passez le tableau d'octets décodés en base64 en tant que paramètre :
Vous pouvez également le deviner grâce au nom de la fonction. Deux opérations sont effectuées, à savoir le décryptage et la désérialisation. Commençons par la partie décryptage. un simple débogage, il a été constaté qu'il s'agissait d'un déchiffrement AES et qu'il y avait une clé prédéfinie Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");. Dans l'exemple fourni avec Shiro, aucune autre méthode n'a été utilisée pour définir cela. clé secrète, donc la valeur par défaut est utilisée ici.
Le IV rencontré dans le décryptage AES est également obtenu à partir des premiers octets du cookie que nous avons transmis, nous pouvons donc facilement construire une valeur de cookie contenant n'importe quel contenu, et le texte brut déchiffré est un contenu sérialisé, la désérialisation est appelée pour la désérialisation.
Enfin, la méthode org.apache.shiro.io.DefaultSerializer#deserialize sera appelée pour la désérialisation :
L'ensemble du processus est très simple : lire le cookie -> décodage base64 -> décryptage -> Désérialisation
Notre charge utile est donc très simple à construire, je vais mettre le PoC complet sur mon GitHub.
Pendant le processus de débogage, j'ai rencontré quelques problèmes et la calculatrice n'est pas apparue avec succès. Enregistrons-le ici.
Je déboguais ce problème depuis longtemps. Je pensais que c'était un problème avec la charge utile ou que le code de Shiro avait changé avec le temps, car le cas que j'ai rencontré à ce moment-là était que la charge utile avait été introduite dans l'âme, ce qui m'a fait très confus. Plus tard, nous avons découvert un problème clé. Dans l'exemple que nous avons cloné depuis github, la version dépendante de commons-beanutils est 1.8.3 et la version de la charge utile générée par ysoserial est 1.9.2, elle ne peut donc pas être exécutée avec succès dans le échantillon par défaut de. J'ai donc changé le numéro de version en 1.9.2 et c'est devenu un succès.
Donc, dans le cas que nous avons rencontré, la version réelle de l'environnement dépendant peut également être comme ça, nous pouvons donc directement réussir.
Une fois ce projet cloné, vous pouvez voir qu'il existe un package commons-collections :
Une exception sera levée lors de la tentative d'utilisation de CommonsCollections1 fournie par ysoserial, et elle ne peut pas réussir
Lors du débogage, J'ai trouvé qu'il avait été lancé ici. Exception, la raison pour laquelle le tableau d'octets ne peut pas être désérialisé est très déroutante
J'ai vérifié la différence entre Class.forName() et ClassLoader.loadClass() en Java et j'ai découvert que forName() utilise toujours le ClassLoader() de l'appelant, tandis que loadClass() peut spécifier un ClassLoader différent. Alors, d'où vient ClasssLoader dans Shiro ? Il est obtenu via Thread.currentThread().getContextClassLoader();, qui est WebappClassLoader. Mais pourquoi cela indique-t-il que le tableau d'octets ne peut pas être chargé ? Après avoir cherché pendant un moment, j'ai vu la discussion ci-dessous sur le blog orange et j'ai appris la vérité sur tout cela :
Shiro resovleClass utilise ClassLoader.loadClass() au lieu de Class.forName(), ClassLoader.loadClass ne prend pas en charge le chargement de classes de type tableau.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!