콘텐츠로 이동하기
위협 인텔리전스

Citrix NetScaler ADC 및 Gateway 어플라이언스 실제 공격 대응 가이드

2026년 9월 29일
Mandiant

Google Threat Intelligence Group

Mandiant Services

Stop attacks, reduce risk, and advance your security.

Contact Mandiant

해당 블로그의 원문은 2026년 9월 30일 Google Cloud 블로그(영문)에 게재되었습니다. 


서론

2026년 9월 말, Mandiant Consulting과 Google 위협 인텔리전스 그룹(GTIG)은 Citrix NetScaler ADC 및 NetScaler Gateway 어플라이언스에 영향을 미치는 제로데이 취약점(CVE-2026-88772)이 실제 환경에서 활발히 악용되고 있음을 확인했습니다. 북미와 유럽의 정부 기관, 금융 서비스, 기술, 교육, 법률·전문 서비스 부문 조직이 이번 악용 캠페인의 영향을 받았을 가능성이 높은 것으로 파악되며, 해당 캠페인은 최소 9월 초부터 지속되어 온 것으로 확인됐습니다. 벤더 공지에 따르면, 위협 행위자들은 두 번째 제로데이 취약점(CVE-2026-88771) 또한 활발히 악용하고 있습니다.

CVE-2026-88772 악용은 인증 절차를 우회하고 NSPPE(NetScaler Packet Processing Engine)의 예외 처리되지 않은 종료를 유발해 초기 root 권한 접근을 확보합니다. 공격자의 침해 후 도구 모음을 분석한 결과, 신규 발견된 맞춤형 PHP 웹셸 WHIPSHOT이 확인됐습니다. 이 웹셸은 Base64로 인코딩된 명령·제어(C&C) 페이로드를 정상 HTTP 헤더 안에 은닉할 수 있습니다. 도구 모음에는 이와 함께 작동하는 새로운 Python 터널러 SLAPSHOT도 포함되어 있으며, 내부 네트워크로 트래픽을 프록시해 정찰과 자격 증명 탈취를 수행할 수 있습니다. 실제로 관측된 침해 사례 중 최소 1건에서, 위협 행위자는 이 프록시를 경유해 수작업으로 내부 정찰과 자격 증명 탈취를 진행했습니다.

Citrix는 이번에 조치된 취약점에 대한 고객 안내와 권장 업데이트를 여기에 공개했습니다. 방어 담당자께서는 Citrix 문서를 확인하시고 해당 취약점 패치를 최우선으로 적용하시기 바랍니다. 이번 블로그에서는 Mandiant의 침해 차단 및 복구 가이드도 함께 제공합니다.

캠페인 개요

초기 침투

인증 이전 단계의 암호화 핸드셰이크 과정에서 NSPPE는 수신되는 DTLS 레코드 구조를 파싱합니다. Google 위협 인텔리전스 그룹은 익스플로잇 코드를 확보하지는 못했으나, 현장 텔레메트리 분석 결과 특수하게 변조되거나 단편화된 레코드 헤더를 전송할 경우 패킷 엔진 내부에서 힙 메모리 경계 손상(Heap Memory Boundary Corruption)이 유발되는 것으로 보입니다. 이를 통해 제어 흐름이 전환되면서, 기반 FreeBSD 플랫폼에서 운영체제 root 권한으로 임의의 셸코드가 실행됩니다.

익스플로잇에 성공한 시도는 두 가지 로그 흔적을 남겼습니다.

0-PPE-0 : default SSLLOG SSL_HANDSHAKE_FAILURE 0 : SPCBId - ClientIP - ClientPort - VserverServiceIP - VserverServicePort 443 - ClientVersion DTLSv1.0 - CipherSuite "TLS1-AES-256-CBC-SHA" - Session New - Reason "Handshake failure-Internal Error"

그림 1: Syslog에 기록된 SSL 핸드셰이크 실패

