Операторы MongoDB в идентификаторах GridFS и строках LINQ драйверов обходят фильтры запросов

MongoDB

Сводка по множественным уязвимостям в MongoDB появилась 14 сентября. Она опирается на бюллетени безопасности MongoDB от 10 и 11 сентября. Речь идёт о драйверах почти для всех популярных языков программирования и о ядре сервера. Одни уязвимости позволяют удалённо вызвать отказ в обслуживании, другие ведут к утечке или подмене данных, третьи дают обойти политику безопасности. Пострадать может любая организация, которая держит MongoDB как основную базу: от веб-приложений на PHP и Python до корпоративных систем на Java и .NET.

Детали уязвимостей

Основная часть проблем касается GridFS, встроенного механизма MongoDB для хранения файлов. Файл разбивается на фрагменты, а метаданные и сами фрагменты лежат в двух служебных коллекциях. Драйверы строят запрос к этим коллекциям из идентификатора файла, который передаёт вызывающая сторона. До исправления значение попадало в фильтр без принудительного точного сравнения. Если вместо строки или числа передать массив либо документ с оператором запроса, сервер прочитает его как условие выборки, а не как значение. Точечный запрос после этого превращается в открытый.

Практические последствия различаются по операциям. В Ruby-драйвере чтение по такому идентификатору позволяло получить содержимое чужого файла или склеить фрагменты нескольких файлов сразу. Удаление работало грубее: коллекция метаданных чистится по одному документу, а коллекция фрагментов обрабатывается пакетно. Один вызов мог вырезать фрагменты у всех файлов в хранилище, оставив метаданные на месте. Сервер сам отклоняет идентификатор с оператором в фильтре удаления для коллекции метаданных, но к фрагментам обращаются по другому полю, и такой защиты там нет. Исправления получили драйверы для C, C++, C#, Go, Java, PHP, Python, Ruby и Rust. Версии с патчами различаются: Python-драйвер закрыт в ветке 4.18.1, Go сразу в двух, 1.17.10 и 2.9.1, Rust в 3.9.1.

Вторая группа проблем связана с интеграцией MongoDB и фреймворка Laravel. В построителе запросов вызов с тремя аргументами и знаком равенства означает строгую проверку совпадения. Если в качестве значения передать массив, драйвер встраивал его как есть, и сервер трактовал массив как документ с операторами. Приложение, которое отправляет в такой вызов необработанный пользовательский ввод, получает два сценария. Поиск по идентификатору начинал возвращать первый попавшийся документ, а удаление по идентификатору стирало коллекцию целиком. Отдельная уязвимость закрывает ту же схему в связях между документами. Значение с оператором, попавшее в поле внешнего ключа или в ключ встроенного документа, принималось как обычное условие, поэтому запрос выбирал произвольную запись, а операция над вложенными документами затрагивала все элементы сразу. В версии 5.11.0 такие значения отклоняются с исключением, при этом обычные составные идентификаторы по-прежнему принимаются.

В .NET-драйвере нашли ту же уязвимость в другом виде. Драйвер переводит запросы LINQ, язык запросов к коллекциям в коде на C#, в фильтры MongoDB. При переводе строковых операций часть символов попадала в регулярное выражение без экранирования. Обратная косая черта и закрывающая квадратная скобка не обрабатывались, а скобка в середине набора символов досрочно завершала класс. Всё, что шло после неё, сервер читал как полноценный шаблон. Атакующий, который управляет текстом, попадающим в фильтр, может переписать правило сопоставления: условие начнёт совпадать со всем, ни с чем или с выбранным образцом. Отсюда раскрытие записей, которые фильтр должен был отсеять, обновления и удаления не в том объёме, ошибки компиляции на стороне сервера и перегрузка процессора тяжёлыми шаблонами.

Java-драйвер для реактивных приложений преподнёс уязвимость иного рода. При шифровании на стороне клиента драйвер держит нативный контекст библиотеки libmongocrypt на время операции. Ключи шифрования он запрашивает у внешней системы управления ключами. Если операцию отменяют, а это происходит при разрыве соединения клиентом или по тайм-ауту, контекст освобождался в потоке отмены без проверки, что с ним всё ещё работает другой поток. Запись в освобождённую память приводит к падению виртуальной машины Java или к незаметному повреждению данных в компоненте, который хранит ключи шифрования. Разработчики подтвердили проблему на сервисе Spring Boot WebFlux: один прерванный HTTP-запрос уничтожал процесс. Задевает она реактивные потоки, а также обёртки для Kotlin и Scala. Окно уязвимости узкое, контекст освобождается только при промахе кеша ключей, но повторяется при истечении срока жизни ключа.

Меньше известно о двух других правках. В драйвере для языка C проверку размера добавили в процедуру нормализации имени пользователя во время аутентификации по протоколу SASL, который согласует учётные данные между клиентом и сервером. В ядре сервера исправили разбор констант в правилах проверки коллекций; патч перенесли в ветки 7.0, 8.0, 8.3 и 9.0.

Об эксплуатации в реальных атаках в исходных материалах не сообщается. Такие наборы патчей обычно публикуют до появления публичных эксплойтов, чтобы администраторы успели обновиться. Поэтому первое действие для владельцев MongoDB состоит в том, чтобы сверить версии драйверов и сервера с исправленными. Приложениям на Laravel нужна версия интеграции 5.11.0, сервисам на реактивном Java-драйвере 5.11.1. Тем, кто хранит файлы в GridFS, стоит обновить драйвер независимо от языка.

Общая черта всех этих уязвимостей в подстановке служебного документа с операторами туда, где ожидается простое значение. Уязвимость повторяется от драйвера к драйверу, от фреймворка к фреймворку, и каждый раз её исправляют отдельно. Пока проверки не станут едиными для всех точек, где значение встречается с фильтром, такие находки будут появляться снова.

Ссылки

Комментарии: 0