red-team

استدعاء الأدوات الآمن في البيئات المعزولة عن الشبكة

كان الإنذار الأول حزمة DNS واحدة فقط، تخرج من مختبر يُفترض أنه معزول، متجهةً إلى نطاق يبدو وكأنه معرّف بصيغة base32. كان المشغّل يدقّق العملية باستخدام وكيل محلي يعمل بـ Qwen 2.5 7B عبر Ollama، ومن المفترض نظريًا أن الوكيل لا يملك أي وصول شبكي خارج النطاق الداخلي للمختبر. خرجت الحزمة لأن إحدى الأدوات التي يستطيع الوكيل استدعاءها هي resolve_target، وقرر النموذج، أمام prompt مسموم وصل عبر بانر SMB مُفهرس في الـ RAG لدينا، أن الخطوة المنطقية التالية هي تحليل اسم نطاق كامل (FQDN) كان المهاجم قد دسّه عمدًا في ذلك البانر.

لم تكن هذه عملية APT. كانت إثبات مفهوم نفذناه على أنفسنا، وساعدنا على إدراك أمر بقينا نفكّر فيه أشهرًا: tool calling هو سطح الهجوم الجديد، وفي البيئات المعزولة يبدو هذا السطح أكثر أمانًا مما هو عليه فعليًا. وللإسبويلر: ليس كذلك.

ماذا يحدث حين تمنح النموذج يدين

غيّر استدعاء الأدوات كل شيء. انتقلنا من نماذج تبصق نصًا إلى وكلاء يشغّلون nmap، ويقرؤون ملفات، ويستعلمون متّجهات، وإن غفلنا قليلًا أجروا طلبات خارجية. وصف Yao وزملاؤه ذلك جيدًا في عملهم حول jailbreaking via function calling، حيث أبلغوا عن معدلات نجاح تفوق 90% في مهاجمة GPT-4o و Claude 3.5 Sonnet و Gemini 1.5 Pro عبر آلية الوظائف نفسها (Wu et al., 2024)[1]. النموذج محاذٍ تجاه المستخدم، لكنه ليس بالضرورة محاذيًا تجاه الأدوات التي تضعها بين يديه.

المشكلة بنيوية. حين يقرر LLM أي أداة سيستدعي، فإنه يستهلك سياقًا. وهذا السياق، في اختبار اختراق، نصّ غير موثوق بالتعريف: استجابات HTTP، بانرات، ملفات مسترجعة، مخرجات ماسحات. صاغ Wang وزملاؤه ذلك صياغة رسمية في From Allies to Adversaries، مبيّنين كيف يستطيع المهاجم حقن manipulator tools أو تسميم الاستجابات لإجبار الوكيل على استدعاء وظائف لا ينبغي له استدعاؤها (Wang et al., 2024)[2]. الحدّ بين "البيانات" و"التعليمات" يتآكل، والنموذج، إذ لا يمتلك مُحلّل دلالات يفرّق بينهما، يبتلع السمّ كله.

أسطورة العزل عن الشبكة

يفترض كثيرون أنه ما دام النموذج يعمل محليًا على Ollama دون منفذ إلى الإنترنت فالمشكلة تختفي. لا تختفي، بل تتحوّل. رأينا ثلاثة أنماط متكررة في مختبراتنا:

  • تسريب عبر قناة جانبية: DNS داخلي، ARP، سجلات تتزامن لاحقًا، ملفات مؤقتة يلتقطها أحدهم.
  • التحرّك الداخلي (pivoting): يملك الوكيل وصولًا شرعيًا إلى شبكة العميل؛ يستخدمه المهاجم بروكسي.
  • تسميم RAG ذاتيًا: يخزّن الوكيل ملاحظات سيقرأها وكيل لاحق على أنها سياق موثوق.

هذه الأخيرة مزعجة بشكل خاص. تصف أعمال Shi وآخرين حول Log-To-Leak هذا المتجه بدقة: يستدعي الوكيل أداة تسجيل تبدو بريئة، فينتهي بها الأمر إلى تسريب الاستعلامات والاستجابات والحالة الداخلية (Shi et al., 2025)[3]. ولأن السجل يعيش داخل المحيط الأمني، لا يرى SIEM التقليدي شيئًا غريبًا.

