
وسيط الإغراء التجريبي
أداة خادوع شبكية معبأة بحاويات (honeypot) تعلن عن خدمات SSH وRDP وSMB، وتراقب كل اتصال وارد باستخدام eBPF، وتقوم بعكس الوساطة (reverse-proxy) لكل جلسة إلى حاوية خادعة معزولة.
يفصل التصميم بين مهمتين:
هذه أداة دفاعية لكشف ودراسة النشاط غير المصرح به على شبكات تملكها أو مخول بمراقبتها. انشرها فقط حيث لديك تلك الصلاحية.
flowchart TB
A["المهاجم / الماسح الضوئي"]
subgraph host["مضيف الخداع"]
direction TB
NIC["eth0 الوسيط<br/>منشورة: 22, 3389, 445"]
subgraph brk["حاوية الوسيط"]
direction TB
E["مصنف TC من eBPF<br/>يسجل كل SYN<br/>يرى عنوان IP الحقيقي للمصدر"]
P["وسيط عكسي<br/>CONNECT إلى الخلفية"]
L["سجلات JSON منظمة"]
end
subgraph dec["decoynet (شبكة داخلية، لا مسار للمضيف)"]
direction LR
S["ssh-decoy<br/>OpenCanary ssh<br/>المنفذ 2222"]
D["rdp-decoy<br/>OpenCanary rdp<br/>المنفذ 3389"]
M["smb-decoy<br/>خادم Impacket SMB<br/>المنفذ 445"]
end
end
A --> NIC
NIC --> E
NIC --> P
E --> L
P --> S
P --> D
P --> M
أربع حاويات في المجموع:
تعيش الخداع على شبكة Docker داخلية (decoynet) بدون مسار إلى المضيف أو العالم الخارجي. فقط الوسيط يمكنه الوصول إليها. لا شيء يفعله المهاجم داخل الخداع يمكنه الوصول إلى شبكة المضيف مباشرة.
ينشر الوسيط المنافذ 22 و3389 و445 إلى المضيف، لذلك تصل الحزم الواردة إلى eth0 الخاصة بالوسيط. ثم يحدث شيئان لكل حزمة:
broker/bpf/decoy.bpf.c) يقوم بتحليل رؤوس Ethernet وIP وTCP، ولكل محاولة اتصال جديدة (تم تعيين SYN، ولم يتم تعيين ACK) يكتب حدث conn_event إلى مخزن مؤقت حلقي: عنوان IP ومنفذ المصدر، منفذ الوجهة، أعلام TCP، وما إذا كان المنفذ خدمة معلنة. يتم تمرير الحزمة دون تغيير (TC_ACT_OK).CONNECT إلى الخدمة الخلفية لتلك الخدمة، ثم يعيد توجيه البايتات في كلا الاتجاهين.يتم تعبئة خريطة advertised_ports من eBPF عند بدء التشغيل من config.yaml، لذلك يمكن للمصنف وضع علامة على ما إذا كان المسح قد أصاب منفذًا مخدمًا أم منفذًا غير مرغوب فيه. وهذا يجعل عمليات المسح الأفقي للمنافذ مرئية على الرغم من أن ثلاثة منافذ فقط يتم توجيهها عبر الوسيط.
إذا كنت ترغب في الإعلان بأن "كل شيء مفتوح" وتوجيه منافذ وجهة عشوائية إلى الوسيط، قم بتوسيع المصنف لإعادة كتابة منفذ الوجهة أو استخدم إعادة توجيه TPROXY / bpf_sk_assign. النسخة الحالية تترك مسار الحزمة دون تغيير وتقتصر على المراقبة، وهو الإعداد الافتراضي الأكثر أمانًا.
cyber-decoy/
├── README.md
├── docker-compose.yml # مجموعة من 4 حاويات
├── docker-compose.override.yml # تطوير محلي على macOS: بدون صلاحيات eBPF، إعادة تعيين المنفذ 22
├── Makefile # مساعدات build / up / down / bpf
├── LICENSE
├── scripts/
│ └── setup.sh # فحوصات ما قبل التشغيل للمضيف
├── broker/
│ ├── Dockerfile # يجمّع كائن eBPF + ثنائي Go
│ ├── config.yaml # الخدمات المعلنة (قابلة للتكوين)
│ ├── go.mod
│ ├── main.go # نقطة الدخول
│ ├── bpf/
│ │ └── decoy.bpf.c # مصنف TC من eBPF
│ └── internal/
│ ├── config/config.go # محمل الإعدادات
│ ├── proxy/proxy.go # وسيط TCP عكسي
│ └── bpf/loader.go # تحميل + إرفاق eBPF، دفق الأحداث
└── decoys/ # الثلاثة جميعها تشغل OpenCanary
├── ssh/
│ ├── Dockerfile
│ └── opencanary.conf # وحدة ssh، منفذ 2222
├── rdp/
│ ├── Dockerfile
│ └── opencanary.conf # وحدة rdp، منفذ 3389
└── smb/
├── Dockerfile # عملية Python واحدة، غير جذر
├── smb_decoy.py # Impacket SimpleSMBServer + تسجيل JSON
└── requirements.txt # impacket (مثبت الإصدار)
docker-compose.override.yml المرفق، والذي يعتمد على علامات !reset / !override).sudo mount -t bpf bpf /sys/fs/bpf.تكتشف صورة الوسيط بنية البناء الخاصة به وتمرر الماكرو __TARGET_ARCH_* المناسب إلى clang، لذلك يتم بناؤه على كل من x86_64 و aarch64 (Apple Silicon، Graviton). لاحظ أن gcc-multilib غير مثبت عمدًا: إنها حزمة خاصة بـ x86 فقط وليس لها مرشح arm64، وإدراجها يكسر البناء على arm64 مع رمز خروج apt 100. فقط clang و libbpf-dev مطلوبان لتجميع كائن eBPF.
يقوم Docker Desktop على macOS بتشغيل الحاويات داخل جهاز LinuxKit وليس على نواة المضيف، لذا فإن إرفاق eBPF من نوع TC/TCX لن يعمل بشكل عام هناك. هذا ليس قاتلاً: eBPF هو جهد أفضل حسب التصميم، لذا يسجل الوسيط ebpf disabled: attach failed ويعمل الوسيط العكسي وجميع الخداع الثلاثة بشكل طبيعي ويقومون بالتسجيل. يمكنك تطوير واختبار مسار الوسيط بالكامل محليًا، ثم الحصول على مراقبة eBPF حقيقية عند النشر على مضيف Linux.
يتم تحميل docker-compose.override.yml تلقائيًا ويجعل هذا الأمر ممتعًا: فهو يزيل صلاحيات eBPF (غير مفيدة في الجهاز الظاهري) ويعيد تعيين منفذ المضيف 22 إلى 2022، نظرًا لأن sshd الخاص بجهاز Mac يمتلك المنفذ 22.
docker compose up --build # تطوير محلي، يتم تطبيق التجاوز
docker compose -f docker-compose.yml up -d # نشر حقيقي، يتم تجاوز التجاوز
قم بتشغيل فحص ما قبل التشغيل أولاً:
./scripts/setup.sh
# 1. بناء جميع الصور الأربع (يتم تجميع كائن eBPF داخل صورة الوسيط)
make build
# 2. تشغيل المجموعة
make up
# 3. مشاهدة ما يحدث
make logs
ثم قم باستكشافه من جهاز آخر (أو localhost لاختبار سريع):
ssh -p 22 user@DECOY_HOST # يصل إلى خادع SSH
nc DECOY_HOST 3389 # يصل إلى خادع RDP
nc DECOY_HOST 445 # يصل إلى خادع SMB
nc DECOY_HOST 8080 # غير معلن: يتم مراقبته بواسطة eBPF، لا وسيط
يقوم الوسيط بإصدار JSON لأحداث فحص eBPF والجلسات الموجهة عبر الوسيط؛ ويقوم كل خادع بإصدار أحداث JSON من OpenCanary. لمشاهدة وصول بيانات الدخول:
docker compose logs -f ssh-decoy | grep 4002
على عكس الركن المستند إلى الإعلان فقط، يكمل الآن ssh -p 22 user@DECOY_HOST تبادل مفاتيح حقيقي ويطلب كلمة مرور. يتم التقاط كل محاولة. تحقق من أن بصمة الخدمة تصمد تحت فحص الإصدار:
nmap -sV -p 22,3389,445 DECOY_HOST
الإيقاف باستخدام:
make down
يتم تعريف الخدمات في broker/config.yaml. كل إدخال قابل للتبديل وإعادة التعيين بشكل مستقل:
services:
- name: ssh
enabled: true
listen_port: 22
backend: ssh-decoy:2222
لإضافة خدمة، أضف إدخالًا هنا، وانشر المنفذ في docker-compose.yml، وأضف حاوية خادعة. لتعطيل خدمة، قم بتعيين enabled: false (واختياريًا، قم بإزالة المنفذ المنشور).
لاحظ أن المنفذ 22 على المضيف عادة ما يكون مشغولاً بواسطة خادم SSH الحقيقي للمضيف. للمختبر، يمكنك إعادة تعيين الجانب المنشور في docker-compose.yml، على سبيل المثال "2022:22"، وتوجيه الماسح الضوئي إلى هناك.
جميع الخداع الثلاثة تشغل OpenCanary (من Thinkst)، مهيأة بحيث تقوم كل حاوية بتمكين وحدة واحدة بالضبط. يتم إصدار السجلات بصيغة JSON على stdout، لذا يعمل docker compose logs وأي أداة شحن SIEM بدون تجهيزات إضافية.
يقوم OpenCanary بتمييز كل حدث برقم logtype عددي. تلك التي ستراها هنا:
شكل بيانات SSH التي تم التقاطها:
{"dst_port": 2222, "logtype": 4002, "node_id": "decoy-ssh",
"src_host": "10.0.0.66", "src_port": 42958,
"logdata": {"USERNAME": "admin", "PASSWORD": "Passw0rd123"}}
هذا نتيجة مباشرة لبنية الوسيط، وهو الأمر الأكثر أهمية لفهمه عند قراءة هذه السجلات.
يقوم الوسيط بإنهاء اتصال TCP للمهاجم ويفتح اتصالًا جديدًا إلى الخداع. لذلك من وجهة نظر OpenCanary، العميل هو الوسيط. سيكون كل src_host في حدث الخداع هو عنوان الوسيط على شبكة decoynet، وليس المصدر الحقيقي.
لا يزال عنوان IP الحقيقي للمصدر ملتقطًا، ولكن في مكان مختلف:
لذلك يتطلب الإسناد ربط سجلات الوسيط بسجلات الخداع، بالانضمام على الطابع الزمني والخدمة. يسجل الوسيط remote (عنوان المهاجم الحقيقي) و backend لكل جلسة، وهو ما يجعل الانضمام ممكنًا:
docker compose logs broker | grep 'session opened' # من
docker compose logs ssh-decoy | grep '"logtype": 4002' # ما حاولوا
إذا كنت بحاجة إلى عنوان IP الحقيقي داخل الخداع نفسه، فإن الخيارات هي إرسال بروتوكول PROXY (الخداع لا تحلله، لذا يعني هذا تعديلها)، أو استبدال الوسيط من مساحة المستخدم بإعادة توجيه شفافة (TPROXY أو bpf_sk_assign من eBPF) التي تحافظ على عنوان المصدر الأصلي. كلاهما مدرج ضمن خريطة الطريق. حتى ذلك الحين، تعامل مع الوسيط كمصدر للحقيقة لـ "من" والخداع كمصدر للحقيقة لـ "ماذا".
على عكس SSH و RDP، لا يستخدم هذا الخادع OpenCanary. وحدة smb من OpenCanary هي مجرد مراقب سجلات: فهي تتابع ملفًا وتحلل أسطر smbd_audit الصادرة عن خادم Samba حقيقي، مما يعني تشغيل Samba بالإضافة إلى rsyslog بالإضافة إلى opencanaryd تحت supervisord، سلسلة من خمس حلقات يمكن لأي منها أن يفشل بصمت.
يستبدل smb-decoy كل ذلك بعملية Python واحدة مبنية على SimpleSMBServer من Impacket، وهو تطبيق نقي بلغة Python لـ SMB1/2/3. يرتبط بالمنفذ 445، ويقدم مشاركات طعم للقراءة فقط، ويجيب على تفاوض SMB2/3 (لذا يرى nmap -sV خدمة حقيقية)، ويسجل الاتصالات ومحاولات مصادقة NTLM ككائن JSON واحد لكل سطر على stdout. اسم المستخدم والمجال ومحطة العمل التي تم التقاطها من رسالة المصادقة للمهاجم هي عائد التقاط بيانات الدخول.
ملاحظة أمنية: حمل خادم smb من Impacket اجتياز مسارًا خطيرًا، CVE-2021-31800، الذي أثر بشكل خاص على أدوات الخداع. تم إصلاحه في الإصدار 0.9.23. يقوم requirements.txt بتثبيت إصدار حالي ويجب عدم خفضه إلى أقل من ذلك. تعمل الحاوية أيضًا بدون صلاحيات الجذر، للقراءة فقط، مع إسقاط جميع الصلاحيات باستثناء NET_BIND_SERVICE.
يمتلك كل خادع ملف opencanary.conf (مثبت في /etc/opencanaryd/). الإعدادات المفيدة:
ssh.version في decoys/ssh/opencanary.conf. يدعي حاليًا SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.1. اجعله يطابق نظام التشغيل الذي تتظاهر بأنه؛ لافتة Ubuntu على جهاز يدعي أنه Windows هي علامة واضحة.addShare(...) في decoys/smb/smb_decoy.py، بالإضافة إلى ملفات الطعم التي تم إنشاؤها في decoys/smb/Dockerfile. أسماء المشاركات وأسماء الملفات هي الطُعم.backend في broker/config.yaml.لتمكين وحدة OpenCanary أخرى (ftp، telnet، mysql، vnc، redis، وغيرها متاحة)، قم بتعيين <module>.enabled و <module>.port، أضف حاوية خادعة، وأضف خدمة مطابقة إلى broker/config.yaml.
يقوم ssh-decoy بتركيب وحدة تخزين مسماة في /var/lib/opencanary (ssh.key_path)، لذا يبقى المفتاح المضيف الذي تم إنشاؤه بعد إعادة التشغيل. بدونه يقوم OpenCanary بإنشاء مفتاح جديد في كل بداية ويكون تغيير البصمة علامة واضحة.
أصبح خادع SMB الآن عملية واحدة، لذا فإن استكشاف الأخطاء وإصلاحها بسيط.
docker compose logs -f smb-decoy
كل سطر هو JSON. يجب أن ترى حدث smb_decoy_start واحدًا عند التشغيل، ثم أحداث smb_connect و smb_auth_attempt و smb_tree_connect عندما يتفاعل العملاء. اختبره من المضيف باستخدام أي عميل SMB:
# macOS Finder: Go > Connect to Server
open 'smb://guest@localhost/HR-Payroll'
# أو من Linux
smbclient -L //localhost -p 445 -N
المشكلات الشائعة:
smb_decoy_start وتخرج الحاوية: تحقق من تثبيت requirements.txt بشكل نظيف. Impacket يحتاج Python 3.8+؛ الصورة تستخدم 3.12.smb_auth_attempt: بعض العملاء يسردون المشاركات بشكل مجهول دون مصادقة أبدًا. لا يزال ذلك ينتج smb_connect و smb_tree_connect. فرض المصادقة عن طريق ربط مشاركة باسم مستخدم.src_host هو عنوان الوسيط، وليس المهاجم الحقيقي. اربط مع سجلات الوسيط على الطابع الزمني.NET_ADMIN (و BPF / PERFMON على النوى الحديثة) لتحميل وإرفاق برنامج eBPF. يطلب ملف Compose هذه الصلاحيات المحدودة. إذا رفضها المضيف أو إصدار Docker، فإن الحل البديل هو privileged: true على خدمة الوسيط، وهو أوسع ويجب استخدامه فقط عندما لا تعمل الصلاحيات المحدودة.internal بدون مسار للمضيف. حافظ على ذلك. تعامل مع كل حاوية خادعة على أنها قد تكون مخترقة.bpf_sk_assign، مما يلغي الحاجة إلى ربط سجلات الوسيط والخداع.MIT. انظر LICENSE.
| الحاوية | الدور | الشبكة |
|---|
broker | الباب الأمامي العام: مراقبة eBPF بالإضافة إلى وسيط عكسي | edge + decoynet |
ssh-decoy | وحدة ssh من OpenCanary (مصافحة حقيقية، تلتقط بيانات الدخول) | decoynet فقط |
rdp-decoy | وحدة rdp من OpenCanary (محاكاة NLA، تلتقط أسماء المستخدمين) | decoynet فقط |
smb-decoy | Impacket SimpleSMBServer (SMB2/3 حقيقي، يلتقط المصادقة) | decoynet فقط |
| الحاوية | وحدة OpenCanary | تستمع إلى | ما تفعله فعليًا |
|---|
ssh-decoy | ssh | 2222 | تبادل مفاتيح SSH حقيقي عبر twisted.conch. تلتقط كل زوج اسم مستخدم/كلمة مرور. |
rdp-decoy | rdp | 3389 | تحاكي خادمًا مفعّلاً NLA، تعيد دائمًا فشل تسجيل الدخول، تستخرج اسم المستخدم mstshash. |
smb-decoy | Impacket | 445 | خادم SMB2/3 نقي بلغة Python. يقدم مشاركات طعم ويسجل الاتصالات ومحاولات مصادقة NTLM بصيغة JSON. |
| logtype | الثابت في opencanary/logger.py | المعنى |
|---|
| 1000 | LOG_BASE_BOOT | بدء تشغيل الخفي |
| 4000 | LOG_SSH_NEW_CONNECTION | فتح اتصال SSH |
| 4001 | LOG_SSH_REMOTE_VERSION_SENT | أرسل العميل سلسلة إصداره |
| 4002 | LOG_SSH_LOGIN_ATTEMPT | محاولة تسجيل دخول SSH (تتضمن USERNAME و PASSWORD) |
| 5000 | LOG_SMB_FILE_OPEN | فتح ملف SMB (تتضمن USER و SHARENAME و FILENAME) |
| 14001 | LOG_RDP | اتصال RDP / محاولة تسجيل دخول |
| الطبقة | هل يعرف عنوان IP الحقيقي للمصدر؟ | هل يعرف ما تمت محاولته؟ |
|---|
مصنف eBPF (تمت مراقبة الفحص) | نعم | لا، فقط بيانات SYN |
وسيط الوسيط (تم فتح الجلسة) | نعم | لا، فقط عدد البايتات |
خادع OpenCanary (logtype 4002) | لا | نعم، بيانات الدخول/الملفات |