qat0: Process <PID> NSPPE-<##> exit with orphan rings 5:500
pitboss[<##>]: pitboss <DATETIME> NOT restarting NSPPE-<##> (<PID>)

그림 2: FreeBSD 커널과 어플라이언스 워치독 데몬(pitboss)이 기록한 NSPPE 프로세스 종료 로그(/var/log/messages)

거점 확보 및 지속성 유지

익스플로잇에 성공한 뒤, 초기 웹셸 페이로드는 대상 httpd.conf 파일을 수정해 스스로를 설치합니다. 특정 비(非)스크립트 파일 형식을 실행 가능한 PHP 스크립트로 처리하도록 시스템을 구성하며, 이는 WHIPSHOT(PHP 웹셸)과 SLAPSHOT(Python 프록시/터널러)을 비롯한 추가 맞춤형 악성코드 배포를 위한 사전 작업입니다.

웹 서버 지속성 확보 기법 A: 패키지 핸들러 위장(.deb)

한 사례에서 초기 인스톨러는 /etc/httpd.conf를 수정해, 웹 서버가 .deb 파일을 PHP 스크립트처럼 처리하도록 만들었습니다.

php_flag engine on
  <FilesMatch "\.deb$">
    Header set Cache-Control "no-cache"
  </FilesMatch>
AddHandler application/x-httpd-php .deb

그림 3: 패키지 핸들러 위장을 통한 지속성 확보

이 설정 변경으로 공격자는 /netscaler/gui/vpn/scripts/linux 경로에 파일 확장자를 위장한 웹셸을 배치할 수 있었습니다.

웹 서버 지속성 확보 기법 B: 아이콘 앨리어싱 및 서명 파일 핸들러(.sig)

다른 침해 사례에서 위협 행위자는 웹셸 실행을 이미지 요청처럼 위장하는, 한층 은밀한 설정 훅을 구현했습니다.

php_flag engine on#
AliasMatch ^/vpn/media/(.+).ico$ /var/netscaler/gui/vpn/scripts/linux/$1.sig
AddHandler application/x-httpd-php .sig

그림 4: 아이콘 앨리어싱 및 서명 파일 핸들러를 통한 지속성 확보

이 설정 지시어는 다음 세 가지 동작을 수행합니다.

  1. mod_php 엔진을 활성화합니다.

  2. .sig 파일을 실행 가능한 PHP 스크립트로 등록합니다.

  3. /vpn/media/ 경로에서 .ico로 끝나는 모든 HTTP 요청을, /var/netscaler/gui/vpn/scripts/linux/ 내부의 동일한 이름을 가진 .sig 파일로 직접 매핑합니다.

예를 들어 클라이언트가 /vpn/media/e6ee7c85.ico에 접근하면, 실제로는 설치된 PHP 웹셸 e6ee7c85.sig가 응답을 처리하게 됩니다.

최소 한 사례에서는 웹 서버 접근 로그상 GET 요청이 HTTP 404 응답을 반환했음에도, 처리 시간이 비정상적으로 길고 응답 크기가 수 킬로바이트에 달하는 정황이 확인됐습니다. 이후 며칠간 공격자는 존재하지 않는 .sig 파일에 접근을 시도했고, 이로 인해 httperror-vpn 로그에 파일 없음 오류가 기록됐습니다. 이는 공격자가 여러 침해 환경에서 유사한 웹셸을 동시에 관리하고 있을 가능성을 시사합니다.

Root 권한 지속성 확보

CVE-2026-88772의 초기 익스플로잇은 root 권한으로 실행되지만, 이후 웹 서버(httpd)가 처리하는 요청은 권한이 없는 웹 서비스 컨텍스트에서 동작합니다. 위협 행위자는 웹셸이 지속적으로 root 권한으로 실행되도록 하기 위해, 경량 인스톨러 웹셸을 이용해 /bin/sh 실행 파일에 setuid(Set User ID) 비트를 설정했습니다.

chmod u+s /bin/sh

그림 5: User ID 설정 명령어

위협 행위자는 /bin/sh의 권한을 변경함으로써, 이후 웹 서버가 처리하는 모든 웹 요청이 상승된 권한으로 실행되도록 보장할 수 있었습니다. 인스톨러는 /etc/httpd.conf 수정 사항과 SUID 셸 변경을 함께 적용하기 위해 NetScaler 어플라이언스 전체 재부팅(/netscaler/nsshutdown -R)을 실행했습니다.

웹셸의 다른 변종에서는, 위협 행위자가 웹 서비스를 직접 재시작하고 /bin/sh 실행 파일에 root setuid 권한을 부여하는 명령을 실행했습니다.

system('/bin/httpd -k restart -f /etc/httpd.conf && chmod u+s /bin/sh');

그림 6: 웹 서비스 재시작 명령어

악성코드 분석

위협 행위자는 다수의 PHP 웹셸과 터널러 악성코드를 배포해, 피해 조직 네트워크 내부로 트래픽을 프록시하고 내부 정찰, 측면 이동(Lateral Movement), 자격 증명 수집을 수행했습니다.

인스톨러 및 독립 실행형 웹셸

Mandiant는 .deb 및 .sig 확장자 파일로 배치된 경량 PHP 웹셸 여러 개를 복구했습니다. 이들은 직접적인 명령 실행과 어플라이언스 지속성 자동 확보 기능을 제공합니다. 직접 관측된 침해 사례 전반에서 웹셸 파일명은 피해 조직마다 달랐으며, "nginstaller"의 변형에 숫자를 덧붙인 형태의 파일명을 사용하는 경량 웹셸이 다수 확인됐습니다.

  • 변종 1 — 인스톨러형CLI(명령줄 인터페이스)로 실행되면 /etc/httpd.conf를 수정하고, /bin/sh에 setuid root 권한을 부여(chmod u+s /bin/sh)하며, /etc/crontab에서 /vpn/scripts/linux 관련 항목을 삭제한 뒤 /netscaler/nsshutdown -R로 어플라이언스를 재부팅합니다. HTTP를 통해서는 HTTP_NSC_LDAP 헤더에서 Base64로 인코딩된 명령을 추출해 shell_exec()로 실행하고, 결과를 Base64로 인코딩해 반환합니다.
  • 변종 2 — 404 위장형위조된 404 Not Found 응답 코드를 반환하는 변종입니다. 설정 변경을 적용하기 위해 Apache 데몬을 재시작(/bin/httpd -k restart -f /etc/httpd.conf)하고, 수신된 페이로드를 eval()로 실행합니다. 탐지 회피를 위해 정규 표현식(#^.*/vpn/scripts/linux.*\n#m)을 사용해 시스템 /etc/crontab에서 자신의 설치 경로를 체계적으로 삭제합니다. HTTP_NSC_LDAP 헤더로 수신한 Base64 인코딩 페이로드를 eval()을 통해 PHP 코드로 직접 실행합니다.
  • 변종 3 — e6ee7c85.sig마찬가지로 HTTP 404 Not Found 응답을 강제하지만, Base64 페이로드를 HTTP_NSC_CLIENTTYPE 요청 헤더에서 추출한다는 점이 다릅니다. 실행 방식은 동일하게 eval()을 사용합니다.

WHIPSHOT

nohup <python> -c 'import base64;exec(base64.b64decode("<payload>"))' /tmp/.uxdport /tmp/.uxdlock > /dev/null 2>&1 </dev/null &

그림 7: SLAPSHOT을 백그라운드로 실행하는 명령어

  • 루프백 IPC: SLAPSHOT이 활성화되면, WHIPSHOT은 /tmp/.uxdport에 기록된 동적 TCP 포트를 읽어 127.0.0.1:<port>로 소켓 연결을 수립하고 클라이언트 요청을 중계합니다.
  • 탐지 회피: WHIPSHOT은 표준 오류 보고를 비활성화(error_reporting(0))하고 HTTP 404 Not Found 응답 헤더를 설정하되, 터널링된 TCP 응답은 HTTP 본문에 담아 반환합니다.

SLAPSHOT

SLAPSHOT은 Python으로 작성된 TCP 터널링 도구입니다. 내부 네트워크 브리지 역할을 하며, WHIPSHOT으로부터 명령을 받아 임의의 TCP 스트림을 내부 호스트로 전달합니다.

주요 기능과 동작은 다음과 같습니다.

  • 동적 포트 바인딩 및 잠금: 실행되면 127.0.0.1의 임시 포트(Ephemeral Port)에 바인딩하고, 사용 중인 포트 번호를 /tmp/.uxdport 같은 지정 파일에 기록합니다. 또한 fcntl.flock을 사용해 /tmp/.uxdlock 같은 잠금 파일에 배타적 파일 잠금을 걸어, 단일 인스턴스만 동시 실행되도록 보장합니다.

     

  • 자체 통신 프로토콜: 프록시와의 통신에는 자체 바이너리 프로토콜이 사용됩니다. 각 메시지는 4바이트 빅엔디언 길이 접두사 + JSON 페이로드 구조로 구성됩니다

    • open: 대상 호스트·포트로 외부 TCP 소켓 연결 수립

    • push: 열린 세션에 데이터 기록

    • pull: 열린 세션 소켓에서 데이터 폴링 및 읽기

    • exch: 열린 세션 소켓과 C&C 데이터 송수신

    • close: 지정된 네트워크 세션 종료

    • ping: 기본 상태 점검 수행

  • 유효 타임아웃: 데몬은 연결 활동을 모니터링하다가, 15분간 비활성 상태인 개별 세션 소켓을 자동 종료합니다. 활성 세션이나 명령이 10분간(UXD_IDLE_EXIT 변수로 조정 가능) 없으면, SLAPSHOT은 포트 파일과 잠금 파일을 삭제하고 프로세스를 종료해 메모리 사용량과 탐지 위험을 최소화합니다.

시사점

이번 캠페인은 피해 조직 네트워크에 대한 초기 침투 경로로 엣지 장비가 지속적으로 표적이 되고 있다는 점을 다시 한번 보여줍니다. 이는 GTIG가 다양한 위협 행위자를 추적하며 확인해 온 흐름이기도 합니다. 실제로 이러한 취약점은 2025년 기업 대상 제로데이의 약 절반을 차지했습니다. 

애플리케이션 전송 컨트롤러(ADC), VPN 게이트웨이, 방화벽을 포함한 이들 어플라이언스가 매력적인 표적으로 남아 있는 이유는 다음과 같습니다.

  • 인터넷에 직접 노출되어 있음

  • EDR(엔드포인트 탐지 및 대응) 도구의 관측 범위 밖에 위치함

  • 네트워크 내부로 더 깊이 침투하는 데 활용 가능한 자격 증명을 저장하거나 처리하는 경우가 많음

이 전술의 효과가 이미 입증된 만큼, GTIG는 위협 행위자들이 앞으로도 엣지 장비 취약점을 계속 악용할 것으로 전망합니다.

헌팅, 침해 차단 및 복구 가이드

조직은 먼저 기존 로그와 설정 파일을 분석해 잠재적인 침해 징후를 탐지하는 것부터 시작해야 합니다.

헌팅 전략

Citrix NetScaler ADC 어플라이언스

  1. 웹 서버 설정 검증: 모든 NetScaler ADC 어플라이언스에서 /etc/httpd.conf를 점검해 비인가 MIME 타입, 스크립트 핸들러 지시어, 웹 경로 앨리어싱이 있는지 확인하세요.

침해 징후 세부 내용
AddHandler / AddType .deb, .sig, .html, .rpm, .tgz 등 PHP가 아닌 확장자를 PHP 스크립트로 실행하도록 등록한 항목
AliasMatch 공개 웹 경로(/vpn/media/, /vpn/theme/, /vpn/images/)를 어플라이언스 스크립트 디렉터리로 우회시키는 항목

위 항목 중 하나라도 발견되면 침해된 것으로 간주해야 합니다.

grep -En -i "application/x-httpd-php|php_flag|AliasMatch" /etc/httpd.conf

그림 8: 웹 경로 앨리어싱

2. 어플라이언스 스테이징 및 클라이언트 플러그인 디렉터리 감사: 네이티브 클라이언트 플러그인 경로(/var/netscaler/gui/vpn/scripts/linux/, /var/netscaler/gui/vpns/scripts/vista/, /var/netscaler/gui/vpns/scripts/mac/)와 웹 자산 경로(/netscaler/ns_gui/vpn/media/, /var/vpn/theme/)의 내용을 점검하세요. 이들 디렉터리에 있는 정상적인 클라이언트 산출물은 컴파일된 바이너리 또는 아카이브 파일입니다. 따라서 ASCII 텍스트로 식별되거나 PHP 스크립트 마커를 포함한 파일은 모두 비정상입니다. 기본 설치 상태에서 이들 디렉터리에는 정상적인 컴파일 클라이언트 바이너리와 정적 웹 자산만 존재합니다. 비스크립트 확장자로 위장한 평문 스크립트나 PHP 코드를 포함한 파일이 있는지 확인하시기 바랍니다.

file /var/netscaler/gui/vpn/scripts/linux/* /var/netscaler/gui/vpns/scripts/vista/* /var/netscaler/gui/vpns/scripts/mac/* /netscaler/ns_gui/vpn/media/* 2>/dev/null | grep -E "ASCII text|PHP script"
grep -rlE "<\?php|eval\(|base64_decode\(|shell_exec\(" /var/netscaler/gui/ /netscaler/ns_gui/ /var/vpn/ /netscaler/portal/ 2>/dev/null

그림 9: 비스크립트 확장자로 위장한 스크립트 또는 PHP 코드 포함 파일 점검

3. 웹 서버 접근 로그 및 오류 로그 검토: /var/log/httperror*에서 위장되거나 비표준인 확장자를 참조하는 구문 오류, 파싱 오류, 실행 오류를 확인하세요. 이 로그는 접근 로그가 삭제된 경우에도 남아 있습니다. /var/log/httpaccess.log에서 정적 미디어·아이콘·스크립트 경로를 대상으로 하면서 위조된 HTTP 404 상태 코드를 반환하거나 비정상적으로 큰 응답 페이로드를 보이는 요청을 감사하세요.  접근 로그에서 /vpn/scripts/ 또는 /vpn/media/ 주변의 갑작스러운 시간대 공백이나 잘린 줄을 검색하세요. 이는 공격자의 정규식 기반 로그 삭제 도구가 실행된 흔적일 수 있습니다.

grep -E -i "\.(deb|sig|rpm|tgz|sh|so|dat|ico|png|html)" /var/log/httperror*

그림 10: 비표준 확장자 및 실행 오류 검색

grep -E "/vpn/media/|/vpn/scripts/|/vpn/theme/" /var/log/httpaccess.log* | awk '$9 ~ /200|404/ && $10 > 5000'

그림 11: 비정상적으로 큰 응답 페이로드 검색

4. 임시 IPC 흔적 확인: 어플라이언스의 /tmp/ 디렉터리에서 SLAPSHOT이 생성한 잠금 파일과 포트 포인터 파일을 점검하세요. /tmp/.uxdport 또는 /tmp/.uxdlock이 존재한다면, SLAPSHOT 프록시 데몬이 현재 실행 중이거나 최근에 실행된 것입니다. 대응 담당자는 .uxdport에 기록된 포트 번호를 기록해 두고, sockstat -4 -l 명령으로 해당 포트를 수신 중인 프로세스를 확인해야 합니다

ls -la /tmp/.uxdport* /tmp/.uxdlock

그림 12: SLAPSHOT이 생성한 파일 검색

5. 셸 및 바이너리 권한 검증: /bin/sh를 점검해 비인가 SUID 권한이 설정되지 않았는지 확인하세요. 권한이 -rwsr-xr-x로 표시되고 소유자가 root라면, 해당 바이너리는 지속적인 setuid 권한 상승을 위해 변조된 상태입니다.

ls -l /bin/sh

그림 13: 비인가 SUID 권한 확인

6. 프로세스 실행 및 셸 히스토리 점검: 활성 시스템 프로세스 중 /tmp/.uxdport를 참조하거나 nohup으로 실행되는 비정상 Python 인터프리터가 있는지 확인하세요. /var/log/sh.log를 검토해 변경 작업 시간대(Change Window) 외에 실행된 관리 명령이 있는지 확인하세요. 강제 재부팅(/netscaler/nsshutdown -R)과 수동 Apache 재시작(httpd -k restart)이 대표적인 점검 대상입니다.

ps aux | grep -E "python.*(\.uxd|uxdport|uxdlock|base64)"

그림 14: 비정상 Python 인터프리터 프로세스 점검

참고: Citrix는 조직의 NetScaler 인프라에서 잠재적 침해 지표를 식별할 수 있는 IOC Scanner 사용 안내를 공개했습니다. 자세한 내용은 다음 Citrix 문서를 참고하시기 바랍니다.

참고: 조직은 NetScaler 인프라에서 시작된 잠재적 측면 이동을 식별하기 위해, 헌팅 기법을 전체 인프라에 걸쳐 종합적으로 적용해야 합니다. 이때 다른 PAM(권한 접근 관리) 플랫폼을 비롯한 환경 전반이 대상에 포함되어야 합니다.

침해 차단 및 복구 전략

최신 보안 업데이트를 아직 적용하지 않은 조직은 즉시 노출 범위와 위험도를 평가해야 합니다. 다만 NetScaler Gateway에 대한 광범위한 인터넷 차단이나 엄격한 IP 허용 목록 적용은, Citrix Virtual Apps and Desktops(구 XenApp·XenDesktop)로 원격 근무를 지원하는 조직에 상당한 업무 중단을 초래할 수 있습니다. 이에 Mandiant는 패치를 최우선으로 하되, 조직의 리스크 프로필에 따라 적절한 차단 조치와 보완 통제를 병행하는 표적화된 단계적 접근을 권장합니다.

즉시 적용할 완화 조치

조직은 위험 감내 수준, 침해 정황, 운영 요건을 고려해 다음 선택지를 검토하시기 바랍니다.

옵션 1 — 최신 Citrix 빌드 적용 (Mandiant 권장)

해당 취약점을 해결한 최신 Citrix 빌드를 적용하세요. 현재 운영 중인 트랙에 따라 아래 수정 릴리스(또는 이후 버전)로 업그레이드해야 합니다.

  • NetScaler 14.1 Track: 14.1-73.37 이상 업그레이드

  • NetScaler 13.1 Track: 13.1-64.23 이상 업그레이드

참고: 14.1-FIPS 및 13.1-FIPS/NDcPP 배포 환경용 패치 빌드도 별도로 제공됩니다.

Citrix 고객 다운로드 포털에서 해당 빌드를 찾을 수 없는 경우, Citrix에 Severity 1 지원 케이스를 개설해 필요한 수정 사항이 포함된 최신 빌드를 확인하고 확보하시기 바랍니다.

옵션 2 — 침해되었거나 의심되는 어플라이언스 격리

침해가 확인되었거나 의심되는 경우, 해당 NetScaler 어플라이언스를 네트워크에서 격리하세요. 특히 해당 장비가 원격 접속이나 기타 핵심 서비스를 제공하고 있다면 상당한 업무 중단이 발생할 수 있습니다.

앞서 설명한 헌팅 전략으로 침해 지표가 확인된 경우, Mandiant는 다음 차단 조치를 권장합니다.

  • 노드 격리. 침해가 확인되었거나 의심되는 어플라이언스를 즉시 네트워크에서 분리하세요.

  • HA 동기화 중단. 고가용성(HA) 쌍으로 구성된 NetScaler는 두 노드를 각각 독립적으로 평가해야 합니다. 침해된 노드가 변조된 httpd.conf 파일 등 악성 변경 사항을 대기 노드로 복제하지 못하도록, 양쪽 노드가 모두 검증될 때까지 설정 동기화를 비활성화하세요.

  • 아웃바운드 제한. 확인된 위협 행위자 인프라를 차단하고, 어플라이언스의 외부 인터넷 연결을 제한하세요. 특히 임의의 아웃바운드 TCP/UDP 트래픽을 차단하고, 명시적으로 필요한 경우가 아니라면 TCP/25 기반 아웃바운드 SMTP를 차단해야 합니다.

  • 하이퍼바이저 네트워크 격리 검토. NetScaler가 가상화 환경에서 VPX 어플라이언스로 구동 중이라면, vSphere vSwitch와 포트 그룹 설정을 검토하세요. NetScaler VPX가 적절히 분리되어 있는지, 그리고 ESXi vmk0나 vCenter 같은 하이퍼바이저 관리 인터페이스 또는 기타 고민감도 인프라 계층과 동일한 L2 네트워크를 공유하고 있지는 않은지 확인해야 합니다. 추가 권고 사항은 vSphere 대상 Mandiant 보안 강화 가이드를 참고하세요.

옵션 3 — 표적화된 보완 통제 적용

즉시 패치가 불가능한 경우, 해당 어플라이언스를 업데이트할 수 있을 때까지 노출된 공격 표면을 줄이기 위한 표적 통제를 적용해야 합니다. 아래 DTLS 및 UDP/443 통제는 CVE-2026-88772에만 해당합니다. CVE-2026-88771 완화에는 의존할 수 없습니다. 두 취약점을 모두 해결하려면 수정 빌드 설치가 반드시 필요합니다.

Part A — 네트워크 제한

  • 운영상 가능한 경우 DTLS 비활성화. 패치가 지연된다면, DTLS가 필요하지 않은 인터넷 노출 NetScaler Gateway 가상 서버에서 DTLS를 비활성화하세요. 이번 캠페인에서 익스플로잇 페이로드는 DTLS를 이용해 UDP/443으로 전달됐습니다.

  • 상위 구간에서 인바운드 UDP/443 차단. DTLS가 명시적으로 필요하지 않다면, 해당 어플라이언스로 향하는 인바운드 UDP/443을 차단하세요. 이 통제는 상위 경계 방화벽이나 엣지 라우터에서 구현해야 합니다. NetScaler 로컬 ACL에만 의존하면, 트래픽이 차단되기 전에 이미 취약한 패킷 처리 엔진(nsppe)에 도달하게 됩니다.

  • 가능한 경우 상위 구간 IP 허용 목록 적용. NetScaler를 로드 밸런싱 용도로만 사용하거나, 예측 가능한 외부 출발지 IP 집합을 상대하는 Access Gateway를 운영하는 조직은, 어플라이언스에 도달하기 전에 비인가 트래픽을 차단하는 상위 네트워크 ACL 적용을 고려하세요.

다만 대규모 원격 근무를 지원하는 공개 VPN의 경우, 가정용 동적 IP 주소로 인해 관리·운영 부담이 커질 수 있어 현실적으로 적용이 어려울 수 있습니다.

  • 포렌식 분석을 위한 가상 어플라이언스 상태 보존. VMware vSphere 등 하이퍼바이저에서 NetScaler VPX를 가상 어플라이언스로 운영 중이라면, 재부팅 전에 운영상 가능한 한 메모리 상태를 포함한 전체 VM 스냅샷을 확보하세요.

Part B — 자격 증명 교체 및 세션 종료

조직은 침해된 어플라이언스에 저장된 자격 증명이 모두 노출됐다고 가정하고 대응해야 합니다. 자격 증명 교체와 세션 종료는 어플라이언스와 연결 시스템 전반에서 함께 진행되어야 합니다.

  • 활성 세션 폐기. 관리자·Gateway·VPN 세션을 무효화해 탈취 가능성이 있는 세션 토큰을 제거. Citrix Virtual Apps and Desktops의 게이트웨이로 사용 중이라면 활성 ICA/HDX 세션 종료도 포함 (Citrix CTX584227 참고).

  • 어플라이언스 비밀 정보 교체. NetScaler 관리자 자격 증명, 로컬 계정, SSH 키, TLS 인증서 및 관련 개인 키

  • 연동 자격 증명 교체. LDAP 바인드·서비스 계정, RADIUS 공유 비밀키, TACACS 자격 증명, SNMP 커뮤니티 문자열, NITRO/API 자격 증명

  • 하위 Citrix 인프라 감사. Citrix StoreFront 서버, Delivery Controller(DDC), 내부 Virtual Apps and Desktops 호스트 점검. Windows 이벤트 로그에서 비정상 대화형 로그온, 예기치 않은 RDP 활동, 자격 증명 덤핑 흔적, 측면 이동 증거 확인

침해된 어플라이언스에 저장된 TLS 인증서와 관련 개인 키의 폐기·교체도 함께 검토하시기 바랍니다.

순서 주의: 자격 증명 교체는 어플라이언스 패치가 완료된 이후에 수행해야 합니다.

Part C — 제어 플레인 제한

NetScaler의 제어 플레인과 관리 플레인에 다음과 같은 추가 제한을 적용하세요.

  • 인터넷 노출 서비스를 필수 포트와 프로토콜로만 제한

  • NetScaler 어플라이언스에 기본 차단(Default-Deny) 아웃바운드 방화벽 규칙 적용

  • 명시적으로 승인된 목적지와 서비스로만 아웃바운드 허용 — DNS, NTP, 필수 OCSP/CRL 서비스, 승인된 백엔드 애플리케이션, 승인된 관리·보안 인프라

  • 문서화된 업무 요건이 없는 한 TCP/25 아웃바운드 SMTP 명시적 차단

  • NSIP 및 관리 인터페이스의 인터넷 노출 차단

  • SSH, HTTPS 관리, NITRO/API 접근을 전용 관리 네트워크, 승인된 점프 호스트, 명시적으로 승인된 출발지 IP 대역으로 제한

Part D — 로깅 및 탐지 엔지니어링

탐지는 대응 전략의 핵심 요소입니다. Mandiant는 ① 필요한 텔레메트리 확보와 ② 네트워크·어플라이언스·파일 시스템·아이덴티티 활동을 상호 연관 분석하는 탐지 구현이라는 두 축으로 접근할 것을 권장합니다.

로깅 및 가시성 확보

아래 탐지 항목 중 상당수는 NetScaler가 기본적으로 전달하지 않는 로그에 의존합니다. 탐지 로직을 구현하기 전에, SIEM이 다음 텔레메트리를 수신하고 있는지 확인하세요.

로그 비고
NetScaler 감사 로그(ns.log) syslog action을 통해 전달, SSL 관련 이벤트 포함
FreeBSD 시스템 로그(/var/log/messages) NSPPE 종료·크래시와 pitboss 메시지 기록. 표준 ns.log 전달에 포함되지 않음 ?
웹 서버 로그(/var/log/httpaccess.log, /var/log/httperror*) NetScaler Web Logging 또는 AppFlow 텔레메트리. TLS가 어플라이언스에서 종단되므로 상위 네트워크 장비는 HTTP 경로·헤더를 검사할 수 없음
방화벽·네트워크 플로우 로그 NetScaler NSIP 및 SNIP 주소 발신 트래픽
Secret Server 등 PAM 감사 로그  
 

탐지 엔지니어링

프로토콜 및 트래픽 분석

  • 익스플로잇 패턴 DTLS 실패 알림: ClientVersion이 DTLSv1.0이고 사유가 Handshake failure-Internal Error인 SSL_HANDSHAKE_FAILURE 이벤트를 탐지하세요. 이번 활동에서 익스플로잇 성공 시 이 이벤트가 생성됐습니다. 개별 발생 건을 검토하되, 동일 어플라이언스에서 집중 발생한 클러스터를 우선 조사하세요.

  • DTLS 실패와 엔진 종료·크래시 상관 분석: DTLS 핸드셰이크 실패 직후 수 분 내에 동일 어플라이언스에서 NSPPE 종료·크래시가 발생했다면, 이는 강력한 익스플로잇 신호이므로 최우선으로 조사해야 합니다.

  • 예기치 않은 인바운드 UDP/443 검토: DTLS가 비활성화되었거나 사용될 이유가 없는 어플라이언스에 집중하세요. 각 Gateway와 정상적으로 DTLS 연결을 맺는 출발지를 기준선으로 설정하세요. 익스플로잇에 필요한 트래픽량이 매우 적기 때문에, 양 기반 이상 탐지만으로는 포착하지 못할 수 있습니다.

어플라이언스 프로세스 및 메모리 안정성

  • NSPPE 종료·크래시 모니터링: NSPPE 프로세스가 종료되었거나 시그널에 의해 강제 종료됐음을 나타내는 커널 메시지에 대해 고심각도 알림을 생성하세요. /var/core/ 하위의 신규 NSPPE 코어 파일 생성도 함께 모니터링해야 합니다.

  • pitboss가 NSPPE를 재시작하지 않는 경우 알림: pitboss NOT restarting NSPPE 메시지를 모니터링하세요. 이 메시지와 NSPPE 커널 종료·크래시 이벤트는 두 조건이 동시에 충족될 것을 요구하지 말고 각각 독립적으로 알림을 설정해야 합니다.

  • 가용성 이벤트와 상관 분석: 동일 시간대에 인터넷 노출 Gateway에서 발생한 예기치 않은 HA 페일오버나 어플라이언스 재시작은 익스플로잇 정황을 뒷받침하는 보조 증거로 간주하세요.

파일 시스템 및 설정 무결성

  • 핵심 VPN 스크립트 경로 모니터링: 다음 VPN 관련 디렉터리에서 nsgclient.sig 등 .sig 파일의 생성·수정·배치를 탐지하세요.

    • /var/netscaler/gui/vpn/scripts/linux/

    • /netscaler/ns_gui/vpn/scripts/linux/

    • /var/netscaler/gui/vpns/scripts/vista/

    • /var/netscaler/gui/vpns/scripts/mac/

    • /netscaler/ns_gui/vpn/media/

    • /var/vpn/theme/

  • 비인가 웹 서버 설정 변경 탐지: /etc/httpd.conf, /nsconfig/httpd.conf, /flash/nsconfig/httpd.conf의 무단 변경을 모니터링하세요. 특히 다음 항목에 알림을 설정해야 합니다.

    • AddHandler application/x-httpd-php .[ext] 지시어 추가·수정

    • php_flag engine on 설정 등 PHP 실행을 활성화하는 변경. 위협 행위자는 .php 외 확장자를 사용하는 PHP 웹셸을 사용했으며, 실제 확장자는 환경마다 다를 수 있습니다.

    • /vpn/media/, /vpn/theme/, /vpn/images/ 같은 웹 자산 경로를 스크립트 디렉터리나 실행 파일로 매핑하는 Alias, AliasMatch, RewriteRule 지시어

  • 런타임 상태 정기 점검: 다음 항목을 주기적으로 모니터링하세요.

    • /bin/sh에 SUID 비트 설정 여부

    • /tmp/.uxdport 또는 /tmp/.uxdlock 존재 여부

    • nohup으로 실행되었거나 Base64 인코딩 페이로드를 포함한 Python 프로세스

    • /nsconfig/ 하위 영구 설정·시작 파일의 예기치 않은 변경

아웃바운드 및 상호작용 모니터링

  • 정적·클라이언트 스크립트 경로에 대한 의심스러운 웹셸 상호작용 탐지. 정상 클라이언트도 /vpn/media/ 리소스에 일상적으로 접근하므로, 요청 경로 자체보다 행위 패턴에 집중하세요.

    • /vpn/media/*.ico 또는 /vpn/scripts/ 경로에 대한 404 응답 중 수 KB 크기의 본문을 반환하거나 처리 시간이 비정상적으로 긴 건

    • HTTP 오류 로그에 기록된, /vpn/scripts/ 하위의 .sig 등 비표준 파일 관련 "File does not exist" 항목. 접근 로그가 변조·삭제되어도 이 기록은 남아 있을 수 있습니다.

    • /vpn/scripts/, /vpn/media/ 요청 주변의 httpaccess.log 공백, 비정상 항목, 잘린 줄

  • 비정상 어플라이언스 아웃바운드 식별. NetScaler 어플라이언스에서 직접 발신되는 아웃바운드 연결을 모니터링하고, 목적지가 조직의 승인된 허용 목록을 벗어날 경우 알림을 생성하세요. 다음 활동을 우선 조사해야 합니다.

    • 자격 증명 볼트 및 PAM 시스템 관련 통신

    • 예기치 않은 포트를 통한 도메인 컨트롤러 연결

    • 단시간 내 다수 내부 시스템으로의 연결

    • TCP/25 아웃바운드 SMTP

침해 지표 (IOCs)

네트워크 & 전송 계층 지표

유형

설명

지표

인바운드 네트워크 트래픽

제로데이 익스플로잇 전달에 사용된 프로토콜 

UDP :443 (DTLSv1.0)

HTTP 요청 헤더

nsginstaller.deb가 사용하는 인바운드 명령 실행 헤더

HTTP_NSC_LDAP

HTTP 요청 헤더

nsgclient.sig가 사용하는 인바운드 명령 실행 헤더

HTTP_NSC_CLIENTTYPE

HTTP 요청 헤더

WHIPSHOT이 사용하는 청크 방식 Base64 전송 헤더

HTTP_X_UX / HTTP_X_UX_[0-9]+

URI 경로

AliasMatch를 통해 .sig 웹셸로 연결되는 위장 아이콘 요청 URI

/vpn/media/nsgclient.ico / /vpn/media/*.ico

URI 경로

NetScaler Gateway상 악성 PHP 웹셸 배치 경로

/vpn/scripts/linux/nsginstaller*.deb/vpn/scripts/linux/nsgclient*.deb/vpn/scripts/linux/*.php

IPv4 주소

스캐닝 및 스테이징 인프라

143.198.7.94

IPv4 주소

NetScaler 익스플로잇 및 기본 웹셸 백도어 설치

157.254.167.12

이 블로그에서 다룬 활동을 보안 커뮤니티가 함께 헌팅하고 식별할 수 있도록, 등록 사용자를 위한 GTI Collection에 침해 지표(IOC)를 포함해 제공합니다.

파일 지표

파일 경로

/tmp/.uxdport

SLAPSHOT 활성 포트 흔적

파일 경로

/tmp/.uxdlock

SLAPSHOT 프로세스 잠금 흔적

탐지 규칙

YARA 규칙

rule G_APT_Backdoorwebshell_WHIPSHOT_1
{
    meta:
        description = "Detects WHIPSHOT PHP webshell tunneling frontend deployed on Citrix NetScaler ADC appliances"
        author = "GTIG"
        family = "WHIPSHOT"
        

    strings:
        // Chunked transport headers
        $sh1 = "HTTP_X_UX" ascii
        $sh2 = "HTTP_X_UX_" ascii

        // IPC lock and port pointers to local proxy daemon
        $si1 = "/.uxdport" ascii
        $si2 = "/.uxdlock" ascii
        $si3 = "/tmp/.uxdport /tmp/.uxdlock" ascii

        // Socket forwarding logic
        $sf1 = "fsockopen" ascii
        $sf2 = "127.0.0.1" ascii
    condition:
        filesize < 50KB and (
            ($sh1 or $sh2) and ($si1 or $si2 or $si3) and ($sf1 or $sf2)
        )
}
rule G_APT_Tunneler_SLAPSHOT_1
{
    meta:
        description = "Detects SLAPSHOT Python proxy daemon and tunneling tool deployed alongside WHIPSHOT on compromised NetScaler appliances"
        author = "GTIG"
        family = "SLAPSHOT"

   strings:
        // Lock and port files
        $ss1 = "/tmp/.uxdport" ascii fullword
        $ss2 = "/tmp/.uxdlock" ascii fullword
        $ss3 = "UXD_IDLE_EXIT" ascii fullword
        $ss4 = "127.0.0.1" ascii

        // Wire protocol command verbs
        $sc1 = "\"open\"" ascii fullword
        $sc2 = "\"conn\"" ascii fullword
        $sc3 = "\"push\"" ascii fullword
        $sc4 = "\"pull\"" ascii fullword
        $sc5 = "\"exch\"" ascii fullword
        $sc6 = "\"close\"" ascii fullword
        $sc7 = "\"ping\"" ascii fullword

        // Protocol parameter names
        $sp1 = "\"sid\"" ascii fullword
        $sp2 = "\"host\"" ascii fullword
        $sp3 = "\"port\"" ascii fullword
        $sp4 = "\"data\"" ascii fullword
    condition:
        filesize < 30KB and (
            ($ss1 and $ss2 and $ss3) or
            ($ss4 and ($ss1 or $ss2) and 3 of ($sc*) and 2 of ($sp*)) or
            ($ss3 and 3 of ($sc*) and 2 of ($sp*))
        )
}
rule G_Hunting_Config_NetScaler_PHP_1
{
    meta:
        description = "Detects unauthorized Apache configuration directives registering non-standard extensions as PHP scripts, or aliasing web paths to appliance script directories on Citrix NetScaler ADC"
        author = "GTIG"

    strings:
        // NetScaler appliance configuration context markers
        $ns1 = "/netscaler" ascii nocase
        $ns2 = "/var/netscaler" ascii nocase
        $ns3 = "/vpn/" ascii nocase
        $ns4 = "ns_gui" ascii nocase
        $ns5 = "<VirtualHost *:81>" ascii nocase
        $ns6 = "Listen 81" ascii nocase

        // Generic type or handler registration mapping non-standard file extensions to PHP
        $t1 = /Add(Handler|Type)\s+['"]?application\/x-httpd-php['"]?\s+\.([^p\s\r\n][a-zA-Z0-9_-]*|p[^h\s\r\n][a-zA-Z0-9_-]*|ph[^p\s\r\n][a-zA-Z0-9_-]*|php[^s\s\r\n][a-zA-Z0-9_-]*|phps[a-zA-Z0-9_-]+)/ ascii nocase

        // Diversion of web asset paths (media, theme, help, logon, images) to script staging directories
        $a1 = "AliasMatch" ascii nocase
        $a2 = /\^?\/vpn(s)?\/(media|theme|themes|images|help|logon|support)\// ascii nocase
        $a3 = /\/var\/netscaler\/gui\/vpn(s)?\/scripts\// ascii nocase
        $a4 = /\/vpn(s)?\/scripts\// ascii nocase

        // PHP execution flags
        $p1 = "php_flag engine on" ascii nocase

        // Exclusions for web pages, markup, and source code
        $not_html1 = "<html" ascii nocase
        $not_html2 = "<!DOCTYPE" ascii nocase
        $not_html3 = "<?xml" ascii nocase
        $not_code1 = "package " ascii
        $not_code2 = "#include " ascii
    condition:
        filesize < 100KB and not (
            $not_html1 or $not_html2 or $not_html3 or $not_code1 or $not_code2
        ) and (1 of ($ns*)) and (
            // Any directive registering a non-PHP extension as PHP
            $t1 or
            // Any AliasMatch diverting web paths to script directories
            ($a1 and ($a2 or $a3 or $a4)) or
            // Generic combination of php_flag engine on with script directory aliasing
            ($p1 and $a1 and ($a3 or $a4))
        )
}
rule G_Hunting_Backdoorwebshell_NetScaler_C2Headers_1
{
    meta:
        description = "Detects standalone PHP webshells deployed on NetScaler appliances extracting commands from custom or native SetEnvIf HTTP headers"
        author = "GTIG"

   strings:
        // NetScaler C2 header patterns (both HTTP_NSC_* and raw NSC_*, covering all native SetEnvIf variables)
        $h1 = /(HTTP_)?NSC_[a-zA-Z0-9_]+/ ascii
        $h2 = /(HTTP_)?NSC_(USER|NONCE|LDAP|CLIENTTYPE|FT_HIDE)/ ascii nocase

        // Specific named NetScaler SetEnvIf headers
        $hs1 = "HTTP_NSC_LDAP" ascii fullword nocase
        $hs2 = "HTTP_NSC_CLIENTTYPE" ascii fullword nocase
        $hs3 = "HTTP_NSC_USER" ascii fullword nocase
        $hs4 = "HTTP_NSC_NONCE" ascii fullword nocase
        $hs5 = "HTTP_NSC_FT_HIDE" ascii fullword nocase
        $hs6 = "NSC_USER" ascii fullword nocase
        $hs7 = "NSC_NONCE" ascii fullword nocase
        $hs8 = "NSC_LDAP" ascii fullword nocase
        $hs9 = "NSC_CLIENTTYPE" ascii fullword nocase
        $hs10 = "NSC_FT_HIDE" ascii fullword nocase

        // Dynamic execution sinks
        $e1 = "eval(base64_decode(" ascii
        $e2 = "shell_exec(base64_decode(" ascii
        $e3 = "system(base64_decode(" ascii
        $e4 = "passthru(base64_decode(" ascii
        $e5 = "eval(" ascii
        $e6 = "base64_decode(" ascii
        $e7 = "shell_exec(" ascii
        $e8 = "passthru(" ascii
        $e9 = "system(" ascii
        $e10 = "exec(" ascii
        $e11 = "popen(" ascii
        $e12 = "proc_open(" ascii
        $e13 = "assert(" ascii

        // Concealment and response markers
        $c1 = "http_response_code(404)" ascii
        $c2 = "REQUEST_METHOD" ascii
        $c3 = "<FATO>" ascii
        $c4 = "</FATO>" ascii
    condition:
        filesize < 50KB and (
            // Any NSC header accessed alongside dynamic execution
            ((1 of ($h*) or 1 of ($hs*)) and ($e1 or $e2 or $e3 or $e4 or ($e6 and ($e5 or $e7 or $e8 or $e9 or $e10 or $e11 or $e12 or $e13)))) or
            // Any NSC header paired with concealment markers
            ((1 of ($h*) or 1 of ($hs*)) and ($c3 or $c4 or ($c1 and $c2))) or
            // Standalone FATO marker webshell
            (($c3 and $c4) and ($e1 or $e2 or ($e5 and $e6) or ($e6 and $e7)))
        )
}
rule G_Hunting_Script_NetScaler_Persistence_1
{
    meta:
        description = "Detects appliance staging, installer, and anti-forensic maintenance scripts deployed during NetScaler compromise"
        author = "GTIG"

    strings:
        // Appliance restart / shutdown commands
        $cmd1 = "/netscaler/nsshutdown" ascii
        $cmd2 = "nsshutdown -R" ascii

        // SUID root backdoor creation
        $cmd3 = "chmod u+s /bin/sh" ascii

        // Web server reload / restart
        $cmd4 = "/bin/httpd -k restart" ascii
        $cmd5 = "httpd -k restart -f /etc/httpd.conf" ascii

        // Forensic access log scrubbing regex pattern (across any staging path)
        $scrub1 = /#\^\.\*\/vpn(s)?\/(scripts|media|theme|themes|help|logon)/ ascii

        // Apache configuration modification strings
        $cfg1 = "AddHandler application/x-httpd-php" ascii
        $cfg2 = "AddType application/x-httpd-php" ascii
        $cfg3 = "php_flag engine on" ascii
        $cfg4 = "AliasMatch" ascii
        $cfg5 = "SetEnvIf" ascii
    condition:
        filesize < 50KB and (
            // Log scrubber + privilege escalation or web server restart
            ($scrub1 and ($cmd3 or $cmd4 or $cmd5)) or
            // SUID root backdoor creation + appliance command or config modification
            ($cmd3 and ($cmd1 or $cmd2 or $cmd4 or $cmd5 or $cfg1 or $cfg2 or $cfg3 or $cfg4 or $cfg5)) or
            // Configuration tampering + appliance command
            (($cfg1 or $cfg2 or $cfg4) and ($cmd1 or $cmd2 or $cmd4 or $cmd5)) or
            // Generic 2 of the specific appliance maintenance commands
            (2 of ($cmd*))
        )
}

Google Security Operations 

Google Security Operations 고객은 Mandiant Frontline Threats 룰팩에서 다음 탐지 규칙을 이용하실 수 있습니다.

  • SUID or SGID Bit Set on System Shell Binary

  • HTTPD Custom Configuration Restart Chained with Permission Modification

  • NetScaler Web Directory Suspicious Filewrite

  • Python Base64 In-Memory Execution with Hidden Temp File

  • Citrix NetScaler Crontab Installation Path Scrubbing

  • Citrix NetScaler ns_monuploadd_err.pl Log Poisoning Command Injection

  • Citrix NetScaler DTLSv1.0 Handshake Failure Internal Error or NSPPE Crash

  • Citrix NetScaler Masqueraded Webshell HTTP Request or PHP Error Telemetry

감사의 말

이번 분석은 다음 분들의 도움 없이는 불가능했습니다. Bella Valdescruz, Bhavesh Dhake, Chris Linklater, Christopher Romano, Geoff Carstairs, Greg Blaum, Josh Thackston, Kimberly Goody, Lianis Oliva, Matthew Quick, Michael Edie, Omar ElAhdan, Peter Ukhanov, Sagun Chetry, Stuart Carrera, Tyler McLellan.

게시 위치