كيف عالجنا الأمر عمليًا

أصارحك: لا توجد رصاصة فضية. ما نملكه سلسلة من الطبقات، كل منها تفترض أن السابقة قد تفشل. في طبقتنا الداخلية نفصل ثلاث مسؤوليات، لأن خلطها في ثنائي (binary) واحد طلبٌ صريح للمتاعب.

من جهة، يقوم Gandalf بدور البوابة. هو الشيء الوحيد الذي يخاطب النموذج ويخاطب المشغّل. يحتوي على مكوّن نسمّيه Sentinel يطبّق سياسات قبل وبعد كل tool call: يتحقّق أن الوسائط ضمن نطاق CIDR المصرّح به، وأن الأمر لا يطابق أي توقيع تسريب، وأن الوكيل لا يحاول الخروج من الحديقة. وإن شمّ شيئًا مريبًا، يقطع kill-switch الجلسة ويرمي السياق ويُطلق تنبيهًا.

من جهة أخرى، Gwaihir هو المنفّذ. حين تجتاز مكالمة Sentinel، تتجسّد كعملية ابن (child process) مزوّدة بفلتر seccomp-bpf لا يسمح إلا بمجموعة الـ syscalls الفرعية التي تحتاجها تلك الأداة بعينها. لا connect() عشوائي، ولا execve() إلى ثنائيات خارج الـ allowlist. هذا مستلهم مباشرة مما طرحه Wei وزملاؤه في Securing AI Agent Execution، حيث يرون أن المنفّذ يجب أن يُجهَّز ديناميكيًا فقط بالصلاحيات المطلوبة للخطوة الحالية من الخطة (Wei et al., 2025)[4]. مبدأ الحدّ الأدنى من الامتياز مُطبَّق لكل syscall لا لكل دور.

ثم Beorn، الذي يحافظ على الـ RAG بمتّجهات HTB التي نستخدمها معرفةً تشغيلية (نحو 9115 الآن). لا يستقبل Beorn مدخلات من النموذج مباشرة؛ بل تمرّ الاستعلامات عبر Gandalf الذي يطبّعها. الـ RAG للقراءة فقط من منظور الوكيل، وهذا يقتل متجه التسميم الذاتي الذي ذكرته آنفًا.

مثال على شكل سياسة Sentinel لأداة مسح:

{
  "tool": "nmap_scan",
  "scope_cidr": ["10.10.11.0/24"],
  "deny_flags": ["-oN", "--script=http-fetch", "-iL"],
  "max_runtime_s": 120,
  "seccomp_profile": "gwaihir/profiles/nmap.json",
  "kill_switch": {
    "on_outbound_dns": true,
    "on_unexpected_egress": true,
    "on_token_budget_exceeded": 4096
  }
}

كان on_outbound_dns هو ما اصطاد لنا حادثة بانر SMB. أي عملية تحليل لا تقع داخل المنطقة الداخلية للمختبر تُطلق kill-switch قبل أن تغادر الحزمة الواجهة.

النماذج المحلية: لماذا Qwen و Llama و Phi

العمل على نماذج مستضافة (hosted) ببساطة لا يتوافق مع العزل عن الشبكة. لكن ثمة سبب أدق: النماذج التجارية يكون tool calling فيها مدرَّبًا على schema ثابت، وحين تخرج عنه تميل إلى هلوسة الوسائط. مع Qwen 2.5 و Llama 3.1 و Phi-4 المقدَّمة عبر Ollama نستطيع أن نُلزم structured output بقواعد GBNF، مما يُقلّص كثيرًا سطح "الوسائط الإبداعية". ليس مثاليًا، لكنه قابل للتدقيق.

