💠 Предисловие
В JavaScript почти все объекты наследуют свойства и методы через prototype chain — цепочку прототипов. Если у объекта нет собственного свойства, движок идет выше по цепочке и ищет его в прототипе. Именно поэтому объект может "уметь" что-то, даже если это явно в нем не записано.
Prototype Pollution — это уязвимость, при которой атакующий может изменить прототип объектов приложения и тем самым незаметно "подмешать" свойства или значения в другие объекты, которые с ним напрямую вообще не связаны.
🟡Тип: Web, Client-Side / Server-Side
🟡Как это работает в общих чертах:
- Приложение принимает объект из недоверенного источника
- Объединяет его с другим объектом, копирует поля, парсит query/body/json без жесткой фильтрации
- Специальные ключи, например __proto__, constructor.prototype, prototype попадают в процесс merge / assignment
- В результате меняется не один конкретный объект, а поведение множества объектов в приложении через цепочку прототипов
Главная опасность в том, что само "загрязнение" часто выглядит безобидно и даже не ломает приложение сразу. Но после этого любой участок кода, который опирается на свойства объекта, может начать работать по-другому.
🟡Пример уязвимого кода
deepMerge(defaultConfig, JSON.parse(req.body))
🟥Если deepMerge не фильтрует опасные ключи, то пользовательские данные могут изменить прототип вместо обычной вложенной структуры.
🟡Разновидности:
- Client-Side - эксплуатация загрязняет протитипы в DOM и остается в браузере пользователя
- Server-Side - реализуется если бекенд приложения написан на Javascript-подобном языке (Node.js, TypeScript и пр.)
🟡Влияние:
- Обход авторизации и эскалация привилегий, например если код доверяет полям вроде isAdmin, role, authenticated и они явно не определяются при создании объекта
- Подмена настроек безопасности или логики приложения
- XSS на клиенте, если polluted-свойства доходят до DOM-логики или шаблонов
- Отказ в обслуживании, если поломать ожидаемую структуру объектов
- В отдельных случаях — SSRF, RCE или иные критичные эффекты, если загразнение доходит до опасных участков кода и нужных gadget chain
😎 Как защититься?
- Не смешивать недоверенные объекты с внутренними структурами "как есть"
- Фильтровать и явно запрещать опасные ключи (__proto__, prototype, constructor)
- Использовать безопасные структуры данных там, где это уместно, например Map или объекты через Object.create(null)
- Использовать schema validation и allowlist полей вместо парадигмы "принимаем любой JSON"
- Обновлять библиотеки для merge / parsing / cloning, т.к. prototype pollution очень часто живет именно в зависимостях
- Проверять только собственные свойства объекта через Object.hasOwn() / hasOwnProperty, а не доверять значениям из prototype chain
- Не строить критичную логику на "если поле не задано, значит false" без явной инициализации
Стоит отдельно сказать, что просто почистить пару известных гаджетов — обычно слабая защита. Проблема не только в конкретном sink'е, а в самом факте неконтролируемого изменения прототипа. Пока сохраняется возможность загрязнять объекты, новые способы применения почти всегда найдутся.
📖 Имхо-зона
Еще одна интересная уязвимость, для качественной эксплуатации которой нужно читать много кода (JS-кода, что большинство избегает). Самое интересное при ее изучении - смотреть на борьбу защитных механизмов, созданных для противодействия ей, и того, как их обходят с помощью той же самой уязвимости. Вообще, осознание этой баги скорее всего откроет вам понимание того, как в принципе работает JavaScript (я так свое время переоткрыл для себя fetch-конструкции), так что если хотите стать крутым вебером, то рано или поздно вам нужно будет пройти через эту уязвимость. Из ресурсов, которые существуют, лучше всего это объяснено в Portswigger Web Security Academy - объяснение там мне понравилось даже больше, чем в OSWE (в защиту OSWE скажу, что там вы глубже поймете теорию этой баги).
#edu #vuln #web #prototype_pollution




Дискуссия