
Template Injection dans les modèles d'email entraîne une exécution de code sur Jira Service Management Server
L'injection de modèles dans les modèles d'e-mail conduit à l'exécution de code sur Jira Service Management Server
J'ai expliqué comment déployer et déboguer ici, vous pouvez vous y référer.
Dans la Description de ce CVE, il est clairement indiqué que le bug se trouve dans la fonctionnalité Email Template. Avec les privilèges admin, l'utilisateur peut librement modifier les modèles des e-mails de notification. Ce bug nécessite des privilèges admin pour être exécuté, donc même s'il permet une RCE, il n'est pas très grave, mais j'ai quand même décidé d'écrire ce blog pour le plaisir :) ceux qui cherchent à comprendre SSTI peuvent s'y référer.
atlassian-jira-servicedesk-4.17.0-m0006-standalone et j'ai essayé de diff avec la version 4.18.0 pour voir les différences :

Quand j'ai vu ce tas, ... évidemment je n'ai pas lu chaque fichier un par un, je suis trop paresseux. Blague à part, en réalité, en plus du correctif de sécurité, les versions comportent aussi des améliorations et des changements de fonctionnalités. Tout différer et tout lire serait trop long. Bien sûr, dans certains cas, je dois m'y résoudre, mais dans ce cas, je ne l'ai pas fait =)))
SendBulkMail pour créer un e-mail de notification. Je ne détaillerai pas la pile d'appels de cet endpoint pour éviter de m'étendre, car elle est similaire à celle de CVE-2019-11581.Dans la fonction SimpleNote.render, je peux voir la variable de contexte pour voir ce que je peux utiliser :
D'après ma propre expérience, je fais attention à vérifier les contextes/classes contenant des mots-clés comme : Utils, Manager, service, ... . Je remarque le contexte $jirautils (classe com.atlassian.jira.util.JiraUtils) qui contient la méthode public static <T> T loadComponent(String className, Class<?> callingClass) :
Je cherche de la documentation pour comprendre son fonctionnement, comment l'utiliser, car l'entrée est un String className et la sortie est une classe, il est donc très probable que cette méthode charge une classe arbitrairement :
Je vais dans System -> Email templates et télécharge le modèle actuel sur ma machine
Il y a de nombreux modèles différents, chacun utilisé pour un type de notification différent, donc je cherche un fichier qui peut être utilisé pour plusieurs types de notifications, comme email\html\includes\header.vm
Après avoir téléchargé le modèle modifié, et en utilisant SendBulkMail pour envoyer un e-mail de notification, j'obtiens la sortie suivante dans l'e-mail :
En gros, cette classe n'a pas de constructeurs ou n'est pas acceptée. J'essaie d'utiliser une autre classe avec un constructeur (constructeur sans paramètre) pour voir :
Et le résultat est comme prévu. J'ai réussi à obtenir la classe :
On voit que 3 classes sont ajoutées à la liste noire :
org.springframework.expression.spel.standard.SpelExpressionParser,\
com.atlassian.jira.component.ComponentAccessor,\
com.atlassian.jira.plugin.ComponentClassManager
En examinant chaque classe, je remarque que la classe SpelExpressionParser a une méthode public SpelExpression parseRaw(String expressionString). Après une brève recherche sur Google pour trouver comment l'utiliser, cela ressemble à ceci :
#set($SpelExpressionParser = $jirautils.loadComponent('org.springframework.expression.spel.standard.SpelExpressionParser',$i18n.getClass()))
$SpelExpressionParser.parseRaw("T(java.lang.Runtime).getRuntime().exec('calc')")
Comme vous le voyez, la méthode parseRaw renvoie un objet de classe org.springframework.expression.spel.standard.SpelExpression sans réellement évaluer l'expression que j'ai fournie. Je regarde la classe SpelExpression et vois la méthode getValue :
@Nullable
public Object getValue() throws EvaluationException {
CompiledExpression compiledAst = this.compiledAst;
if (compiledAst != null) {
try {
EvaluationContext context = this.getEvaluationContext();
return compiledAst.getValue(context.getRootObject().getValue(), context);
} catch (Throwable var4) {
if (this.configuration.getCompilerMode() != SpelCompilerMode.MIXED) {
throw new SpelEvaluationException(var4, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION, new Object[0]);
}
}
this.compiledAst = null;
this.interpretedCount.set(0);
}
ExpressionState expressionState = new ExpressionState(this.getEvaluationContext(), this.configuration);
Object result = this.ast.getValue(expressionState);
this.checkCompile(expressionState);
return result;
}
En regardant l'entrée/sortie, en tant que personne jouant sur le spirituel, je décide d'essayer directement sans lire la documentation :
#set($SpelExpressionParser = $jirautils.loadComponent('org.springframework.expression.spel.standard.SpelExpressionParser',$i18n.getClass()))
$SpelExpressionParser.parseRaw("T(java.lang.Runtime).getRuntime().exec('calc')").getValue()
et le résultat :
Ainsi, nous avons pu contourner le sandbox de Velocity pour obtenir une RCE. Cependant, en me mettant à la place de l'auteur - la personne qui a découvert ce bug - il y a encore des questions auxquelles je ne peux pas répondre :
En étudiant ces trois classes, je vois qu'elles peuvent former une chaîne :
ComponentAccessor.getComponentClassManager()
=> ComponentClassManager.newInstance(String className) (Cette classe peut charger une classe arbitraire)
=> SpelExpressionParser
Si l'auteur utilisait $jirautils comme moi, il n'aurait pas besoin des deux autres classes. Mais si l'auteur n'utilisait pas $jirautils, comment aurait-il pu obtenir ComponentAccessor ???