Найдите уязвимость, для обнаружения которой ваши тесты никогда не были написаны. Демонстрация ReGrade, моделирующая CVE-2023-5968: обнаружьте утечку хеша пароля, сравнивая приложение с самим собой.
Практическая демонстрация обнаружения zero-day уязвимостей с помощью ReGrade. Вы направите обычный набор тестов на ReGrade, запишете его работу против крошечного сервиса, воспроизведёте её на второй копии того же сервиса и с помощью Claude Code + ReGrade MCP-инструментов выявите утечку хеша пароля — уязвимость, для поиска которой не был написан ни один тест.
В основе — реальный баг: CVE-2023-5968, где endpoint обновления имени пользователя в платформе совместной работы возвращал полный объект пользователя вместе с bcrypt-хешем пароля. Он был выпущен в 2017 году и пережил 7 лет тестов, ревью и аудитов. (Разбор от Curtail.)
В чём фишка: здесь нет v2. Вы сравниваете приложение с самим собой. Новый экземпляр хеширует
свои пароли с другими bcrypt-солями, поэтому значения утёкшего хеша различаются между двумя копиями —
и именно эту энтропию ReGrade и подсвечивает. Уязвимость уже заложена в коде, который вы выпустили;
для её обнаружения не нужно менять версию.
regrade с и задайте
(или ).REGRADE_API_KEY~/.regrade/keyclaude plugin marketplace add https://app.regrade.curtail.com/downloads/latest/marketplace.json
затем claude plugin install regrade@regrade --scope user и подключите его один раз (/mcp,
войдя в ту же учётную запись, что и для вашего ключа).Крошечный API «командного чата» (app/store.py) с пользователями и каналами. Docker Compose запускает две
идентичные копии — один и тот же образ, один и тот же код, без флага версии:
http://localhost:8001 — запись ведётся против него.http://localhost:8002 — воспроизведение выполняется против него.В одном endpoint заложен дефект:
| Endpoint | Поведение |
|---|---|
GET /users/<id> | Санитизирован — никогда не возвращает пароль. Чистый базовый вариант. |
PATCH /users/<id> (переименование) | Баг — возвращает полный объект пользователя включая bcrypt-хеш password. Один пропущенный вызов sanitize(), точно как в CVE-2023-5968. |
Пароли хешируются bcrypt при загрузке, поэтому instance-a и instance-b хранят разные хеши для одного и того же пользователя.
git clone https://github.com/Curtail-Inc/hello-ReGrade-security
cd hello-ReGrade-security
docker compose up -d --build
traffic/test_api.py — это обычный набор функциональных тестов: вход, чтение пользователя, переименование пользователя,
получение списка каналов. Он проверяет CRUD-поведение и не содержит проверок безопасности — он никогда не проверяет,
утекает ли password. (С чего бы? Никто не знал, что баг существует.)
Запустите прокси-сенсор перед instance-a:
regrade proxy --target http://localhost:8001 --port 19870
В другом терминале запустите тот же набор тестов — просто укажите BASE_URL на прокси. Это
единственное изменение:
BASE_URL=http://localhost:19870 python -m pytest traffic/test_api.py
Все тесты проходят без изменений. Остановите прокси (Ctrl-C); запись загрузится и выведет
Recording ID: <uuid> — запомните его.
regrade replay --rec-id <RECORDING_ID> --target http://localhost:8002
С обеих сторон один и тот же код — отличаются только те значения, которые генерирует свежий экземпляр.
Откройте этот репозиторий в Claude Code и попросите провести вас по воспроизведению. Руководствуясь CLAUDE.md
из этого репозитория, он:
summarize_deltas → несколько дельт: токен входа token, метки времени created_at каналов
и — незаметно среди них — $.password.create_id_mapping для сессионного $.token (динамический ID, который не нужно отбрасывать как шум),create_filter_rule DROP для $.channels[*].created_at (метка времени, создаваемая при каждой загрузке),apply_profile_to_replay, затем query_deltas(unlabeled_only=true) — повторяйте, пока не останется только одна
дельта.$.password — bcrypt-хеш в теле ответа.
Утечка уровня CVE, найденная без каких-либо предварительных знаний и без единой проверки безопасности.Сессионный токен и хеш пароля оба различаются между двумя запусками — оба являются строками с высокой энтропией, которые меняются каждый раз. Один — легитимный шум, который вы маппите; другой — нарушение безопасности. Их невозможно различить по принципу «оно изменилось» — нужно смотреть на то, что именно изменилось. Отфильтруйте все поля с высокой энтропией как шум — и вы спрячете уязвимость.
В этом и урок: ReGrade не проверяет ожидания, он сравнивает поведение — и поэтому может выявлять баги, о которых никто не додумался написать тест.
app/store.py — это один файл. Пути GET вызывают sanitize(); путь PATCH забывает это сделать.
Посмотрите на код после того, как найдёте проблему с ReGrade — суть в том, что ReGrade поймал её на одном лишь
трафике, сравнив приложение с самим собой.
Apache-2.0.