FlawOpen

flawopen.com/AI Security/berriai-litellm-ghsa-7hp6-salt-key-session-token-privilege-escalation

● Соответствие FlawOpen: CWE-345 · CVSS 9.9 · Критический
Исследования · FlawOpen

LiteLLM: повышение привилегий из-за повторного использования salt key

LiteLLM применял один путь шифрования и к секретам, и к токенам сеанса интерфейса и CLI. Аутентифицированный internal_user мог провести контролируемые им данные идентичности через этот путь и получить proxy_admin; эта роль позволяла обратиться к выполнению команд через MCP stdio. Исправление привязывает токен к отдельному префиксу и аутентифицируемому контексту AES-GCM. CVE и подтверждённой эксплуатации в реальных атаках не указано.[1], [2]

💡 Простыми словами (ELI5)

В здании одной и той же печатью запечатывают конверт для хранения и выдают пропуск администратора. Посетитель может повлиять на содержимое конверта, а охранник позднее принимает его за настоящий пропуск. Исправление LiteLLM задаёт пропускам сеанса отдельную метку и криптографически связывает её с печатью.[1], [2]

Ключевые понятия и термины

Salt key
Настроенный в LiteLLM ключевой материал, который используют функции шифрования. Уязвимость повторно использовала этот путь без привязки шифротекста к назначению.[1], [2]
Аутентифицируемые дополнительные данные (AAD)
Данные, например префикс типа токена, которые AES-GCM аутентифицирует вместе с шифротекстом. Изменение или отсутствие этих данных нарушает проверку.[2]
internal_user и proxy_admin
Роли LiteLLM. В бюллетене сказано, что аутентифицированный internal_user мог повысить роль до proxy_admin.[1]
MCP stdio
Транспорт Model Context Protocol, запускающий локальный процесс и передающий сообщения через стандартные ввод и вывод. Бюллетень указывает, что proxy_admin мог через него выполнять команды.[1]
CWE-345
Недостаточная проверка подлинности данных. Это основная редакционная классификация FlawOpen; в бюллетене также указаны CWE-269 и CWE-441.[1], [5], [6], [7]

Связи со стандартами

  • Концептуальное соответствие FlawOpen для MITRE CWE; ни поставщик, ни запись CVE не присваивали этот идентификатор слабого места данной исследовательской демонстрации. CWE-345

Пошаговый сценарий атаки

Шаг 1

Пользователь с низкими привилегиями обращается к управлению ключами

Начальное условие бюллетеня — аутентифицированная роль internal_user. Анонимный доступ не нужен, но эта роль уже должна быть у пользователя.[1]

Шаг 2

Метаданные создают шифротекст через общий путь

В бюллетене сказано, что данные идентичности из метаданных ключа могли шифроваться и возвращаться. Ошибка состояла в том, что затем этот шифротекст принимался как другой тип токена.[1], [2]

Шаг 3

Обработчик сеанса принимает шифротекст иного назначения

До исправления шифрование значений и проверка сеанса использовали общий helper и путь ключа без аутентифицированной привязки назначения. Данные могли интерпретироваться как proxy_admin.[1], [2]

Шаг 4

Административная роль открывает MCP stdio

По бюллетеню, роль proxy_admin позволяла выполнять команды через MCP stdio. Это возможность локального процесса, а не команда, запущенная самой моделью.[1]

Шаг 5

Привязка назначения отклоняет повторное использование

Исправление использует litellm_login_ как AAD AES-GCM и отдельный декодер. Установите исправленную версию; отключение EXPERIMENTAL_UI_LOGIN — временная мера с функциональными ограничениями.[1], [2], [3], [4]

Анализ первопричины

Бюллетень BerriAI GHSA-7hp6-4w63-5g45 описывает смешение назначений аутентификации. LiteLLM использовал один helper шифрования значений для сохранённых секретов и токенов сеанса UI/CLI; проверка сеанса расшифровывала значения тем же путём ключа. Аутентифицированный internal_user мог запросить API-ключ с сериализованными данными идентичности в метаданных; путь управления шифровал и возвращал это значение. Если предъявить шифротекст как bearer token, обработчик сеанса мог расшифровать его и принять поддельную роль proxy_admin. Эта роль открывала доступ к выполнению команд через MCP stdio. Исправление upstream f7cd90f меняет выпуск и проверку: префикс litellm_login_, AAD AES-GCM, связанный с этим префиксом, и отдельный декодер. Salt key остаётся ключевым материалом; границей безопасности служат аутентифицированное назначение токена и соответствующий декодер. Шифротекст для хранения больше не проходит как токен сеанса. Бюллетень указывает CVSS 3.1 9.9 Critical; версии после 1.91.0 затронуты по умолчанию, а 1.87.0–1.90.x — только при EXPERIMENTAL_UI_LOGIN=true. Исправленные выпуски по веткам: 1.100.4, 1.101.3, 1.102.2, 1.103.1 и 1.104.0rc2. Временная мера EXPERIMENTAL_UI_LOGIN=false отключает уязвимый путь, но также нарушает CLI SSO и вход Claude Code gateway. CVE не указан, эксплуатация в реальных атаках не подтверждена. Фрагмент кода показывает изменение границы аутентификации upstream, а не эксплойт и не полную реализацию.[1], [2], [3], [4]

Исходный код: Уязвимый vs Защищённый вариант

Исправление upstream заменяет общие функции для значений при выпуске и проверке токена сеанса. encrypt_bearer_token аутентифицирует префикс как AAD AES-GCM; decrypt_bearer_token требует тот же префикс и данные. Поэтому шифротекст хранения нельзя использовать как токен входа. Это сокращённый фрагмент commit, а не самостоятельная система аутентификации. Общий ключ salt сам по себе не доказывает наличие отдельного ключа для каждой цели; подтвержденное здесь разделение обеспечивают аутентифицированный тип токена и соответствующий декодер.[2], [3]

Фрагмент исходного кода: litellm/proxy/auth/auth_checks.py и litellm/proxy/common_utils/encrypt_decrypt_utils.py — сокращённые фрагменты upstream commit f7cd90f

УЯЗВИМАЯ РЕАЛИЗАЦИЯ
# До исправления шифротекст сохраненных значений также выпускался как токен сеанса.
return encrypt_value_helper(valid_token.model_dump_json(exclude_none=True))

# При проверке токена сеанса использовалась общая функция расшифровки значений.
decrypted_token = decrypt_value_helper(hashed_token, key="ui_hash_key")
БЕЗОПАСНЫЙ ИСПРАВЛЕННЫЙ ВАРИАНТ
# После исправления у токенов сеанса есть собственный тип и AAD.
LITELLM_SESSION_TOKEN_PREFIX = "litellm_login_"
return encrypt_bearer_token(
    valid_token.model_dump_json(exclude_none=True),
    prefix=LITELLM_SESSION_TOKEN_PREFIX,
)

# Для проверки используется соответствующий декодер.
decrypted_token = decrypt_bearer_token(
    hashed_token, prefix=LITELLM_SESSION_TOKEN_PREFIX
)

Чек-лист по защите системы для инженеров

Источники

← AI Security Directory →