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

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

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

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-88776 — CVE-2026-88776에 대한 기술 분석 문서로, NetScaler nsppe의 Oracle TNS 레코드 파서에서 발생하는 인증 전 정수 언더플로우로 인해 범위를 벗어난 쓰기와 패킷 엔진 크래시가 발생하는 취약점을 다룹니다. | Kitploit
도구/GitHubGitHub/scyrix-llc/cve-2026-88776
Memory ForensicsVulnerability AnalysisExploitationReverse EngineeringNetwork SecurityBinary AnalysisPapers & Research
GitHubscyrix-llc/cve-2026-88776

CVE-2026-88776

CVE-2026-88776에 대한 기술 분석 문서로, NetScaler nsppe의 Oracle TNS 레코드 파서에서 발생하는 인증 전 정수 언더플로우로 인해 범위를 벗어난 쓰기와 패킷 엔진 크래시가 발생하는 취약점을 다룹니다.

저장소 보기
21128일 전아직 검토되지 않음

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-88776: NetScaler nsppe (인증 전 OOB 쓰기)

DataStream / Oracle TNS 레코드 파서의 정수 언더플로

요약

NetScaler는 Oracle 데이터베이스를 로드 밸런싱할 수 있습니다. ORACLE 타입 vserver를 지정하면 패킷 엔진이 Oracle TNS 프로토콜을 직접 구사하기 시작하며, 내장 핸들러로 클라이언트 트래픽을 파싱합니다. 이 핸들러는 연결마다 작은 상태 머신을 유지하여 TNS 레코드 내에서 자신이 어디에 있는지 기억합니다.

그 상태 중 하나는 "레코드 중간에 있으며, 더 많은 바이트가 들어오고 있다"는 의미입니다. 다음 세그먼트가 도착하면 핸들러는 그 길이를 읽고 10을 뺍니다. 모든 세그먼트의 10바이트는 헤더이기 때문입니다.

세그먼트가 10바이트보다 짧으면 어떻게 되는지 아무도 묻지 않았습니다.

그런 세그먼트를 보내십시오. 1~9바이트의 짧은 세그먼트가 해당 상태로 진입하면 길이가 음수가 되는데, 길이가 부호 없는 16비트 레지스터에 저장되어 있으므로 음수가 되지 않습니다. 대신 최대 약 64KB에 달하는 거대한 값으로 래핑됩니다. 그런 다음 핸들러는 여러분의 패킷에서 그만큼의 데이터를 .bss에 있는 고정된 0x748바이트 스크래치 버퍼로 기꺼이 memcpy합니다. 엔진은 PIE가 아니므로 덮어써지는 주소는 절대 움직이지 않습니다.

로그인할 필요도 없습니다. vserver 뒤에 데이터베이스가 있을 필요조차 없습니다. 어플라이언스가 TNS 핸드셰이크에 직접 응답하기 때문입니다. 공격 전체가 하나의 TCP 대화입니다.

두 가지 측면 모두 라이브로 시연되었습니다:

  • 래핑된 길이를 발사하면 패킷 엔진이 SIGSEGV로 죽어 어플라이언스의 데이터플레인이 약 1분간 다운됩니다. 결정적이고, 반복 가능하며, 루프 가능합니다.
  • 길이를 조정하면 조용한 버전이 됩니다. 공격자 바이트가 스크래치 버퍼 옆에 위치한 전역 변수 위에 덮어써지고 엔진은 계속 실행됩니다. 이는 단순한 크래시가 아닌 쓰기 프리미티브입니다.

근본 원인

상태 머신과 이를 조종하는 방법

핸들러는 연결별 상태를 [pcb+0x15a]에 저장합니다. 대부분의 상태는 지루합니다. 여기서 중요한 것은 0x1F4로, "레코드 중간, 계속 대기 중"을 의미하며, 여러분은 평범한 프로토콜 바이트만으로 연결을 이 상태로 유도합니다:

record type 6 -> deep-parser state 6 -> byte 0x03 (TTIFUN)
  -> state 0x126 -> TTC function byte 0x5e (OALL8)
  -> jump table 0x23589e0, index 0x59 -> writer at bfe27b:
       [pcb+0x15a] = 0x1F4

해당 연결의 다음 패킷은 0x1F4에서 디스패치되어 bd49a7을 거쳐 bd5362로 진입한 뒤 여기에 도달합니다:

