FlawOpen

flawopen.com/Teardowns/cve-2026-77244-mcp-atlassian-http-auth-bypass

● CVE-2026-77244 · CVSS 10.0[1] · Критический
Исследования · FlawOpen

CVE-2026-77244: неаутентифицированный HTTP-доступ к инструментам mcp-atlassian

CVE-2026-77244 позволяла неаутентифицированным вызывающим сторонам обращаться к HTTP-обработчикам инструментов проекта сообщества mcp-atlassian, которые могли использовать учётные данные Jira или Confluence из окружения оператора. Основное исправление вышло в 0.22.0; пользователям SSE нужна версия 0.23.1 или новее из-за отдельной связанной ошибки транспорта.[1], [4], [5], [6]

Вектор CVSSCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N[1]
💡 Простыми словами (ELI5)

На стойке регистрации должны проверять пропуск посетителя, прежде чем открыть дверь для сотрудников. В прежнем процессе при отсутствии пропуска сотрудник стойки одалживал посетителю мастер-ключ. Ошибка объединила две проблемы: HTTP-запрос мог попасть к инструментам без проверенной личности, а инструменты могли использовать общие учётные данные Atlassian оператора. Исправление отклоняет неаутентифицированные HTTP-запросы и требует явного разрешения такого fallback учётных данных.[1], [3]

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

Транспорт MCP
Канал передачи запросов Model Context Protocol между клиентом и сервером. Эта CVE касается HTTP-транспорта mcp-atlassian; stdio по этому бюллетеню не затронут.[1], [4]
Fallback глобальных учётных данных
Использование учётных данных Jira или Confluence из окружения сервера, если у запроса нет собственной личности. При удалённо доступном HTTP это может превратить анонимный запрос в действие от имени оператора.[1], [4]
Граница аутентификации
Серверная проверка, устанавливающая личность до передачи запроса защищённым обработчикам инструментов. Сетевой доступ, непустой токен или предполагаемое доверие к прокси сами по себе личность не подтверждают.[1], [7]
CWE-287
Неправильная аутентификация: приложение недостаточно подтверждает заявленную личность перед предоставлением доступа.[1], [7]
CWE-303
Проверяющий компонент реализует аутентификацию некорректно: он принимает любой непустой токен, не проверяя его действительность.[1], [9]
CWE-862
HTTP-запрос мог попасть к привилегированным обработчикам инструментов без проверки авторизации, требующей подтверждённую личность пользователя.[1], [10]

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

  • Ссылка CWE для слабого места, описанного в этом руководстве: CWE-287 CWE-303 CWE-862

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

Уведомление GitHub описывает цепочку в проекте сообщества mcp-atlassian, а не уязвимость продукта Atlassian. До версии 0.22.0 HTTP-middleware могла пропустить запрос без пользовательской личности к обработчикам инструментов. Если развёртывание использовало учётные данные оператора из переменных окружения, JiraFetcher или ConfluenceFetcher мог перейти на эти глобальные учётные данные. В уведомлении также описан верификатор непрозрачных токенов, принимавший любую непустую строку; это не доказывает реальную личность вызывающего. Защита должна одновременно проверять личность запроса и выбор учётных данных: отсутствующий или непроверенный пользователь не должен наследовать привилегированные данные оператора. Исправление 0.22.0 возвращает HTTP 401 для неаутентифицированных streamable-HTTP POST MCP и запрещает глобальный fallback в HTTP-контексте, если ALLOW_GLOBAL_CRED_FALLBACK=true явно не задан для однопользовательского развёртывания. Поведение stdio не изменилось. Отдельно GHSA-5j8j-256g-vvp5 описывает несовпадение путей SSE-middleware, исправленное в 0.23.1; это не CVE-2026-77244. Обновите SSE до 0.23.1 или новее и не считайте, что патч CVE в 0.22.0 исправляет и это отдельное уведомление. Уведомление не подтверждает эксплуатацию; статья не утверждает, что произошёл реальный взлом. В уведомлении CVE для этой цепочки указаны CWE-287, CWE-303 и CWE-862 — ошибки аутентификации, проверки токена и авторизации. В уведомлении репортёром указан crazydude123.[1], [3], [4], [5], [6], [7], [9], [10]

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

Шаг 1

HTTP-endpoint MCP доступен по сети

В развёртывании работает сервер сообщества mcp-atlassian через HTTP, и неаутентифицированная сторона может обратиться к транспорту. Развёртывания только со stdio находятся вне затронутого этой CVE пути.[1]

Шаг 2

Запрос доходит до инструмента без проверенного пользователя

До 0.22.0 HTTP-запрос без собственной личности мог пройти middleware вместо отказа на транспортной границе. В уведомлении также сказано, что непустая строка не проверялась независимо как личность.[1], [3]

Шаг 3

Инструмент выбирает общие учётные данные оператора

Если собственной личности нет, а в окружении сервера заданы данные Jira или Confluence, fetcher может использовать глобальные учётные данные. Запрос получает права включённых инструментов оператора; точный эффект зависит от его привилегий и конфигурации.[1], [3]

Шаг 4

Отклонять на транспорте и безопасно отказывать при выборе учётных данных

Версия 0.22.0 возвращает HTTP 401 для неаутентифицированных streamable-HTTP запросов и блокирует глобальный fallback в HTTP-контексте без явного разрешения. Для SSE установите 0.23.1 или новее: там исправлена отдельная ошибка путей.[1], [4], [5], [6]

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

Это сокращённое представление потока, а не дословная копия функций. В upstream-патче добавлен HTTP 401 на транспортной границе и второй запрет перед возвратом глобального fetcher в HTTP-контексте; оба обхода требуют явного ALLOW_GLOBAL_CRED_FALLBACK.[1], [3], [4]

УЯЗВИМАЯ РЕАЛИЗАЦИЯ
# Упрощенное представление затронутого потока управления.
async def handle_request(scope, ctx):
    user_identity = scope.get("state", {}).get("user_atlassian_auth_type")
    # Отсутствие идентификатора не отклонялось на границе HTTP.
    return await call_tool_handler(ctx, user_identity=user_identity)

def get_fetcher(user_identity, global_config):
    if user_identity:
        return fetcher_for_user(user_identity)
    # В качестве запасного варианта используются учетные данные оператора из окружения.
    return fetcher_from_config(global_config)
БЕЗОПАСНЫЙ ИСПРАВЛЕННЫЙ ВАРИАНТ
# Краткое представление защитных мер, объединенных в версии 0.22.0.
async def handle_request(scope, ctx):
    user_identity = scope.get("state", {}).get("user_atlassian_auth_type")
    if is_mcp_http_post(scope) and not user_identity and not allow_global_fallback():
        return http_401("Authentication required")
    return await call_tool_handler(ctx, user_identity=user_identity)

def get_fetcher(user_identity, global_config, *, in_http_context):
    if user_identity:
        return fetcher_for_user(user_identity)
    if in_http_context and not allow_global_fallback():
        raise PermissionError("Refusing operator credentials for an unauthenticated HTTP request")
    return fetcher_from_config(global_config)

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

Источники