الأمن السيبراني الأدلة التقنية

مراقبة الشبكة واكتشاف التسلل IDS مع Suricata وZeek

مختبر حقيقي يقرأ الحركة نفسها بأداتين، فيكشف Suricata الهجمات المعروفة ويكتب Zeek سجلاً لكل اتصال، مع الضبط وفقد الحزم.

مراقبة الشبكة واكتشاف التسلل IDS مع Suricata وZeek

لنفرض أن لديك شركة توزيع صغيرة في الدمام فيها أربعون موظفاً، ولها متجر إلكتروني يستقبل طلبات العملاء، وسيرفر بريد، وسيرفر ملفات، وبرنامج محاسبة يديره مورد خارجي على سيرفر قديم في غرفة الشبكة، وفي المكتب طابعات وكاميرات مراقبة وشبكة للضيوف. وقبل شهر ثبت فريق تقنية المعلومات، وهو شخصان فقط، وكيل Wazuh على كل السيرفرات كما شرحنا في مراقبة السيرفرات مع Wazuh، فأصبح يرى تخمين كلمات المرور على SSH والملفات الدخيلة في مجلد الموقع، ثم جاء المدقق الداخلي بسؤالين لم يجد الفريق لهما جواباً: ماذا يحدث على الشبكة نفسها بين هذه الأجهزة؟ وأين نظام كشف التسلل ومنعه الذي يطلبه الضابط 2-5-3-6 من الضوابط الأساسية للأمن السيبراني؟ والسؤال الأول هو الأهم في الحقيقة، فالطابعة لا يثبت عليها وكيل، وسيرفر المورد لا يسمح لك بلمسه، وجهاز الضيف لا تعرف عنه شيئاً، وإذا أرسل أي منها شيئاً غريباً فلن تراه في أي لوحة لديك.

وفي هذا المقال سوف نحل هذه المشكلة كما يحلها فريق صغير بأدوات مفتوحة المصدر: نضع مستشعراً Sensor يرى نسخة من حركة الشبكة Network Traffic، ونشغل عليه أداتين مختلفتين في فلسفتهما، Suricata الذي يطابق الحركة بعشرات الآلاف من التوقيعات Signatures وينبه عندما يرى هجوماً يعرفه، وZeek الذي لا يحكم على شيء وإنما يكتب سجلاً منظماً لكل اتصال وكل طلب، فتسأله لاحقاً عما حدث. وقد بنينا مختبراً Lab صغيراً فيه سيرفر لمتجر الشركة وجهاز عميل يرسل حركة عادية وحركة تشبه ما تفعله أدوات الفحص والهجوم، ثم أعطينا الحركة نفسها للأداتين، وكل الأرقام والمخرجات في المقال من هذا المختبر.

📚
المواضيع في السلسلة: (1) كيف يعمل محرك كشف SIEM من الداخل؟ (2) حلول الأمن التجارية وبدائلها مفتوحة المصدر. (3) مراقبة السيرفرات مع Wazuh. (4) مراقبة الشبكة واكتشاف التسلل مع Suricata وZeek (الموضوع الحالي). (5) Security Onion. (6) Wazuh أم Security Onion؟
دليلمراقبة السيرفرات مع Wazuh: من السجل Log إلى حظر المهاجمشغلنا Wazuh مع وكيل على سيرفر تجريبي، فكشف تخمين كلمات مرور SSH وحظر المهاجم تلقائياً، ونبه على ملف PHP دخيل خلال ثانية.دليلكيف يعمل محرك كشف SIEM من الداخل؟كيف يجمع SIEM ملايين السجلات ويوحدها ويطبق قواعد الكشف، مع خط أنابيب Vector وLoki وقاعدة Sigma تطلق فعلاً.دليلحلول الأمن التجارية وبدائلها مفتوحة المصدر: متى توفر فعلاً؟لكل فئة من حلول الأمن التجارية الشائعة في السعودية بديل مفتوح المصدر، وهنا تعرف متى يوفر عليك فعلاً ومتى يكون التجاري أفضل.

وسوف نناقش في هذا المقال ما يلي:

  • ما الذي لا يراه وكيل Wazuh، ولماذا تحتاج عيناً على الشبكة نفسها.
  • الفرق بين كشف التسلل IDS ومنعه IPS، وأين يقف المستشعر، والفرق بين منفذ SPAN وجهاز TAP.
  • من أين جاءت الأداتان، ورخصة كل منهما ونشاط تطويرها، والبديل التجاري وسعره.
  • بناء المختبر وتسجيل الحركة في ملف PCAP، وتنزيل قواعد ET Open بالأداة suricata-update.
  • التشغيل الأول الذي أعطى 42 تنبيهاً، منها 41 لا معنى لها، وفاته هجوم حقيقي، والسبب الذي اسمه Checksum Offloading.
  • ما رأته القواعد الجاهزة وما فاتها، وكتابة قواعد محلية Local Rules وقراءة التنبيه في eve.json سطراً سطراً.
  • Zeek وسجلاته: conn وhttp وdns وfiles وsoftware، وسكربت صغير يرفع إشعاراً Notice، وكيف تربط السجلات بمعرف واحد.
  • التشغيل على الواجهة الحية Live Interface: الذاكرة ووقت البدء وفقد الحزم الذي لا تراه إلا إذا بحثت عنه.
  • ضبط التنبيهات Tuning حتى لا يتعلم الفريق تجاهلها، ثم تصميم المستشعر في الشركة، وضوابط الهيئة الوطنية للأمن السيبراني، وقائمة تحقق للتشغيل.

ما الذي لا يراه وكيل Wazuh؟

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

  • الأجهزة المدمجة Embedded Devices: الطابعات وكاميرات المراقبة وجهاز البصمة ونظام التكييف الذكي، وكلها أجهزة Linux صغيرة لا يمكن تثبيت شيء عليها، ولا تحدث إلا عندما يتذكر أحد ذلك، وإذا اخترقت فهي نقطة ممتازة للمهاجم لأنه لا أحد يراقبها.
  • سيرفرات الموردين: سيرفر المحاسبة الذي يديره المورد عن بعد، والعقد في الغالب يمنعك من تثبيت برامج عليه، ومع ذلك فهو داخل شبكتك ويتصل بالإنترنت.
  • الأجهزة الشخصية وأجهزة الضيوف: جوالات الموظفين وحواسيب الزوار على شبكة الضيوف، وإذا كانت شبكة الضيوف غير مفصولة جيداً كما شرحنا في تقسيم الشبكة Network Segmentation فهي تصل إلى ما لا يجب أن تصل إليه.
  • ما لا يكتبه التطبيق في السجلات: حتى على السيرفر الذي عليه وكيل، فإن طلباً ضاراً يصل إلى تطبيق لا يسجل الرابط كاملاً، أو اتصالاً يخرج من عملية مخترقة إلى عنوان غريب، قد لا يترك أثراً في أي ملف يقرؤه الوكيل.

والحل الذي استقرت عليه الصناعة منذ التسعينات هو مراقبة أمن الشبكة Network Security Monitoring، واختصاراً NSM، وفكرتها بسيطة: بدلاً من أن تسأل كل جهاز عما فعل، تضع جهازاً واحداً في نقطة تمر بها الحركة، وتجعله يرى نسخة منها، فيرى كل الأجهزة في وقت واحد، ولا يحتاج أن يثق بأي منها. والمقابل أن هذا الجهاز يرى الحركة فقط، فإذا كانت مشفرة بـ TLS فهو يرى من اتصل بمن ومتى وكم بايت، واسم الموقع في الغالب، ولكنه لا يرى المحتوى، ولذلك لا تغني مراقبة الشبكة عن الوكيل ولا يغني الوكيل عنها، وإنما يكمل كل منهما ما لا يراه الآخر.

كشف التسلل IDS أم منعه IPS؟

نظام كشف التسلل Intrusion Detection System، واختصاراً IDS، يقرأ نسخة من الحركة ويكتب تنبيهاً Alert عندما يرى شيئاً يطابق نمط هجوم معروف، ولكنه لا يتدخل في الحركة نفسها، فالطلب الضار يصل إلى السيرفر في كل الأحوال، وكل ما تحصل عليه هو أنك تعرف. أما نظام منع التسلل Intrusion Prevention System، واختصاراً IPS، فيقف في طريق الحركة مثل الجدار الناري Firewall، فإذا رأى الطلب الضار أسقطه قبل أن يصل. وقد يتساءل البعض: إذا كان المنع أفضل فلماذا لا نشغل IPS دائماً؟ والإجابة أن الجهاز الذي يقف في الطريق يصبح نقطة فشل، فإذا توقف أو تباطأ توقفت الشبكة أو تباطأت معه، وإذا أخطأت قاعدة واحدة فهي لا تكتب تنبيهاً خاطئاً فقط وإنما تقطع خدمة حقيقية عن العملاء، وتخيل أن قاعدة تبحث عن كلمة OR في الروابط قررت إسقاط كل بحث في متجرك يحتوي على هذه الكلمة.

لذلك تبدأ أغلب الفرق بوضع الكشف، وتراقب التنبيهات أسابيع حتى تعرف أي القواعد دقيقة في شبكتها، ثم تنقل عدداً قليلاً من القواعد الموثوقة إلى وضع المنع، وهذا ما سوف نفعله في هذا المقال، فكل ما فيه في وضع الكشف. وSuricata نفسه يعمل بالوضعين، ويشرح توثيق IPS الفرق بينهما، وصفحة الإعداد على Linux طرق وضعه في طريق الحركة عبر NFQUEUE مع iptables أو nftables أو عبر AF_PACKET بين كرتي شبكة. أما Zeek فليس فيه وضع منع أصلاً، لأنه مصمم للتسجيل وليس للحكم.

والمستشعر Sensor في وضع الكشف يأخذ نسخة من الحركة بإحدى طريقتين، والشكل التالي يبين ذلك:

  • منفذ مراقبة SPAN، ويسمى أيضاً Port Mirroring: إعداد في السويتش Switch يرسل نسخة من حركة منافذ أخرى أو VLAN كاملة إلى منفذ واحد يتصل به المستشعر. وهو مجاني في أغلب السويتشات المدارة، ولكن السويتش يعطي النسخ أولوية أقل من الحركة الحقيقية، فإذا ازدحم أسقط بعض الحزم المنسوخة دون أن يخبرك.
  • جهاز TAP: جهاز يوضع على الكابل نفسه بين جهازين، وينسخ كل ما يمر فيه إلى المستشعر، فلا يسقط شيئاً ولا يعتمد على إعداد السويتش، ولكنه جهاز إضافي تشتريه لكل نقطة، وإذا وضعته فأنت تقطع الكابل لحظة التركيب.
الإنترنت والموظفون يتصلون بالسويتش، والسويتش يرسل الحركة الأصلية إلى السيرفرات ونسخة من كل حزمة عبر منفذ SPAN أو جهاز TAP إلى المستشعر الذي يشغل Suricata ليطابق التوقيعات ويكتب eve.json وfast.log، وZeek ليكتب سجلاً لكل اتصال مثل conn وdns وhttp وssl، والمستشعر يرسل النتائج إلى SIEM مثل Wazuh أو Elastic أو Loki
المستشعر يأخذ نسخة من الحركة، فلا يبطئ الشبكة ولا يوقفها، ولكنه أيضاً لا يمنع الهجوم

وفي المختبر لا يوجد سويتش، وإنما شبكة Docker، لذلك سوف نسجل الحركة أولاً من داخل العميل بالأداة tcpdump إلى ملف PCAP ونعطيه للأداتين، والسبب أن الملف ثابت، فكل تجربة تقرأ الحزم نفسها بالترتيب نفسه، وتستطيع أن تقارن بين الإعدادات بعدل. ثم في جزء لاحق سوف نشغل الأداتين على الواجهة الحية للسيرفر، بأن نضع ال Container الخاص بكل منهما في مساحة الشبكة Network Namespace الخاصة بالسيرفر، فيرى كل ما يدخل إليه ويخرج منه، وهذا يشبه أن تشغل المستشعر على السيرفر نفسه بدلاً من منفذ SPAN، وقد شرحنا مساحات الشبكة في Docker في شبكات Docker وأفضل الممارسات.

أداتان وفلسفتان: من أين جاء Suricata وZeek؟

قصة Zeek أقدم، فقد بدأه Vern Paxson في مختبر Lawrence Berkeley الوطني سنة 1995 كما يذكر ملف الرخصة نفسه، تحت اسم Bro، ونشر فكرته في ورقة Bro: A System for Detecting Network Intruders in Real-Time، وكانت الفكرة الأساسية أن المهاجم الذكي يستطيع أن يتجنب أي توقيع ثابت، فالأفضل أن تفهم البروتوكول نفسه وتكتب ما يحدث فيه، ثم تكتب منطق الكشف بلغة برمجة كاملة بدلاً من قواعد مطابقة. وفي أكتوبر 2018 أعلن المشروع تغيير الاسم إلى Zeek. والرخصة BSD، أي أنك تستخدمه وتعدله وتبيع خدمات فوقه دون أن تنشر كودك، وآخر إصدار رئيسي هو Zeek 9.0.0 بتاريخ 21 أغسطس 2026، ومعه سلسلتان مدعومتان 8.2 و8.0 صدر لكل منهما إصدار تصحيحي في 19 أغسطس، فالمشروع نشط ويصدر بانتظام.

