
إثبات قابلية الاستغلال (PoC) لـ CVE-2026-43512 (تجاوز مصادقة Digest في Apache Tomcat)
نتيجة تحليل قابلية الاستغلال: تم تأكيد السبب الجذري. الاستغلال الكامل غير قابل للتكرار ضد نشر قياسي لـ
UserDatabaseRealm. انظر التحليل للتفاصيل.
إن CVE-2026-43512 هي ثغرة أمنية في آلية مصادقة DIGEST في Apache Tomcat. لا يتحقق أسلوب RealmBase.getDigest() من القيمة المرجعة من getPassword(username) قبل إنشاء مدخلات تجزئة A1. عندما لا يوجد اسم مستخدم في Realm المُهيأ، تُرجع getPassword() القيمة ، والتي يحولها عامل سلاسل النصوص في Java بصمت إلى النص الحرفي المكوّن من أربعة أحرف .
null"null"لذلك يحسب الخادم:
A1 = MD5("<username>:<realm>:null")
العميل الذي يقدم استجابة DIGEST محسوبة بالنص الحرفي "null" ككلمة مرور ينتج تجزئة مطابقة. وفقًا للنشرة الأمنية، يشكل هذا تجاوزًا للمصادقة.
يحتوي هذا المستودع على بيئة تكاثر مصغرة وإثبات مفهوم مبني بلغة Go للتحقق من هذا الادعاء مقابل مثيل Tomcat حقيقي.
| النطاق المتأثر | أُصلح في |
|---|---|
| 7.0.0 – 7.0.109 | 7.0.110 |
| 8.5.0 – 8.5.100 | 8.5.101 |
| 9.0.0.M1 – 9.0.117 | 9.0.118 |
| 10.1.0.M1 – 10.1.54 | 10.1.55 |
| 11.0.0.M1 – 11.0.21 | 11.0.22 |
مسار الكود القابل للاستغلال في RealmBase.java (جميع الفروع المتأثرة):
// RealmBase.java — vulnerable
protected String getDigest(String username, String realmName, String algorithm) {
if (hasMessageDigest(algorithm)) {
return getPassword(username); // returns null for unknown users
}
// null is concatenated as the literal "null" by Java
String a1 = username + ":" + realmName + ":" + getPassword(username);
return HexUtils.toHexString(
ConcurrentMessageDigest.digest(algorithm, a1.getBytes(...))
);
}
الإصلاح (الالتزام 6565a6c يضيف حارسًا صريحًا لقيمة null:
// RealmBase.java — patched
protected String getDigest(String username, String realmName, String algorithm) {
String password = getPassword(username);
if (password == null) {
return null;
}
...
}
تشغيل إثبات المفهوم ضد Tomcat 11.0.0-M1 مع تفعيل تسجيل بمستوى FINE يكشف ما يلي:
Digest: 2388e2c78407def640f37f092a8d3a84 ← client
Server digest: 2388e2c78407def640f37f092a8d3a84 ← server
Failed to authenticate user [ghost]
تجزئات DIGEST متطابقة. الخطأ في getDigest() حقيقي ومؤكد. ومع ذلك، لا تزال المصادقة تفشل لأن RealmBase.authenticate() تحتوي على فحص ثانٍ مستقل:
// RealmBase.authenticate()
if (serverDigest.equals(clientDigest)) {
return getPrincipal(username); // returns null for non-existent users
}
return null;
في نشر قياسي لـ UserDatabaseRealm مدعوم بـ tomcat-users.xml، يقوم getPrincipal() بإجراء بحث في قاعدة بيانات المستخدمين في الذاكرة. بالنسبة لاسم مستخدم غير موجود في تلك القاعدة، يُرجع null. يعامل المتصل قيمة Principal بقيمة null كفشل مصادقة ويصدر استجابة 401.
cve-2026-43512-poc/
├── Dockerfile # Tomcat 11.0.0-M1 (affected version)
├── tomcat-users.xml # Minimal Realm config — no user "ghost"
├── web.xml
├── exploit/
│ ├── exploit.go # PoC — Go, stdlib only
│ └── go.mod
└── README.md
| الأداة | الإصدار | ملاحظات |
|---|---|---|
| Podman | ≥ 4.0 | يعمل Docker أيضًا |
| Go | ≥ 1.22 | فقط لتشغيل إثبات المفهوم محليًا |
podman build -t tomcat-cve-2026-43512 .
podman run -d --name tomcat-vuln -p 8080:8080 tomcat-cve-2026-43512
انتظر بضع ثوانٍ حتى يكمل Tomcat بدء التشغيل، ثم تحقق من أنه يعمل:
curl -si http://localhost:8080/protected/secret.html | head -1
# Expected: HTTP/1.1 401
cd exploit
go run exploit.go \
-target http://localhost:8080 \
-path /protected/secret.html \
-username ghost
الخيارات المتاحة:
| الخيار | الافتراضي | الوصف |
|---|---|---|
-target | http://localhost:8080 | URL الأساسي لـ Tomcat |
-path | /protected/ | مسار المورد المحمي |
-username | ghost | اسم المستخدم للاستخدام — يجب ألا يكون موجودًا في tomcat-users.xml |
لملاحظة حالة المصادقة الداخلية، أضف ملف logging.properties وقم بتركيبه:
org.apache.catalina.authenticator.level = FINE
org.apache.catalina.realm.level = FINE
podman run -d --name tomcat-vuln -p 8080:8080 \
-v ./logging.properties:/usr/local/tomcat/conf/logging.properties:ro \
tomcat-cve-2026-43512
سيظهر السجل نتيجة مقارنة التجزئة مباشرة، مما يؤكد ما إذا كانت التجزئات متطابقة.
podman stop tomcat-vuln && podman rm tomcat-vuln
============================================================
CVE-2026-43512 — Tomcat DIGEST Auth Bypass PoC
============================================================
Target : http://localhost:8080/protected/secret.html
Username : "ghost" (must NOT exist in tomcat-users.xml)
Password : "null" (literal string)
------------------------------------------------------------
[1] Sending unauthenticated request to obtain DIGEST challenge...
[+] HTTP 401 received — DIGEST challenge:
Digest realm="UserDatabase", qop="auth", nonce="...", opaque="..."
[*] realm="UserDatabase" nonce="..." qop="auth" algorithm="MD5"
[2] Computing DIGEST response with password="null"...
Digest username="ghost", realm="UserDatabase", ...
[3] Sending request with crafted DIGEST credentials...
------------------------------------------------------------
[✗] HTTP 401 — exploit failed.
The UserDatabaseRealm provides a second line of defence:
getPrincipal("ghost") returned null after the digest matched.
============================================================
| المورد | الرابط |
|---|---|
| النشرة الأمنية من Apache Tomcat | https://tomcat.apache.org/security-9.html |
| التزام الإصلاح | https://github.com/apache/tomcat/commit/6565a6cb6499e56fe2f34457cec99f9d1c4f39e9 |
RealmBase.java (الرئيسي) | https://github.com/apache/tomcat/blob/main/java/org/apache/catalina/realm/RealmBase.java |
| RFC 2617 — مصادقة HTTP DIGEST | https://datatracker.ietf.org/doc/html/rfc2617 |
| التحليل الكامل — مقالة مدونة | https://return-zero.dev/posts/cve-2026-43512 |
هذا المستودع مخصص للأغراض التعليمية وتحليل قابلية الاستغلال المحلية فقط. تم إجراء جميع الاختبارات ضد بيئة حاويات مستضافة ذاتيًا. لا تشغّل إثبات المفهوم هذا ضد أنظمة لا تملكها أو ليس لديك إذن كتابي صريح لاختبارها.