مختبر Docker قابل للاستغلال عن قصد مع بنية DNS قابلة للتوجيه ومفاتيح إجابة قابلة للقراءة آليًا لكل هدف، مع تقييم دقة الماسح واستدعائه ونطاقه محليًا.
مختبر اختبار أمني. أسطول من الأهداف المعرّضة للثغرات عن قصد، وبنية شبكية قابلة للتوجيه مع DNS موثوق لاكتشاف الأصول وتعدادها، ومفتاح إجابات قابل للقراءة آليًا لكل هدف، ولوحة تحكم لإدارة كل ذلك.
كل شيء هنا معرّض للثغرات عن قصد. للاختبار المحلي فقط. لا تعرّضه للإنترنت أو لشبكة غير موثوقة. المنافذ المنشورة ترتبط بـ
127.0.0.1؛ أهداف المختبر لا ترتبط بأي شيء على الإطلاق.الماسح الخاص بنا يحصل على RCE داخل هذه الحاويات عن قصد، لذا تُعامَل الحاوية كحدّ أمني: كل صورة مثبّتة بالـ digest، وكل خدمة تُسقط جميع القدرات وتضيف الحد الأدنى فقط، و
./lime auditيفرض ذلك. اقرأ SECURITY.md قبل التشغيل الأول، بما في ذلك الجزء المتعلق بما لا يغطيه عزل الحاويات.
لوحة التحكم على http://127.0.0.1:7000، تعرض المختبر أثناء تشغيله.
كان limeyard يُسمّى vuln_apps. أُعيدت تسميته لأنه لم يعد مجرد مجلد تطبيقات: فهو الآن يضم خدمات مجرّدة، ومنطقة DNS، وزوج WAF، وهدفًا دقيقًا، وملفات APK تجريبية، ولا شيء من ذلك تطبيقات.
كان الأسطول القديم يحقق 9 من 9، بلا أخطاء ولا إيجابيات كاذبة. المعيار الذي لا يمكن أن يفشل لا يمكنه كشف الانحدار. كانت ثلاثة أمور خاطئة بنيويًا:
127.0.0.1:70xx، فلم يكن لدى تعداد النطاقات الفرعية شيء لتعده، وكان مسح المنافذ يُسلَّم إجابته، ولم يرَ بصمات الخدمات أبدًا خدمة غير HTTP.تحتاج إلى Docker (مع إضافة compose) وgit. لا شيء آخر.
curl -fsSL https://raw.githubusercontent.com/clickswave/limeyard/main/install.sh | bash
يقوم ذلك باستنساخ المختبر إلى ./limeyard، وكتابة ملف .env الخاص به برمز API جديد، وبناء وتشغيل لوحة التحكم، ثم يسأل عمّا تريد تشغيله. قبل أن يبدأ أي شيء يعرض تكلفة الاختيار، مقيسة في وضع الخمول على الجهاز المرجعي، مقابل ما هو متاح على جهازك:
This selection, idle, on the box it was measured on:
17 targets, 1 scenarios, 45 containers
RAM about 2.9 GB resident (host has 22.4 GB available)
disk about 11.0 GB of images to pull (host has 111 GB free)
CPU near idle once up (3% of one core); pulling and first boots are the busy part
Start it? [y/N]
غير تفاعلي: curl ... | bash -s -- --light --yes (أو --all، --none، --pick dvwa,juice-shop,estate). ضع نسخة الاستنساخ في مكان آخر باستخدام LIMEYARD_DIR=/path.
تكون اللوحة بعد ذلك على http://127.0.0.1:7000، ونفس الأشياء يدويًا:
./lime setup # the wizard again, any time
./lime start --all # every light target
./lime start crapi --heavy # a heavy one, explicitly
./lime scenario-up estate # the network estate: DNS, vhosts, services
./lime status # what is up
./lime stop --all --heavy # everything down; images and volumes stay
./lime credits # who wrote each target, and under what licence
./lime doctor # environment, attribution and disk checks
./lime doctor --fix # apply every check's automatic remedy, then re-check
./lime audit # container hardening + supply chain invariants
./lime pin # report image drift against the registry
يدويًا، بدون المثبّت:
git clone https://github.com/clickswave/limeyard && cd limeyard
cp .env.example .env
echo "LIMEYARD_DIR=$PWD" >> .env
echo "LIME_TOKEN=$(openssl rand -hex 24)" >> .env # required, see SECURITY.md
docker compose up -d --build # control plane + UI on http://127.0.0.1:7000
./lime setup
يحمل كل بيان كتلة resources مقيسة (الحاويات، ذاكرة الخمول، قرص الصور، وحدة المعالجة في الخمول). يجمع المعالج وشريط الاختيار في اللوحة وصفحة كل هدف من هذه الكتلة، لذا يكون التقدير نفسه في كل مكان.
فرعان، لا غير.
main هو ما يستنسخه المثبّت وما تحصل عليه إن لم تفعل شيئًا. يتحرك عبر طلبات السحب، لا عبر الدفع المباشر أبدًا.dev هو الفرع الافتراضي وحيث يستقر العمل. افتح طلبات السحب مقابله.| target | شيء واحد تحت الاختبار، من kind معلن. يملك ملف compose، وإعدادًا اختياريًا، ومفتاح إجاباته الخاص. يعمل كمشروع compose معزول خاص به، لذا لا يتشارك هدفان يستخدمان Postgres أبدًا نسخة واحدة |
| scenario | عدة أهداف موصولة في طوبولوجيا شبكية مع DNS موثوق. ما يُقيَّم عليه اكتشاف الأصول |
| truth | مفتاح الإجابات القابل للقراءة آليًا. انظر truth/schema.md |
| doctor | قائمة واحدة من الفحوصات مع حكم لكل منها: البيئة، والإسناد، وسلسلة التوريد، والتحصين. الفحوصات ذات العلاج الواضح تحمل إصلاحًا بنقرة واحدة في اللوحة (/doctor) و--fix على سطر الأوامر: إنشاء الشبكات، واستعادة القرص، وتثبيت الصور، وجلب المصادر، وإعادة التحقق من الأهداف العاملة، وإعادة كتابة الارتباطات خارج loopback. الإسناد وتعارضات المنافذ والتحصين تحتاج إلى شخص |
الأنواع: web api bench cve service estate edge control mobile.
targets/<kind>/<slug>/ target.yml, compose.yml, setup.sh, truth.yml
scenarios/<slug>/ scenario.yml, compose.yml, zones/
control/limed/ the daemon: CLI + HTTP API + scorer
control/ui/ the SvelteKit control panel
truth/ the contract, and dated scorecards
يشغّل docker compose up -d حاويتين ولا شيء آخر: limeyard_control (خادم limed، الذي يحتفظ بمقبس Docker) وlimeyard_ui (لوحة SvelteKit). كلاهما يرتبط بـ loopback فقط. اللوحة على http://127.0.0.1:7000 وواجهة API الخام على http://127.0.0.1:7099. كلاهما يحتاج .env، وLIME_TOKEN فيه إلزامي: تحتفظ اللوحة بالرمز على جانب الخادم ولا يراه المتصفح أبدًا.
| الصفحة | الغرض منها |
|---|---|
| Targets | كل هدف، مع تصفية حسب النوع والحالة، وترتيب، وتحديد متعدد مع Start وStop وRestart. انقر على صف للوصول إلى الهدف |
| Target | الحقائق (العنوان، بيانات الاعتماد، الحزمة، المصدر الأصلي، التحقق، بصمات الصور)، والسجل الحي، ومفتاح الإجابات مع سلبياته |
| Scenarios | البنية الشبكية: المُحلِّل، والمناطق، والشبكة الفرعية، وجدول مضيفين مع حالة كل حاوية. تشغيل، وإيقاف، وإعادة تشغيل |
| Scorecard | أحدث تشغيل مع الفروقات، والتغطية لكل فئة ولكل هدف، والمعرّفات المفقودة، وسجل يمكنك عرضه، وفرق بين تشغيلين |
| Ports | ما يرتبط على localhost، وما يوجد فقط على جسر المختبر |
| Doctor | قائمة واحدة من الفحوصات مع حكم لكل منها. يعرض Fix وFix all الأوامر الدقيقة وتعديلات الملفات أولاً، محسوبة من المختبر كما هو، ثم ينفذها عند التأكيد. الإصلاح الذي يترك فحصه فاشلاً يصرّح بذلك |
| Credits | من كتب كل هدف وتحت أي رخصة |
تحدّث اللوحة نفسها: يبثّ limed انتقالات الحالة عبر SSE، لذا يظهر هدف بدأ من سطر الأوامر دون تحديث. يحمل الترويسة وحدة المعالجة والذاكرة والقرص الحر للمضيف من نفس البث، ملوّنة فقط عندما تستحق الملاحظة. كل جدول يُرتَّب بالنقر على ترويسة عمود؛ والنقرة الثانية تعكس الاتجاه.
بعد تغيير في اللوحة أو الخادم، أعد بناء الزوج:
docker compose up -d --build
للعمل على اللوحة مقابل خادم عامل دون إعادة بناء الصورة:
cd control/ui && npm install
LIMED_URL=http://127.0.0.1:7099 LIME_TOKEN=<from .env> npm run dev # :7000
كل استدعاء عدا /api/health يحتاج X-Lime-Token.
GET /api/targets list, with state and attribution
GET /api/targets/<slug> plus truth, images, lab addresses
GET /api/targets/<slug>/logs SSE, docker compose logs -f
POST /api/targets/<slug>/<action> start | stop | restart | pull | setup
POST /api/targets/bulk {action, slugs}: a pool of three, per-slug refusals
GET /api/scenarios with per-host container state
POST /api/scenarios/<slug>/<action> up | down | restart
GET /api/scorecards newest first, by_class carries false positives
GET /api/scorecards/<id> one card
POST /api/score {tool, findings, save?, targets?}
GET /api/doctor checks with verdict, reason, value, items, fix
POST /api/doctor/fix {ids}: those checks, or every fixable one when empty
GET /api/ports /api/credits /api/truth /api/status
GET /api/events SSE: state, scenario, tick
ثلاث طبقات، لأن طبقة واحدة كانت المشكلة الأصلية.
lime-web جسر. أهداف الويب وAPI، منشورة على 127.0.0.1:70xx.lime-lab جسر، 10.66.0.0/16، عناوين IP ثابتة، بلا ارتباط بالمضيف. ينضم الماسح إلى هذه الشبكة كحاوية ويرى شبكة فرعية حقيقية بمضيفين حقيقيين ومنافذ حقيقية بدلاً من قائمة منافذ loopback.lime-edge طبقة WAF، مع إمكانية الوصول إلى الأصل لكنه غائب من DNS.DNS هو BIND موثوق على مناطق .test (RFC 6761 يحجزها). أسماء الشبكة البديلة في Docker ليست مصدر الحقيقة عن قصد: فهي لا تظهر أبدًا في نقل المنطقة، وستجعل الطوبولوجيا تتعارض مع مفتاح الإجابات.
| النطاق | الاستخدام |
|---|---|
| 7000 | الواجهة |
| 7099 | limed API |
| 7001-7099 | أهداف الويب وAPI |
| 7100-7199 | أجنحة المعايير |
| 7200-7299 | مختبرات CVE |
| 7300-7399 | أهداف edge وcontrol |
| 5353 | DNS المختبر |
| لا شيء | أهداف الخدمات والبنية الشبكية، عناوين lime-lab فقط |
يشحن كل هدف ملف truth.yml. يحوّل المُقيِّم النتائج إلى دقة واستدعاء وF1، لكل هدف ولكل فئة:
curl -s -XPOST localhost:7099/api/score -H 'Content-Type: application/json' \
-d '{"tool":"crossfyre","save":true,"findings":[...]}'
يُحتسب ثلاثة أمور، لا واحد. الاستدعاء: هل وجدنا ما هو موجود. الدقة: هل تجنّبنا الإبلاغ عمّا ليس موجودًا، مقيسة مقابل مدخلات negative التي يحملها كل مفتاح إجابات. النطاق: ما لم نحاوله بشكل صحيح، مسجّلاً حتى لا يعيد أحد مناقشته كل ربع سنة.
هدف mirage موجود من أجل الثاني فقط. لا شيء فيه معرّض للثغرات وكل شيء فيه يبدو كذلك، لذا أي نتيجة ضده هي إيجابية كاذبة بحكم البناء.
يحتوي CONTRIBUTING.md على كل شيء: ما تكون عليه المساهمة عادةً، والثوابت التي يفرضها ./lime audit، ولماذا تصحيح مفتاح إجابات يستحق هنا أكثر من ميزة جديدة. النسخة المختصرة من القواعد أدناه. كل من يشارك ملتزم بـمدونة السلوك.
targets/<kind>/<slug>/
target.yml manifest, including a REQUIRED upstream block
compose.yml the containers. 127.0.0.1 binds only
setup.sh optional one-time init, run after start
truth.yml the answer key
قواعد تحافظ على نظافة المختبر:
127.0.0.1.[lime-web] فقط. لا تضف شبكة لكل مشروع: شبكات كثيرة جدًا ويستنفد Docker مجمّع العناوين.[default, lime-web] وتضع قاعدة البيانات على [default] فقط. يحصل كل هدف على خدمة قاعدة بيانات وحجم خاصين به.[lime-lab] بعنوان IP ثابت ولا تنشر أي منفذ مضيف.upstream إلزامي. يُفشل lime doctor أي هدف بدونه ويرفض المدير تسجيله. انظر أدناه.repo: وcompose: في البيان ويُجلب المصدر وقت التشغيل إلى <target>/src بدلاً من ذلك../lime measure <slug> يطبع كتلة resources للصقها. يجمع المثبّت واللوحة هذه معًا لتحذير شخص قبل تشغيله، لذا فالتخمين هنا كذبة هناك.الأهداف هي برمجيات أشخاص آخرين معرّضة للثغرات عن قصد، لذا يُسجَّل المصدر بدلاً من افتراضه، ويُقيَّد وقت التشغيل بدلاً من الوثوق به. يحتوي SECURITY.md على الصورة الكاملة؛ النسخة المختصرة:
./lime pin عن الانحراف.raesene/bwapp (مؤرشف، آخر إعادة بناء 2022، بلا رخصة) وdelfer/alpine-ftp-server (فرد واحد)، مسمّاتان بذلك.no-new-privileges، وcap_drop: ALL مع حد أدنى من cap_add لكل صورة، وسقف pid وسقف ذاكرة. طبقات قواعد البيانات تجلس على شبكات internal: true بلا مسار للخارج.limed بمقبس Docker، وهو root على المضيف، لذا يتطلب سرًا مشتركًا في كل استدعاء. الهدف المخترق يمكنه الوصول إليه ولا يتعلم شيئًا../lime audit عند privileged، أو شبكة المضيف، أو تركيب مقبس في هدف، أو ارتباط خارج loopback، أو صورة غير مثبّتة، أو رمز مفقود.لا شيء هنا يحمي من هروب حاوية على مستوى النواة. لذلك، استخدم جهازًا افتراضيًا قابلًا للتخلص منه.
كل شيء هنا تقريبًا كتبه شخص آخر، وعدة أهداف لا تعلن أي رخصة على الإطلاق. لذا فإن نسب الفضل للمؤلف بوابة صارمة، لا مجرد عُرف:
target.yml كتلة upstream إلزامية: المؤلف، والمستودع، والرخصة، وتاريخ آخر تحقق من بنائه. يسجّل packager الشخص الذي حوّل شيئًا إلى حاوية عندما يختلف عن كاتبه.none declared تُعرض كتحذير، وهو أيضًا إشارة عدم إعادة التوزيع./credits في الواجهة و./lime credits كل هدف ومؤلف ورخصة. يعيد ./lime credits --markdown توليد القسم أدناه.يشغّل limeyard عمل أشخاص آخرين. كل هدف وسيناريو أدناه بناه شخص آخر ما لم يُذكر Clickswave.
| الهدف | المؤلف | الرخصة | المصدر |
|---|---|---|---|
| mirage | Clickswave | MIT | repo |
| الهدف | المؤلف | الرخصة | المصدر |
|---|---|---|---|
| Log4Shell lab | Christophe Tafani-Dereeper (christophetd) | Apache-2.0 | repo |
| الهدف | المؤلف | الرخصة | المصدر |
|---|---|---|---|
| ModSecurity CRS pair | OWASP Core Rule Set project (coreruleset) | Apache-2.0 | repo |
| الهدف | المؤلف | الرخصة | المصدر |
|---|---|---|---|
| AndroGoat | Satish Patnayak | none declared | repo |
| السيناريو | المؤلف | الرخصة | المصدر |
|---|---|---|---|
| estate | Clickswave | MIT | - |
| الهدف | المؤلف | الرخصة | المصدر |
|---|---|---|---|
| Open services | Clickswave (composition of upstream official images) | mixed, per-image | - |
| الهدف | المؤلف | الرخصة | المصدر |
|---|---|---|---|
| bWAPP | Malik Mesellem (pkg: Rory McCune (raesene)) | none declared | repo |
| DVWA | Robin Wood (digininja) | GPL-3.0 | repo |
| FaultLine ISP | Clickswave | MIT | repo |
| OWASP Juice Shop | Bjoern Kimminich (OWASP Juice Shop project) | MIT | repo |
| OWASP Mutillidae II | Jeremy Druin (webpwnized), OWASP Mutillidae II | GPL-3.0 | repo |
| OWASP RailsGoat | OWASP RailsGoat project | MIT | repo |
| OWASP WebGoat + WebWolf | OWASP WebGoat project | GPL-2.0 | repo |