
ثغرة في AWS SAM CLI (CVE-2025-3047, CVE-2025-3048)
يقدّم ملف README هذا تحليلًا تفصيليًا لثغرتين أمنيتين تم اكتشافهما في واجهة الأوامر السطرية لنموذج التطبيقات الخادمة من AWS (AWS SAM CLI) – CVE-2025-3047 وCVE-2025-3048 – إلى جانب المشكلات على مستوى الكود والإصلاحات. تتعلق كلتا الثغرتين بمعالجة غير سليمة للروابط الرمزية (symlinks) أثناء عملية البناء باستخدام حاويات Docker. يغطي كل قسم أدناه ثغرة CVE واحدة مع ملخص، والكود المتأثر (مع مراجع إلى شيفرة SAM CLI المصدرية)، والتصحيح وكيفية حلّه للمشكلة، وإرشادات المعالجة.
ترتبط هذه العيوب بمعالجة غير سليمة للروابط الرمزية (symlinks) أثناء عملية sam build --use-container. تؤثر كلتا المشكلتين على بيئات التطوير المحلية (لا تؤثران على خدمات أو موارد AWS المنشورة)، إلا أنهما قد تسمحان بوصول غير مصرّح به إلى ملفات على الجهاز المضيف من خلال إساءة استخدام طريقة معالجة AWS SAM CLI للروابط الرمزية. يُنصح بشدة بترقية AWS SAM CLI إلى الإصدارات المصححة (1.133.0+ لـ CVE-2025-3047، و1.134.0+ لـ CVE-2025-3048).
GHSA-px37-jpqx-97q9 هي ثغرة تجاوز مسار في AWS SAM CLI <= v1.132.0 سمحت بوصول غير مصرّح به إلى الملفات على الجهاز المضيف أثناء sam build --use-container. عند بناء تطبيق خادم (serverless) داخل حاوية Docker، كان SAM CLI يتبع الروابط الرمزية في المشروع افتراضيًا. يمكن للمهاجم الذي يزرع رابطًا رمزيًا ضارًا في المشروع (يشير إلى ملف حساس على المضيف) استغلال الصلاحيات المرتفعة لحاوية Docker لجعل هذا الملف يُركَّب داخل الحاوية ويُنسخ إلى موقع يمكن الوصول إليه داخل الحاوية. في الواقع، كان هذا يعني إمكانية قراءة ملفات المضيف المقيّدة (خارج دليل المشروع) وإخراجها عبر حاوية البناء. تم إصلاح المشكلة في v1.133.0. (للحفاظ على التوافق مع الإصدارات السابقة للحالات المشروعة، قدّم SAM CLI v1.133.0 علامة اختيارية --mount-symlinks لإعادة تفعيل السلوك القديم عند الحاجة.)
السبب الجذري والمكوّن المتأثر: تكمن المشكلة الأساسية في كيفية تركيب AWS SAM CLI لأدلة المشروع وروابطها الرمزية في حاوية Docker المستخدمة للبناء. في كود AWS SAM CLI (الوحدة samcli.local.docker.container)، قبل التصحيح، كانت جميع الروابط الرمزية ذات المستوى الأعلى في دليل المشروع تُحلَّل وتُركَّب تلقائيًا في الحاوية بنفس الصلاحيات المرتفعة التي تتمتع بها عملية الحاوية. تعمل الحاوية كـ root افتراضيًا، لذا فإن حلّ رابط رمزي يشير إلى مسار حساس على المضيف وتركيبه كان يمنح الحاوية وصولاً إلى هذا الملف، وهو ما لا يملكه مستخدم المضيف غير المتميز عادةً. لم يقيّد الكود الضعيف بشكل كافٍ الروابط الرمزية التي يجب اتباعها/تركيبها.
تحديدًا، في الدالة التي تنشئ تركيبات وحدات تخزين Docker للبناء، كان SAM CLI يتعامل بشكل غير مشروط مع الروابط الرمزية كملفات/أدلة فعلية لتركيبها. كانت الثغرة تكمن في منطق تنسيق الحاويات في SAM CLI، وتحديدًا في طريقة Container.create في samcli/local/docker/container.py. في الإصدارات الضعيفة، كانت هذه الطريقة تحاول دائمًا حلّ وتركيب أهداف الروابط الرمزية من المشروع إلى حاوية Docker، بغض النظر عن السياق. يظهر الكود المُشكِل أدناه، من SAM CLI v1.132.0:
# samcli/local/docker/container.py (v1.132.0 - vulnerable snippet)
if self._host_dir:
mount_mode = "rw,delegated" if self._mount_with_write else "ro,delegated"
LOG.info("Mounting %s as %s:%s, inside runtime container", self._host_dir, self._working_dir, mount_mode)
_volumes = {
self._host_dir: {
"bind": self._working_dir,
"mode": mount_mode,
},
**self._create_mapped_symlink_files(), # Always resolve and mount symlinks (vulnerable)
}
في الكود أعلاه، تقوم _create_mapped_symlink_files() بفحص الروابط الرمزية داخل دليل المشروع وتجهيزها للتركيب. ولأن هذا كان مُدرجًا بشكل غير مشروط، فإن جميع الروابط الرمزية (بما في ذلك تلك التي تشير إلى خارج المشروع) كانت ستُركَّب في الحاوية. (انظر aws/aws-sam-cli#7865)
تكمن المشكلة هنا في أنه أثناء sam build --use-container, كان SAM CLI يتعامل مع الروابط الرمزية كملفات يجب تركيبها في الحاوية. إذا كان الرابط الرمزي يشير، على سبيل المثال، إلى /etc/shadow على المضيف، فإن حاوية Docker (التي قد تعمل بصلاحيات مرتفعة) كانت ستركّب هذا الملف. هذا هو أسلوب تجاوز المسار القائم على الروابط الرمزية الكلاسيكي، مما يؤدي إلى تصعيد الامتيازات – حيث يمكن قراءة ملفات المضيف التي لا يملك المستخدم وصولاً إليها عادةً بواسطة الحاوية ثم تظهر في مخرجات البناء.
التصحيح (الكود المُصحح في v1.133.0): يقدّم الإصلاح مفهوم سياق البناء ويعطّل حلّ الروابط الرمزية أثناء عمليات بناء الحاويات. في الإصدار المُصحح، تأخذ Container.create معاملًا إضافيًا يحدد السياق (BUILD مقابل INVOKE)، ولن تحلّ الروابط الرمزية إلا إذا كان السياق هو الاستدعاء (عند تشغيل الدوال محليًا)، وليس أثناء البناء. فيما يلي الكود المُصحح من الإصدار المُصحح:
# samcli/local/docker/container.py (v1.133.0+ - patched snippet)
if self._host_dir:
mount_mode = "rw,delegated" if self._mount_with_write else "ro,delegated"
LOG.info("Mounting %s as %s:%s, inside runtime container", self._host_dir, self._working_dir, mount_mode)
mapped_symlinks = self._create_mapped_symlink_files() if self._resolve_symlinks(context) else {}
_volumes = {
self._host_dir: {
"bind": self._working_dir,
"mode": mount_mode,
},
**mapped_symlinks, # Only mount symlinks if explicitly allowed by context (not in build)
}
في الكود المُصحح، يتم تغليف _create_mapped_symlink_files() خلف فحص للسياق. يحدد تعداد ContainerContext الجديد سياقات مثل BUILD وINVOKE، وتُرجع _resolve_symlinks(context) القيمة False لسياق البناء. وبالتالي، ستكون mapped_symlinks قاموسًا فارغًا أثناء البناء، مما يعني عدم تركيب أي روابط رمزية في الحاوية افتراضيًا.
مرجع تغيير الكود: تم تنفيذ الإصلاح في Pull Request#7865 (“إصلاح: حلّ الروابط الرمزية في الاستدعاء المحلي فقط”) وتم إصداره كجزء من v1.133.0. يُظهر الفرق في GitHub تقديم ContainerContext ومنطق التركيب الشرطي. بعدم تركيب أهداف الروابط الرمزية أثناء البناء، لم تعد الحاوية تحصل على وصول إلى ملفات خارج دليل المشروع. (إذا أراد المستخدم بالفعل السماح بروابط رمزية إلى مسارات المضيف، فعليه الآن الاشتراك صراحةً عبر علامة --mount-symlinks، التي أُضيفت بعد هذا الإصلاح.)
كيف يحل التصحيح المشكلة: بعد التصحيح، لن يتم اتباع أي روابط رمزية في المشروع أثناء مرحلة البناء. ستبقى ببساطة كروابط رمزية في الحاوية (تشير إلى مسارات لن تُركَّب) أو سيتم تجاهلها، بدلاً من استبدالها بمحتويات أهدافها. هذا يغلق الثغرة التي كان بإمكان المهاجم من خلالها خداع عملية البناء لنسخ ملفات حساسة من المضيف. باختصار، أصبحت رؤية حاوية البناء محصورة الآن في دليل المشروع نفسه (بالإضافة إلى وحدات التخزين المسموح بها صراحةً)، مما يلغي تصعيد الامتيازات غير المقصود.
المعالجة: يجب على جميع المستخدمين الترقية إلى AWS SAM CLI v1.133.0 أو إصدار أحدث للحصول على هذا الإصلاح. بعد الترقية، يكون السلوك الافتراضي آمنًا. فقط إذا كنت تثق صراحةً في مشروعك وتحتاج إلى السلوك القديم، يجب عليك استخدام sam build --use-container --mount-symlinks. بالنسبة لمعظم المطورين، يُوصى بترك هذه العلامة معطّلة (الافتراضي) لضمان عدم قدرة الروابط الرمزية على تجاوز مساحة العمل. ومن الممارسات الجيدة أيضًا مراجعة أي روابط رمزية في مشاريعك للتأكد من أنها لا تشير إلى مواقع حساسة.
GHSA-pp64-wj43-xqcr هي ثغرة ذات صلة تؤثر على AWS SAM CLI <= v1.133.0 (تم إصلاحها في v1.134.0). يمكن لثغرة في التخزين المؤقت لمخرجات البناء في AWS SAM CLI أن تسمح بتسريب ملفات حساسة من الحاوية مرة أخرى إلى مساحة عمل المضيف بعد البناء. إذا تضمن المشروع روابط رمزية، فبعد تشغيل sam build --use-container, كانت محتويات أهداف الروابط الرمزية تُنسخ إلى دليل التخزين المؤقت المحلي للبناء كملفات أو مجلدات عادية. في الواقع، يمكن لمطور لا يملك وصولاً إلى ملفات معينة على المضيف أن يحصل على هذا الوصول لأن محتويات تلك الملفات تنتهي في مخرجات البناء .aws-sam على المضيف. على سبيل المثال، يمكن أن يؤدي رابط رمزي في المشروع يشير إلى /secret/config إلى ظهور المحتوى الفعلي لـ /secret/config في مجلد .aws-sam/build الخاص بالمشروع بعد بناء الحاوية، حتى لو لم يتمكن المستخدم من قراءة /secret/config مباشرة.
السبب الجذري والمكوّن المتأثر: كان جوهر هذه المشكلة في كيفية نسخ SAM CLI للملفات من الحاوية (أو من عملية البناء) إلى دليل مخرجات البناء المحلي للمشروع. توجد الدالة المسؤولة عن نسخ الملفات في samcli/lib/utils/osutils.py، وتحديدًا أداة copytree المخصصة. في الإصدارات الضعيفة، استخدمت هذه الدالة shutil.copy2 من بايثون دون تحديد follow_symlinks=False، والتي تتبع الروابط الرمزية افتراضيًا وتنسخ محتويات الملف. يوضح المقتطف أدناه (من v1.133.0) المنطق المُشكِل:
# samcli/lib/utils/osutils.py (v1.133.0 - vulnerable snippet)
# ... inside osutils.copytree ...
else:
try:
shutil.copy2(new_source, new_destination) # follow_symlinks is True by default (vulnerable)
except OSError as e:
if e.errno != errno.EINVAL:
raise e
هنا، إذا كان new_source رابطًا رمزيًا، فسيقوم shutil.copy2 بحلّ الرابط الرمزي ونسخ الملف الهدف إلى new_destination. لم تكن هناك علامة لإخباره بالحفاظ على الرابط الرمزي. وبالتالي، فإن الرابط الرمزي لملف حساس سيؤدي إلى ظهور محتويات ذلك الملف في الدليل الوجهة. (انظر aws/aws-sam-cli#7890)
من الناحية العملية، تخيل أن عملية البناء أنشأت رابطًا رمزيًا config -> /etc/secret-config (ربما كجزء من طبقات التبعيات أو كبقايا من سيناريو CVE-2025-3047). كان الكود أعلاه سينسخ محتويات /etc/secret-config إلى مخرجات البناء المحلية باسم config. يمكن للمستخدم المحلي الذي لا يستطيع قراءة /etc/secret-config مباشرة أن يفتح ببساطة الملف تحت .aws-sam/build/.../config ويرى محتوياته.
التصحيح (الكود المُصحح في v1.134.0): كان الإصلاح مباشرًا – الحفاظ على الروابط الرمزية بدلاً من اتباعها عند النسخ. في وحدة shutil بلغة بايثون، يتم ذلك بتمرير follow_symlinks=False. يغيّر الكود المُصحح (v1.134.0) استدعاء النسخ على النحو التالي:
# samcli/lib/utils/osutils.py (v1.134.0 - patched snippet)
else:
try:
shutil.copy2(new_source, new_destination, follow_symlinks=False) # Do not follow symlinks (fixed)
except OSError as e:
if e.errno != errno.EINVAL:
raise e
مع follow_symlinks=False، إذا كان new_source رابطًا رمزيًا، ستقوم الدالة بنسخ الرابط الرمزي نفسه بدلاً من الملف الذي يشير إليه. بعبارة أخرى، سيحتوي مخرج البناء على رابط رمزي بنفس الهدف، بدلاً من ملف حقيقي بمحتويات الهدف.
مرجع تغيير الكود: تم التغيير في Pull Request#7890 (“إصلاح: الحفاظ على الروابط الرمزية عند نسخ الملفات بعد البناء”) وتم إصداره في v1.134.0. يؤكد الفرق في GitHub على هذه الـ PR إضافة معامل follow_symlinks=False إلى shutil.copy2, إلى جانب اختبارات وحدة محدّثة لضمان الحفاظ على الروابط الرمزية. يذكر وصف الـ PR صراحةً: “لن يتم تحويل الروابط الرمزية بعد الآن إلى نسخ من الملفات وستحافظ على وضعها كروابط رمزية.” هذا يعني أن مخرجات البناء ستحتوي على روابط رمزية (تشير إلى مسارات الملفات الأصلية) بدلاً من نسخ غير مصرّح بها لبيانات الملفات.
كيف يحل التصحيح المشكلة: بعد هذا الإصلاح، لم يعد نسخ الملفات بعد البناء في SAM CLI يسرب محتويات الملفات. إذا تم إنشاء رابط رمزي أثناء البناء، فسيظل رابطًا رمزيًا في المخرجات. لن يحصل المستخدم المحلي بأعجوبة على إذن قراءة لمحتوى الهدف – سيرى فقط رابطًا رمزيًا لا يزال يشير إلى المسار الأصلي. ما لم يكن المستخدم يملك بالفعل إذن قراءة الملف الهدف، فإن الرابط الرمزي في المخرجات غير ضار (سيُظهر خطأً إذا تم فك الإشارة إليه دون الوصول المناسب). بشكل أساسي، يتم تخفيف الأثر على السرية: لا يتم إظهار الملفات الحساسة عن غير قصد في مساحة عمل المستخدم. يكمل هذا الإصلاح تصحيح CVE-2025-3047: فقد أوقف الحاوية عن التقاط ملفات المضيف عبر الروابط الرمزية؛ بينما يمنع إصلاح CVE-2025-3048 أيًا منها مرّت (أو وُجدت بشكل مشروع) من أن تُحفظ كملفات عادية في المخرجات.
المعالجة: يجب على المستخدمين ترقية إلى AWS SAM CLI v1.134.0 أو أحدث للحصول على هذا التصحيح. بمجرد الوصول إلى v1.134.0+، ستحافظ عملية البناء على الروابط الرمزية افتراضيًا، مما يصلح هذه الثغرة. بعد الترقية، يُوصى بتنظيف وإعادة بناء أي تطبيقات SAM لضمان إعادة توليد أي مخرجات بناء مخزنة مؤقتًا بموجب السلوك الجديد الأكثر أمانًا (تنصح نشرة AWS الأمنية بتشغيل sam build --use-container جديدة بعد الترقية). لا توجد حلول بديلة لهذه المشكلة في الإصدارات الأقدم بخلاف إزالة الروابط الرمزية الحساسة يدويًا أو عدم استخدام بناء الحاويات، لذا فإن الترقية هي الحل الوحيد القوي. بشكل عام، تعامل مع مجلد بناء SAM CLI المحلي كمخرجات حساسة – مع التصحيح، يجب ألا يحتوي بعد الآن على أسرار غير متوقعة، لكن من الجيد مراقبة ما ينتهي به الأمر في مخرجات البناء الخاصة بك.
تتضمن كل من CVE-2025-3047 وCVE-2025-3048 نقاط ضعف في معالجة الروابط الرمزية قد تؤدي إلى كشف ملفات سرية أثناء عمليات البناء المحلية. يمكن أن تسمح CVE-2025-3047 بقراءة الملفات داخل حاوية Docker (وربما نسخها إلى الخارج)، بينما يمكن أن تسمح CVE-2025-3048 بوصول تلك الملفات إلى المخرجات المحلية حيث يمكن لمستخدم أو مهاجم قراءتها لاحقًا. صُنّفت هذه الثغرات بمستوى شدّة متوسط، لأنها تتطلب بعض التفاعل من المستخدم (تشغيل بناء على مشروع ضار) ولكنها قد تؤدي إلى أثر كبير على السرية. تضمن التصحيحات المنسقة أنه افتراضيًا، لن يتبع SAM CLI الروابط الرمزية أثناء عمليات البناء في الحاويات ولن ينسخ أهداف الروابط الرمزية إلى المخرجات.
يجب على المطورين ومحترفي DevSecOps الذين يستخدمون SAM CLI التأكد من أن واجهة الأوامر السطرية الخاصة بهم محدّثة (v1.134.0 أو أحدث) والبقاء حذرين بشأن المشاريع التي تحتوي على روابط رمزية غير متوقعة. إذا كنت تحتفظ بنسخ مفروعة أو معدّلة من SAM CLI، فيجب عليك دمج هذه الإصلاحات نفسها. من خلال فهم هذه التغييرات البرمجية، يمكن للمرء أن يقدّر كيف يمكن لتعديل منطقي بسيط – مثل إضافة شرط أو معامل دالة – أن يسدّ ثغرة أمنية خطيرة. ضع دائمًا في الاعتبار أمان معالجة الملفات، خاصة عندما يتعلق الأمر بالروابط الرمزية والتفاعلات مع الحاويات، لمنع مشاكل تجاوز المسار المماثلة في مشاريعك الخاصة.
المراجع:
container.py وosutils.py توضح الإصلاحات