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のような名前だ。各ファイルはSKU別・FortiOS別のメモリ破壊クライアントである。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段階