
إصلاح قائم على وكيل Byte Buddy بلغة Java لـ CVE-2021-44228، ثغرة log4j 2.x "JNDI LDAP".
أداة تعتمد على وكيل Java من Byte Buddy لإصلاح الثغرة CVE-2021-44228 (ثغرة "JNDI LDAP" في log4j 2.x).
تقوم بثلاثة أشياء:
jndi: ("عمليات البحث").System.err (أي stderr) تشير إلى محاولة JNDI لـ log4j (بما في ذلك سلسلة النص التي تمت محاولتها، مع تنظيف أي أحرف ${} لمنع الحقن الانتقالي)."(log4j jndi disabled)" في رسالة السجل (لمنع الحقن الانتقالي).أضف -javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar إلى أوامر java الخاصة بك.
ملاحظة: إذا كان لديك بالفعل Byte Buddy في مسار الفئات (classpath)، فجرب استخدام log4j-jndi-be-gone-1.0.0.jar.
$ java -javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar -jar path/to/some.jar
اعتبارًا من الإصدار 1.1.0، يهدف log4j-jndi-be-gone بشكل افتراضي إلى معالجة الإصدارات المُعاد تغليفها (المعروفة باسم "المُظللة" أو shaded) من log4j التي قد تكون مضمنة في JAR تحت اسم حزمة بديل لمنع التعارضات بين إصدار التطبيق من التبعية وإصدار تبعية التبعية نفسها. ومع ذلك، تجدر الإشارة إلى أن log4j لا يبدو أنه يُعاد تغليفه بسهولة ضمن أسماء/بادئات حزم بديلة بسبب استخدام التأمل (reflection) مع أسماء فئات ثابتة و/أو أسماء فئات من ملفات التكوين المضمنة.
يمكن تعطيل هذا السلوك بوضع =structureMatch=0 بعد مسار JAR الوكيل في الوسيط -javaagent:، على سبيل المثال:
-javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar=structureMatch=0
مما سيؤدي إلى نفس سلوك المطابقة كما في الإصدار 1.0.0، أي مقارنة بسيطة دقيقة للنص مقابل اسم الفئة.
يمكنك بناء JAR باستخدام ./gradlew (build/libs/log4j-jndi-be-gone-1.0.0(-standalone).jar) أو الحصول عليه من صفحة الإصدارات.
يدعم JAR وكيل log4j-jndi-be-gone Java 6-17+.
يبدأ التنفيذ بمطابقة الفئات التي تحتوي على لاحقات تتطابق مع الحزم الفرعية الداخلية واسم فئة org.apache.logging.log4j.core.lookup.JndiLookup، و lookup.JndiLookup، حيث يمكن توقع أن org.apache.logging.log4j.core قد تم تشويهها بواسطة قواعد إعادة التغليف التي لم تسعَ للحفاظ على أسماء الحزم. بالإضافة إلى ذلك، بدلاً من مجرد إجراء فحوصات مماثلة لجميع أنواع log4j المتوقعة الأخرى، فإنه يضمن أنها موجودة تحت نفس الحزمة الأساسية أيضًا.
ثم يتجول التنفيذ عبر بنية أي فئات lookup.JndiLookup محتملة من log4j تم تحديدها، محاولاً التحقق من:
org.apache.logging.log4j.core.config.plugins.Plugin المتوقع عبر جميع إصدارات 2.x، بما في ذلك معاملات التعليق التوضيحي وقيمهاlookup()، مطابقةً لمعدِّلاتها وتوقيع نوعها (وتجاهل الإصدار ذو الوسيطة الواحدة من 2.0)convertJndiName()، مطابقةً لمعدِّلاتها وتوقيع نوعهاCONTAINER_JNDI_RESOURCE_PATH_PREFIX، مطابقةً لمعدِّلاتهلن يعمل log4j-jndi-be-gone إذا تم تشويش مكتبة log4j أو إذا تم تعديل حزم/أسماء فئاتها بخلاف إعادة التغليف الأساسية (أي "التظليل").
يضم log4j-jndi-be-gone-1.0.0-standalone.jar Byte Buddy. إذا كنت تستخدم Byte Buddy بالفعل، فقد تواجه مشاكل معه. جرب اعتبارًا من الإصدار 1.1.0، يضم JAR المستقل log4j-jndi-be-gone نسخة معاد تغليفها من Byte Buddy ضمن بادئة حزمة خاصة به. من المفترض أن يمنع هذا أي تعارضات.log4j-jndi-be-gone-1.0.0.jar بدلاً من ذلك، مع ملاحظة أن log4j-jndi-be-gone يتطلب Byte Buddy 1.12.x.
إذا كنت قد استبدلت فئات JndiLookup الخاصة بك بتطبيقات تحاول القيام بالتصيد (honeypotting) أو تسجيل استدعاءات lookup()، فمن المحتمل أن يقوم log4j-jndi-be-gone بتعطيل طريقة lookup الخاصة بها، مما يمنعها من العمل.
يحتوي الدليل tests/jnditest على حالة اختبار بسيطة حيث يقوم استدعاء تسجيل log4j بتمرير سلسلة تنسيق JNDI LDAP. كما يقوم بإعداد مستمع منفذ خاص به لتحديد ما إذا تم إجراء محاولة اتصال بواسطة log4j ويفشل الاختبار إذا تم استلام اتصال.
$ ./tests/jnditest/test-uninstrumented.sh
BUILD SUCCESSFUL in 1s
6 actionable tasks: 5 executed, 1 up-to-date
BUILD SUCCESSFUL in 1s
3 actionable tasks: 3 up-to-date
JUnit version 4.12
.16:08:49.547 [main] ERROR trust.nccgroup.jnditest.test.JndiTest - Hello, _${jndi:ldap://127.0.0.1:8899/evil}_!
E
Time: 0.929
There was 1 failure:
1) logging(trust.nccgroup.jnditest.test.JndiTest)
java.lang.AssertionError: jndi ldap connection received
at org.junit.Assert.fail(Assert.java:88)
at trust.nccgroup.jnditest.test.JndiTest.logging(JndiTest.java:55)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at org.junit.runner.JUnitCore.runMain(JUnitCore.java:77)
at org.junit.runner.JUnitCore.main(JUnitCore.java:36)
at trust.nccgroup.jnditest.Main.main(Main.java:24)
FAILURES!!!
Tests run: 1, Failures: 1
$ ./tests/jnditest/test-instrumented.sh
BUILD SUCCESSFUL in 1s
6 actionable tasks: 5 executed, 1 up-to-date
BUILD SUCCESSFUL in 1s
3 actionable tasks: 3 up-to-date
JUnit version 4.12
.log4j jndi lookup attempted: (sanitized) ldap://127.0.0.1:8899/evil
16:09:06.064 [main] ERROR trust.nccgroup.jnditest.test.JndiTest - Hello, _(log4j jndi disabled)_!
Time: 1.362
OK (1 test)
مرخص بموجب ترخيص Apache 2.
تم اختبار log4j-jndi-be-gone على OpenJDK 6 و8 و11 و17، وعلى أجهزة HotSpot وOpenJ9 الافتراضية (JVMs).