
حقن القوالب بدون مصادقة في Atlassian Jira
4.4.x
5.x.x
6.x.x
7.0.x
7.1.x
7.2.x
7.3.x
7.4.x
7.5.x
7.6.x before 7.6.14 (the fixed version for 7.6.x)
7.7.x
7.8.x
7.9.x
7.10.x
7.11.x
7.12.x
7.13.x before 7.13.5 (the fixed version for 7.13.x)
8.0.x before 8.0.3 (the fixed version for 8.0.x)
8.1.x before 8.1.2 (the fixed version for 8.1.x)
8.2.x before 8.2.3 (the fixed version for 8.2.x)
set JVM_SUPPORT_RECOMMENDED_ARGS= في الملف ./bin/setenv.bat (أو ما يعادله في ./bin/setenv.sh لنظام لينكس) لتتمكن من تشغيل التصحيح عن بُعدset JVM_SUPPORT_RECOMMENDED_ARGS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
Remote JVM Debug مع قيمة host و port لتكون localhost:5005 و Command line aguments for remote JVM-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
شغّل الملف ./bin/config.bat (أو config.sh في لينكس) وعدّل "Jira home" إلى مجلد jira home الخاص بك

شغّل الملف ./bin/start-jira.bat (أو ./bin/start-jira.sh في لينكس). تحقق من أن إصدار جافا والمنفذين 8080 و5005 متاحان، وإلا فلن يعمل!
نختار الوضع الخاص (private mode)

في هذه الخطوة، تذكّروا استخدام عنوان بريد إلكتروني يمكنه استقبال الإيميل :)

أكملوا الإعدادات (Conf) واختبروا الاتصال بشكل كامل

في http://localhost:8080/secure/admin/EditApplicationProperties!default.jspa فعّل ميزة Contact Administrators Form

سأذكر بعض الملاحظات فقط عند البناء، يمكنكم الاطلاع على بناء Jira من المصدر
بعد قراءة الإشعار الأمني علمنا أن هذا الخطأ يقع في ContactAdministrators و SendBulkMail (وهذا الأخير سأتجاهله لأنه يتطلب مصادقة). لذلك بدأت باختبار ميزة ContactAdministrators والتقاط الطلب

لاحظت أن الطلب يذهب إلى /secure/ContactAdministrators.jspa، لذا نظرت إلى الملف ./atlassian-jira/WEB-INF/web.xml لمعرفة أي class سيُوجَّه إليه هذا الطلب


بهذا تتم معالجة الطلب في JiraWebworkActionDispatcher، لذا وضعت نقطة توقف (breakpoint) في init و server في هذا class وشغّلت التصحيح

وهكذا توقف البرنامج عند الدالة server، وبعد تتبّع قليل انتقل البرنامج إلى ContactAdministrators.doExecute()

ثم مررنا عبر send، وهنا يعرض البرنامج قائمة بحسابات الأدمن النشطة

ثم يمر البرنامج عبر الدالة sendTo. هنا نرى أن البرنامج ينشئ MailQueueItem ويضيفه إلى mailQueue.

يستدعي البرنامج الدالة EmailBuilder.withSubject. هنا يتم تحويل نص موضوع البريد الإلكتروني (الذي يرسله المهاجم) من String إلى TemplateSources وتُسند إلى المعامل subjectTemplate في EmailBuilder.

في الدالة renderLater ينشئ البرنامج EmailRenderer ويستخدمه لإنشاء RenderingMailQueueItem

من هنا، عند العودة إلى الدالة ContactAdministrators.sendTo، بعد إنشاء MailQueueItem يضيف البرنامج العنصر إلى mailQueue ثم يعود إلى دالة doExecute لإجراء Redirect. إذا اتبعنا مسار التصحيح هذا فلن نتمكن من الوصول إلى الجزء الذي يعرض فيه البرنامج البريد الإلكتروني، وبالتالي لا يمكن الوصول إلى النقطة التي يحدث فيها حقن القالب. فماذا أفعل لتتبّع معالجة البريد الإلكتروني؟؟؟
رأيت أن في EmailRenderer دالة renderNow (والبرنامج يستدعي renderLater فقط). توقعت أنه عندما يُستدعى البريد الموجود في queue ليتم عرضه، فإنه سيسلك نفس مسار renderNow، لذلك قررت التتبّع بدءًا من الدالة renderNow

من renderNow يستدعي البرنامج EmailRenderer.render(). وضعت نقطة توقف هنا وأعدت إرسال الطلب لأرى إن كان البرنامج سيصل فعلاً إلى هذه النقطة

لحسن الحظ، سار البرنامج في الاتجاه الذي توقعته. بعدها استدعى البرنامج renderEmailSubject

ثم يستدعي البرنامج DefaultVelocityTemplatingEngine.render(this.subjectTemplate)

نصل إلى DefaultVelocityTemplatingEngine.applying و DefaultVelocityTemplatingEngine.asPlainText

ثم يواصل الاستدعاء إلى asPlainText(Writer writer)

نصل إلى toWriterImpl، ولأن writer الذي مررته هو Fragment، يقفز البرنامج إلى else
private void toWriterImpl(Writer writer, boolean attachCartridge) throws IOException {
if (this.source instanceof File) {
File template = (File)this.source;
if (attachCartridge) {
this.context.attachEventCartridge(DefaultVelocityTemplatingEngine.this.createDefaultCartridge());
}
DefaultVelocityTemplatingEngine.this.velocityManager.writeEncodedBody(writer, template.getPath(), "", DefaultVelocityTemplatingEngine.this.applicationProperties.getEncoding(), this.context);
} else if (this.source instanceof Fragment) {
Fragment fragment = (Fragment)this.source;
if (attachCartridge) {
this.context.attachEventCartridge(DefaultVelocityTemplatingEngine.this.createDefaultCartridge());
}
DefaultVelocityTemplatingEngine.this.velocityManager.writeEncodedBodyForContent(writer, fragment.getContent(), this.context);
}