
CVE-2026-35616 탐지 툴킷으로, FortiClient EMS의 사전 인증 API 우회 취약점을 탐지합니다. 취약한 버전 식별 및 수정 지침을 제공하는 Python 스캐너와 Nmap NSE 스크립트가 포함되어 있습니다.
Fortinet FortiClient EMS 7.4.5 및 7.4.6의 치명적인 인증 우회 취약점으로 인해 완전히 인증되지 않은 원격 공격자가 단일 HTTP 헤더(X-SSL-CLIENT-VERIFY)를 스푸핑하여 API 인증을 우회할 수 있습니다. 이 결함은 Django 미들웨어가 신뢰할 수 있는 리버스 프록시뿐만 아니라 사용자 제어 헤더의 클라이언트 인증서 메타데이터를 신뢰하기 때문에 발생합니다. 이를 통해 공격자는 전체 관리 API에 접근할 수 있으며, 나아가 기업 전체의 관리 대상 엔드포인트에서 임의 코드 실행이 가능해집니다.
2026년 3월 31일부터 활발히 악용됨. 2026년 4월 6일 CISA KEV에 등재됨.
| 필드 | 세부 정보 |
|---|---|
| CVE ID | CVE-2026-35616 |
| 공급업체 | Fortinet |
| 제품 | FortiClient Enterprise Management Server (EMS) |
| 영향받는 버전 | 7.4.5, 7.4.6 |
| 영향받지 않는 버전 | 7.2.x 브랜치, 7.4.4 및 이전 버전 |
| CVSS v3.1 | 9.1 (치명적) |
| CWE | CWE-284 - 부적절한 접근 제어 |
| 공격 경로 | 네트워크 |
| 인증 | 불필요 |
| 사용자 상호작용 | 없음 |
| 악용 성숙도 | 실제 환경에서 악용됨 |
| CISA KEV | 2026년 4월 6일 등재 (마감: 2026년 4월 9일) |
| 패치 | 핫픽스 제공됨; 7.4.7에서 완전 수정 |
| 발견자 | Simo Kohonen (Defused Cyber), Nguyen Duc Anh |
FortiClient Enterprise Management Server (EMS)는 Fortinet의 중앙 집중식 엔드포인트 관리 플랫폼입니다. 조직 전체에 FortiClient 에이전트를 배포, 구성 및 모니터링하기 위한 명령 및 제어 계층 역할을 합니다. Fortinet 관리 환경에서 모든 엔드포인트를 관리하는 두뇌라고 생각하면 됩니다:
공격자가 EMS에 대한 관리 권한을 획득하면 본질적으로 조직 내 모든 관리 대상 엔드포인트의 키를 소유하게 됩니다.
FortiClient EMS는 내부적으로 비교적 표준적인 웹 애플리케이션 스택을 사용합니다:
+----------------+ +----------------+ +----------------+
| 브라우저 / | HTTPS | Apache | WSGI | Django |
| API 클라이언트 | -------> | (mod_ssl) | -------> | 백엔드 |
+----------------+ +----------------+ +----------------+
상호 TLS(mTLS)가 구성되면 Apache의 mod_ssl이 클라이언트 인증서 검증을 처리합니다. 인증서 검증 후 Apache는 검증 결과를 신뢰할 수 있는 WSGI 환경 변수를 통해 Django에 전달합니다:
SSL_CLIENT_VERIFY - 검증 상태 (SUCCESS, NONE, FAILED)SSL_CLIENT_S_DN - 인증서의 주체 고유 이름(Distinguished Name)SSL_CLIENT_SERIAL - 인증서 일련 번호이것은 표준적이고 안전한 패턴입니다. 문제는 Django 미들웨어가 이 데이터를 읽는 방식에 있습니다.
FortiClient EMS 7.4.5 및 7.4.6에서 Django 인증 미들웨어는 HTTP 요청 헤더에서도 동일한 정보를 수락하도록 수정되었습니다:
X-SSL-CLIENT-VERIFYX-SSL-CLIENT-S-DNX-SSL-CLIENT-SERIAL이는 Apache가 TLS 종료 지점이 아닌 리버스 프록시 배포를 지원하기 위해 추가된 것으로 보입니다. 그러나 미들웨어는 이 두 소스를 구분하지 않습니다. WSGI 변수를 먼저 확인하지만, 해당 변수가 없으면(mTLS가 구성되지 않았거나 직접 연결인 경우) HTTP 헤더로 대체됩니다 - 모든 클라이언트가 설정할 수 있는 헤더입니다.
개념적 분석은 다음과 같습니다:
안전한 경로 (의도된 방식):
Apache mod_ssl이 인증서 검증 --> WSGI 환경 변수 설정 --> Django가 환경 변수 읽음 [OK]
안전하지 않은 경로 (취약점):
공격자가 HTTP 헤더를 직접 설정 --> Django가 헤더 읽음 --> 신뢰함 [FAIL]
미들웨어는 사실상 클라이언트가 자신의 인증서 검증 상태를 스스로 증명하도록 신뢰합니다. 이는 경비원이 누군가에게 "저기, 다른 경비원이 이미 신분증을 확인했나요?"라고 묻고 "네"라고 대답하면 들여보내는 것과 같습니다.
1단계: 공격자가 EMS API 엔드포인트에 POST 요청 전송
다음 헤더 포함:
X-SSL-CLIENT-VERIFY: SUCCESS
X-SSL-CLIENT-S-DN: CN=admin
X-SSL-CLIENT-SERIAL: 0000000000000001
2단계: Django 미들웨어가 WSGI 환경 변수 확인 → 없음
HTTP 헤더로 대체 → X-SSL-CLIENT-VERIFY: SUCCESS 발견
3단계: 미들웨어가 요청을 관리자 신원으로 인증된 것으로 처리
4단계: 공격자가 전체 관리 API에 접근
5단계: 관리 API에서 공격자는 다음을 수행할 수 있음:
- 모든 관리 대상 엔드포인트에 악성 정책 푸시
- 저장된 자격 증명 및 인증서 추출
- 소프트웨어 배포를 통한 페이로드 배포
- ZTNA 구성 수정
- 더 넓은 네트워크로 피벗
전체 공격은 단일 HTTP 요청만 필요합니다. 무차별 대입, 자격 증명 스터핑, 사회 공학이 필요 없습니다. 단 하나의 위조된 헤더만 있으면 됩니다.
이 취약점의 심각성은 서버 자체를 넘어섭니다. FortiClient EMS는 힘 증폭기(force multiplier)입니다 - 이를 침해하면 공격자는 모든 관리 대상 엔드포인트에 대한 영향력을 얻습니다:
즉각적 영향:
하위 영향 (관리 대상 엔드포인트 경유):
기업 위험:
| 버전 | 상태 |
|---|---|
| FortiClient EMS 7.4.6 | 취약 |
| FortiClient EMS 7.4.5 | 취약 |
| FortiClient EMS 7.4.4 및 이전 버전 | 영향 없음 |
| FortiClient EMS 7.2.x | 영향 없음 |
| 날짜 | 이벤트 |
|---|---|
| 2026년 3월 하순경 | Simo Kohonen과 Nguyen Duc Anh가 취약점 발견 및 보고 |
| 2026년 3월 31일 | 허니팟(Defused Cyber)에서 첫 악용 시도 기록 |
| 2026년 4월 4일 | Fortinet이 7.4.5 및 7.4.6용 긴급 핫픽스 출시 |
| 2026년 4월 6일 | CISA가 CVE-2026-35616을 KEV 카탈로그에 등재 (마감: 2026년 4월 9일) |
| 2026년 4월 13일 | 본 탐지 툴킷 공개 |
Python 스크립트는 차등 응답(differential-response) 기법을 사용하여 여러 API 엔드포인트를 테스트합니다.
작동 방식:
HTTP 401 Unauthorized 예상)X-SSL-CLIENT-VERIFY: SUCCESS를 주입한 동일한 요청 전송401에서 다른 코드(일반적으로 500 또는 200)로 변경되면 인증 우회가 확인됨악용 페이로드는 전송되지 않습니다. 이 테스트는 프로덕션 환경에서 안전합니다.
사용법:
# 의존성 설치 (이 스크립트에는 표준 라이브러리만 필요)
pip install -r requirements.txt
# 단일 대상
python CVE-2026-35616_FortiClientEMS_detector.py -t 192.168.1.100
# 사용자 지정 포트
python CVE-2026-35616_FortiClientEMS_detector.py -t ems.corp.local -p 8443
# 파일에서 대량 스캔
python CVE-2026-35616_FortiClientEMS_detector.py -f targets.txt
# JSON 출력을 파일로 저장
python CVE-2026-35616_FortiClientEMS_detector.py -t 192.168.1.100 --json -o results.json
# SSL 인증서 검증 활성화
python CVE-2026-35616_FortiClientEMS_detector.py -t 192.168.1.100 --verify-ssl
# 느린 네트워크를 위한 타임아웃 증가
python CVE-2026-35616_FortiClientEMS_detector.py -t 192.168.1.100 --timeout 20
옵션:
| 플래그 | 설명 | 기본값 |
|---|---|---|
-t, --target | 대상 IP 또는 호스트명 | - |
-f, --file | 대상 목록 파일, 한 줄에 하나씩 (#로 시작하는 줄은 건너뜀) | - |
-p, --port | 대상 포트 | 443 |
--timeout | 연결 타임아웃(초) | 10 |
--verify-ssl | SSL 인증서 검증 활성화 | 비활성화 |
--json | 결과를 JSON 형식으로 출력 | 꺼짐 |
-o, --output | 결과를 파일로 저장 | - |
출력 예시:
╔══════════════════════════════════════════════════════════════╗
║ CVE-2026-35616 - FortiClient EMS 인증 우회 탐지기 ║
║ 사전 인증 API 접근 우회 → 권한 상승 ║
║ CVSS: 9.1 (치명적) | CISA KEV: 활발한 악용 ║
╚══════════════════════════════════════════════════════════════╝
[*] 192.168.1.100:443 스캔 중...
대상: 192.168.1.100:443
============================================================
[*] FortiClient EMS 감지됨 (버전: 알 수 없음)
취약점 테스트 결과:
[취약] /api/v1/auth/signin 기준: 401 → 스푸핑: 500
[취약] /api/v1/system/status 기준: 401 → 스푸핑: 500
[취약 아님] /api/v1/endpoints 기준: 401 → 스푸핑: 401
[!] 대상은 CVE-2026-35616에 취약할 가능성이 높음
사전 인증 API 우회가 확인됨. 즉시 핫픽스 적용!
조치: FortiClient EMS 7.4.7로 업그레이드 또는 핫픽스 적용
# NSE 스크립트 설치
sudo cp CVE-2026-35616_FortiClientEMS.nse /usr/share/nmap/scripts/
sudo nmap --script-updatedb
# 기본 스캔
nmap -p 443 --script CVE-2026-35616_FortiClientEMS <target>
# 서브넷 스캔
nmap -p 443 --script CVE-2026-35616_FortiClientEMS 10.0.0.0/24
# 파일에서 여러 대상 스캔
nmap -p 443 --script CVE-2026-35616_FortiClientEMS -iL targets.txt
# 서비스 버전 탐지 포함
nmap -sV -p 443 --script CVE-2026-35616_FortiClientEMS <target>
Nmap 출력 예시:
PORT STATE SERVICE
443/tcp open https
| CVE-2026-35616_FortiClientEMS:
| 취약:
| FortiClient EMS 사전 인증 API 우회
| 상태: 취약
| ID: CVE:CVE-2026-35616
| 위험 요소: 치명적 (CVSS: 9.1)
| 공개 날짜: 2026-04-04
| 추가 정보:
| 영향받는 엔드포인트: 2
| 조치: FortiClient EMS 7.4.5/7.4.6용 핫픽스 적용 또는 7.4.7로 업그레이드
| CISA KEV 마감: 2026년 4월 9일
| 참고 자료:
| https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-35616
|_ https://www.cisa.gov/known-exploited-vulnerabilities-catalog
curl로 수동 확인하려면:
# 1단계: 기준 요청 - 401이 반환되어야 함
curl -sk -X POST https://<TARGET>:443/api/v1/auth/signin \
-H "Content-Type: application/json" \
-d '{}' \
-o /dev/null -w "%{http_code}\n"
# 2단계: 스푸핑 요청 - 401 이외의 코드가 반환되면 취약할 가능성이 높음
curl -sk -X POST https://<TARGET>:443/api/v1/auth/signin \
-H "Content-Type: application/json" \
-H "X-SSL-CLIENT-VERIFY: SUCCESS" \
-H "X-SSL-CLIENT-S-DN: CN=admin" \
-H "X-SSL-CLIENT-SERIAL: 0000000000000001" \
-d '{}' \
-o /dev/null -w "%{http_code}\n"
첫 번째 요청이 401을 반환하고 두 번째 요청이 500 또는 200을 반환하면 해당 인스턴스는 취약합니다.
환경에서 다음 징후를 확인하세요:
X-SSL-CLIENT-VERIFY 헤더가 포함된 요청검토할 로그 소스:
즉시 조치 (지금 수행):
단기 조치 (이번 주 내):
장기 조치:
X-SSL-CLIENT-VERIFY, X-SSL-CLIENT-S-DN, X-SSL-CLIENT-SERIAL 헤더를 제거하는 규칙 배포Kerem Oruç - 사이버보안 엔지니어