Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-0257 — Palo Alto Networks PAN-OS는 GlobalProtect 포털과 게이트웨이의 결함으로 인한 인증 우회 취약점을 포함하고 있어, 공격자가 승인되지 않은 VPN 연결을 설정할 수 있습니다. 이 취약점을 악용하려면 포털 또는 게이트웨이에 대한 네트워크 접근이 필요합니다. | Kitploit
도구/GitHubGitHub/tushargurav28/cve-2026-0257
Vulnerability AnalysisExploitationWeb Application ExploitationNetwork SecurityCryptographyPenetration TestingAuthenticationRed Teaming
GitHubtushargurav28/cve-2026-0257

CVE-2026-0257

Palo Alto Networks PAN-OS는 GlobalProtect 포털과 게이트웨이의 결함으로 인한 인증 우회 취약점을 포함하고 있어, 공격자가 승인되지 않은 VPN 연결을 설정할 수 있습니다. 이 취약점을 악용하려면 포털 또는 게이트웨이에 대한 네트워크 접근이 필요합니다.

3113개월 전아직 검토되지 않음
저장소 보기

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-0257: GlobalProtect 인증 우회

개요

이 익스플로잇은 서버의 공개적으로 접근 가능한 TLS 인증서만 사용하여 인증 쿠키를 위조함으로써 Palo Alto GlobalProtect 게이트웨이/포털에 비인증 VPN 접근을 달성합니다.


핵심 취약점 (작동 원리)

GlobalProtect는 클라이언트가 인증할 수 있도록 사전 인증 쿠키(portal-userauthcookie)를 사용합니다. 치명적인 결함은 다음과 같습니다:

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. 임의의 사용자 이름으로 쿠키를 위조
  3. 공개 키로 쿠키를 암호화
  4. 서버에 전송 → 서버가 복호화 → 유효한 것으로 수락

이것은 전형적인 끊어진 인증(broken authentication) 결함입니다 — 디지털 서명이나 HMAC이 필요했던 곳에 암호화를 사용한 것입니다.


익스플로잇 체인 (5단계)

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단계: 원시 TLS ClientHello 구축

왜 원시(raw) 방식인가? 서버의 인증서를 DER(원시 바이너리) 형식으로 가져와야 하기 때문입니다. Python의 ssl 모듈은 내부적으로 전체 TLS 핸드셰이크를 완료하며 동일한 방식으로 원시 인증서 바이트를 노출하지 않습니다. 원시 TCP 연결을 수행하고 수작업으로 제작된 ClientHello를 전송함으로써 바이트 수준에서 서버의 응답을 가로챌 수 있습니다.

와이어 형식

TLS 레코드는 다음과 같습니다:

┌──────────────────────────────────────────────────┐
│ 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 SuitesTLS_RSA_WITH_AES_128_CBC_SHA 포함 9개 스위트핵심: RSA 전용 암호화 스위트를 포함하여 서버가 RSA 인증서를 사용하도록 강제
Compression0x00 (없음)필수

포함된 확장:

확장ID용도
SNI (서버 이름 표시)0x0000연결하려는 호스트 이름을 서버에 알림
서명 알고리즘0x000D지원하는 서명 알고리즘을 광고
지원 그룹0x000A지원하는 EC 곡선 (P-256, P-384, P-521)
EC 포인트 형식0x000B비압축 EC 포인트

[!NOTE] 암호화 스위트에는 의도적으로 RSA 키 교환 암호화(0x002F = TLS_RSA_WITH_AES_128_CBC_SHA)가 포함되어 있습니다. 이는 서버가 ECDSA 인증서 대신 RSA 인증서로 응답하도록 유도합니다 — 익스플로잇이 RSA에서만 작동하므로 이는 매우 중요합니다.


2단계: 서버 응답 수신 및 파싱

ClientHello 전송 후, 서버는 여러 TLS 레코드를 다시 전송합니다:

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)인 레코드의 페이로드를 연결합니다:

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) 찾기:

핸드셰이크 스트림 내부에서 각 메시지에는 4바이트 헤더가 있습니다: [type(1)] [length(3)]. 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

3단계 — 개별 DER 인증서 추출:

Certificate 메시지에는 각각 3바이트 길이 접두사가 붙은 인증서 목록이 포함됩니다:

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(핸드셰이크 타입 14)을 스캔하며, 이는 서버가 전송을 마쳤음을 알려주므로 읽기를 중단할 수 있습니다.


3단계: X.509 인증서 파싱 (ASN.1/DER)

X.509 인증서는 ASN.1(Abstract Syntax Notation One)을 기반으로 하는 바이너리 형식인 DER(Distinguished Encoding Rules)로 인코딩됩니다.

ASN.1 TLV (태그-길이-값) 형식

DER의 모든 요소는 다음과 같습니다:

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

길이 인코딩:

  • 바이트가 0x80 미만: 길이는 해당 바이트 그대로 (단문 형식)
  • 바이트가 0x80 이상: 하위 7비트 = 길이를 인코딩하는 후속 바이트 수 (장문 형식)
# Example: length byte = 0x82 → 2 more bytes follow
# Next 2 bytes: 0x06 0x4F → length = 0x064F = 1615 bytes

코드: rd_tl()

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 인증서 구조

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 트리를 탐색합니다:

도구 다운로드