Намеренно уязвимая лаборатория Docker с маршрутизируемой DNS-инфраструктурой и машиночитаемыми ключами ответов для каждой цели, локально оценивающая точность, полноту и охват сканера.
Лаборатория для тестирования безопасности. Парк намеренно уязвимых целей, маршрутизируемая сетевая инфраструктура с авторитетным DNS для обнаружения активов, машиночитаемый ключ ответов для каждой цели и панель управления для всего этого.
Всё здесь намеренно уязвимо. Только локальное тестирование. Не выставляйте это в интернет или в недоверенную сеть. Публикуемые порты привязаны к
127.0.0.1; цели лаборатории не привязаны ни к чему вообще.Наш собственный сканер намеренно получает RCE внутри этих контейнеров, поэтому контейнер рассматривается как граница безопасности: каждый образ закреплён по дайджесту, каждый сервис сбрасывает все capabilities и добавляет обратно минимум, и
./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
токеном, собирает и запускает control plane, затем спрашивает, что запускать. Прежде чем
что-либо запустится, он показывает, сколько стоит выбор, измеренный в простое на
эталонной машине, против того, что свободно на вашей машине:
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). Разместите checkout в другом месте с
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 (контейнеры, RAM в простое,
диск образов, CPU в простое). Мастер, полоса выбора панели и страница каждой
цели суммируют из него, поэтому оценка везде одинакова.
Две, и только две.
main — это то, что клонирует установщик и что вы получите, если ничего не делаете.
Она движется через pull request, никогда через прямой push.dev — ветка по умолчанию и место, куда попадает работа. Открывайте pull request
против неё.| target | одна вещь под тестом, объявленного kind. Владеет compose-файлом, опциональной настройкой и своим собственным ключом ответов. Запускается как собственный изолированный compose-проект, поэтому две цели, использующие Postgres, никогда не делят один |
| scenario | несколько целей, связанных в сетевую топологию с авторитетным DNS. То, против чего оценивается обнаружение активов |
| truth | машиночитаемый ключ ответов. См. truth/schema.md |
| doctor | один список проверок с вердиктом для каждой: окружение, атрибуция, цепочка поставок, харденинг. Проверки с однозначным исправлением несут исправление в один клик в панели (/doctor) и --fix в CLI: создать сети, освободить диск, закрепить образы, получить исходники, перепроверить запущенные цели, переписать привязки вне 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 socket) и 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 | факты (адрес, учётные данные, стек, upstream, верификация, дайджесты образов), живой лог и ключ ответов с его негативами |
| Scenarios | estate: резолвер, зоны, подсеть и таблица хостов с состоянием каждого контейнера. Поднять, опустить, перезапустить |
| Scorecard | последний прогон с дельтами, покрытие по классам и по целям, пропущенные id, история, которую можно просмотреть, и diff двух прогонов |
| Ports | что привязано на localhost и что существует только на лабораторном бридже |
| Doctor | один список проверок с вердиктом для каждой. Fix и Fix all сначала показывают точные команды и правки файлов, вычисленные из лаборатории как она есть, и выполняют их после подтверждения. Исправление, оставляющее свою проверку проваленной, сообщает об этом |
| Credits | кто написал каждую цель и под какой лицензией |
Панель обновляет себя сама: limed стримит переходы состояний через SSE, поэтому цель, запущенная из CLI, появляется без обновления. Заголовок несёт CPU, RAM и свободный диск хоста из того же потока, окрашиваясь только когда на них стоит обратить внимание. Каждая таблица сортируется по клику на заголовок столбца; второй клик меняет направление.
После изменения панели или демона пересоберите пару:
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 bridge. Web и API цели, публикуемые на 127.0.0.1:70xx.lime-lab bridge, 10.66.0.0/16, статические IP, без привязки к хосту. Сканер
присоединяется к этой сети как контейнер и видит реальную подсеть с реальными хостами и
реальными портами вместо списка портов loopback.lime-edge уровень WAF, с достижимым origin, но отсутствующим в DNS.DNS — это авторитетный BIND на зонах .test (RFC 6761 резервирует его). Сетевые алиасы
Docker намеренно не являются источником истины: они никогда не появляются в zone
transfer и заставили бы топологию расходиться с ключом ответов.
| диапазон | использование |
|---|---|
| 7000 | UI |
| 7099 | limed API |
| 7001-7099 | web и api цели |
| 7100-7199 | наборы бенчмарков |
| 7200-7299 | CVE лаборатории |
| 7300-7399 | edge и control цели |
| 5353 | лабораторный DNS |
| нет | service и estate цели, только адреса lime-lab |
Каждая цель поставляет truth.yml. Скорер превращает находки в precision,
recall и F1, по цели и по классу:
curl -s -XPOST localhost:7099/api/score -H 'Content-Type: application/json' \
-d '{"tool":"crossfyre","save":true,"findings":[...]}'
Считаются три вещи, а не одна. Recall: нашли ли мы то, что есть.
Precision: избежали ли мы сообщения о том, чего нет, измеряется против
записей negative, которые несёт каждый ключ ответов. Scope: то, что мы корректно
не пытались сделать, записано, чтобы никто не пересматривал это каждый квартал.
Цель 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 socket, который является root на хосте, поэтому он требует
общего секрета на каждом вызове. Взломанная цель может достучаться до него и ничего не узнать../lime audit проваливается на privileged, host networking, монтировании сокета в
цели, привязке вне loopback, незакреплённом образе или отсутствующем токене.Ничто здесь не защищает от побега из контейнера на уровне ядра. Для этого используйте одноразовую VM.
Почти всё здесь было написано кем-то другим, и несколько целей не объявляют никакой лицензии вообще. Поэтому указание автора — жёсткое требование, а не соглашение:
target.yml несёт обязательный блок upstream: автор, репозиторий, лицензия
и дата, когда мы последний раз проверяли, что это собирается. packager записывает человека, который
контейнеризировал что-то, когда это отличается от того, кто это написал.none declared отображается как
предупреждение, что также является сигналом не распространять./credits в UI и ./lime credits перечисляют каждую цель, автора и
лицензию. ./lime credits --markdown регенерирует раздел ниже.limeyard запускает работу других людей. Каждая цель и сценарий ниже были созданы кем-то другим, если не указано Clickswave.
| target | author | licence | source |
|---|---|---|---|
| mirage | Clickswave | MIT | repo |
| target | author | licence | source |
|---|---|---|---|
| Log4Shell lab | Christophe Tafani-Dereeper (christophetd) | Apache-2.0 | repo |
| target | author | licence | source |
|---|---|---|---|
| ModSecurity CRS pair | OWASP Core Rule Set project (coreruleset) | Apache-2.0 | repo |
| target | author | licence | source |
|---|---|---|---|
| AndroGoat | Satish Patnayak | none declared | repo |
| scenario | author | licence | source |
|---|---|---|---|
| estate | Clickswave | MIT | - |
| target | author | licence | source |
|---|---|---|---|
| Open services | Clickswave (composition of upstream official images) | mixed, per-image | - |
| target | author | licence | source |
|---|---|---|---|
| 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 |