
نسخة إثبات مفهوم قابلة للتشغيل عبر Docker للثغرة CVE-2026-71569، وهي تجاوز لحماية SSRF في التحقق من صحة webhook الخاص بـ camel-a2a في Apache Camel، مع أدوات فحص والتحقق من الإصلاح.
مُعيد إنتاج عملي قابل للتشغيل لثغرة Apache Camel حيث صنّف WebhookUrlValidator
مضيف webhook لإشعار الدفع في موضعين غير متوافقين:
fc00::/7 ("fc"، "fd")؛isLoopbackAddress
وisAnyLocalAddress وisLinkLocalAddress وisSiteLocalAddress.هذه الدوال المسندة لا تغطي النطاق نفسه: isSiteLocalAddress تُبلّغ عن كتلة fec0::/10 المهجورة،
وليس عن العناوين المحلية الفريدة fc00::/7 التي حلّت محلها، ولا تحمل JDK أي دالة مسندة لها
على الإطلاق. لذلك كان العنوان نفسه يُقبل أو يُرفض اعتمادًا على طريقة كتابته.
| بيئة التشغيل | الدليل | الحزمة |
|---|---|---|
| Camel Spring Boot | camel-spring-boot/ | Spring Boot 3.5.13 + camel-a2a 4.22.1 |
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.39.0 (Quarkus 3.39.0، Camel 4.22.0) |
يجب أن يُحلّ اسم المضيف إلى fc00::/7، وهو ما يرتبه ملف compose عبر إدخال extra_hosts
(يكتبه Docker في /etc/hosts الخاص بالحاوية عند بدء التشغيل) — لذا شغّل هذا عبر Docker، وليس باستخدام
java -jar مباشرةً:
cd camel-spring-boot # or: cd camel-quarkus
mvn clean package
docker compose up -d --build
until curl -sf http://localhost:8080/exploit/ready >/dev/null; do sleep 1; done
curl -s http://localhost:8080/exploit/attack
docker compose down
على إصدار متأثر (متطابق على كلا زمني التشغيل):
Resolution in this environment: internal-service.example -> fd00:0:0:0:0:0:0:1
1) The unique local address as an IP LITERAL
url: https://[fd00::1]/push-notification
verdict: REFUSED - Webhook URL must not point to private/internal IPv6 ranges (SSRF protection): [fd00::1]
2) The SAME address reached through a host name
url: https://internal-service.example/push-notification
verdict: ACCEPTED, resolved to fd00:0:0:0:0:0:0:1
3) A legitimate host name that merely BEGINS with "fc" (the bug's other direction)
url: https://fcm.googleapis.com/push-notification
verdict: REFUSED - Webhook URL must not point to private/internal IPv6 ranges (SSRF protection): fcm.googleapis.com
4) CONTROL - an IPv4 private address, which isSiteLocalAddress does cover
verdict: REFUSED - ... site-local/private address ...
5) CONTROL - a link-local IPv6 literal
verdict: REFUSED - ... private/internal IPv6 ranges ...
>>> PROVEN: ...
المسباران 1 و2 هما العنوان نفسه، والحكمان متعارضان. المسبار 3 هو التناقض نفسه في
الاتجاه المعاكس: الفحص المسبق للنص طابق المضيف الخام دون أن يثبت أولاً أنه عنوان IP
حرفي، لذا رُفض أي اسم يبدأ بـ fc أو fd رفضاً قاطعاً — ومن بينها fcm.googleapis.com، وهو
نقطة نهاية push عادية تماماً. المسباران 4 و5 هما ضوابط تُظهر أن الحارس يعمل حيث كُتب
بشكل صحيح.
كل مسبار يمرر allowLocal=false، وهو الافتراضي (allowLocalWebhookUrls=false) والتهيئة
التي يُتوقع أن يصمد فيها الحارس.
يصل عنوان URL للـ webhook على هيئة TaskPushNotificationConfig.url من المستدعي الذي يسجّل إشعار
الـ push، لذا فهو خاضع لتحكم المهاجم. بعد ذلك يرسل PushNotificationDispatcher إشعار الـ push
إلى ذلك العنوان، مع أي ترويسة Authorization محمولة في التهيئة نفسها —
والتي يوفّر المستدعي المسجّل مخططها وبيانات اعتمادها.
لا تُعاد الاستجابة إلى ذلك المستدعي، لذا ما تنتجه هو طلب HTTPS صادر أعمى
من عملية Camel إلى عنوان داخلي كُتب الحارس ليرفضه. ويتطلب الوصول إلى خدمة بتلك الطريقة إضافةً إلى ذلك
أن يُحلّ الاسم إلى fc00::/7 في البيئة الهدف، وأن تكون هناك خدمة HTTPS تستمع على ذلك العنوان،
وأن تنجح مصافحة TLS للاسم المطلوب — ولهذا صُنّفت الخطورة متوسطة.
| الخاصية | القيمة |
|---|---|
| المكوّن | camel-a2a (WebhookUrlValidator) |
| CWE | CWE-918 (تزوير الطلبات من جانب الخادم) |
| الخطورة | متوسطة |
| ناقل الهجوم | عنوان URL لـ webhook إشعار push يُحلّ اسم مضيفه إلى fc00::/7 |
| الأثر | طلب HTTPS صادر أعمى، مع ترويسة Authorization المهيّأة، إلى عنوان داخلي كان ينبغي للحارس أن يرفضه |
| الإصدارات المتأثرة | من 4.21.0 قبل 4.22.2 |
| الإصدارات المُصلحة | 4.22.2، 4.23.0 |
| JIRA | CAMEL-24371 |
| الفضل | 2122295973 at qq dot com، أُبلغ عبر فريق أمن ASF |
الاستشارة: https://camel.apache.org/security/CVE-2026-71569.html
يمر كلا المسارين الآن عبر InetAddress.getByName، الذي يحلّل عنوان IPv6 حرفياً بين قوسين دون المساس
بـ DNS، ويصنّف العنوان الناتج من بايتاته الخام بمصنّف واحد مشترك — لذا يُحكم على المضيف
بالعنوان الذي يدل عليه لا بطريقة كتابته. يتعرّف ذلك المصنّف على fc00::/7
والمساحة العنوانية المشتركة 100.64.0.0/10، إضافةً إلى العناوين المتوافقة مع IPv4 وNAT64 (64:ff9b::/96) و6to4
(2002::/16) التي لا يكون عنوان IPv4 المضمّن فيها قابلاً للتوجيه عالمياً.
أُصلح على main في apache/camel#25406 (54041cce)، ونُقل
إلى camel-4.22.x في #26583 (c4a5c5fc).
اضبط camel.version على 4.22.2 وشغّله مرة أخرى. تم التحقق مقابل 4.22.1 → 4.22.2:
1) The unique local address as an IP LITERAL
verdict: REFUSED - Webhook URL must not point to a unique local address (SSRF protection): [fd00::1]
2) The SAME address reached through a host name
verdict: REFUSED - Webhook URL must not point to a unique local address (SSRF protection): internal-service.example
3) A legitimate host name that merely BEGINS with "fc"
verdict: ACCEPTED, resolved to 216.239.38.55
كلا التهجئتين تحصلان الآن على الحكم نفسه، مع الرسالة نفسها، كما اختفى الإيجاب الكاذب في الفحص 3 أيضاً.
ضع المصادقة أمام مسار إعداد إشعارات الدفع في A2A بحيث لا يصل عنوان URL الخاص بالـ webhook من مُستدعٍ غير موثوق، وامنع الخروج من عملية Camel إلى fc00::/7 على مستوى الشبكة. وحيث لا تحتاج عملية النشر إلى IPv6، فإن بدء تشغيل JVM بالخيار -Djava.net.preferIPv4Stack=true يزيل فئة العناوين التي تعتمد عليها الثغرة. لاحظ أن allowLocalWebhookUrls=false هو بالفعل الإعداد الافتراضي ولا يسدّ الثغرة بمفرده — فالفحص 2 أعلاه يعمل معه.
نُشر هذا المستودع لأغراض تعليمية ودفاعية: لمساعدة مستخدمي Apache Camel على فهم الثغرة، والتحقق مما إذا كانوا متأثرين بها، وتأكيد أن الترقية تحلّها. لا تستخدم هذه المواد ضد أنظمة لا تملكها أو لا تديرها.