أما Suricata فبدأ في 2009 كمحرك جديد لكشف التسلل ومنعه، وصدر أول إصدار تجريبي عام في ليلة رأس السنة 2009، ثم أسست مؤسسة OISF غير الربحية وصدر الإصدار 1.0 في 2010 بتمويل من وزارة الأمن الداخلي الأمريكية. وكان الهدف محركاً متعدد الخيوط Multi-threaded يستفيد من المعالجات متعددة الأنوية، ويقرأ نفس صيغة قواعد Snort تقريباً، ويفهم البروتوكولات بنفسه فيكتب سجلات لها أيضاً، والفروق بين الصيغتين في صفحة الاختلاف عن Snort. والرخصة GPL-2.0 كما في ملف الرخصة، وآخر إصدار هو Suricata 8.0.7 بتاريخ 15 سبتمبر 2026، والسلسلة 7.0 ما زالت تستقبل إصدارات تصحيحية آخرها 7.0.17 في يوليو، وهذا مهم إذا كانت توزيعتك تحمل السلسلة القديمة.

والفرق بين الأداتين في سؤال كل منهما، وهذا ما سوف نراه بالأرقام في كل قسم من المقال: Suricata يسأل «هل هذه الحركة تطابق هجوماً أعرفه؟»، فإذا كانت الإجابة لا فهو صامت تماماً، وإذا كانت نعم كتب تنبيهاً واضحاً يفهمه أي شخص. وZeek يسأل «ماذا حدث في الشبكة؟»، فيكتب سطراً لكل اتصال ولكل طلب HTTP ولكل استعلام DNS ولكل ملف، سواءً كان عادياً أو ضاراً، ويترك لك الحكم، فهو لا يصرخ ولكنه يحتفظ بالدليل. ولذلك تعمل الأداتان معاً في أغلب منصات مراقبة الشبكة، ومنها Security Onion الذي سوف نتناوله في المقال القادم.

والبديل التجاري، كم يكلف؟

البديل التجاري المباشر هو Snort مع قواعد الاشتراك من Cisco Talos، أو الأجهزة الجاهزة لمراقبة الشبكة التي تبيعها شركات مثل Corelight المبنية على Zeek، أو أنظمة NDR التي ذكرناها في حلول الأمن التجارية وبدائلها. وأغلب هذه الشركات لا تنشر أسعارها، والاستثناء الواضح هو صفحة اشتراكات Snort، فالاشتراك التجاري Business بقواعد Talos سعره 399 دولاراً لكل مستشعر في السنة، والاشتراك الشخصي 29.99 دولاراً ولكنه للطلاب والشبكات المنزلية فقط، وذلك عند مراجعتنا للصفحة في أكتوبر 2026. أما Suricata فيقرأ مجاناً قواعد ET Open من Proofpoint، وتبيع Proofpoint نسخة أوسع اسمها ET Pro لم نجد سعرها منشوراً في صفحتها. والتكلفة الحقيقية في الحالتين ليست الرخصة، وإنما الجهاز والقرص والوقت الذي يقضيه الفريق في قراءة التنبيهات وضبطها، كما سوف نرى.

المختبر Lab: سيرفر متجر وعميل وحركة نعرف كل طلب فيها

الطريقة الصحيحة لتجربة أداة كشف ليست أن تشغلها على شبكتك وتنتظر ما يظهر، والسبب أنك لا تعرف ماذا كان يجب أن يظهر، فلا تستطيع أن تحكم على النتيجة. لذلك بنينا حركة نعرف كل طلب فيها مسبقاً، ثم نقيس ما رأته كل أداة. والمختبر جهازان على شبكة Docker خاصة بالعنوان 10.250.45.0/24، ولا يتصل أي منهما بشيء خارجها:

  • السيرفر 10.250.45.10: Debian 12 عليه nginx 1.22.1 يخدم صفحة المتجر على HTTP وعلى HTTPS بشهادة موقعة ذاتياً للاسم shop.lab.example، وdnsmasq يجيب عن أسماء المختبر فقط ولا يمرر أي سؤال إلى الإنترنت، وفيه مجلد /uid/ يرد بنص يشبه مخرج الأمر id لحساب root، ووضعناه عن قصد لنرى قاعدة تراقب ردود السيرفر وليس طلبات العميل فقط.
  • العميل 10.250.45.20: Debian 12 عليه curl وdig وtcpdump، يرسل الحركة ويسجلها.

وملف Dockerfile للسيرفر كما يلي، ومعه ملف إعداد nginx وملف dnsmasq وسكربت تشغيل صغير:

