
إفصاح مفصل عن CVE-2025-68664، وهي ثغرة حرجة في إلغاء التسلسل في جوهر LangChain تسمح بتسريب الأسرار واحتمال تنفيذ تعليمات برمجية عن بُعد عبر مطالبات مصممة وتدفقات تسلسلية.
المؤلف: ياردن بورات
بحث Cyata: ثغرة LangGrinch في LangChain
نُشر على: https://cyata.ai/blog/langgrinch-langchain-core-cve-2025-68664/

أمس، نشرت LangChain تحذيرًا حرجًا بشأن ثغرة اكتشفتها في langchain-core: CVE-2025-68664 / GHSA-c67j-w6g6-q2cm.
في وقت سابق من هذا العام، ركزت أبحاثي على اختراق مديري الأسرار في عملنا "Vault Fault" – الأنظمة المصممة خصيصًا كحدود أمان حول بيانات اعتمادك الأكثر حساسية. تكرر استنتاج واحد مرارًا وتكرارًا: عندما تعالج المنصة عن غير قصد بيانات مشكلة من قبل المهاجم كهيكل موثوق، تنهار تلك الحدود بسرعة. هذه المرة، النظام الذي "ينكسر" ليس مدير الأسرار الخاص بك. إنه إطار عمل الوكلاء الذي قد يستخدمها.
لماذا تستحق هذه الثغرة اهتمامًا خاصًا:
إنها في النواة. هذا ليس خطأ أداة معينة، ولا حالة طرفية للتكامل، ولا "حزمة مجتمعية فعلت شيئًا غريبًا." واجهات API المعرضة للخطر (dumps() / dumpd()) موجودة في قلب langchain-core نفسه.
نطاق التأثير هائل. من حيث التنزيلات، langchain هو أحد أكثر مكونات إطار عمل الذكاء الاصطناعي انتشارًا في العالم اليوم. حتى نهاية ديسمبر 2025، تظهر القياسات العامة للحزم مئات الملايين من التثبيتات، حيث يبلغ pepy.tech عن ~847 مليون تنزيل إجمالي ويظهر pypistats ~98 مليون تنزيل في الشهر الماضي.
يمكن لمطالبة واحدة أن تشغل آليات متعددة. الطريق الأكثر شيوعًا في الواقع ليس "يرسل لك المهاجم كتلة مسلسلة وتستدعي load()." إنه أكثر دقة: يمكن لمخرجات LLM التأثير على حقول مثل additional_kwargs أو response_metadata، ويمكن تسلسل هذه الحقول ثم إلغاء تسلسلها عبر وظائف الإطار العادية مثل سجلات/أحداث التدفق. بكلمات بسيطة، هذا يعني أن الاستغلال يمكن تشغيله بمطالبة نصية واحدة تتتالى في مسار داخلي معقد بشكل غير متوقع.
قبل أن تستمر في القراءة، تم إصدار التصحيحات بالفعل في الإصدارين 1.2.5 و 0.3.81. إذا كنت تستخدم LangChain في الإنتاج، فإن الأمر أكثر تعقيدًا مما يبدو؛ يرجى التحديث في أقرب وقت ممكن.
يستخدم LangChain تنسيق تسلسل داخلي خاص، حيث تمثل القواميس التي تحتوي على العلامة 'lc' كائنات LangChain. كانت الثغرة أن dumps() و dumpd() لم تفلت بشكل صحيح القواميس التي يتحكم بها المستخدم والتي تتضمن عن طريق الخطأ المفتاح المحجوز 'lc'. وبالتالي، بمجرد أن يتمكن المهاجم من جعل دورة التنسيق في LangChain تسلسل ثم لاحقًا إلغاء تسلسل محتوى يتضمن المفتاح 'lc'، يمكنه إنشاء كائن عشوائي غير آمن، مما قد يؤدي إلى تشغيل العديد من المسارات الصديقة للمهاجم.
يسرد التحذير 12 تدفقًا مختلفًا معرضًا للخطر وهي شائعة جدًا في حالات الاستخدام الواقعية مثل تدفق الأحداث القياسي، والتسجيل، وسجل الرسائل/الذاكرة، أو التخزين المؤقت:

