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のような名前だ。各ファイルはである。2つの公開n-dayが、フォルダが部品カタログのように見えるまでハードウェアとファームウェア全体に再スタンプされていた。
金脈はオフセットマトリクスであり、新しいバグではない。その議論はシリーズのPhase 0にある。Phase 1では、2つのプリミティブを挙動として扱う。Phase 2は、検索可能なSKU / トレイン / CPUマップである。Phase 3はハンティングである。
このリポジトリは、同じキットの残りの部分であり、別のエクスプロイトファミリーではない。
exp/プログラムは、シリーズで既に説明されているイメージ別クライアントである。shellcode/のファイルは、それらのクライアントが呼び出すように書かれていたランチャー、インプラント取得、および設定窃取リスナーである。exp_*.pyファイルにはインプラントが含まれていなかった。それらは(1)「シェルコードサーバー」から生のバイトを取得するか、(2)GET /s.jsしてevalすることだけを知っていた。それらの欠落していた部分がここにある。プリミティブ:
| ファミリー | CVE | Fortinetアドバイザリ | 公開情報 |
|---|---|---|---|
| A | CVE-2024-21762 | FG-IR-24-015 | Assetnote — Two Bytes is Plenty |
| B | CVE-2023-27997 | FG-IR-23-097 | Lexfo — XORtigate, Bishop Fox |
両方ともCISAの既知の悪用された脆弱性カタログに掲載されている。poc.pyはどちらのCVEも実装していない。既に分類されている2つのファミリー(チャンク書き込み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ディレクトリリスティングである。これらは、このツリーがかつてWebルートとしてホストされていたことを示すだけである。
この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
│
├─ クライアントが「ステージャー」種別の場合 (Family A ARM / 一部のamd64)
│ shellcode/shellcode_server.py を起動
│ shellcode/shellcode_arm または shellcode_amd64 を読み込む
│ send_cmd.py を起動 (コールバック待機)
│ :9999 で設定レシーバーを起動
│ 選択した exp_*.py を実行
│
└─ クライアントが「Node /s.js」種別の場合 (ほとんどのFamily B + 多くのFamily 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を経由する。確認されたヒットは、クラッシュしたデーモンではなく、ネットワークエッジの侵害として扱うこと。What a hit meansを参照。
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以上のアプライアンスから選択できる。SNMPや管理APIではなく公開SSL-VPNリソースハッシュによって駆動される、マトリクスと同じSKU。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段階これは、Family B / Node Family Aクライアントがexecute_cmdの後に取得するファイルである。これは一般的なリバースシェルではない。
C2_HOST:9999**(C2ホスト。TARGET_HOSTでも、エクスプロイトに渡されるlhost / ATTACKER_HOSTでもない — このコピーは1つのオペレーターコールバック用に焼き込まれた)。/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と同じ目標(設定窃取)、異なるトランスポート(シェルコールバック vs Node)。コメントアウトされたバリアントは、生のcatの代わりにbase64フレーミングを使用していた。
shellcode/shellcode_server.py — Family 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のみを配布するのではなくステージャーをコンパイルする作者と一致する。
1行のみ: pwntools。exp_*.pyファイルとshellcode_server.pyがこれをインポートする。poc.pyもpocsuite3、python-dotenv、mmh3、requestsを必要とする。
exp/クライアントとの関係| シリーズで既に説明済み | このツリーでのロール |
|---|---|
Family A ARM exp_*.py(ステージャー引数) | 第1段階。shellcode_server.py + shellcode_arm(またはamd64)を呼び出し、その後lhost:7777。 |
Family A/B Node exp_*.py(/s.js) | 第1段階。親s.jsのHTTP GET、その後eval。 |
6.0 DシリーズTFTP / /tmp/sバリアント | TFTP経由で同じs.js目標。依然としてオペレーターHTTPルート上のこのファイルを期待。 |
親ディレクトリの何も第3のCVEを実装していない。件数、SKUヒートマップ、トレイン整列(Family Aは7.4.0 / 7.4.1まで、Family Bは27997ウィンドウ内)はPhase 0とPhase 2にある。元のカタログで最も再ターゲットが多かったのは: FortiGate-60E、100E、60F、100F — ブランチオフィスハードウェア。
最初のドロップは「コールバックを取得する」で止まっていた。第2段階の本体が欠落していた。このツリーは目的を明確にする:
/data/config/*.gzからFortiOS設定を窃取する。それは資格情報とトポロジーの侵害である(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、enc=付き/remote/hostcheck_validateGET /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が公開開示されパッチ適用された後、発見されたままの状態で公開される。
研究・教育目的のみ。 このリポジトリをホストしている当事者と標的システムの所有者の両方から明示的な書面による許可を得ていない限り、いかなるホストに対してもこのマテリアルを実行、展開、または使用しないこと。