# Lab web server: plain HTTP on 80, HTTPS with a self-signed certificate on 443, dnsmasq for the lab DNS names
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends nginx openssl dnsmasq \
 && rm -rf /var/lib/apt/lists/* \
 && openssl req -x509 -newkey rsa:2048 -nodes -days 30 -subj "/CN=shop.lab.example" \
      -keyout /etc/ssl/private/lab.key -out /etc/ssl/certs/lab.crt \
 && echo "<html>shop lab</html>" > /var/www/html/index.html \
 && mkdir -p /var/www/html/uid && printf 'uid=0(root) gid=0(root) groups=0(root)\n' > /var/www/html/uid/index.html
COPY default.conf /etc/nginx/sites-enabled/default
COPY dnsmasq.conf /etc/dnsmasq-lab.conf
COPY start.sh /start.sh
CMD ["sh","/start.sh"]
# dnsmasq.conf: answer for the lab names only, never forward to the internet
no-resolv
address=/shop.lab.example/10.250.45.10
address=/lab.example/10.250.45.10
log-queries

والعميل أبسط من ذلك:

# Lab client: generates normal traffic and captures it with tcpdump
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends curl dnsutils tcpdump openssh-client ca-certificates \
 && rm -rf /var/lib/apt/lists/*

ثم نبني الجهازين ونشغلهما على شبكة بعناوين ثابتة، حتى تبقى العناوين في كل المخرجات واحدة:

docker build -t ids-web:lab web
docker build -t ids-client:lab client
docker network create --subnet 10.250.45.0/24 ids-lab
docker run -d --name ids-web --network ids-lab --ip 10.250.45.10 ids-web:lab
docker run -d --name ids-client --network ids-lab --ip 10.250.45.20 ids-client:lab sleep infinity
🛑
كل الحركة في هذا المقال تخرج من جهاز في المختبر إلى سيرفر في المختبر نفسه، ولا تلمس أي جهاز آخر. ولا ترسل طلبات تشبه الهجمات إلى سيرفر لا تملكه أو لم تحصل على إذن مكتوب بفحصه، حتى لو كان الهدف «التجربة فقط»، فهذا في نظر صاحب السيرفر وفي نظر القانون هجوم، وقد شرحنا حدود الإذن ونطاقه في اختبار الاختراق المصرح به.

الحركة: ماذا أرسلنا ولماذا؟

أرسل العميل أحد عشر طلباً، لكل طلب سبب، والجدول التالي يصفها بالكلمات بدلاً من نسخ الطلبات نفسها، لأن الهدف أن تفهم ما تراه الأداة وليس أن تحفظ طلبات هجوم:

الطلبماذا يشبهما نتوقعه
استعلاما DNS عن shop.lab.example وadmin.lab.exampleجهاز يبحث عن أسماء داخليةسجل DNS فقط، فلا شيء ضار هنا
طلبا HTTP وHTTPS عاديان للصفحة الرئيسيةزائر عادي للمتجرسجلات فقط، وأي تنبيه هنا إنذار كاذب False Positive
طلب ترويسة User-Agent فيه نمط اختبار ثغرة Shellshock المعروفةأداة فحص تبحث عن سيرفرات Bash قديمةتنبيه من القواعد الجاهزة
طلب فيه نمط حقن SQL كلاسيكي في معامل الرابطمحاولة SQL Injection يدويةتنبيه
طلب يحاول قراءة /etc/passwd بالرجوع في المجلداتمحاولة Directory Traversalتنبيه
طلب للمجلد /uid/ الذي يرد بنص يشبه مخرج id لحساب rootرد سيرفر بعد تنفيذ أمر ناجحتنبيه على الرد وليس على الطلب
ثلاثة طلبات عادية بترويسة User-Agent لأدوات Nmap وsqlmap وNiktoأدوات فحص معروفة تعلن عن نفسهاتنبيه أو إشعار، بحسب القواعد

والطلبات الضارة في هذا الجدول لا تضر السيرفر فعلاً، فهو nginx يخدم ملفات ثابتة، فلا يوجد Bash يتأثر بـ Shellshock ولا قاعدة بيانات تتأثر بالحقن ولا ملف index.php، وهذا مقصود، فنحن نختبر المراقبة وليس الثغرات. ولتسجيل الحركة نشغل tcpdump داخل العميل قبل الطلبات ونوقفه بعدها، بهذا الشكل:

docker cp traffic.sh ids-client:/traffic.sh
docker exec ids-client sh -c 'tcpdump -i eth0 -w /tmp/lab.pcap -U host 10.250.45.10 & sleep 2; sh /traffic.sh; sleep 2; kill $!'
docker cp ids-client:/tmp/lab.pcap ./lab.pcap

والملف الناتج فيه 102 حزمة Packet بحجم 12,897 بايتاً، وهو ما سوف نقرؤه في كل التجارب التالية.

قواعد ET Open: 69 ألف قاعدة بأمر واحد

Suricata بلا قواعد مثل كاميرا بلا تسجيل، فالمحرك يفك الحزم ويفهم البروتوكولات، ولكن ما يقرر التنبيه هو القواعد Rules. وأشهر مصدر مجاني هو ET Open، وتديره الأداة suricata-update التي تأتي مع Suricata، فتنزل القواعد وتدمجها في ملف واحد وتطبق عليها قوائم التفعيل والتعطيل التي تكتبها أنت، ثم تختبرها. واستخدمنا الـ Docker Image الذي يصونه Jason Ish من فريق Suricata في مستودع docker-suricata، مع Volume يحفظ القواعد بين التشغيلات:

docker volume create suricata-lib
docker run --rm -v suricata-lib:/var/lib/suricata jasonish/suricata:8.0.7 suricata-update

وآخر سطور المخرج كما يلي:

25/10/2026 -- 22:50:23 - <Info> -- Loaded 69237 rules.
25/10/2026 -- 22:50:26 - <Info> -- Disabled 16 rules.
25/10/2026 -- 22:50:26 - <Info> -- Enabled 0 rules.
25/10/2026 -- 22:50:26 - <Info> -- Modified 0 rules.
25/10/2026 -- 22:50:26 - <Info> -- Dropped 0 rules.
25/10/2026 -- 22:50:27 - <Info> -- Enabled 136 rules for flowbit dependencies.
25/10/2026 -- 22:50:27 - <Info> -- Creating directory /var/lib/suricata/rules.
25/10/2026 -- 22:50:27 - <Info> -- Backing up current rules.
25/10/2026 -- 22:50:27 - <Info> -- Writing rules to /var/lib/suricata/rules/suricata.rules: total: 69237; enabled: 53269; added: 69237; removed 0; modified: 0
25/10/2026 -- 22:50:29 - <Info> -- Writing /var/lib/suricata/rules/classification.config
25/10/2026 -- 22:50:30 - <Info> -- Testing with suricata -T.
25/10/2026 -- 22:53:05 - <Info> -- Running suricatasc -c reload-rules.
25/10/2026 -- 22:53:05 - <Error> -- Reload command failed: b'Unable to connect socket to /var/run/suricata/suricata-command.socket: ioerror: `No such file or directory (os error 2)`\n'
25/10/2026 -- 22:53:05 - <Info> -- Done.

في المخرج أعلاه لاحظ التالي:

  • الملف فيه 69,237 قاعدة، المفعل منها 53,269 فقط، والباقي معطل في المصدر نفسه لأنه كثير الإنذارات الكاذبة أو قديم أو ثقيل، وأضافت الأداة 136 قاعدة لأن قواعد أخرى تعتمد عليها عبر flowbits، وهي علامات تضعها قاعدة على الاتصال لتقرأها قاعدة أخرى لاحقاً.
  • الملف suricata.rules حجمه 46 MB تقريباً، وكله نص يقرؤه المحرك عند كل بدء، ولهذا سوف نرى أن وقت البدء يقاس بالدقائق.
  • اختبار القواعد suricata -T أخذ وحده دقيقتين ونصفاً من 22:50:30 إلى 22:53:05، وهو خطوة مهمة لأن قاعدة واحدة فيها خطأ صياغة قد تمنع المحرك من البدء.
  • رسالة الخطأ في آخر المخرج طبيعية هنا، فالأداة تحاول أن تطلب من Suricata العامل إعادة تحميل القواعد عبر Socket، ولا يوجد Suricata يعمل في هذا ال Container، وعلى المستشعر الحقيقي سوف ينجح هذا الأمر فتتحدث القواعد دون إعادة تشغيل.

والقواعد تتغير يومياً تقريباً، لأن الثغرات الجديدة وحملات البرمجيات الخبيثة لا تتوقف، لذلك شغل suricata-update مرة يومياً بمهمة مجدولة Cron Job على المستشعر، وراجع عدد القواعد المضافة والمحذوفة في سجلها، فإذا قفز عدد التنبيهات في يوم ما فأول سؤال: هل تغيرت القواعد أمس؟

التشغيل الأول: 42 تنبيهاً، وهجوم حقيقي لم يظهر

الآن نعطي الملف لـ Suricata بالإعداد الافتراضي، بالخيار -r الذي يقرأ ملف PCAP بدلاً من واجهة شبكة، و-l لمجلد السجلات، كما في خيارات سطر الأوامر:

docker run --rm -v suricata-lib:/var/lib/suricata -v "$PWD:/lab" jasonish/suricata:8.0.7 \
  suricata -r /lab/lab.pcap -l /lab/out/a -v

وأهم سطور السجل suricata.log كما يلي:

[1 - Suricata-Main] 2026-10-25 22:54:44 Notice: suricata: This is Suricata version 8.0.7 RELEASE running in USER mode
[1 - Suricata-Main] 2026-10-25 22:55:57 Info: detect: 1 rule files processed. 53269 rules successfully loaded, 0 rules failed, 0 rules skipped
[1 - Suricata-Main] 2026-10-25 22:55:59 Info: detect: 53274 signatures processed. 1214 are IP-only rules, 4530 are inspecting packet payload, 47294 inspect application layer, 110 are decoder event only
[1 - Suricata-Main] 2026-10-25 22:56:18 Notice: threads: Threads created -> RX: 1 W: 8 FM: 1 FR: 1   Engine started.
[1 - Suricata-Main] 2026-10-25 22:56:18 Info: suricata: time elapsed 3.114s
[12 - RX#01] 2026-10-25 22:56:18 Warning: pcap: 1/2th of packets have an invalid checksum, consider setting pcap-file.checksum-checks variable to no or use '-k none' option on command line.
[1 - Suricata-Main] 2026-10-25 22:56:19 Info: counters: Alerts: 42

والتنبيهات في الملف fast.log بعد تجميعها بحسب اسم القاعدة كما يلي:

      1  GPL ATTACK_RESPONSE id check returned root
     39  SURICATA TCPv4 invalid checksum
      2  SURICATA UDPv4 invalid checksum

في المخرج أعلاه لاحظ التالي:

  • من بدء التشغيل حتى «Engine started» مرت 94 ثانية، منها 73 ثانية لتحميل القواعد، بينما قراءة الحزم نفسها أخذت ثلاث ثوان، وفي تشغيل منفرد بعده على الجهاز نفسه بدأ المحرك في 59 ثانية، فالرقم يتغير مع حمل الجهاز. وهذا يعني أن إعادة تشغيل المستشعر ليست مجانية، فخلال هذه الدقيقة والنصف لا يرى شيئاً، ولهذا يحدث القواعد بإعادة التحميل وليس بإعادة التشغيل.
  • السطر 53274 signatures processed يقسم القواعد بحسب ما تفحصه: 47,294 قاعدة تفحص طبقة التطبيق Application Layer، أي HTTP وDNS وTLS بعد أن يفهمها المحرك، و4,530 تبحث في محتوى الحزمة نفسها، و1,214 تكتفي بالعناوين، و110 قواعد لأحداث فك الحزم Decoder Events، مثل الحزمة التالفة. وهذا التقسيم سوف يفسر لنا بعد قليل لماذا فات أحد الهجمات.
  • 41 تنبيهاً من 42 تقول إن الـ Checksum غير صحيح، والتحذير قبل السطر الأخير يقول إن نصف الحزم فيها Checksum غير صحيح، ويقترح الحل بنفسه.
  • والأخطر هو ما لا يظهر في المخرج: الطلب الذي يحمل نمط Shellshock، والذي تعرفه ET Open منذ سنوات، لم يطلق أي تنبيه.

ولو كانت هذه شبكة الشركة الحقيقية لوصل إلى الفريق في اليوم الأول عشرات التنبيهات بعنوان «invalid checksum»، فيقرأ أولها باهتمام، ويتجاهل العاشر، ويضيف بعد أسبوع قاعدة تخفيها كلها، وهو لا يعرف أن الهجوم الوحيد المهم في ذلك اليوم لم يصل إليه أصلاً.

لماذا ظهرت نصف الحزم تالفة؟ Checksum Offloading

كل حزمة TCP وUDP تحمل حقلاً اسمه الـ Checksum، وهو رقم يحسب من محتوى الحزمة، فإذا وصلت الحزمة وحسبها المستقبل فوجد رقماً مختلفاً عرف أنها تلفت في الطريق فأسقطها. وحساب هذا الرقم لكل حزمة عمل يستهلك المعالج، لذلك تتركه أغلب أنظمة التشغيل الحديثة لكرت الشبكة نفسه، فتسلم النواة Kernel الحزمة إلى الكرت والحقل فارغ أو جزئي، ويملؤه الكرت لحظة الإرسال، وهذا يسمى Checksum Offloading. والمشكلة أن أدوات الالتقاط مثل tcpdump تأخذ نسخة الحزمة قبل أن تصل إلى الكرت، فترى الحقل كما تركته النواة، وفي الشبكات الافتراضية مثل Docker والأجهزة الافتراضية VM لا يوجد كرت حقيقي أصلاً، فيبقى الحقل غير مكتمل في كل الحزم التي يرسلها الجهاز، ولهذا كانت كل الحزم «التالفة» في المختبر خارجة من السيرفر 10.250.45.10، فهو الطرف الذي التقطنا حزمه قبل اكتمالها من جهة العميل.

في الأعلى نواة السيرفر تترك حقل الـ Checksum لكرت الشبكة، ونقطة الالتقاط عبر tcpdump أو AF_PACKET ترى الحزمة قبل اكتمال الحساب، فيظنها المستشعر تالفة. وفي الأسفل الملف نفسه lab.pcap بثلاث طرق: الإعداد الافتراضي أعطى 41 تنبيه invalid checksum وتنبيهاً حقيقياً واحداً وفاته طلب Shellshock وكل قواعد HTTP المحلية، والخيار -k none أعطى 41 تنبيه checksum وتنبيهين حقيقيين هما 2022028 و2100498، ومع ملف suppress والقواعد المحلية صفر تنبيه checksum وسبعة تنبيهات حقيقية
الـ Checksum الفارغ لا يضيف ضجيجاً فقط، وإنما يخفي هجمات حقيقية

وهنا يأتي السؤال المهم: لماذا فات Shellshock ولم يفت تنبيه id check returned root؟ والإجابة في التقسيم الذي رأيناه في السجل. فعندما يرى Suricata أن حزمة رد السيرفر الأولى SYN-ACK تالفة، فهو يتصرف كما يتصرف أي جهاز سليم ويهملها في تتبع الاتصال، فلا يكتمل المصافحة الثلاثية TCP Handshake في نظره، فلا يعيد تجميع البيانات ولا يحلل HTTP، وبالتالي لا تعمل أي قاعدة من قواعد طبقة التطبيق، وقاعدة Shellshock منها لأنها تبحث في ترويسات HTTP بعد فهمها. أما قاعدة 2100498 فهي قديمة وبسيطة، تبحث عن النص uid=0(root) في محتوى أي حزمة IP دون أن تهتم بالاتصال، كما يظهر في نصها داخل ملف القواعد:

alert ip any any -> any any (msg:"GPL ATTACK_RESPONSE id check returned root"; content:"uid=0|28|root|29|"; classtype:bad-unknown; sid:2100498; rev:7; metadata:created_at 2010_09_23, confidence Medium, signature_severity Informational, updated_at 2019_07_26;)

والحل عندما تقرأ حركة ملتقطة من جهاز افتراضي أن تطلب من Suricata ألا يفحص الـ Checksum بالخيار -k none، فنعيد التشغيل بالقواعد نفسها:

docker run --rm -v suricata-lib:/var/lib/suricata -v "$PWD:/lab" jasonish/suricata:8.0.7 \
  suricata -r /lab/lab.pcap -k none -l /lab/out/d -v

فأصبح عدد التنبيهات 43، منها 41 للـ Checksum كما هي، وتنبيهان حقيقيان:

10/25/2026-21:07:23.427698  [**] [1:2022028:2] ET WEB_SERVER Possible CVE-2014-6271 Attempt [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 10.250.45.20:46734 -> 10.250.45.10:80
10/25/2026-21:07:23.498924  [**] [1:2100498:7] GPL ATTACK_RESPONSE id check returned root [**] [Classification: Potentially Bad Traffic] [Priority: 2] {TCP} 10.250.45.10:80 -> 10.250.45.20:46762

لاحظ أن تنبيهات الـ Checksum لم تختف مع -k none، والسبب أن الخيار يمنع المحرك من إهمال الحزم، ولكن قواعد فك الحزم Decoder Events في ET Open ما زالت تكتب تنبيهاً لكل حزمة حقلها غير صحيح، وهما القاعدتان 2200074 لـ TCP و2200075 لـ UDP. وما دمنا نعرف السبب ونعرف أنه ليس هجوماً، نسكت القاعدتين في ملف العتبات العامة Global Thresholds، مع تعليق يشرح السبب لمن يأتي بعدك:

# silence the invalid-checksum decoder events; they are an artifact of Docker's checksum offloading, not an attack
suppress gen_id 1, sig_id 2200074
suppress gen_id 1, sig_id 2200075

وعلى المستشعر الحقيقي الذي يستقبل الحركة من منفذ SPAN فالأمر مختلف، فالحزم المنسوخة من السويتش خرجت فعلاً على السلك فحقولها مكتملة، والمشكلة تظهر عندما تشغل الأداة على السيرفر نفسه أو داخل جهاز افتراضي. وتوثيق التقاط الحزم ينصح عموماً بإيقاف تقنيات الـ Offloading في كرت الالتقاط بالأداة ethtool، وأهمها LRO وGRO التي تدمج الحزم الصغيرة في حزمة كبيرة فتفسد تتبع حالة TCP، ويقول إن Checksum Offloading يمكن تركه مع AF_PACKET ولكن يجب إيقافه مع الالتقاط عبر PCAP. وعلى الشبكة الحقيقية أبق فحص الـ Checksum في الوضع الافتراضي auto ما استطعت، والسبب أن الحزمة التالفة فعلاً قد تكون محاولة من المهاجم لخداع المستشعر، فيرى المستشعر حزمة لن يقبلها السيرفر، أو العكس.

ماذا يحدث إذا نسيت -k none مع قواعدك المحلية؟

لكي نتأكد أن الأمر ليس صدفة، شغلنا القواعد المحلية الخمس التي سوف نكتبها في القسم التالي وحدها، بالخيار -S الذي يحمل ملف القواعد هذا فقط ويتجاهل غيره، مرة دون -k none ومرة معه. وعدد التنبيهات في المرة الأولى صفر، أي أن خمس قواعد كل منها يطابق طلباً موجوداً فعلاً في الملف لم يطلق أي منها شيئاً، لأن كل قواعدنا من نوع http تحتاج اتصالاً مكتملاً، وفي المرة الثانية خمسة تنبيهات، تنبيه لكل قاعدة. والدرس هنا أوسع من Docker: إذا كتبت قاعدة واختبرتها ولم تطلق شيئاً، فلا تفترض أن الحركة لم تحدث، وتأكد أولاً أن المستشعر يرى الاتصال كاملاً، ومن أسرع الطرق لذلك أن تنظر في eve.json، فإذا لم تجد فيه سجلات http للطلبات التي تعرف أنها حدثت فالمشكلة في الالتقاط وليست في القاعدة.

ما الذي رأته قواعد ET Open وما الذي فاتها؟

بعد إصلاح الـ Checksum رأت 53,269 قاعدة جاهزة شيئين فقط من أحد عشر طلباً، وقبل أن نحكم على هذا الرقم لنفهم كل تنبيه:

  • القاعدة 2022028 «Possible CVE-2014-6271 Attempt»: وهي ثغرة Shellshock في Bash التي نشرت عنها وكالة الأمن السيبراني الأمريكية تنبيهاً في 25 سبتمبر 2014، وتشرح فيه أن المهاجم يستطيع تنفيذ أوامر عن بعد بوضع كود في متغيرات البيئة Environment Variables، وأن سيرفرات الويب التي تشغل سكربتات CGI عبر Bash من أكثر ما تأثر بها، لأن السيرفر ينقل ترويسات الطلب مثل User-Agent إلى متغيرات البيئة. وتفاصيل الثغرة وتقييمها في صفحة CVE-2014-6271. والقاعدة أنشئت في نوفمبر 2015 بحسب الحقل created_at في نصها، وما زالت تطلق إلى اليوم لأن أدوات الفحص الآلي على الإنترنت ما زالت تجرب هذا النمط على كل سيرفر تجده. والكلمة «Possible» في اسمها صادقة، فهي تقول إن أحداً حاول، ولا تقول إن المحاولة نجحت، وفي حالتنا لم تنجح لأن السيرفر nginx يخدم ملفات ثابتة.
  • القاعدة 2100498 «id check returned root»: وهي تراقب رد السيرفر وليس طلب العميل، فاتجاه التنبيه من 10.250.45.10:80 إلى العميل، وتفترض أن ظهور نص مثل uid=0(root) في الحركة يعني أن أحداً نفذ الأمر id على جهاز ما ورأى أنه root، وهذه من أهم أنواع القواعد لأنها تكشف نجاح الهجوم وليس محاولته. ولكنها في المختبر إنذار كاذب، لأننا نحن من وضع هذا النص في صفحة ثابتة، وتصنيفها في ملف القواعد signature_severity Informational وconfidence Medium، أي أن كاتبها نفسه يقول لك تحقق قبل أن تقلق.

أما ما فاتها فهو حقن SQL، ومحاولة قراءة /etc/passwd، وأدوات الفحص الثلاث. وقد يتساءل البعض: كيف تفوت 53 ألف قاعدة أبسط هجمات الويب؟ والإجابة أن قواعد ET مكتوبة لتكون دقيقة على شبكات كبيرة، فأغلبها يستهدف ثغرة محددة في منتج محدد برابط محدد، أو بنية تحتية معروفة لبرمجية خبيثة، ونادراً ما تجد فيها قاعدة عامة تنبه على كل رابط فيه OR 1=1، لأن قاعدة كهذه على شبكة فيها آلاف المواقع تكتب آلاف التنبيهات يومياً من أدوات فحص لا تضر أحداً. وبعض القواعد العامة موجود ولكنه معطل افتراضياً في الـ 16 ألف قاعدة المعطلة. والنتيجة العملية أن القواعد الجاهزة طبقة أولى ممتازة لما هو معروف، ولكنها لا تعرف تطبيقك، ولا تعرف ما هو «غير طبيعي» في شبكتك، وهذا عملك أنت.

قواعد محلية Local Rules تعرف متجرك

في شركة الدمام يعرف الفريق أن المتجر لا يستخدم أبداً معاملاً في الرابط يحتوي على علامات اقتباس، وأنه لا يوجد أي سبب لأن يطلب أحد etc/passwd من سيرفر الويب، وأن ظهور أداة فحص مثل sqlmap أو Nikto في الحركة الداخلية يعني أن أحداً يفحص دون علمهم، أو أن الفحص الخارجي الذي اتفقوا عليه مع شركة الاختبار بدأ. وهذه المعرفة لا تملكها ET Open، فنكتبها في ملف قواعد محلي local.rules:

# Local rules for the lab (sid >= 1000000 is the local range)
# 1) SQL injection patterns in any HTTP query string
alert http any any -> any any (msg:"LAB SQL injection pattern in URI (OR 1=1)"; flow:to_server,established; http.uri; content:"OR"; nocase; pcre:"/(%27|')[\s+]*OR[\s+]*(%27|')?1(%27|')?[\s+]*=[\s+]*(%27|')?1/Ui"; classtype:web-application-attack; sid:1000001; rev:2;)
# 2) Directory traversal in the URI
alert http any any -> any any (msg:"LAB Directory traversal attempt (etc/passwd)"; flow:to_server,established; http.uri; content:"etc/passwd"; nocase; classtype:web-application-attack; sid:1000002; rev:1;)
# 3) Known scanner user agents
alert http any any -> any any (msg:"LAB Scanner user agent (sqlmap)"; flow:to_server,established; http.user_agent; content:"sqlmap"; nocase; classtype:web-application-attack; sid:1000003; rev:1;)
alert http any any -> any any (msg:"LAB Scanner user agent (Nikto)"; flow:to_server,established; http.user_agent; content:"Nikto"; nocase; classtype:web-application-attack; sid:1000004; rev:1;)
alert http any any -> any any (msg:"LAB Scanner user agent (Nmap)"; flow:to_server,established; http.user_agent; content:"Nmap"; nocase; classtype:web-application-attack; sid:1000005; rev:1;)

وكل قاعدة في Suricata تتكون من ثلاثة أجزاء كما يشرح مدخل القواعد: الإجراء Action، ثم الرأس Header الذي يختار الحركة، ثم الخيارات Options بين القوسين التي تبحث داخلها، والشكل التالي يفكك القاعدة الثانية:

نص القاعدة 1000002 ملوناً، وتحته خمس بطاقات: الإجراء alert وبدائله drop وpass، والرأس http any any إلى any any ومعناه، والرسالة msg والاتجاه flow:to_server,established ولماذا فشلت عند تلف الـ Checksum، ومكان البحث http.uri كمخزن Sticky Buffer مع content وnocase وpcre، والهوية classtype وsid في المجال 1000000 فما فوق وrev
الرأس يختار الحركة، والخيارات تحدد أين تبحث وعن ماذا، والهوية تجعل التنبيه قابلاً للإدارة

وفي القواعد أعلاه لاحظ التالي:

  • alert http لا تعني المنفذ 80، وإنما أي حركة تعرف عليها Suricata على أنها HTTP بتحليل محتواها، فلو شغل أحد سيرفر ويب على المنفذ 8443 أو 5000 فسوف تراه القاعدة أيضاً، وهذه من أهم الفروق بين المطابقة بالبروتوكول والمطابقة بالمنفذ.
  • http.uri وhttp.user_agent مخازن ثابتة Sticky Buffers، أي أن كل content بعدها يبحث في هذا الجزء من الطلب فقط، وhttp.uri يحتوي على الرابط بعد فك ترميزه Normalized، فالنص %27 يصبح علامة اقتباس، وهذا يمنع المهاجم من الهروب بترميز الأحرف، والتفاصيل في كلمات HTTP في القواعد.
  • القاعدة الأولى تبحث أولاً عن النص البسيط OR ثم تطبق pcre، والترتيب مقصود، فالمحرك يستخدم content كمرشح سريع ولا يشغل التعبير النمطي Regular Expression الأبطأ إلا على الطلبات التي فيها هذا النص. والخيار U في نهاية التعبير يطبقه على الرابط بعد فك الترميز، وi يتجاهل حالة الأحرف.
  • المجال من 1000000 إلى 1999999 في sid محجوز للاستخدام المحلي بحسب سجل توزيع أرقام القواعد الذي يحيل إليه توثيق Suricata، فلا يتعارض رقمك مع قاعدة من ET يوماً ما.
  • الحقل rev في القاعدة الأولى 2 وليس 1، لأننا عدلناها بعد أول نسخة، ومن الجيد أن تزيده مع كل تعديل حتى تعرف من التنبيه أي نسخة من القاعدة أطلقته.

ولتجربة القواعد بسرعة دون انتظار ET Open، نحملها وحدها بالخيار -S، وهنا الفرق الكبير في وقت البدء:

docker run --rm -v "$PWD:/lab" jasonish/suricata:8.0.7 \
  suricata -r /lab/lab.pcap -k none -S /lab/local.rules -l /lab/out/c -v
[1 - Suricata-Main] 2026-10-25 22:54:38 Notice: suricata: This is Suricata version 8.0.7 RELEASE running in USER mode
[1 - Suricata-Main] 2026-10-25 22:54:39 Notice: threads: Threads created -> RX: 1 W: 8 FM: 1 FR: 1   Engine started.
[1 - Suricata-Main] 2026-10-25 22:54:40 Info: suricata: time elapsed 0.551s

أي أن المحرك بدأ في ثانية واحدة بدلاً من دقيقة ونصف، لذلك اكتب قواعدك واختبرها على ملف PCAP بهذا الشكل حتى تتأكد أنها تطلق على ما تريد ولا تطلق على الطلبات العادية في الملف نفسه، ثم أضفها إلى المستشعر. وبعد ذلك نجمع كل شيء: ET Open مع القواعد المحلية بالخيار -s الذي يضيف ملفاً إلى القواعد ولا يستبدلها، مع ملف الإسكات:

docker run --rm -v suricata-lib:/var/lib/suricata -v "$PWD:/lab" jasonish/suricata:8.0.7 \
  suricata -r /lab/lab.pcap -k none -s /lab/local.rules \
  --set threshold-file=/lab/threshold.config -l /lab/out/b -v

والمخرج سوف يكون كما يلي، وهو الملف fast.log كاملاً:

10/25/2026-21:07:23.682638  [**] [1:1000004:1] LAB Scanner user agent (Nikto) [**] [Classification: Web Application Attack] [Priority: 1] {TCP} 10.250.45.20:46794 -> 10.250.45.10:80
10/25/2026-21:07:23.427698  [**] [1:2022028:2] ET WEB_SERVER Possible CVE-2014-6271 Attempt [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 10.250.45.20:46734 -> 10.250.45.10:80
10/25/2026-21:07:23.498924  [**] [1:2100498:7] GPL ATTACK_RESPONSE id check returned root [**] [Classification: Potentially Bad Traffic] [Priority: 2] {TCP} 10.250.45.10:80 -> 10.250.45.20:46762
10/25/2026-21:07:23.450338  [**] [1:1000001:2] LAB SQL injection pattern in URI (OR 1=1) [**] [Classification: Web Application Attack] [Priority: 1] {TCP} 10.250.45.20:46740 -> 10.250.45.10:80
10/25/2026-21:07:23.474463  [**] [1:1000002:1] LAB Directory traversal attempt (etc/passwd) [**] [Classification: Web Application Attack] [Priority: 1] {TCP} 10.250.45.20:46750 -> 10.250.45.10:80
10/25/2026-21:07:23.520791  [**] [1:1000005:1] LAB Scanner user agent (Nmap) [**] [Classification: Web Application Attack] [Priority: 1] {TCP} 10.250.45.20:46772 -> 10.250.45.10:80
10/25/2026-21:07:23.623061  [**] [1:1000003:1] LAB Scanner user agent (sqlmap) [**] [Classification: Web Application Attack] [Priority: 1] {TCP} 10.250.45.20:46780 -> 10.250.45.10:80

في المخرج أعلاه لاحظ التالي:

  • سبعة تنبيهات، كل واحد منها يقابل طلباً في جدول الحركة، ولا يوجد تنبيه واحد على الطلبين العاديين، وهذا بالضبط ما نريده: كل تنبيه يعني شيئاً.
  • الأرقام بين القوسين المربعين [1:1000004:1] هي gid ثم sid ثم rev، والرقم gid هو 1 لكل قواعد النص، وهو نفسه الذي كتبناه في ملف الإسكات.
  • الأسطر ليست مرتبة بالوقت، فالسطر الأول وقته 23.682 والثاني 23.427، والسبب أن ثمانية خيوط Workers تعالج الاتصالات بالتوازي وكل منها يكتب عندما ينتهي، لذلك رتب بالوقت دائماً عندما تحقق في حادثة، ولا تفترض أن ترتيب الملف هو ترتيب الأحداث.
  • الأولوية Priority مأخوذة من classtype، فـ web-application-attack أولويته 1، وbad-unknown أولويته 2، وهذا هو الرقم الذي تبني عليه الفرز عندما تصلك مئات التنبيهات.
  • تحميل القواعد في هذا التشغيل أخذ قرابة ثلاث دقائق، لأننا شغلنا ثلاث نسخ من Suricata في الوقت نفسه على الجهاز، فالرقم يتغير مع الحمل، والقاعدة أن تحسب حساب دقيقة إلى ثلاث دقائق لكل إعادة تشغيل.

قراءة التنبيه في eve.json سطراً سطراً

الملف fast.log مريح للعين، ولكن ما ترسله إلى أي SIEM هو الملف eve.json، وهو سطر JSON لكل حدث، والتنبيه فيه أغنى بكثير، كما في توثيق مخرجات EVE. وهذا تنبيه حقن SQL كما كتبه Suricata، بعد تنسيقه للقراءة:

{
    "timestamp": "2026-10-25T21:07:23.450338+0000",
    "flow_id": 1087599324921222,
    "pcap_cnt": 49,
    "event_type": "alert",
    "src_ip": "10.250.45.20",
    "src_port": 46740,
    "dest_ip": "10.250.45.10",
    "dest_port": 80,
    "proto": "TCP",
    "ip_v": 4,
    "pkt_src": "wire/pcap",
    "tx_id": 0,
    "alert": {
        "action": "allowed",
        "gid": 1,
        "signature_id": 1000001,
        "rev": 2,
        "signature": "LAB SQL injection pattern in URI (OR 1=1)",
        "category": "Web Application Attack",
        "severity": 1
    },
    "ts_progress": "request_complete",
    "tc_progress": "response_complete",
    "http": {
        "hostname": "10.250.45.10",
        "url": "/?id=1%27+OR+%271%27=%271+--+-",
        "http_user_agent": "curl/7.88.1",
        "http_content_type": "text/html",
        "http_method": "GET",
        "protocol": "HTTP/1.1",
        "status": 200,
        "length": 22
    },
    "app_proto": "http",
    "direction": "to_server",
    "flow": {
        "pkts_toserver": 4,
        "pkts_toclient": 3,
        "bytes_toserver": 377,
        "bytes_toclient": 464,
        "start": "2026-10-25T21:07:23.449834+0000",
        "src_ip": "10.250.45.20",
        "dest_ip": "10.250.45.10",
        "src_port": 46740,
        "dest_port": 80
    }
}

في المخرج أعلاه لاحظ التالي:

  • "action": "allowed" لا تعني أن القاعدة سمحت بشيء، وإنما أن المستشعر في وضع الكشف فلم يمنع، وفي وضع المنع مع قاعدة drop سوف تجد blocked.
  • الحقل http.status هو 200، وهذا أهم سطر في التنبيه كله، فهو يقول إن السيرفر رد بنجاح على الطلب الذي فيه الحقن، وفي تطبيق حقيقي يعني ذلك أن تسأل فوراً: هل نفذ التطبيق الاستعلام؟ ولو كان الرد 400 أو 403 لكان التنبيه أقل إلحاحاً بكثير. وقد أرسل Suricata هذا الحقل لأنه انتظر الرد قبل أن يكتب التنبيه، كما يظهر في tc_progress: response_complete.
  • http_user_agent هو curl/7.88.1، أي أن الطلب لم يأت من أداة فحص تعلن عن نفسها، وإنما من أداة سطر أوامر، وهذا قد يعني شخصاً يجرب يدوياً، وهو في الغالب أخطر من أداة آلية.
  • flow_id رقم يتكرر في كل أحداث الاتصال نفسه في eve.json، فتجد به سجل http وسجل fileinfo وسجل flow لهذا الاتصال. وفي هذا التشغيل كتب Suricata أحداثاً من سبعة أنواع: 7 alert و8 http و5 dns و8 fileinfo و11 flow و1 tls و1 stats، أي أنه يكتب سجلات للبروتوكولات أيضاً وليس التنبيهات فقط، وهذا ما يقربه من Zeek.

Zeek: سجل لكل اتصال، وأنت تسأل لاحقاً

الآن نعطي الملف نفسه لـ Zeek، والأمر يختلف في فكرته: لا يوجد ملف قواعد، وإنما سكربتات Scripts بلغة Zeek، والكلمة local في الأمر تحمل الملف site/local.zeek الذي يأتي مع Zeek ويفعل مجموعة مختارة من السكربتات الإضافية، مثل كشف الحقن في HTTP وجرد البرامج وحساب الهاش لكل ملف، ثم نضيف سكربتنا scanner-ua.zeek الذي سوف نشرحه بعد قليل. ونستخدم الـ Docker Image الرسمي zeek/zeek:

docker run --rm -v "$PWD:/lab" -w /lab/out/z2 zeek/zeek:9.0.0 \
  zeek -C -r /lab/lab.pcap local /lab/scanner-ua.zeek

والخيار -C يطلب من Zeek أن يتجاهل الـ Checksum، وهو المقابل لـ -k none في Suricata، وسوف نرى بعد قليل ماذا يحدث بدونه. والأمر كله أخذ 38 ثانية على جهاز المختبر من تشغيل ال Container حتى انتهائه، والحزم نفسها قليلة جداً. وكتب Zeek ملفاً لكل نوع من السجلات كما يشرح دليل السجلات، وعدد الأسطر في كل ملف كما يلي:

السجلالأسطرماذا فيه
conn.log11سطر لكل اتصال TCP أو UDP: من وإلى، والمنفذ، والمدة، والبايتات، وكيف انتهى
http.log8سطر لكل طلب HTTP: الطريقة والرابط والمضيف وUser-Agent ورمز الرد
dns.log3الاستعلامات والإجابات ورمز النتيجة
ssl.log1إصدار TLS والتشفير واسم السيرفر SNI
files.log8كل ملف مر في الحركة، ونوعه وحجمه وبصماته MD5 وSHA1 وSHA256
notice.log3الإشعارات التي رفعها سكربتنا
software.log6البرامج وإصداراتها كما أعلنت عن نفسها في الحركة
known_hosts.log وknown_services.log2 و3الأجهزة والخدمات التي ظهرت لأول مرة
capture_loss.log1نسبة الحزم المفقودة في الالتقاط

والشكل التالي يبين ما يحدث داخل Zeek، وكيف ترتبط هذه السجلات ببعضها:

أربع مراحل: الحزم من الواجهة أو من ملف، ثم محرك الأحداث Event Engine الذي يعيد تجميع TCP ويفهم البروتوكول ويطلق أحداثاً مثل connection_state_remove وhttp_header، ثم السكربتات base وlocal وسكربتنا scanner-ua.zeek، ثم السجلات. وفي الأسفل conn.log بأحد عشر اتصالاً ومعرف CxECn7mE7YUrGVLz7 يرتبط بأسطر في http.log وfiles.log وnotice.log، وبجانبها سجلات dns وssl وsoftware وknown_services وcapture_loss
محرك الأحداث يفهم البروتوكول، والسكربتات تقرر ماذا يكتب، والـ uid يربط كل ما يخص الاتصال الواحد

والفكرة المهمة في هذا التصميم أن محرك الأحداث Event Engine مكتوب بلغة C++ ويقوم بالعمل الثقيل، أي إعادة تجميع TCP وفك البروتوكولات، ثم يطلق أحداثاً مثل http_header عند كل ترويسة وconnection_state_remove عند انتهاء كل اتصال، والسكربتات تستمع لهذه الأحداث وتقرر ماذا تكتب، فكل سجل تراه في الجدول أعلاه هو سكربت يمكنك أن تقرأه وتعدله، وهذا سبب مرونة Zeek الكبيرة، ويشرح ذلك مدخل لغة السكربتات.

conn.log: من اتصل بمن، وكيف انتهى الاتصال؟

السجلات بصيغة نص مفصول بـ Tab، وأسهل طريقة لقراءتها الأداة zeek-cut التي تختار الأعمدة بالاسم:

zeek-cut -d ts id.orig_h id.resp_h id.resp_p service duration orig_bytes resp_bytes conn_state history < conn.log | sort | head -4
2026-10-25T21:07:23+0000	10.250.45.20	10.250.45.10	443	ssl	0.029142	723	2256	SF	ShADadFf
2026-10-25T21:07:23+0000	10.250.45.20	10.250.45.10	53	dns	0.000258	58	62	SF	Dd
2026-10-25T21:07:23+0000	10.250.45.20	10.250.45.10	53	dns	0.000323	114	61	SF	Dd
2026-10-25T21:07:23+0000	10.250.45.20	10.250.45.10	80	http	0.000771	105	258	SF	ShADadFf

في المخرج أعلاه لاحظ التالي:

  • الخيار -d يحول الوقت من رقم Unix إلى تاريخ مقروء، وبدونه سوف ترى 1792962443.449834.
  • العمود service هو البروتوكول الذي تعرف عليه Zeek من المحتوى، وليس من رقم المنفذ، تماماً مثل alert http في Suricata.
  • conn_state هو SF في كل الاتصالات، ومعناه اتصال بدأ ثم انتهى بشكل طبيعي، وهذا الحقل من أهم ما في Zeek، فالقيمة S0 مثلاً تعني أن أحداً أرسل محاولة اتصال ولم يرد عليه أحد، وكثرتها من عنوان واحد علامة فحص منافذ Port Scan، وREJ تعني أن السيرفر رفض الاتصال، والقائمة كاملة في شرح conn.log.
  • history يحكي الاتصال حرفاً حرفاً: الحرف الكبير من العميل والصغير من السيرفر، فـ ShADadFf تعني SYN من العميل، ثم SYN-ACK من السيرفر، ثم ACK، ثم بيانات من العميل، ثم ACK وبيانات من السيرفر، ثم FIN من الطرفين. واتصال الـ DNS عبر UDP تاريخه Dd، أي بيانات في كل اتجاه.
  • عمودا البايتات يكفيان وحدهما لاكتشاف أشياء كثيرة دون أي قاعدة، فجهاز في الشبكة يرسل 2 GB إلى عنوان خارجي في الثالثة فجراً لن يطلق أي توقيع، ولكنه سطر واحد واضح في conn.log إذا بحثت عنه.

http.log: كل طلب، حتى العادي

نختار من http.log معرف الاتصال والطريقة والرابط ورمز الرد والوسوم Tags، وتركنا عمود User-Agent عن قصد لأن أحد الطلبات يحمل فيه نمط Shellshock:

zeek-cut uid method uri status_code tags < http.log
CZWif81yWQ3YNOW0Jd	GET	/	200	(empty)
CVRuAvtf4fbzDc8P8	GET	/	200	(empty)
CxECn7mE7YUrGVLz7	GET	/?id=1'+OR+'1'='1+--+-	200	HTTP::URI_SQLI
CXVOEM38VtmAE88S61	GET	/index.php?file=../../../../../etc/passwd	404	(empty)
C9pQdQ2D7qaviuEGPf	GET	/uid/	200	(empty)
CJPmBslOvHgpIEg0d	GET	/	200	(empty)
CwQ8Ug1XrnENoHCz7g	GET	/	200	(empty)
CGAMV02UOAJEKIyrmj	GET	/	200	(empty)

في المخرج أعلاه لاحظ التالي:

  • كل الطلبات الثمانية هنا، العادية والضارة، وهذا هو الفرق الجوهري عن Suricata، فلو ظهرت غداً ثغرة جديدة في مسار معين، تستطيع أن تبحث في http.log لآخر ثلاثة أشهر عن أي طلب لهذا المسار قبل أن تكتب له أي قاعدة.
  • محاولة قراءة /etc/passwd ردها 404، أي أنها فشلت، وهذه معلومة لا تعطيك إياها القاعدة وحدها، وتغير أولوية التنبيه تماماً.
  • طلب الحقن وصل إليه الوسم HTTP::URI_SQLI دون أن نكتب أي شيء، والسبب السكربت detect-sql-injection الذي يحمله local. ولكنه لم يرفع إشعاراً في notice.log، لأنه يرفع الإشعار SQL_Injection_Attacker فقط عندما يتجاوز عدد الطلبات المشبوهة من المصدر نفسه العتبة sqli_requests_threshold، وقيمتها في كود السكربت 50 طلباً خلال 5 دقائق، فطلب واحد يوسم ولا يزعجك، وأداة حقن آلية ترسل مئات الطلبات تطلق إشعاراً، وهذا نفس منطق العتبات الذي سوف نطبقه على Suricata في قسم الضبط.
  • الرمز uid في أول عمود هو نفسه في conn.log وfiles.log، كما سوف نرى بعد قليل.

software.log: جرد مجاني لما يعمل في شبكتك

من أكثر ما يفاجئ من يشغل Zeek أول مرة أنه يعرف إصدارات البرامج في الشبكة دون أن يسأل أحداً، لأن البرامج تعلن عن نفسها في الحركة، فالسيرفر يكتب اسمه وإصداره في ترويسة Server، والمتصفح والأدوات في User-Agent، وSSH في رسالة الترحيب:

zeek-cut host host_p software_type unparsed_version < software.log | grep SERVER
10.250.45.10	80	HTTP::SERVER	nginx/1.22.1

وفي شركة الدمام يعني هذا السطر أن تعرف إصدار nginx أو Apache أو IIS على كل سيرفر ويب، بما فيها السيرفر الذي يديره المورد ولم يخبرك عنه، ثم تقارنه بالثغرات المنشورة كما شرحنا في مختبر الثغرات. ومع السكربت software يأتي سكربتان يحملهما local، أحدهما يرفع إشعاراً عندما يتغير إصدار برنامج على جهاز، والآخر عندما يظهر إصدار تحدده أنت كضعيف. ولاحظ أيضاً أن known_services.log سجل الخدمات الثلاث على السيرفر: HTTP على 80 وSSL على 443 وDNS على 53، فلو ظهرت غداً خدمة رابعة على سيرفر لم يغير فيه أحد شيئاً، فهذا سؤال يستحق أن تسأله.

سكربت Zeek يرفع إشعاراً Notice

Zeek لا يكتب تنبيهات بالمعنى الذي في Suricata، وإنما إشعارات Notices عبر إطار الإشعارات Notice Framework، وأي سكربت يستطيع أن يرفع إشعاراً. وهذا سكربت صغير يرفع إشعاراً عندما يحمل طلب HTTP اسم أداة فحص معروفة في User-Agent، وهو المقابل للقواعد 1000003 إلى 1000005:

# Raise a notice when an HTTP request carries the user agent of a known scanning tool
@load base/frameworks/notice

module LabScanners;

export {
    redef enum Notice::Type += { Scanner_User_Agent };
    const scanner_ua = /sqlmap|[Nn]ikto|Nmap Scripting Engine/ &redef;
}

event http_header(c: connection, is_orig: bool, original_name: string, name: string, value: string)
    {
    if ( is_orig && name == "USER-AGENT" && scanner_ua in value )
        NOTICE([$note=Scanner_User_Agent,
                $msg=fmt("scanner user agent %s", value),
                $conn=c,
                $identifier=cat(c$id$orig_h, value)]);
    }

في السكربت أعلاه لاحظ التالي:

  • السكربت لا يقرأ الحزم، وإنما يستمع للحدث http_header الذي يطلقه محرك الأحداث عند كل ترويسة، والمتغير is_orig صحيح عندما تكون الترويسة من العميل.
  • القائمة scanner_ua معرفة بـ &redef، أي أنك تستطيع أن تضيف إليها أدوات أخرى من ملف local.zeek دون أن تعدل السكربت.
  • $identifier هو الذي يمنع التكرار، فالإشعار لنفس العنوان ونفس الأداة لا يتكرر خلال مدة الإسكات الافتراضية، وهي ساعة، كما يظهر في عمود suppress_for بالقيمة 3600 في السجل.

والإشعارات الثلاثة في notice.log كما يلي:

zeek-cut note msg src < notice.log
LabScanners::Scanner_User_Agent	scanner user agent Nmap Scripting Engine	10.250.45.20
LabScanners::Scanner_User_Agent	scanner user agent sqlmap/1.8#stable	10.250.45.20
LabScanners::Scanner_User_Agent	scanner user agent Mozilla/5.00 (Nikto/2.5.0)	10.250.45.20

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

ماذا يحدث إذا شغلت Zeek بدون -C؟

شغلنا الأمر نفسه دون -C ودون السكربتات الإضافية، فكتب Zeek هذا التحذير في أول المخرج:

1792962443.682936 warning in /usr/local/zeek/share/zeek/base/misc/find-checksum-offloading.zeek, line 54: Your trace file likely has invalid TCP and UDP checksums, most likely from NIC checksum offloading.  By default, packets with invalid checksums are discarded by Zeek unless using the -C command-line option or toggling the 'ignore_checksums' variable.  Alternatively, disable checksum offloading by the network adapter to ensure Zeek analyzes the actual checksums that are transmitted.

وهو تحذير أوضح من تحذير Suricata، فهو يسمي السبب Checksum Offloading ويعطيك الحلين، والسكربت الذي كتبه موثق في find-checksum-offloading. والأهم ما حدث للسجلات:

zeek-cut uri status_code response_body_len < http.log | head -3
zeek-cut name < weird.log | sort | uniq -c
zeek-cut conn_state history resp_bytes < conn.log | sort | uniq -c
/	-	0
/	-	0
/index.php?file=../../../../../etc/passwd	-	0
      1 bad_TCP_checksum
      1 bad_UDP_checksum
      9 data_before_established
      9 inappropriate_FIN
      1 S0	Dc	-
      1 S0	Dc	0
      9 SH	ScADF	0

في المخرج أعلاه لاحظ التالي:

  • Zeek رأى الطلبات، لأن حزم العميل سليمة، ولكنه لم ير أي رد، فرمز الرد فارغ وحجم الرد صفر في كل الطلبات، ولو سألت هذا السجل «هل نجحت محاولة قراءة passwd؟» لما وجدت جواباً.
  • حالة الاتصالات SH بدلاً من SF، وhistory هو ScADF دون أي حرف صغير، أي أن السيرفر في نظر Zeek لم يرسل شيئاً، وresp_bytes صفر.
  • السجل weird.log امتلأ بأحداث غريبة مثل data_before_established، وهي نتيجة طبيعية لاتصالات يرى نصفها فقط، ولم يكتب Zeek في هذا التشغيل files.log ولا software.log لأن الملفات وترويسة Server كلها في الردود المفقودة.

والخلاصة من التجربتين أن الأداتين تعاملان الحزمة التالفة بنفس المنطق، فتهملانها، والنتيجة في Suricata تنبيهات مفقودة، وفي Zeek سجلات ناقصة، وفي الحالتين لا يوجد خطأ يوقف التشغيل، وإنما رسالة تحذير واحدة في أول السجل، فاقرأها.

الطلب نفسه بعينين: من التنبيه إلى القصة

الآن نضع ما رأته الأداتان للطلب نفسه جنباً إلى جنب، وهو طلب الحقن، والشكل التالي يبين ذلك:

في الأعلى طلب HTTP واحد من 10.250.45.20 إلى 10.250.45.10 على المنفذ 80 فيه نمط حقن SQL وUser-Agent هو curl/7.88.1. في اليمين Suricata: قواعد ET Open وحدها لم تطلق شيئاً لهذا الطلب، وبعد إضافة القاعدة المحلية 1000001 ظهر تنبيه بالأولوية 1 والإجراء allowed ورمز الرد 200. وفي اليسار Zeek: سطر في conn.log بالمعرف CxECn7mE7YUrGVLz7 وحالة SF و105 بايت إلى 258، وسطر في http.log بالرابط ورمز 200 والوسم HTTP::URI_SQLI، وسطر في files.log لملف text/html بحجم 22 بايت مع بصمة md5
Suricata يجيب عن «هل هذا هجوم أعرفه؟»، وZeek يجيب عن «ماذا حدث في الشبكة؟»

وفي الشركة يبدأ التحقيق دائماً من التنبيه، لأنه الشيء الذي يصلك، ثم ينتقل إلى Zeek ليحكي القصة. فالتنبيه يعطيك العنوانين والمنفذين والوقت، وبها تجد الاتصال في conn.log ومعه الـ uid، ثم تبحث بالـ uid في كل السجلات دفعة واحدة:

grep -h CxECn7mE7YUrGVLz7 conn.log http.log files.log | cut -f1-12
1792962443.449834	CxECn7mE7YUrGVLz7	10.250.45.20	46740	10.250.45.10	80	tcp	http	0.000771	105	258	SF
1792962443.450124	CxECn7mE7YUrGVLz7	10.250.45.20	46740	10.250.45.10	80	1	GET	10.250.45.10	/?id=1'+OR+'1'='1+--+-	-	1.1
1792962443.450323	F7xXMP1SYdHIXMYbEb	CxECn7mE7YUrGVLz7	10.250.45.20	46740	10.250.45.10	80	HTTP	0	MD5,SHA1,SHA256	text/html	-

ثلاثة أسطر تقول: اتصال انتهى طبيعياً، فيه طلب واحد بالرابط المشبوه، رد عليه السيرفر بملف HTML صغير له بصمة معروفة. وفي حادثة حقيقية تكمل من هنا بالعنوان نفسه: ماذا طلب هذا الجهاز قبل ذلك بساعة؟ بماذا اتصل بعدها؟ هل سأل DNS عن اسم غريب؟ وهذه الأسئلة لا تجيب عنها أي قاعدة، وإنما سجل كامل للحركة، ولهذا يقال إن Suricata يخبرك متى تنظر، وZeek يخبرك ماذا ترى.

على الواجهة الحية Live Interface: الذاكرة ووقت البدء وفقد الحزم

ملف PCAP مثالي للتجربة، ولكن المستشعر الحقيقي يقرأ من كرت الشبكة دون توقف، وهنا تظهر أسئلة لا يجيب عنها الملف: كم يستهلك من الذاكرة؟ وكم يحتاج ليبدأ؟ وهل يفقد حزماً؟ لذلك شغلنا الأداتين على واجهة السيرفر نفسه، بوضع كل Container في مساحة الشبكة الخاصة بال Container الخاص بالسيرفر بالخيار --network container:، مع الصلاحيات التي يحتاجها الالتقاط:

docker run -d --name ids-suricata --network container:ids-web \
  --cap-add NET_ADMIN --cap-add NET_RAW --cap-add SYS_NICE \
  -v suricata-lib:/var/lib/suricata -v "$PWD:/lab" jasonish/suricata:8.0.7 \
  suricata -i eth0 -k none -s /lab/local.rules --set threshold-file=/lab/threshold.config -l /lab/out/ls

docker run -d --name ids-zeek --network container:ids-web \
  --cap-add NET_ADMIN --cap-add NET_RAW -v "$PWD:/lab" -w /lab/out/lz zeek/zeek:9.0.0 \
  zeek -C -i eth0 local /lab/scanner-ua.zeek

والخيار -i eth0 يجعل Suricata يلتقط عبر AF_PACKET، وهي طريقة الالتقاط الافتراضية والأسرع على Linux كما في توثيق AF_PACKET. وبعد أن بدأ المحرك أرسلنا الحركة نفسها من العميل، فظهرت التنبيهات السبعة نفسها في fast.log والإشعارات الثلاثة نفسها في notice.log، والأرقام التي قسناها كما يلي:

القياسالقيمة
وقت بدء Suricata مع 53,274 قاعدة72 ثانية، من 23:07:07 إلى 23:08:19، منها 55 ثانية لتحميل القواعد
ذاكرة Suricata مع ET Open والقواعد المحلية465 MiB
ذاكرة Suricata مع القواعد المحلية الخمس فقط35 MiB
ذاكرة Zeek مع local والسكربت97 MiB
حجم الـ Docker ImageSuricata 592 MB، وZeek 720 MB

والرقم الأهم في هذا الجدول هو الفرق بين 465 و35 ميجابايت، فهو يقول إن ذاكرة Suricata تذهب في أغلبها إلى القواعد وليس إلى الحركة، فالمحرك يبني من القواعد هياكل بحث في الذاكرة تسمح له بمطابقة كل حزمة مع عشرات الآلاف من الأنماط في وقت واحد، وكلما زادت القواعد كبرت هذه الهياكل. وعلى شبكة حقيقية تزيد الذاكرة أيضاً مع عدد الاتصالات المتزامنة، لأن المحرك يحتفظ بحالة كل اتصال ويعيد تجميع بياناته، لذلك لا تأخذ هذه الأرقام كتقدير لشبكتك، وإنما كحد أدنى، وراجع اعتبارات الضبط قبل أن تختار الجهاز.

المشكلة التي لم نتوقعها: حزم مفقودة دون أي رسالة

بعد التشغيل الحي أرسلنا عشرين طلباً متتالياً بترويسة Nikto إلى صفحات مختلفة، لنختبر الضبط في القسم التالي، فكان المتوقع عشرين تنبيهاً من القاعدة 1000004، ولكن الذي ظهر في fast.log ثمانية عشر فقط، بينما سجل Zeek على الواجهة نفسها الطلبات العشرين كلها في http.log. ولم يكتب Suricata أي خطأ أو تحذير في سجله، والجواب كان في ملف الإحصاءات stats.log الذي يكتبه كل ثماني ثوان تقريباً:

grep -E 'capture.kernel_(packets|drops)' out/ls/stats.log | tail -n 2
capture.kernel_packets                                       | Total                     | 305
capture.kernel_drops                                         | Total                     | 20

أي أن النواة أسقطت 20 حزمة من 305 قبل أن تصل إلى Suricata، وكلها خلال دفعة الطلبات، فضاع طلبان كاملان من نظر المحرك. وعندما أعدنا تشغيل Suricata بالإعداد نفسه وأرسلنا الدفعة نفسها لم يتكرر الفقد، فوصلت التنبيهات العشرون والعداد صفر، أي أن الفقد يحدث تحت ظروف حمل معينة على الجهاز وليس في كل مرة. وهذا بالضبط ما يجعله خطيراً، فهو لا يظهر في الاختبار ويظهر يوم الهجوم الحقيقي، عندما تكون الحركة في أعلى مستوياتها.

والدرس الذي خرجنا به أن كل مستشعر يجب أن يراقب نفسه قبل أن يراقب الشبكة، وذلك بثلاثة أشياء:

  • العداد capture.kernel_drops في stats.log أو في أحداث stats داخل eve.json، كما يشرح توثيق الإحصاءات، وأي رقم أكبر من الصفر يستحق أن تعرف سببه.
  • السجل capture_loss.log في Zeek من السكربت capture-loss، وفيه نسبة الفقد بحسب فجوات TCP التي رآها، وقد كانت 0.0 في تشغيل الملف. وفي التشغيل الحي رفع هذا السكربت أيضاً الإشعار CaptureLoss::Too_Little_Traffic لأن الحركة كانت قليلة جداً في أول دقائقه، وهو إشعار مفيد على المستشعر الحقيقي لأنه يعني غالباً أن منفذ SPAN توقف عن الإرسال.
  • اختبار دوري بحركة معروفة، مثل طلب واحد يومياً من جهاز تعرفه برابط لا يستخدمه أحد، وقاعدة محلية له، فإذا لم يصل التنبيه في موعده فالمستشعر لا يرى ما تظن أنه يراه.

ضبط التنبيهات Tuning: حتى لا يتعلم الفريق تجاهلها

في محرك كشف SIEM من الداخل تكلمنا عن إرهاق التنبيهات Alert Fatigue، أي أن كثرة التنبيهات التي لا معنى لها تعلم الفريق أن يتجاهل كل التنبيهات، والمستشعر على الشبكة أكثر أداة معرضة لذلك، لأن الإنترنت مليء بأدوات الفحص الآلي التي تجرب كل شيء على كل عنوان. وفي شركة الدمام لو وضع الفريق المستشعر على المنفذ المتجه إلى الإنترنت لوصلته كل يوم مئات تنبيهات Shellshock من أدوات فحص لا تستهدفه شخصياً، ولا يوجد على سيرفراته Bash يتأثر بها. والضبط لا يعني إخفاء التنبيهات، وإنما أن يقود كل تنبيه إلى قرار، والشكل التالي يبين القرارات الأربعة الممكنة:

تنبيه من Suricata يمر إلى التحقق في سجلات Zeek بالعنوان والوقت، ثم قرار واحد من أربعة: هجوم حقيقي يفتح حادثة ويعزل أو يحظر، أو مصدر معروف عمله متوقع مثل سيرفر فحص الثغرات فيسكت بـ suppress لهذا المصدر فقط، أو تنبيه صحيح يتكرر فيحدد بـ threshold من نوع limit لكل مصدر، وفي المختبر 20 طلباً من Nikto أعطت 20 تنبيهاً ثم تنبيهاً واحداً، أو قاعدة واسعة أكثر من اللازم تعدل أو تعطل بـ disable.conf
كل تنبيه لا يقود إلى أحد هذه القرارات الأربعة هو ضجيج

ولنجرب القرار الثالث بالأرقام، أي التنبيه الصحيح الذي يتكرر. سجلنا الطلبات العشرين بترويسة Nikto في ملف burst.pcap، ثم قرأناه بالقواعد المحلية وحدها، فكان في fast.log عشرون تنبيهاً متطابقاً، كلها تقول الشيء نفسه عن المصدر نفسه. وفي الواقع أداة Nikto الحقيقية ترسل آلاف الطلبات في دقائق، وكلها تحمل اسمها، فتتحول القاعدة المفيدة إلى سيل. والحل أن نحدد عدد التنبيهات لكل مصدر خلال مدة، بملف عتبات فيه سطر واحد:

# one alert per source address per 60 seconds for the scanner user agent rules
threshold gen_id 1, sig_id 1000004, type limit, track by_src, count 1, seconds 60
docker run --rm -v "$PWD:/lab" jasonish/suricata:8.0.7 \
  suricata -r /lab/burst.pcap -k none -S /lab/local.rules \
  --set threshold-file=/lab/threshold-burst.config -l /lab/out/f2 -v
Info: threshold-config: Threshold config parsed: 1 rule(s) found
Info: counters: Alerts: 1

تنبيه واحد بدلاً من عشرين، وفيه كل ما تحتاجه: الأداة والمصدر والوقت الأول، وإذا استمر الفحص ساعة فسوف يصلك تنبيه كل دقيقة، فتعرف أنه مستمر دون أن يغرقك. ولاحظ الفرق بين الأنواع الثلاثة في توثيق العتبات: limit ينبه على أول N مرات في المدة ثم يصمت، وthreshold لا ينبه إلا بعد أن يتكرر الحدث N مرات، وهو مناسب لما لا يعني شيئاً مرة واحدة ويعني الكثير إذا تكرر، مثل فشل الدخول، وboth يجمع الاثنين. وZeek فعل الشيء نفسه وحده، فالطلبات العشرون نفسها على الواجهة الحية أعطت في notice.log إشعاراً واحداً، بسبب $identifier ومدة الإسكات ساعة.

أما القرارات الأخرى فهذه طريقتها:

  • مصدر معروف وعمله متوقع: إذا كان لدى الشركة سيرفر فحص ثغرات مثل OpenVAS يفحص الشبكة كل ليلة بإذن، فلا تعطل قواعد الفحص للجميع، وإنما أسكتها لهذا العنوان فقط بسطر مثل suppress gen_id 1, sig_id 1000004, track by_src, ip 10.0.5.20، فإذا فحص جهاز آخر الشبكة بالأداة نفسها فسوف تعرف.
  • قاعدة واسعة أكثر من اللازم: إذا كانت قاعدتك المحلية تنبه على طلبات عادية فعدلها هي، بمخزن أدق أو نص أطول أو اتجاه أضيق، وزد rev. وإذا كانت القاعدة من ET فلا تعدل ملف suricata.rules بيدك لأن suricata-update سوف يعيد كتابته غداً، وإنما ضع رقمها في /etc/suricata/disable.conf أو عدلها عبر modify.conf كما في توثيق suricata-update.
  • هجوم حقيقي: وهنا لا يوجد ضبط، وإنما استجابة، والمستشعر في وضع الكشف لا يمنع شيئاً، فإما أن تحظر من الجدار الناري، أو تستخدم الحظر التلقائي في Wazuh كما فعلنا مع SSH في مقال Wazuh، أو تفتح حادثة إذا كان الهجوم قد نجح.

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

كيف يبدو هذا في الشركة؟

نعود إلى شركة الدمام، والسؤال الآن ليس هل تعمل الأدوات، وإنما أين تضعها وكم تكلف وأين تذهب نتائجها. والشكل التالي يبين التصميم الذي نقترحه لشركة بهذا الحجم:

الإنترنت يمر عبر الجدار الناري إلى السويتش الرئيسي، والسويتش يتصل بـ VLAN السيرفرات وVLAN الموظفين والطابعات والكاميرات، ومنفذ SPAN في السويتش ينسخ VLAN السيرفرات والمنفذ المتجه إلى الجدار الناري إلى مستشعر بكرتي شبكة يشغل Suricata بذاكرة 465 MiB ويكتب eve.json وZeek بذاكرة 97 MiB، ووكيل Wazuh على المستشعر يقرأ eve.json وسجلات JSON ويرسلها إلى Wazuh Manager، ولوحة بقرارات التصميم: راقب الحدود بين المناطق، ومنفذ SPAN يسقط الحزم إذا امتلأ وجهاز TAP لا يسقطها، والحركة المشفرة لا يمكن قراءة محتواها، وسجلات Zeek للاحتفاظ الطويل، وراقب capture.kernel_drops وcapture_loss.log
مستشعر واحد على الحدود بين المناطق، ونتائجه في اللوحة نفسها التي يراقب فيها الفريق السيرفرات

وفي التصميم أعلاه لاحظ التالي:

  • أين تضع المستشعر: ليس على كل منفذ، وإنما على الحدود بين المناطق، أي المنفذ المتجه إلى الجدار الناري، فترى كل ما يدخل ويخرج، وVLAN السيرفرات، فترى من يصل إلى السيرفرات من الداخل. والحركة بين جهازين في VLAN الموظفين نفسها لن تراها، وهذا مقبول في البداية، وإذا أردتها لاحقاً فهذا أحد أسباب تقسيم الشبكة إلى مناطق أصغر.
  • كرتا شبكة: كرت للالتقاط بلا عنوان IP، فلا يستطيع أحد على الشبكة المراقبة أن يصل إلى المستشعر عبره، وكرت للإدارة على شبكة الإدارة فقط، والسبب أن المستشعر الذي يرى كل الحركة هدف ثمين، فلا تجعله جهازاً عادياً على شبكة الموظفين.
  • الذاكرة والمعالج: أرقام المختبر حد أدنى، 465 MiB لـ Suricata و97 MiB لـ Zeek، وعلى شبكة شركة صغيرة بخط إنترنت عادي يكفي في الغالب جهاز متواضع بأربع أنوية وثمانية جيجابايت، ولكن القياس هو الحكم وليس هذا التقدير، فشغل المستشعر أسبوعاً وراقب الذاكرة والعداد capture.kernel_drops قبل أن تعتمد عليه.
  • الحركة المشفرة: أغلب حركة الويب اليوم TLS، فلن يرى Suricata محتوى الطلبات المشفرة إلى المتجر، وسوف يرى فقط ما يظهر قبل التشفير، كما رأينا في ssl.log: الإصدار TLSv1.3 والاسم shop.lab.example. والحل لحركة سيرفراتك أن تضع المستشعر خلف الـ Reverse Proxy الذي يفك التشفير، أو أن تعتمد على سجلات التطبيق عبر Wazuh، وهذا سبب إضافي لأن تعمل الأداتان معاً.
  • القرص والاحتفاظ: في المختبر كان متوسط سطر conn.log نحو 130 بايتاً، وسطر http.log نحو 200، بينما كان حدث flow في eve.json نحو 630 بايتاً وحدث alert نحو 900. فإذا افترضنا للتوضيح فقط مليون اتصال يومياً، فسجل conn.log وحده نحو 130 MB يومياً قبل الضغط، وقرابة 47 GB في السنة، وهذا رقم تخطط له وليس رقماً تفاجأ به. لذلك احتفظ بسجلات Zeek المختصرة مدة أطول، وبأحداث eve.json الكاملة مدة أقصر، وفعل في Suricata أنواع الأحداث التي تحتاجها فقط.

إلى أين تذهب التنبيهات؟ Wazuh

الفريق في شركة الدمام شخصان، ولا يستطيعان مراقبة لوحتين، لذلك يجب أن تصل تنبيهات الشبكة إلى المكان نفسه الذي تصل إليه تنبيهات السيرفرات. وWazuh يقرأ eve.json مباشرة، كما يشرح دليل ربط Suricata بـ Wazuh: تثبت وكيل Wazuh على المستشعر نفسه، وتضيف إلى إعداده قسماً يقرأ الملف بصيغة JSON، فيفك المدير الأحداث بقواعد Suricata الجاهزة فيه، وتظهر التنبيهات بجانب تنبيهات SSH ومراقبة الملفات التي رأيناها في المقال السابق:

<localfile>
  <log_format>json</log_format>
  <location>/var/log/suricata/eve.json</location>
</localfile>

والفائدة الحقيقية هنا هي الربط Correlation، فعندما يصل تنبيه حقن SQL على المتجر من Suricata، ويصل في الدقيقة نفسها من وكيل Wazuh على السيرفر تنبيه بملف PHP جديد في مجلد الموقع، فهذه قصة واحدة يجب أن يراها الفريق كقصة واحدة، وليس كتنبيهين في نظامين. ولم نشغل هذا الربط في مختبر هذا المقال، وإنما شغلنا Wazuh كاملاً في المقال السابق، والإعداد أعلاه من التوثيق الرسمي. وسجلات Zeek بصيغة TSV افتراضياً، وتستطيع أن تجعلها JSON بتحميل السكربت policy/tuning/json-logs.zeek لترسلها بالطريقة نفسها، أو أن تنتظر المقال القادم عن Security Onion الذي يجمع الأداتين مع منصة بحث جاهزة.

ماذا تطلب ضوابط الهيئة الوطنية للأمن السيبراني؟

نعود إلى سؤال المدقق. فإذا كانت جهتك ملزمة بالضوابط الأساسية للأمن السيبراني ECC 2:2024، فالمكون الفرعي 2-5 لإدارة أمن الشبكات يضع في الضابط 2-5-3 حداً أدنى من المتطلبات، ومنها ضابطان يمسان هذا المقال مباشرة، وقد راجعنا نصهما في وثيقة الضوابط الرسمية، كما أن المكون 2-12 يطلب المراقبة والاحتفاظ بالسجلات:

الضابطالنص الرسمي باختصارما يقدمه المستشعرما لا يقدمه
2-5-3-6أنظمة الحماية من الاختراقات Intrusion Prevention Systems (IPS)Suricata يعمل بوضع الكشف ووضع المنع، وقواعد ET مع قواعدك المحليةوضع الكشف وحده لا يمنع، فيجب أن توثق لماذا اخترته، وخطة نقل القواعد الموثوقة إلى المنع
2-5-3-7أمن خدمة أسماء النطاقات Security of Domain Name Service (DNS)سجل كامل لكل استعلام DNS في dns.log وeve.json، وقواعد ET لنطاقات البرمجيات الخبيثة المعروفةلا يغني عن تأمين خادم DNS نفسه وتقييد من يستعلم منه
2-12-3-4المراقبة المستمرة لسجلات الأحداثتنبيهات تصل إلى SIEM، وسجلات للتحقيقالمراقبة المستمرة تحتاج شخصاً يقرأ ويقرر، والأداة لا تغني عنه
2-12-3-5الاحتفاظ بسجلات الأحداث مدة لا تقل عن 12 شهراًسجلات Zeek المختصرة مناسبة للاحتفاظ الطويلغير مغطى بالإعداد الافتراضي، ويحتاج حساب القرص وسياسة تدوير وأرشفة

والنقطة التي يجب أن تكون واضحة للمدقق أن الضابط يذكر IPS، وأن تشغيل Suricata في وضع الكشف قرار تشغيلي له سبب، وهو تجنب قطع الخدمة بقاعدة خاطئة، ويجب أن يكتب في سياسة الجهة مع خطة واضحة، وليس أن يقدم كأنه هو نفسه المنع. وقد شرحنا الضوابط الأخرى ومن يلزم بها في الضوابط الأساسية بلغة بسيطة.

ماذا تحفظ في النسخ الاحتياطي Backup؟

المستشعر نفسه لا يحتفظ ببيانات لا يمكن استعادتها إلا السجلات، فإذا تعطل الجهاز فأنت تحتاج أن تعيد تشغيل مستشعر جديد بنفس السلوك خلال ساعة، وما تحتاجه لذلك صغير جداً:

  • ملف suricata.yaml بعد تعديلاتك، وأهمها تعريف HOME_NET بشبكاتك الحقيقية وإعداد AF_PACKET.
  • ملفات القواعد المحلية والعتبات والإسكات، وملفات disable.conf وmodify.conf الخاصة بـ suricata-update، وهي أثمن ما على المستشعر لأنها خلاصة أسابيع من الضبط، فضعها في مستودع Git وليس فقط في نسخة احتياطية.
  • ملف local.zeek وسكربتاتك.
  • السجلات نفسها إذا كانت لا تصل إلى SIEM، بحسب مدة الاحتفاظ التي تطلبها سياستك.

أما قواعد ET Open فلا تحتاج نسخها، لأن suricata-update ينزلها من جديد، ولكن احتفظ بنسخة من ملف القواعد الذي كان يعمل قبل كل تحديث كبير، وهو ما تفعله الأداة بنفسها كما رأينا في السطر Backing up current rules.

التحديث Upgrade إلى إصدار أحدث

الإصدارات الرئيسية في الأداتين قد تغير أسماء حقول السجلات أو سلوك بعض الكلمات في القواعد، وهذا يؤثر على كل ما بنيته فوقها في SIEM، لذلك اتبع ترتيباً ثابتاً: اقرأ ملاحظات الإصدار في صفحة إصدارات Suricata وصفحة إصدارات Zeek، ثم شغل الإصدار الجديد على ملف PCAP محفوظ من شبكتك مع قواعدك، وقارن عدد التنبيهات وأنواع الأحداث مع الإصدار الحالي، ثم انقل المستشعر. وهذه فائدة إضافية لملف الحركة المعروفة الذي بنيناه في هذا المقال، فهو اختبار جاهز لكل تحديث. وثبت دائماً رقم الإصدار في الـ Docker Image، مثل jasonish/suricata:8.0.7 وzeek/zeek:9.0.0، ولا تستخدم latest، حتى لا يتغير المستشعر وحده في ليلة ما.

مشكلات شائعة Common Problems وحلولها

  • عشرات التنبيهات بعنوان invalid checksum: المستشعر يلتقط من جهاز افتراضي أو من السيرفر نفسه، فاستخدم -k none مع Suricata و-C مع Zeek، أو أوقف الـ Offloading على كرت الالتقاط، ثم تأكد أن تنبيهات طبقة التطبيق عادت.
  • قاعدة محلية لا تطلق رغم أن الطلب موجود: ابحث عن الطلب في أحداث http داخل eve.json، فإذا لم تجده فالمشكلة في الالتقاط أو الـ Checksum، وإذا وجدته فالمشكلة في القاعدة، فاختبرها على ملف PCAP بالخيار -S.
  • Suricata يتأخر دقائق في البدء: طبيعي مع عشرات الآلاف من القواعد، فحدث القواعد بإعادة التحميل وليس بإعادة التشغيل، وعطل الفئات التي لا تخصك عبر disable.conf، مثل قواعد أنظمة لا توجد في شبكتك.
  • تنبيهات أقل من المتوقع تحت الحمل: راجع capture.kernel_drops، وإعداد AF_PACKET من عدد الخيوط وحجم الحلقة، وتأكد أن منفذ SPAN نفسه لا يسقط الحزم، ففقد السويتش لا يظهر في عدادات المستشعر.
  • Zeek يكتب سجلات بلا ردود وحالة الاتصالات SH: نفس مشكلة الـ Checksum، أو أن المستشعر يرى اتجاهاً واحداً فقط من الحركة، وهذا يحدث عندما يكون SPAN مضبوطاً على الإرسال فقط أو الاستقبال فقط.
  • الأمر docker يفشل في Git Bash على Windows بمسارات غريبة: Git Bash يحول المسارات التي تبدأ بـ / إلى مسارات Windows، فضع MSYS_NO_PATHCONV=1 قبل الأمر، واستخدم pwd -W للمسار الحالي.

قائمة تحقق للتشغيل Checklist

إذا كنت ستضع مستشعراً في شبكة جهتك الأسبوع القادم، فهذه الخطوات بالترتيب، وكل خطوة لها ما يثبت أنها تمت:

الخطوةكيف تتأكد أنها تمت
اختر نقاط المراقبة على الحدود بين المناطق، واطلب منفذ SPAN أو TAPرسم الشبكة محدث، وفيه أي VLAN وأي منفذ ينسخ
جهز المستشعر بكرتين، كرت التقاط بلا IP وكرت إدارةلا يرد المستشعر على أي طلب من شبكة الموظفين
اضبط HOME_NET بشبكاتك الحقيقيةالقواعد التي تفرق بين الداخل والخارج تطلق في الاتجاه الصحيح
شغل suricata-update يومياًسجل التحديث اليومي، وعدد القواعد المضافة والمحذوفة
سجل حركة اختبار معروفة واقرأها بالأداتينكل طلب في الجدول له التنبيه أو السجل المتوقع، ولا تنبيه على العادي
تأكد من الـ Checksum وفقد الحزملا تنبيهات invalid checksum، وcapture.kernel_drops صفر، وcapture_loss قريب من الصفر
اكتب قواعد محلية لما تعرفه عن شبكتك واختبرها بالخيار -Sكل قاعدة لها ملف PCAP يطلقها وملف لا يطلقها
أرسل eve.json وسجلات Zeek إلى SIEMتنبيه الاختبار يظهر في اللوحة نفسها التي فيها تنبيهات السيرفرات
راقب أسبوعين، واضبط بالقرارات الأربعةكل سطر إسكات أو عتبة له تعليق بالسبب والتاريخ
احسب القرص لمدة الاحتفاظ المطلوبة12 شهراً على الأقل لمن يلزمه الضابط 2-12-3-5
اختبار يومي بحركة معروفةتنبيه الاختبار يصل كل يوم في موعده

الخلاصة

وصلنا لنهاية الموضوع، وأهم ما ظهر في المختبر:

  • وكيل Wazuh لا يرى الأجهزة التي لا يثبت عليها ولا الحركة نفسها، والمستشعر على الشبكة يكمله ولا يغني عنه، وكلاهما لا يرى محتوى الحركة المشفرة.
  • Suricata يجيب عن «هل هذا هجوم أعرفه؟» بقواعد، وZeek يجيب عن «ماذا حدث؟» بسجل لكل اتصال، والتحقيق يبدأ بتنبيه الأول ويكمل بسجلات الثاني عبر العنوان والوقت والـ uid.
  • الـ Checksum الفارغ في Docker والأجهزة الافتراضية لم يضف 41 تنبيهاً بلا معنى فقط، وإنما أخفى هجوم Shellshock وكل قواعد HTTP المحلية، فتأكد أن المستشعر يرى الاتصال كاملاً قبل أن تثق بأي رقم.
  • 53,269 قاعدة من ET Open رأت طلبين فقط من أحد عشر، وخمس قواعد محلية تعرف المتجر أكملت الصورة إلى سبعة تنبيهات، كل منها يعني شيئاً.
  • القواعد تكلف الذاكرة ووقت البدء: 465 MiB و72 ثانية مع ET Open، مقابل 35 MiB وثانية واحدة مع القواعد المحلية وحدها، وZeek احتاج 97 MiB.
  • فقد الحزم يحدث دون أي رسالة، ففي إحدى الجولات أسقطت النواة 20 حزمة من 305 وضاع طلبان، فراقب capture.kernel_drops وcapture_loss.log واختبر المستشعر يومياً بحركة معروفة.
  • الضبط يعني أن يقود كل تنبيه إلى قرار: استجابة، أو إسكات لمصدر معروف، أو عتبة لما يتكرر، وقد أصبحت عشرون تنبيهاً تنبيهاً واحداً، أو تعديل القاعدة نفسها.
  • الضابط 2-5-3-6 يذكر IPS، فإذا شغلت الكشف فقط فوثق السبب والخطة، والاحتفاظ 12 شهراً يحتاج حساب قرص وسياسة.

وفي المقال القادم بإذن الله سوف نتناول Security Onion، وهي منصة تجمع Suricata وZeek مع البحث واللوحات وإدارة الحالات في توزيعة واحدة، ثم نقارن في المقال الذي بعده متى تكفيك Wazuh ومتى تحتاج Security Onion. وأقرب المقالات إلى هذا الموضوع على الموقع:

دليلمراقبة السيرفرات مع Wazuh: من السجل Log إلى حظر المهاجمشغلنا Wazuh مع وكيل على سيرفر تجريبي، فكشف تخمين كلمات مرور SSH وحظر المهاجم تلقائياً، ونبه على ملف PHP دخيل خلال ثانية.دليلكيف يعمل محرك كشف SIEM من الداخل؟كيف يجمع SIEM ملايين السجلات ويوحدها ويطبق قواعد الكشف، مع خط أنابيب Vector وLoki وقاعدة Sigma تطلق فعلاً.دليلتقسيم الشبكة Network Segmentation بال VLAN وال VPCلماذا تصل كاميرا مخترقة إلى قاعدة بياناتك، وكيف تمنع ذلك بتقسيم الشبكة إلى مناطق لا يمر بينها إلا ما تسمح به.دليلالضوابط الأساسية للأمن السيبراني NCA ECC بلغة بسيطةكيف تقرأ الضوابط الأساسية ECC دون أن تضيع في أرقامها، وعلى من تنطبق، وما المطلوب من جهتك فعلاً في كل مكون.دليلMITRE ATT&CK: كيف يفكر المهاجم خطوة بخطوةلغة موحدة لسلوك المهاجمين مبنية على حوادث موثقة، نقرأ بها هجوم فدية حقيقياً مرحلة بمرحلة ونربط كل خطوة بالدفاع المقابل.دليلمصطلحات الأمن السيبراني للمبتدئين: من CVE إلى Purple Teamماذا تعني الكلمات التي تتكرر في كل عرض أمني، من رقم الثغرة وخطورتها إلى أدوات المراقبة SOC والفرق الحمراء والزرقاء.

تدريب عملي Practice

  • اكتب قاعدة Suricata محلية تنبه على أي طلب HTTP إلى المسار /.env أو /.git/config، ثم سجل ملف PCAP فيه طلب لكل مسار وطلب عادي، وتأكد بالخيار -S أن القاعدة تطلق مرتين فقط.
  • عدل سكربت scanner-ua.zeek ليرفع إشعاراً مختلفاً عندما يرد السيرفر بالرمز 200 على طلب من أداة فحص، وبإشعار أقل أهمية عندما يرد بـ 404، ثم قارن النتيجة على حركة المختبر.
  • ابحث في conn.log لشبكة اختبار عن كل الاتصالات بالحالة S0 أو REJ، واجمعها بحسب العنوان المصدر، وفكر: أي عدد منها في الساعة يعني فحص منافذ في شبكتك؟

سجل التحديثات

  • أكتوبر 2026: كتابة الدليل وتنفيذ المختبر يوم 25 أكتوبر 2026 على Suricata 8.0.7 مع قواعد ET Open وZeek 9.0.0، بملف حركة مسجل وعلى الواجهة الحية في Docker.
نشرة عرب رووت | ArabRoot

معرفة تستحق مكاناً في بريدك.

مقالات مختارة وأدوات مفيدة وأفكار لمشروعك القادم، في رسالة واحدة كل أسبوع.

يمكنك إلغاء الاشتراك متى شئت. الخصوصية

تم استلام طلبك. افتح بريدك واضغط رابط التأكيد لإتمام الاشتراك.