
مستشعر شبكة Spip مكتوب بلغة Go
Spip هو مستشعر مصائد شبكية منخفض التفاعل لنظام Linux، مكتوب بلغة Go. يقبل اتصالات TCP على أي منفذ تعيد المضيف توجيهه إليه، سواء كان عاديًا أو TLS، ويجيب بالقدر الكافي فقط لجعل العميل يرسل رسالته التالية، ويكتب كل تبادل كسجل JSON واحد بأسماء حقول ECS، جاهز لـ SIEM أو بحيرة بيانات. يلتقط خيار UDP الاختياري حزم البيانات ولا يجيب عليها أبدًا.
تشغّل HoneyLabs برنامج Spip لجمع البيانات المنشورة على honeylabs.net.
المتطلبات المسبقة:
iptables وصلاحيات الجذر لتثبيت قواعد إعادة التوجيهgit clone https://github.com/honeylabshq/Spip-Go.git
cd Spip-Go
go build -o spip-agent ./cmd/spip-agent
لتعيين الإصدار المُبلَّغ عنه في observer.version وagent.version (الافتراضي dev)، أضف -ldflags "-X main.version=v1.2.3".
sudo ./scripts/initial_setup.sh
راجع مساعد الإعداد الأولي أدناه.
config.toml
الحد الأدنى من config.toml:name = "spip-agent"
ip = "0.0.0.0"
port = 8080
مفاتيح التكوين الاختيارية:
cert_path / key_path: تفعيل TLS إذا تم تعيين كلاهما. تُحل المسارات النسبية مقابل دليل ملف التكوين.log_file: إلحاق السجلات بهذا الملف بدلاً من stdout. راجع مخرجات السجل أدناه.[loom]: إرسال السجلات أيضاً عبر HTTP. راجع Loom أدناه.read_timeout_seconds / write_timeout_seconds: مهلة القراءة الخاملة ومهلة الكتابة لكل اتصال.rate_limit_per_second / rate_limit_burst: حد المعدل على اتصالات TCP الجديدة، مشترك بين جميع المصادر. يُغلق الاتصال الذي يتجاوز الحد دون قراءته.community_id_seed: بذرة اختيارية 16-بت لتجزئة تدفق Community ID v1 (احذفها أو اضبطها على 0 للقيمة الافتراضية).capture_client_hello: اضبطها على false لاستبعاد tls.client.hello_hex. راجع البصمات.ignore_sources: عناوين أو شبكات تُغلق أو تُهمل اتصالاتها وحزم بياناتها قبل قراءة أي شيء، فلا تُسجَّل أو تُبصم أو تُرسل أبداً. عناوين مجردة وCIDR، IPv4 أو IPv6:
ignore_sources = ["192.0.2.10", "198.51.100.0/24", "2001:db8::/32"]. يتعرض مصيدة honeypot على مضيف مستأجر للاستقصاء من مراقبة المضيف نفسه، وإحصاء تلك الاستقصاءات يشوّه ترتيب المنافذ وأعداد الماسحات. الإدخال الذي لا يُحلَّل يمنع الوكيل من البدء، لأن خطأً مطبعياً هنا سيسجّل بصمت حركة تعتقد أنها مُهملة. تُحصى عمليات الإهمال لكل مصدر وتُبلَّغ في السجل كل ساعة وعند الإيقاف.udp_enabled ومفاتيح udp_*: راجع التقاط UDP.إذا حُذفت حقول الضبط وقت التشغيل هذه أو ضُبطت على 0، يطبّق Spip القيم الافتراضية التالية:
read_timeout_seconds: 30write_timeout_seconds: 10rate_limit_per_second: 20rate_limit_burst: 50000sudo iptables -t nat -F
sudo iptables -t nat -A PREROUTING -p tcp --dport 22 -j RETURN
sudo iptables -t nat -A PREROUTING -p tcp -j REDIRECT --to-port 8080
الأمر الأول يفرّغ جدول nat بالكامل. اتركه خارجًا على مضيف لديه قواعد NAT خاصة به. القاعدتان نفسهما مع ip6tables تغطيان IPv6. يقرأ Spip الوجهة الأصلية لكل اتصال من تتبع الاتصالات في النواة ويسجلها كـ destination.ip وdestination.port.
./spip-agent -config config.toml
-config هو العلم الوحيد، وقيمته الافتراضية config.toml في دليل العمل. يؤدي SIGINT أو SIGTERM إلى إيقاف المستمع، وينتظر حتى 15 ثانية للاتصالات المفتوحة ويكتب أي دفعة Loom معلّقة إلى spool، مع منحها 5 ثوانٍ للإرسال؛ ويُرسل الباقي عند التشغيل التالي.
يختار Spip المستجيب بناءً على منفذ الوجهة الأصلي. لا يقول كل مستجيب إلا ما يكفي لجعل العميل يرسل رسالته التالية: بيانات الاعتماد، أو أمر، أو مغلف بريد.
| المنافذ | الاستجابة |
|---|---|
| 23, 2323 | تفاوض خيارات Telnet ومطالبة login:، ثم Password:، ثم Login incorrect وإغلاق |
| 21, 990 | تحية FTP، وطلب كلمة مرور بعد السطر الأول، ثم 530 Login incorrect. وإغلاق |
| 25, 465, 587 | تحية SMTP، وردود 250 على الأسطر الثلاثة الأولى، و354 على السطر الرابع، ثم إغلاق. لا يُمرَّر أي شيء ولا يُنشأ أي اتصال صادر. |
| 110, 995 | تحية POP3، و+OK بعد السطر الأول، و-ERR invalid login بعد السطر الثاني، ثم إغلاق |
| 143, 993 | تحية IMAP، ثم * BAD Login failed وإغلاق |
| 6379, 6380، أو أي حمولة تشبه أمر Redis | +PONG لـ PING، و+OK لكل شيء آخر |
| أي منفذ آخر | HTTP/1.1 200 OK مع عنوان IP الخاص بالعميل كجسم لطلب HTTP، وعنوان IP المجرد لأي شيء آخر |
العميل الذي يرسل SSH-2.0 دون رسالة تبادل مفاتيح يحصل على البانر SSH-2.0-spip، وينتظر Spip حتى ثانيتين لتبادل المفاتيح حتى يتمكن من حساب بصمة العميل.
تُرسل تحيات telnet وFTP وSMTP وPOP3 وIMAP عند الاتصال. مع تفعيل TLS، يقرأ Spip أولاً بايتًا واحدًا للتمييز بين TLS والنص العادي، لذا لا تُرسل تلك التحيات إلا بعد أن يرسل العميل شيئًا ما.
يكتب Spip سجلات ECS إلى وجهة محلية واحدة، ويمكنه أيضًا إرسال السجلات نفسها إلى خادم Loom:
| الوجهة | الإعداد | السلوك |
|---|---|---|
| محلية | log_file | محذوفة أو فارغة: stdout. مضبوطة على مسار: تُلحق السجلات بذلك الملف. مفعّلة دائمًا. |
| Loom | [loom] مع enabled = true | اختيارية. تُجمَّع السجلات نفسها وتُرسل عبر POST إلى عنوان URL الخاص باستقبال Loom بالإضافة إلى المخرجات المحلية. |
تحمل المخرجات المحلية أيضًا أسطر سجل الوكيل نفسه، مثل أعداد الإسقاط كل ساعة والتحذيرات، بصيغة {"timestamp": <unix seconds>, "level": "INFO", "message": "...", "target": "...", "record_type": "log"}. تُرسل سجلات الاتصال فقط إلى Loom. تُرسل رسائل بدء التشغيل إلى stderr.
يكتب Spip كل سجل كسطر واحد من JSON بأسماء حقول ECS، باستخدام ما يمكنه استخلاصه من الاتصال نفسه فقط (دون إثراء ASN أو geo). الاتصال الذي يرسل عدة رسائل يُنتج سجلاً واحداً لكل رسالة، وكلها تحمل نفس event.id. تشمل الحقول:
@timestamp: وقت السجل بصيغة RFC 3339، بثوانٍ كاملةevent.id: معرّف جلسة لكل اتصال؛ event.sequence: موضع السجل في الجلسة، بدءاً من 1event.duration: نانوثانية منذ فتح الاتصالevent.module، event.dataset (spip.tcp أو spip.udp)، event.kind، event.category، event.type: تتيح لنظام SIEM اختيار سجلات Spipobserver.vendor، observer.product، observer.type، observer.version، agent.type، agent.versionobserver.hostname، observer.id و host.name: قيمة name الخاصة بالوكيل من الإعداداتsource.ip، source.port، destination.ip، destination.portsource.bytes، destination.bytes، network.bytes: البايتات المستلمة والمُرسلة في الجلسة حتى الآنnetwork.transport (tcp أو udp) و network.protocol (tls، dns، netbios أو quic) حيثما كان معروفاًhttp.request.method، http.request.headers، http.request.body.content، http.request.body.bytes، url.path، url.domain، url.port: عندما تكون الحمولة طلب HTTPuser_agent.original: عندما يكون متاحاًevent.summary: الحمولة كنص، للحمولات التي ليست طلبات HTTPevent.original_payload_hex: الحمولة الخام بصيغة hex، تُحفظ دائماًnetwork.community_id، tls.client.*، ssh.client.hash.hassh و http.request.hash.akin عندما تنطبق.