
ईमेल टेम्पलेट्स में टेम्पलेट इंजेक्शन Jira Service Management Server पर कोड निष्पादन की ओर ले जाता है
ईमेल टेम्पलेट्स में Template Injection, Jira Service Management Server पर कोड निष्पादन की ओर ले जाता है
मैंने deploy + debug का निर्देश यहाँ दिया है, आप संदर्भ ले सकते हैं।
इस CVE के Description में भी स्पष्ट रूप से कहा गया है कि bug Email Template सुविधा में है। admin अधिकार के साथ, उपयोगकर्ता सूचना ईमेल के टेम्पलेट को स्वेच्छा से संपादित कर सकता है। इस bug को करने के लिए admin अधिकार की आवश्यकता होती है, इसलिए RCE होने पर भी यह अधिक गंभीर नहीं है, लेकिन फिर भी मैंने मनोरंजन के उद्देश्य से blog लिखने का निर्णय लिया :) जो लोग SSTI सीख रहे हैं वे संदर्भ ले सकते हैं।
atlassian-jira-servicedesk-4.17.0-m0006-standalone deploy किया और diff करके देखा कि इसमें 4.18.0 संस्करण से क्या अंतर है:

जब मैंने यह सब देखा, तो.... बेशक मैंने एक-एक फ़ाइल नहीं पढ़ी, मैं बहुत आलसी हूँ। मज़ाक कर रहा हूँ, वास्तव में security fix के अलावा, संस्करणों में सुविधाओं का उन्नयन + परिवर्तन भी होता है। सब कुछ diff करके ऐसे पढ़ना बहुत समय लेने वाला होता है। बेशक कुछ मामलों में मुझे सब कुछ diff करके पढ़ना पड़ता है, लेकिन इस मामले में मैंने ऐसा नहीं किया =)))
SendBulkMail सुविधा का उपयोग किया। इस endpoint का stack call कैसा है, इसका उल्लेख मैं लंबाई से बचने के लिए नहीं करूँगा क्योंकि यह CVE-2019-11581 के stack call के समान है।SimpleNote.render फ़ंक्शन पर, मैं context चर देख सकता हूँ कि मैं क्या-क्या उपयोग कर सकता हूँ:
अपने निजी अनुभव के अनुसार, मैं उन context/class की जाँच पर ध्यान देता हूँ जिनमें Utils, Manager, service,... जैसे कीवर्ड होते हैं। मैंने ध्यान दिया कि $jirautils context (class com.atlassian.jira.util.JiraUtils) में method public static <T> T loadComponent(String className, Class<?> callingClass) है:
मैंने docs खोजकर पढ़ा कि इसका कार्य क्या है और इसका उपयोग कैसे करें, क्योंकि input में String className है और output एक class है, इसलिए संभवतः यह method मनमाने ढंग से class load कर सकता है:
मैंने System -> Email templates में जाकर वर्तमान template फ़ाइल अपनी मशीन पर डाउनलोड की
कई अलग-अलग टेम्पलेट हैं, प्रत्येक एक अलग प्रकार की सूचना के लिए उपयोग होता है, इसलिए मैंने ऐसी फ़ाइल खोजी जो कई प्रकार की सूचनाओं के लिए साझा उपयोग की जा सके, जैसे email\html\includes\header.vm
संपादित template फ़ाइल अपलोड करने के बाद, मैंने एक सूचना ईमेल भेजने के लिए SendBulkMail का उपयोग किया। मुझे ईमेल में ऐसा output मिला:
मोटे तौर पर, इस class में constructors नहीं हैं या इसे स्वीकार नहीं किया गया। मैंने एक अन्य class आज़माया जिसमें constructors हैं (बिना input वाले constructors) और देखा:
और परिणाम बिल्कुल अपेक्षित था। मैंने class सफलतापूर्वक प्राप्त कर लिया:
हम देखते हैं कि blacklist में 3 class जोड़े गए हैं:
org.springframework.expression.spel.standard.SpelExpressionParser,\
com.atlassian.jira.component.ComponentAccessor,\
com.atlassian.jira.plugin.ComponentClassManager
प्रत्येक class की जाँच करने पर, मैंने पाया कि SpelExpressionParser class में public SpelExpression parseRaw(String expressionString) फ़ंक्शन है; थोड़ी देर google पर उपयोग का तरीका खोजने पर, मोटे तौर पर इसे ऐसे उपयोग किया जा सकता है:
#set($SpelExpressionParser = $jirautils.loadComponent('org.springframework.expression.spel.standard.SpelExpressionParser',$i18n.getClass()))
$SpelExpressionParser.parseRaw("T(java.lang.Runtime).getRuntime().exec('calc')")
जैसा आप देख सकते हैं, parseRaw फ़ंक्शन एक object लौटाता है जो org.springframework.expression.spel.standard.SpelExpression class है, यह हमारे द्वारा दिए गए expression को वास्तव में render नहीं करता। मैंने SpelExpression class में जाकर देखा और getValue method पाया:
@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;
}
input/output को देखकर, मेरे जैसा आध्यात्मिक किस्म का व्यक्ति docs देखे बिना ही सीधे आज़माने का निर्णय लेता है:
#set($SpelExpressionParser = $jirautils.loadComponent('org.springframework.expression.spel.standard.SpelExpressionParser',$i18n.getClass()))
$SpelExpressionParser.parseRaw("T(java.lang.Runtime).getRuntime().exec('calc')").getValue()
और परिणाम:
इस प्रकार हम RCE के लिए velocity के sandbox को bypass कर सकते हैं, लेकिन जब मैं खुद को इस bug के खोजकर्ता author की स्थिति में रखता हूँ, तो कई प्रश्न हैं जिन्हें मैं समझा नहीं सकता:
जब मैंने इन 3 class का अध्ययन किया, तो मैंने पाया कि ये एक chain बना सकते हैं:
ComponentAccessor.getComponentClassManager()
=> ComponentClassManager.newInstance(String className) (Class này có thể load class tùy ý)
=> SpelExpressionParser
यदि author ने मेरी तरह $jirautils का उपयोग किया होता, तो उन अन्य 2 class की आवश्यकता नहीं होती। लेकिन यदि author ने $jirautils का उपयोग नहीं किया, तो ComponentAccessor कैसे प्राप्त होता???