# Detecting CVE-2026-0265 at Scale: PAN-OS CAS Authentication Bypass

JWT algorithm confusion in PAN-OS pan_auth_verify lets an unauthenticated attacker forge HS256 tokens against the CAS RSA public key and log in as any user.

- Payout: Research
- Severity: high
- Program: Palo Alto PAN-OS
- Found by (vendor credit): Harsh Jaiswal from Hacktron AI and our internal security research teams
- Write-up by: Harsh Jaiswal
- Disclosed: 2026-05-22
- CVE: CVE-2026-0265
- Write-up quality: 7/10
- Original: https://bishopfox.com/blog/detecting-cve-2026-0265-at-scale-pan-os-cas-authentication-bypass
- Library page: https://bugbountylibrary.com/r/blog%3Abishopfox-com-blog-detecting-cve-2026-0265-at-scale-pan-os-cas-authentication-bypass
- Tags: area:devices domain:network impact:ato impact:internal-access impact:privesc kind:research platform:research sev:high tech:jwt tech:saml technique:source-review vuln:auth-bypass vuln:crypto

**Target:** GlobalProtect portal and management interface /SAML20/SP/ACS endpoint
**Root cause:** pan_auth_verify in libpanmp_mp.so dispatches signature verification on the attacker-controlled JWT header alg value, accepting alg=HS256 and HMACing over the PEM-encoded RSA public key fetched from CAS for RS256 verification.
**Steps:**
1. Confirm precondition: authentication profile in front of the listener has CAS attached (prelogin returns `<cas-auth>yes</cas-auth>`).
2. Obtain the CAS RSA public key, which Palo Alto's regional CAS service exposes to enrolled devices.
3. Craft a JWT with header `alg=HS256` and a payload claiming the target user (including the CSP-ID/TID tenant claim).
4. Compute the HMAC using the PEM-encoded RSA public key bytes as the HMAC key.
5. Submit the forged token to `/SAML20/SP/ACS`; GlobalProtect rewrites to `/saml/acs.esp` (gpsvc) for VPN session, management rewrites to `/unauth/php/DualLogin.php` (authd) for admin.
**How found:** Independent analysis of patched binaries confirmed the reporter's root cause; the detection angle came from noticing the anonymous prelogin response leaks both the CAS precondition (`<cas-auth>`) and an embedded JWT whose `PanOSversion` claim gives authoritative build info.
**Exploitation:** Attacker reads the CAS-exposed RSA public key, pre-computes an HMAC over a forged HS256 JWT for any user, and submits it to the shared /SAML20/SP/ACS path. On the GlobalProtect portal this yields a VPN session as the forged user; on the management interface it yields a full PAN-OS administrator (config write, CLI shell, RADIUS keys, private keys, Panorama pivot).
**Escalation:** Management-interface surface escalates from auth bypass to full admin: config modification, web terminal CLI shell, credential/key theft, and pivot to Panorama.
**Insight:** PAN-OS prelogin hands anonymous visitors a self-issued JWT containing authoritative version and tenant claims — a free fingerprint for both exploitability preconditions and the parameters an attacker needs; also, the same external path /SAML20/SP/ACS routes to different daemons by Nginx server block, so one URL has two very different blast radii.

## Proof of concept (copied exactly from the original report)

**Anonymous prelogin probe and CAS tag**

```
The probe is a single anonymous GET /global-protect/prelogin.esp. No authentication, no body modification, no state change. The only caveat is that this endpoint can be configured to require an mTLS client certificate, which not only interferes with detection but also mitigates exploitation by unauthorized attackers. 

## Factor 1: CAS attachment 

The prelogin response is a small XML document that the GlobalProtect agent normally consumes before submitting credentials. Among its fields: 

<cas-auth>yes</cas-auth>
```

**JWT version claim extraction steps**

```
Base64-decode the contents of the <saml-request> XML element to get the auto-submit HTML form. 
- Pull the JWT out of the form's hidden input (<input name="Token" value="HEADER.PAYLOAD.SIGNATURE">). 
- Base64url-decode the JWT's middle segment to get a JSON claim set. PanOSversion lives there.
```

**Version verdict table examples**

```
11.1.6-h32 → base 11.1.6's patch is h32. h32 ≥ h32 → not vulnerable. 
- 11.1.6-h23 → same base, same patch. h23 < h32 → vulnerable. 
- 11.2.7-h9 → base 11.2.7's patch is h13. h9 < h13 → vulnerable. 
- 11.1.15 → the entire base is patched. → not vulnerable.
```