المفاضلة حقيقية. تخسر قدرة خام: لا يستدلّ Qwen 7B كما يفعل Claude Opus في سلسلة من خمس خطوات بأدوات مترابطة. تعوّض ذلك بتقسيم الخطة إلى خطوات أصغر، وترك Sentinel يتحقّق من كل منها على حدة. وتربح، في المقابل، تتبّعًا كاملًا، وزمن استجابة متوقَّعًا (ثمن الرموز ميلي ثانية، لا دولارات)، وإمكانية تدقيق النموذج بِتًّا بِتٍّ إذا لزم الأمر.

ما الذي يتكسّر حين تعزل

ليست كلها مكاسب. ثلاثة أشياء تكسّرت عندنا حين عزلنا كل شيء:

الأول، الرؤية لتهديدات ناشئة. دون قياسات تخرج من المختبر لا تستطيع الربط بخلاصات CTI آنيًا. حللناها بقناة لا متناظرة: المختبر لا يخاطب الخارج، لكن عملية خارج المختبر تستطيع كل X دقيقة سحب البيانات من bucket داخلي وإثرائها.

الثاني، تحديث الـ RAG. الـ 9115 متّجهًا من HTB التي نحتفظ بها في Beorn لا تتجدّد من تلقاء نفسها. لا بدّ من إعادة الفهرسة في الخارج ودفع snapshot موقَّع إلى الداخل. احتكاك تشغيلي، نعم، لكنه متوقَّع.

الثالث، تجربة المشغّل (UX). مَن اعتاد محادثة نموذج كبير قد يشعر أحيانًا أن Qwen "أغبى". هو كذلك بمعنى ما. لكن نموذج غبي ينفّذ داخل seccomp-bpf بنطاق CIDR و kill-switch أقل خطرًا بكثير من نموذج لامع يملك وصولًا حرًا إلى الـ syscalls.

الخلاصة

إن أخذت شيئًا من هذا، فليكن: العزل عن الشبكة ليس خاصية، بل معمارية. وضمن هذه المعمارية، استدعاء الأدوات هو الموضع الذي تنكسر فيه الثقة أولًا. ابدأ بالأرخص والأكثر فعالية، بهذا الترتيب:

  1. عرّف نطاق CIDR صريحًا لكل جلسة، لا لكل وكيل. ومكافئ Sentinel يتحقّق منه قبل كل استدعاء.
  2. ضع كل تنفيذ أداة خلف فلتر seccomp-bpf بـ allowlist للـ syscalls. إن لم يكن لديك Gwaihir فانظر إلى bubblewrap أو gVisor أو nsjail.
  3. طبّق kill-switch يستجيب لخروج (egress) غير متوقَّع، لا لأوامر مشبوهة فحسب. سيفاجئك النموذج في الوسائط، لا في أسماء الأدوات.
  4. عامل سياق الـ RAG بوصفه بيانات غير موثوقة. نعم، حتى ذلك الذي وضعته أنت بالأمس.

تلك الحزمة من DNS لم تصل إلى أي مكان. لكن سجلّ Sentinel لا يزال في تقرير ما بعد الحادثة عندنا، يذكّرنا بأن وكيلًا محليًا، بلا إنترنت وبنوايا حسنة، قد يحاول مخاطبة نطاق ملفَّق لأن أحدهم كتبه في بانر قبل ستة أشهر. هذه هي الحقيقة التشغيلية. وما عداها مسرح.

ثِق بالنماذج بالقدر الذي يكفي لتؤدّي عملها. وكُن دائمًا بلا ثقة في سياقها.

المراجع

  1. Wu, Z. et al. (2024). The Dark Side of Function Calling: Pathways to Jailbreaking Large Language Models. arXiv:2407.17915.
  2. Wang, H. et al. (2024). From Allies to Adversaries: Manipulating LLM Tool-Calling through Adversarial Injection. arXiv:2412.10198.
  3. Shi, Y. et al. (2025). Log-To-Leak: Prompt Injection Attacks on Tool-Using LLM Agents via Model Context Protocol. OpenReview.
  4. Wei, J. et al. (2025). Securing AI Agent Execution. arXiv:2510.21236.
  5. Patel, R. et al. (2025). Architecting Resilient LLM Agents: A Guide to Secure Plan-and-Execute Patterns. arXiv:2509.08646.