Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-0257 — 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. | Kitploit
Tools/GitHubGitHub/tushargurav28/cve-2026-0257
Vulnerability AnalysisExploitationWeb Application ExploitationNetwork SecurityCryptographyPenetration TestingAuthenticationRed Teaming
GitHubtushargurav28/cve-2026-0257

CVE-2026-0257

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.

3113 months agoNot yet reviewed
View Repository

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-0257: GlobalProtect Authentication Bypass

Overview

This exploit achieves unauthenticated VPN access to a Palo Alto GlobalProtect gateway/portal by forging an authentication cookie using only the server's publicly available TLS certificate.


The Core Vulnerability (Why It Works)

GlobalProtect uses a pre-authentication cookie (portal-userauthcookie) to allow clients to authenticate. Here's the fatal flaw:

Normal Flow:
  1. Client authenticates (username + password)
  2. Server generates a cookie → encrypts it with server's RSA PUBLIC key
  3. Client stores the encrypted cookie
  4. On reconnect, client sends the cookie → server decrypts with PRIVATE key → trusts it

The Bug:
  The server ONLY checks if the cookie decrypts successfully with its private key.
  It does NOT verify WHO encrypted it or if the plaintext content is legitimate.

Since the RSA public key is embedded in the server's TLS certificate (publicly accessible to anyone who connects), any attacker can:

  1. Grab the public key from the TLS cert
  2. Forge a cookie with any username
  3. Encrypt it with the public key
  4. Send it to the server → server decrypts it → accepts it as valid

This is a textbook broken authentication flaw — using encryption where a digital signature or HMAC was needed.


The Exploit Chain (5 Steps)

flowchart TD
    A["Step 1: Raw TCP Connect"] --> B["Step 2: Send Crafted TLS ClientHello"]
    B --> C["Step 3: Parse ServerHello → Extract DER Certificates"]
    C --> D["Step 4: Walk ASN.1 to Extract RSA Public Key"]
    D --> E["Step 5: Forge PKCS#1 v1.5 Encrypted Cookie"]
    E --> F["Step 6: POST to /ssl-vpn/login.esp"]
    F --> G{"Server Decrypts Cookie"}
    G -->|"Valid plaintext"| H[" Auth Bypass — VPN Access Granted"]
    G -->|"Invalid"| I[" Rejected"]

Step 1: Build a Raw TLS ClientHello

Why raw? We need the server's certificate in DER (raw binary) format. Python's ssl module completes the full TLS handshake internally and doesn't expose the raw cert bytes the same way. By doing a raw TCP connection and sending a hand-crafted ClientHello, we can intercept the server's response at the byte level.

Wire Format

A TLS record looks like this:

┌──────────────────────────────────────────────────┐
│ TLS Record Header (5 bytes)                      │
│ ┌──────┬──────────┬────────────┐                 │
│ │ Type │ Version  │   Length   │                 │
│ │ 0x16 │ 0x03 01  │  2 bytes   │                 │
│ │(Hshk)│(TLS 1.0) │            │                 │
│ └──────┴──────────┴────────────┘                 │
│                                                  │
│ Handshake Message                                │
│ ┌──────┬────────────┬─────────────────────────┐  │
│ │ Type │   Length   │      Body               │  │
│ │ 0x01 │  3 bytes   │  (ClientHello)          │  │
│ │(CHlo)│            │                         │  │
│ └──────┴────────────┴─────────────────────────┘  │
└──────────────────────────────────────────────────┘

Code: build_hello()

The ClientHello body contains:

FieldValuePurpose
Version0x03 0x03 (TLS 1.2)Tell server we speak TLS 1.2
Random4-byte timestamp + 28 random bytesNonce for the handshake
Session ID0x00 (empty)No session resumption
Cipher Suites9 suites including TLS_RSA_WITH_AES_128_CBC_SHAKey: we include RSA-only ciphers to force the server to use its RSA cert
Compression0x00 (none)Required

Extensions included:

ExtensionIDPurpose
SNI (Server Name Indication)0x0000Tell the server which hostname we're connecting to
Signature Algorithms0x000DAdvertise which sig algorithms we support
Supported Groups0x000AEC curves we support (P-256, P-384, P-521)
EC Point Formats0x000BUncompressed EC points

[!NOTE] The cipher suites intentionally include RSA key exchange ciphers (0x002F = TLS_RSA_WITH_AES_128_CBC_SHA). This nudges the server to respond with its RSA certificate rather than an ECDSA one — which is critical because the exploit only works with RSA.


Step 2: Receive & Parse the Server's Response

After sending the ClientHello, the server sends back multiple TLS records:

Server Response:
  ┌─────────────────┐
  │ ServerHello      │  (handshake type 2)
  ├─────────────────┤
  │ Certificate      │  (handshake type 11) ← WE WANT THIS
  ├─────────────────┤
  │ ServerKeyExchange│  (handshake type 12, optional)
  ├─────────────────┤
  │ ServerHelloDone  │  (handshake type 14) ← STOP SIGNAL
  └─────────────────┘

Code: parse_certs()

Phase 1 — Strip TLS record headers:

Each TLS record has a 5-byte header: [type(1)] [version(2)] [length(2)]. The code scans through all records, and for any with type == 22 (Handshake), it concatenates their payloads:

while i + 5 <= len(data):
    t = data[i]                              # content type
    rl = (data[i + 3] << 8) | data[i + 4]   # record length
    if t == 22:                              # Handshake
        hs.extend(data[i + 5: i + 5 + rl])  # grab payload
    i += 5 + rl                              # next record

Phase 2 — Find the Certificate message (type 11):

Inside the handshake stream, each message has a 4-byte header: [type(1)] [length(3)]. We scan for type == 11:

while j + 4 <= len(hs):
    ht = hs[j]                                           # handshake type
    hl = (hs[j+1] << 16) | (hs[j+2] << 8) | hs[j+3]    # 3-byte length
    if ht == 11:  # Certificate!
        # Parse the certificate list inside

Phase 3 — Extract individual DER certificates:

The Certificate message contains a list of certificates, each prefixed by a 3-byte length:

Certificate Message Body:
┌───────────────────────────────────┐
│ Total Certs Length (3 bytes)      │
├───────────────────────────────────┤
│ Cert 1 Length (3 bytes)           │
│ Cert 1 DER data (variable)       │
├───────────────────────────────────┤
│ Cert 2 Length (3 bytes)           │
│ Cert 2 DER data (variable)       │
├───────────────────────────────────┤
│ ...                               │
└───────────────────────────────────┘

In our test run, we got 3 certificates (leaf cert, intermediate CA, root CA).

Code: has_done()

This function scans for ServerHelloDone (handshake type 14), which tells us the server is done sending and we can stop reading.


Step 3: Parse the X.509 Certificate (ASN.1/DER)

X.509 certificates are encoded in DER (Distinguished Encoding Rules), which is a binary format based on ASN.1 (Abstract Syntax Notation One).

ASN.1 TLV (Tag-Length-Value) Format

Every element in DER is:

┌─────┬────────┬───────────────────┐
│ Tag │ Length │ Value (payload)   │
│ 1B  │ 1-5B  │ variable          │
└─────┴────────┴───────────────────┘

Length encoding:

  • If byte < 0x80: length is that byte directly (short form)
  • If byte ≥ 0x80: low 7 bits = number of following bytes that encode the length (long form)
# Example: length byte = 0x82 → 2 more bytes follow
# Next 2 bytes: 0x06 0x4F → length = 0x064F = 1615 bytes

Code: rd_tl()

Download Tool