bd5432: memset(0x30a7e60, 0, 0x748)    ; scratch region (ends 0x30a85a8)
bd5445: mov   rcx,[rbp-0x208]          ; inbound packet buffer
bd544c: movzx eax,WORD [rcx+0xf2]      ; u16 wire length of the segment
bd5453: add   eax,0xfffffff6           ; len = len - 10   <== THE UNDERFLOW
bd5456: mov   WORD [0x30a8476],ax      ; wrapped length PERSISTED to .bss
bd545d: movzx edx,ax                   ; memcpy length (1..9 -> 0xfff7..0xffff)
bd5460: mov   rsi,[rcx+0xe8]
bd5467: add   rsi,0xa                  ; src = payload + 10
bd546b: mov   edi,0x30a8075            ; dst = FIXED .bss global
bd5470: call  1b075e0                  ; memcpy: up to ~64 KB OOB write
bd5475: mov   BYTE [0x30a8478],1

문장처럼 읽으세요. 세그먼트의 길이를 가져옵니다. 10을 뺍니다. 결과를 저장하고, memcpy에 넘기고, 끝. 어느 시점에도 "길이 빼기 10"이 여전히 말이 되는 숫자인지 묻는 곳은 없습니다.

존재하지 않는 가드

우리는 이 함수에서 그 길이 필드에 대한 모든 참조를 세었습니다. 서른 개입니다. 정확히 하나만 검사이며, bd4083에 있고, 그것이 하는 일이라곤 0과 비교하는 것뿐입니다. 길이가 0인 세그먼트는 거부됩니다. 1에서 9까지를 포함한 다른 모든 길이는 곧바로 뺄셈으로 들어갑니다.

코드는 상태 0x1F4가 실행될 때쯤이면 상태 머신이 충분히 완전한 레코드를 전달했을 것이라고 분명히 믿고 있습니다. 상태 머신은 그런 것을 약속한 적이 없습니다. 자체 패킷으로 도착한 미달 세그먼트는 모든 디스패치 조건을 만족하고 다섯 바이트 부족한 채로 나타납니다.

정말로 짜증나는 부분은 벤더가 이 버그 부류를 알고 있다는 것입니다. 바로 같은 바이너리의 다른 곳에서 sub_119c940은 동일한 길이 빼기 상수 연산을 수행하고 cmp eax,0x3f9 / ja로 이를 가드합니다. 안전한 패턴이 바로 거기 있었습니다. 이 지점은 그저 그것을 사용하지 않았을 뿐입니다.

랩어라운드 시 발생하는 일

목적지 0x30a8075는 0x748바이트 크기의 스크래치 영역 안에 있으며, 그 앞에는 정당한 공간이 0x533바이트뿐입니다. 랩된 복사는 그것을 넘어 옆의 DataStream 및 negotiation 전역 변수로 당신의 바이트를 쏟아붓습니다. 그리고 랩된 길이 자체는 0x30a8476에 영속화되며, 이후 코드가 그것을 다시 읽어 신뢰합니다. 그것은 memcpy에서 한 단계 떨어진 두 번째 오염 경로입니다.


트리거

설정은 표준 DataStream 배포의 한 줄짜리 구성입니다:

add lb vserver v ORACLE <ip> 1521

백엔드가 필요하지 않습니다. 어플라이언스는 TNS를 직접 종료하고 자체 Accept 패킷을 구성하며, "Netscaler" 벤더 문자열까지 포함합니다.

대화는 네 단계로 진행됩니다:

1. TNS Connect (version 0x13A)
     <- the appliance answers Resend(11), then Accept(2), 32 bytes, v0x139

2. one complete, benign TTC round-trip
     (some builds like to glue your runt onto the previous segment;
      this resets the segmentation so the runt lands alone)

3. the arming segment: a TNS Data packet whose declared length is
     actual + 32, carrying TTC bytes {03 5E} (TTIFUN / OALL8)
     -> the state machine latches [pcb+0x15a] = 0x1F4, mid-record

4. the runt: 1-9 bytes, sent as its own TCP segment
     -> passes the zero check, dispatches on 0x1F4
     -> length - 10 wraps
     -> memcpy(0x30a8075, payload+10, ~0xFFF6)
     -> the packet engine dies

1~3단계는 순수한 대화입니다. 4단계는 5바이트입니다.


관찰된 크래시 동작

이는 14.1-73.30 어플라이언스에서 동일한 장비, 동일한 빌드로 두 번의 독립적인 실행에서 두 차례 발생했습니다. 두 번 모두 패킷 엔진이 SIGSEGV와 함께 일치하는 코어 덤프를 생성했고, ns.log에는 해당 시간대에 시그널 11이 기록되었으며, 데이터플레인은 약 60초간의 웜 재시작 동안 정지 상태에 있었습니다.

음성 대조군은 두 번의 실행 모두 통과했습니다. 동일한 대화를 실행하되 마지막 세그먼트를 일관성 있는 10바이트로 만들면 아무 일도 일어나지 않습니다. 엔진은 이를 처리하고 계속 진행합니다. 문제가 되는 유일한 변수는 런트(runt) 길이입니다.

도구 다운로드