
فحص ذاتي لـ 7 ثغرات CVE من نوع «الفشل الصامت للإعداد» في log4j لعامي 2025/2026: تحديد 4 وحدات وفقًا لمعيار CVE×الوحدة، وتحليل إعداد log4j2 بشكل بنيوي لتقليل ضجيج applicability، وحساب الإصدار الذي يجب الترقية إليه دفعة واحدة (6 سجلات تشير إلى 2.25.4، لكن سجلًا واحدًا يتطلب 2.25.5، وهو ما لا يستطيع Dependabot الإبلاغ عنه) CVE-2026-49844 / CVE-2026-34477
أداة فحص ذاتي لإعلانات «تعطّل الإعدادات بصمت» في دفعة log4j لعامَي 2025/2026. بدون أي اعتماديات وقت التشغيل، ملف jar واحد، دون اتصال بالشبكة.
⚠️ لنوضّح المعايير أولاً: هذه الدفعة كلها medium، ولا توجد فيها أي ثغرة RCE واحدة، وليست من نوع Log4Shell. القاسم المشترك بينها هو أن الإعدادات تتعطّل دون أي رسالة خطأ —— فتُتجاهل الخصائص بصمت، أو تُعاد تسمية الخصائص بصمت، أو تُفقد السجلات بصمت. لذلك مجرد النظر إلى رقم الإصدار لا يكشف ما إذا كنت متأثراً أم لا، بل يجب قراءة الإعدادات. إذا كنت تبحث عن CVE-2021-44228 (Log4Shell)، فهذه الأداة لن تساعدك.
java -jar log4j-check.jar app.jar # fat jar 里带配置,一步到位
java -jar log4j-check.jar ./target ./src # 构建产物 + 源码
java -jar log4j-check.jar ~/.m2/repository --no-config
Dependabot يخبرك «أن log4j فيه N من الثغرات». هذه الأداة تجيب عن ثلاثة أسئلة لا يجيب عنها:
log4j2.xml، وهي قابلة للقراءة، وقابلة للقراءة بدقة.من بين هذه السبعة، الإصدارات المُصلِحة لستة منها كلها ≤ 2.25.4. لذا سيستنتج الغالبية العظمى الجواب «رقِّ إلى 2.25.4».
لكن CVE-2026-49844 يتطلب 2.25.5 (وعلى خط 2.26 يتطلب 2.26.1). نص إرشاده (advisory) يذكر صراحةً أنه
إصلاح غير مكتمل لـ CVE-2026-34481:
الإصلاح الذي صدر في الإصدار
2.25.4لم يغطِّ جميع مسارات الشيفرة المتأثرة. تم تخصيص CVE-2026-49844 للمشكلة المتبقية …
📌 تصحيح (2026-08-16)
كان هذا القسم يقول أصلاً «وهذا البند بالتحديد هو البند الذي لا يستطيع Dependabot الإبلاغ عنه بنيوياً» —— تلك العبارة الآن صحيحة نصفها فقط.
تحوّل
GHSA-qv9r-c865-cp47في 2026-08-13 إلى حالةreviewedعلى GitHub مع استكمال معلومات الحزمة، والنطاقان المضافان هما>= 2.13.1, < 2.25.5و>= 2.26.0, < 2.26.1:
- ✅ من يشغّل 2.x، أصبح يستقبل تنبيهات Dependabot الآن.
- 🔴 لكن خط المعاينة
3.x(3.0.0-alpha1~3.0.0-beta2) لا يزال بلا نطاق مقابل، وذلك الخط ما زال غير قابل للإبلاغ.نتيجة الترقية لم تتغير إطلاقاً —— لا يزال
log4j-apiيتطلب 2.25.5 (وخط 2.26 يتطلب 2.26.1). ما تغيّر هو فقط «هل تراه الآلة». جدول الحكم في هذه الأداة حُدِّث بنداً بنداً حسب خط الإصدار، والأرقام المطبوعة في التقرير تُحسب من الجدول ولم تعد مكتوبة بشكل ثابت.🔑 هذا الأمر نفسه أجدر بالتسجيل من ذلك المثال السابق: شكل الإرشاد (advisory) يُستكمل من المنبع مع مرور الوقت، ونتيجة فحصك الأخير لا تُحدِّث نفسها، ولن يُخطرك أحد. «فحصتُه آنذاك» و«هو آمن الآن» أمران مختلفان.
إرشاده على GitHub (GHSA-qv9r-c865-cp47)قبل 2026-08-13 كان unreviewed،
ومصفوفة vulnerabilities فيه فارغة —— لا اسم حزمة، ولا نطاق إصدارات، ولا إصدار مُصلِح،
وفي تلك الفترة لم يكن Dependabot يملك أي بيانات يمكن مقارنتها بشجرة اعتمادياتك.
على OSV.dev كان الأمر كذلك آنذاك: الستة الأخرى كلها يمكن الحصول على إحداثيات Maven لها عبر GHSA alias، أما هو فلا
(حتى إنه لا يملك GHSA alias في OSV). هذه شهادة مستقلة من مصدر ثالث،
وtools/recheck_before_publish.py يعيد التحقق منها في كل تشغيل
(والسكربت الآن يُبلِغ أيضاً عن «اكتمال البيانات» هذا بحد ذاته).
🔴 لا تقرأ هذا البند على أنه «Dependabot غير مفيد». إنه «هذه البيانات لم تكن موجودة آنذاك»، وليس «هذه الأداة فيها خلل».
unreviewedحالة طبيعية في مسار GitHub، واستكمال 08-13 هو ذلك المسار نفسه وهو يكتمل بشكل طبيعي.
السلسلة الأخرى هي CVE-2025-68161 (2.25.3) → CVE-2026-34477 (2.25.4):
خاصية verifyHostName أُدخلت منذ 2.12.0، لكنها ظلت تُتجاهَل بصمت حتى 2.25.3،
فإعدادها كان كعدم إعدادها. ومن رقّى وفق 68161 إلى 2.25.3، وكان يضبط التحقق من اسم المضيف بالخاصية (لا بـ system property)، ما زال واقعاً في الثغرة.
| الوحدة | البنود المطابقة | يجب الترقية إلى |
|---|---|---|
log4j-core | 34477 / 34478 / 34480 / 68161 | 2.25.4 |
log4j-api | 49844 | 2.25.5 (خط 2.26: 2.26.1) |
log4j-1.2-api | 34479 | 2.25.4 |
log4j-layout-template-json | 34481 | 2.25.4 |
الإصدار الهدف ليس الرقم نفسه. ومعظم المشاريع تكتب في pom الخاص بها log4j-core فقط
—— والبحث العكسي بذلك الإحداثي الواحد لا يعثر إلا على 4 بنود، والثلاثة الأخرى غير موجودة بالنسبة لهم أصلاً.
هذه الأداة تفحص الوحدات الأربع كلٌّ على حدة وتُصدر الحكم كلٌّ على حدة. وعندما تتباعد إصدارات الوحدات الأربع (تغطية BOM، أو استثناء يدوي، أو WAR قديم يحوي جيلين من jar) فإنها تُبلِغ عن ذلك صراحةً.
| CVE | شرط التفعيل (مأخوذ من النص الرسمي) |
|---|---|
CVE-2026-34477 | خاصية verifyHostName في <Ssl> + مُلحِق Socket / Syslog / SMTP |
CVE-2025-68161 | SocketAppender + TLS |
CVE-2026-34478 | تهيئة Rfc5424Layout مباشرةً (من يستخدم SyslogAppender غير متأثر) |
CVE-2026-34480 | XmlLayout في log4j-core |
CVE-2026-34479 | Log4j1XmlLayout الخاص بالجسر، أو طبقة التوافق مع log4j 1 + org.apache.log4j.xml.XMLLayout |
CVE-2026-34481 | JsonTemplateLayout + قيم الفاصلة العائمة في MapMessage/ObjectMessage |
CVE-2026-49844 | message resolver في JsonTemplateLayout، أو MapMessage.asJson() |
اختبار فعلي (مكوّن حقيقي، انظر أدناه): نفس الحزمة تحتوي على أربع وحدات من الإصدار المتأثر،
Console + RollingFile + PatternLayout) → طبقة الإصدار تُبلغ عن 7 عناصر، والإصابة الفعلية 0Socket+<Ssl verifyHostName>+Rfc5424Layout+XmlLayout → تُبلغ عن 7 عناصر، والإصابة الفعلية 4المعياران السلبيان مذكوران صراحةً في النص الرسمي، ومُوثّقان بشكل منفصل:
من يستخدم SyslogAppender لا يصيبه 34478؛ ومن يستخدم فقط verifyHostname في HTTP appender لا يصيبه 34477.
درجة الموثوقية في هذه الفئة مختلفة تمامًا عن «لم أجد» — الأولى مدعومة بالنص الأصلي، والأخيرة مجرد أننا لم نرَها.
🔴
verifyHostName(بحرف N كبير،<Ssl>، مُصاب) وverifyHostname(بحرف n صغير، HTTP appender، النص الرسمي ينص على أنه غير متأثر) يختلفان بحرف واحد فقط في حالة الأحرف، والنتيجة معاكسة تمامًا. وبالمثلXmlLayout(34480، log4j-core) هو سلسلة فرعية منLog4j1XmlLayout(34479، الجسر). مجرد البحث بالاسم (grep) سيجعل الشخص يتحمل CVEs لا تخصه. لذلك تُجرى طبقة التكوين تحليلًا هيكليًا بدلًا من مطابقة النصوص.
| التنسيق | كيفية القراءة | هل يمكن تحديد انتماء الخاصية بدقة |
|---|---|---|
log4j2*.xml (بما في ذلك وضع strict <Appender type="…">) | تحليل DOM، تم إيقاف XXE | ✅ |
log4j2*.properties | بناء تعيين البادئة→الإضافة وفقًا لـ xxx.type = Foo | ✅ |
log4j2*.yaml / .json | مطابقة نصية (لا يوجد محلل YAML في JDK، وهذه الأداة تلتزم بعدم الاعتماد على أي مكتبات خارجية) | ❌ يُصنَّف كـ«مستند نصي» |
.java | مطابقة نصية (تمت إزالة التعليقات، مع الاعتراف بالسلاسل النصية) | ❌ |
يُفحص التكوين داخل الأرشيف أيضًا (BOOT-INF/classes/، WEB-INF/classes/)،
لذا يكفي إعطاؤه ملف Spring Boot fat jar واحدًا، ويمكن تقليل الضوضاء — دون الحاجة إلى شجرة المصدر.
يتعرف على log4j-spring.xml / log4j2-test.xml / log4j.xml (طبقة توافق 1) جميعها.
«لم يتم العثور على شروط التفعيل» لا يعني الأمان، فهناك أربع حالات على الأقل تجعله طمأنينة زائفة:
ConfigurationBuilder / Configurator.initialize)؛log4j2.configurationFile يشير إلى مكان آخر، أو يُركّب داخل الحاوية)؛