Prototype Pollution: загрязнение Object.prototype приводит к DoS публичных эндпоинтов
Express-эндпоинт POST /profile/settings делает рекурсивный merge JSON-объекта в per-user настройки без фильтрации __proto__. Атакующий загрязняет Object.prototype, после чего hot-path обработчик /catalog падает с 500 на каждом запросе — Denial of Service. Endpoint /api/incident-response регистрирует инцидент и выдаёт флаг.
hardnodejsPro
Задача
# Prototype Pollution: DoS публичного каталога
В этой лабораторной работе вы найдёте классическую уязвимость **Prototype Pollution** в Express-сервисе SecureShop и используете её как **DoS-gadget**, чтобы положить публичный каталог товаров на колени. Уязвимое место — endpoint обновления пользовательских настроек, который рекурсивно мёрджит произвольный JSON в in-memory дерево настроек без фильтрации зарезервированных ключей `__proto__`, `constructor`, `prototype`.
## Поверхность атаки
Сервис SecureShop — стандартный e-commerce backend на Express + better-sqlite3. После логина пользователь может тонко настроить свой профиль через JSON API:
- **POST `/profile/settings`** — принимает JSON-объект с настройками пользователя (тема, нотификации, размер страницы каталога) и сохраняет их в per-user runtime cache.
- **GET `/catalog`** — публичный hot-path для всех посетителей: отрисовывает товары и формирует строку тегов для каждой карточки. Внутри renderer'а используется паттерн `for...in` по объекту тегов товара.
- **GET `/api/incident-response`** — операторский диагностический endpoint. Снимает срез `Object.prototype` при старте сервиса и сообщает любые «лишние» собственные ключи, появившиеся в рантайме. При обнаружении инцидента отдаёт flag-токен для post-mortem отчёта.
## Логика уязвимости
1. На уровне приложения есть единый поток JavaScript-объектов: каталог, теги, настройки, сессии — всё это плоские POJO.
2. `Object.prototype` — общий предок всех POJO. Любое свойство, выставленное на нём, становится **inherited enumerable** ключом для каждого объекта в процессе.
3. `for...in` обходит и собственные, и наследованные перечисляемые ключи. Если злоумышленник добавит на `Object.prototype` ключ с примитивом, любой `for...in` в hot-path начнёт «спотыкаться» на нём.
Когда вы загружаете каталог, рендерер вызывает `tags[tag].toUpperCase()` для каждого тега. Если значение тега — примитив (например, число `1`), `.toUpperCase()` бросает `TypeError`, обработчик возвращает HTTP 500, и каталог становится недоступен для **всех** посетителей сайта.
## Что нужно сделать
Залогиньтесь как `demo` / `demo`, отправьте на `/profile/settings` JSON-payload, который через `__proto__` загрязнит глобальный прототип, и убедитесь, что `/catalog` начал отдавать 500. После этого запросите `/api/incident-response` — он зафиксирует инцидент и вернёт flag.
## Учётные данные
- Обычный пользователь: `demo` / `demo`
- Администратор: `admin` / `SuperSecret`
Для эксплуатации хватит обычного пользователя — endpoint `/profile/settings` доступен всем аутентифицированным.