تشمل العواقب الأكثر تدميراً:
يُصنف هذا تحت CWE-502: إلغاء تسلسل البيانات غير الموثوقة، بتقييم CVSS CNA 9.3 (حرج).
عشية عيد الميلاد، كنت أقوم بأقل الأعمال احتفالية: أنظر إلى كود التسلسل وأتساءل "انتظر… لماذا يعتبر هذا موثوقًا؟"
غالبًا ما تبدو أبحاث الأمن درامية من الخارج. في الواقع، هي عادة قراءة متأنية، وافتراضات صغيرة، وتراكم بطيء للحظات "هذا غريب".
بدأ هذا كما تفعل أشياء كثيرة في Cyata: بسؤال بسيط نطرحه باستمرار عند تقييم مجموعات الذكاء الاصطناعي للمخاطر الحقيقية: أين توجد حدود الثقة في تطبيقات الذكاء الاصطناعي، وهل يعرف المطورون حقًا أين توجد هذه الحدود؟
LangChain هو إطار عمل قوي، ومثل معظم الأطر الحديثة، يجب عليه نقل بيانات منظمة معقدة: الرسائل، واستدعاءات الأدوات، وأحداث التدفق، والتتبعات، والمخازن المؤقتة، و 'المجرى' (runnables).
بالنظر إلى الأبحاث السابقة، كان هناك بالفعل بحث واسع حول أدوات وتكاملات LangChain، ولكن نادرًا ما كانت هناك اكتشافات في المكتبة الأساسية.
بدأت البحث بالعمل بالاتجاه المعاكس. إيجاد الأماكن المثيرة للاهتمام (المستقبلات)، ثم معرفة كيف يمكن للمهاجم الوصول إليها. كان إلغاء التسلسل هدفًا واضحًا.
استغرق الأمر مني وقتًا طويلاً لأجد شيئًا ذا قيمة. لكن بعد فترة، وجدت أنه بافتراض وجود بدائية إلغاء تسلسل يتحكم بها المهاجم، يمكنني تشغيل SSRF أعمى يمكن استخدامه لتسريب متغيرات البيئة (سيتم شرحه بالتفصيل قريبًا). نظرًا لأن النتيجة كانت محدودة بتسريب الأسرار وليس هدفي الرئيسي RCE، واصلت تدقيق إلغاء التسلسل وأخذت وقتي.
لم يكن الخلل قطعة من كود سيئ، بل كان غياب كود. dumps() ببساطة لم تقم بإفلات القواميس التي يتحكم بها المستخدم والتي تحتوي على مفاتيح 'lc'. الإفلات المفقود كان في مسار التسلسل، وليس إلغاء التسلسل.

من الأسهل بكثير ملاحظة شيء خاطئ بدلاً من ملاحظة غياب شيء ما، خاصة عندما تقوم بتدقيق load() وليس dumps(). في أحد أكثر أطر الذكاء الاصطناعي تدقيقًا. عامين ونصف.
من هناك، أصبح البحث تمرينًا منظمًا:
في تلك المرحلة، كان الاكتشاف الرئيسي واضحًا وقابلًا للتنفيذ للإبلاغ المسؤول: كان هناك فجوة في الإفلات في dumps() / dumpd() حول القواميس ذات المفتاح 'lc'.
التقط التحذير لاحقًا ما نراه غالبًا في الممارسة: حقول مثل additional_kwargs و response_metadata يمكن أن تتأثر بمخرج LLM وحقن المطالبات، ويمكن أن تخضع هذه الحقول للتسلسل وإلغاء التسلسل في العديد من التدفقات.
ولفريق LangChain الفضل: كان الرد والإجراءات اللاحقة حاسمة، ليس فقط لتصحيح الخلل ولكن أيضًا لتشديد الإعدادات الافتراضية التي كانت متساهلة للغاية بالنسبة للعالم الذي نعيش فيه الآن.
قرر مشروع LangChain منح مكافأة قدرها 4,000 دولار أمريكي لهذا الاكتشاف. وفقًا لـ huntr، المنصة التي أدارت من خلالها LangChain برنامج المكافآت، سيكون هذا أكبر مبلغ يُمنح على الإطلاق في المشروع، حيث كانت المكافآت السابقة تصل إلى 125 دولارًا.
يقوم LangChain بتسلسل كائنات معينة باستخدام تنسيق قاموس منظم. يستخدم المفتاح 'lc' داخليًا للإشارة إلى 'هذا هيكل LangChain مسلسل'، وليس مجرد بيانات مستخدم عشوائية.
هذا نمط شائع، لكنه يخلق ثابت أمان: يجب معالجة أي بيانات مستخدم قد تحتوي على 'lc' بحذر. وإلا، يمكن للمهاجم إنشاء قاموس 'يبدو وكأنه' كائن داخلي ويخدع مفكك التسلسل ليعطيه قيمة.
يجعل التصحيح النية واضحة في الوثائق المحدثة: أثناء التسلسل، يتم إفلات القواميس البسيطة التي تحتوي على المفتاح 'lc' عن طريق تغليفها. يمنع هذا الخلط بين هذه القواميس وكائنات LangChain المسلسلة الفعلية أثناء إلغاء التسلسل.
لا تقوم وظائف load()/loads() في LangChain بإنشاء فئات عشوائية – فهي تتحقق من قائمة بيضاء تتحكم في الفئات التي يمكن إلغاء تسلسلها. افتراضيًا، تتضمن هذه القائمة البيضاء فئات من langchain_core و langchain_openai و langchain_aws وحزم أخرى في النظام البيئي.
وإليك المهم: معظم الفئات في القائمة البيضاء لها منشئات غير ضارة. يتطلب إيجاد مسارات قابلة للاستغلال البحث في النظام البيئي عن الفئات التي تفعل شيئًا ذا معنى عند إنشائها. تلك التي وجدتها موصوفة بالتفصيل أدناه، ولكن قد يكون هناك أخرى تنتظر الاكتشاف.
تدعم وظيفة loads() في LangChain النوع secret الذي يحل القيم من متغيرات البيئة أثناء إلغاء التسلسل. قبل التصحيح، كانت هذه الميزة secrets_from_env مفعلة افتراضيًا: