
pfSense Plus MIM API toolkit b4acd7ca - 高 - リモート - IDOR - 1つのコントローラーJWTはフリートトークンです
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · pfsense-api-fleet-jwt
Class: IDOR Reach: Remote
pfSense Plus MIM API (Netgate) pin b4acd7ca - github.com/Netgate/pfsense-api
1つの POST /login Bearer が、登録されたすべての device_id で再利用されます。公式のサンプルでは、GET /mim/devices から取得したすべての id に対して、そのトークンを /api/device/pfsense/{device_id}/api にコピーしています。LoginResponse のフィールドは token、user、version、alerts のみです。LDAP MIM のロールはグローバルです。デバイスには紐付けられていません。デバイスを一覧表示できるコントローラのログインは、ファイアウォール A 向けのユーザーとして発行されたトークンで、ファイアウォール B と通信します。
これは1つの顧客テナンシーであり、SaaS のクロステナントの残存物ではありません。未認証の呼び出しは 401 のままです。Plus の RBAC は実行されませんでした。シリーズハブ: pfsense-api-nexus-mim(これは残存物6)。残存物9(verify_ssl=False のサンプル)から盗まれた JWT が、このフリートトークンです。
| ID | CVE はまだなし |
| Class | IDOR |
| Reach | Remote(認証済みコントローラ JWT) |
| CWE | CWE-639 |
| CVSS | High: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| Product | Netgate/pfsense-api(pfSense Plus Nexus Multi-instance Management コントローラ OpenAPI + 生成クライアント) |
| Affected | pin b4acd7ca21dbca739a530403a1f62a7440f7726f(2026-05-25)。タグなし。サーバーは Plus 専用(有償)。CE REST パッケージではありません。 |
| Auth | POST /login からのコントローラ JWT。GET /mim/devices が許可されている場合、device_id の推測は不要です。 |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1:19161 のみ。公開されたクライアント契約の Python モック。Plus イメージはありません。 |
github.com/Netgate/pfsense-api の main b4acd7ca21dbca739a530403a1f62a7440f7726f(2026-05-25、Add Snort-3 API definitions)をピン留めしました。これは jaredhendrickson/pfsense-api / pfrest.org ではありません。
デバイスを一覧表示できるコントローラのログインは、同じ JWT で登録されたすべてのインスタンスと通信します。CSRF は関係ありません(Bearer API)。PHP / shell / export-key はすべてのボックスで、プロキシがフリートトークンを受け入れた時点で、意図された管理者権限です。残存物はデバイスごとのバインドの欠如であり、Diagnostics: Command ではありません。
| 権限 | メソッド / パス | ラボで実証済みか推測か |
|---|---|---|
登録済みの全 device_id を列挙する(名前、アドレス、状態) | ログイン Bearer 付きの GET /mim/devices | 偵察(ラボで実証済み)。 モックは site-a と site-b の両方を返した。未認証は 401。 |
| 1 つのトークンでファイアウォール A とファイアウォール B のシステムステータスを読む | GET /api/device/pfsense/{device_id}/api/system | 偵察(ラボで実証済み)。 device-a=200、device-b=200。Witness は B のみ。 |
| 両方の id に対して診断 PHP ラッパーを POST する | POST /api/device/pfsense/{device_id}/api/diag/cmd/php | 完全性(モック上でのみラボ検証)。 両方 200。さらに RBAC は実行されなかったため、ライブのロールゲートは未証明。 |
ログインに console または system が含まれる場合、登録済みの全ボックスで PHP / シェル / ファイル編集を実行する | POST /diag/cmd/php、POST /diag/cmd/shell、GET/POST /diag/editfile、download/upload | 完全性(OAS + 例から推測)。 プロキシがフリートトークンを受け入れた時点で意図される管理者権限。典型的な pfSense PHP-FPM は root。 |
| 列挙された全インスタンスで CA / リーフ秘密鍵と PKCS12 をエクスポートする | GET /system/certificates/{refid}/export-key、GET /system/certauth/{refid}/export-key、GET /system/certificates/{refid}/export-p12 | 偵察(推測)。 同じ JWT、leftover 4 と同じスキーマ。さらに RBAC は未実行。 |
| 列挙された全インスタンスで ACME アカウントと発行済み鍵をダンプする | GET /services/acme および関連する account/cert GET | 偵察(推測)。 同じ JWT、leftover 5 のスキーマ。 |
| 列挙された全インスタンスでエイリアス、ファイアウォール、インターフェース、VPN、サービスを変更する | 同じ per-device プレフィックス、残り 700 以上の操作 | 完全性(推測)。 同じ JWT、ローカルインスタンスと同じスキーマ。 |
| 列挙された全インスタンスで再起動 / 停止 / 設定復元を行う | POST /diag/control、POST /diag/backup/restore | 可用性 + 完全性(推測)。 同じプロキシ。未実行。 |
| クロスカスタマー SaaS IDOR | n/a | クローズ。 単一顧客テナンシー。Netgate はそのテナンシーでコントローラーを運用している。 |
| 未認証のインターネットから :8443 へ | Bearer なしの GET /mim/devices | このラボではクローズ。 401。誤った Bearer も 401。 |
| オフラインデバイス | サンプルは state != online をスキップ | サンプルによりクローズ。 認証境界ではない。 |
device_id のブラインド数値探索 | GET /api/device/pfsense/1/api/system | 推測要件としてクローズ。 id は長い文字列(ED25519 形状)。列挙により推測が不要になる。 |
必要条件: デバイスプロキシへのアクセスが許可されたコントローラーログイン。MIM コントローラーへのネットワークパス(製品では :8443、このラボではループバック :19161)。未認証ではない。CSRF ではない。
| サーフェス | 理由 |
|---|---|
| 未認証の listing / php / shell | Bearer なしおよび誤った Bearer でのモック 401。securitySchemes に関する仕様の沈黙はサーバー 200 ではない。 |
| SaaS クロステナント | 単一顧客テナンシー。テナンシークラスターはマーケティングページでは依然として計画されていた。 |
mim_user / Nexus グループなし | ログインゲートが保持される場合はクローズと文書化されている。 |
機能的な mim_* ロール名 | それらは存在する(view、logs、usermgmt、services、firewall、interfaces、devices、system、superuser)。それらはグローバルであり、device_id ごとではない。ライブ Plus チェックは実行されなかった(plus-rbac=not-executed)。 |
| 登録 SSH JSON 補間 | Leftover 1。別パック。 |
登録 AutoAddPolicy | Leftover 2。別パック。 |
| JWT なしのデバイス unix ソケット | Leftover 3。ローカル、別パック。 |
| CE REST パッケージ | jaredhendrickson/pfsense-api は別のサーバー。 |
| CE WebGUI QinQ / OpenVPN ウィザード / CARP / limiter / IPsec バナー | すでに CE の leftover としてメール済み。この OAS のフィールド名は同じシンクであり、新しい CVE ではない。 |
意図された admins / mim_superuser フリートコンソール | コントローラー所有者向けの製品モデル。leftover は他の全員に対する欠落したオブジェクト ACL。 |
b4acd7ca を固定し、OpenAPI に対して公式の例を読みました。管理対象ファイアウォールと通信するすべてのサンプルは次のことを行います:
devApi = AuthenticatedClient(
base_url=CONTROLLER_URL+"/api/device/pfsense/{}/api".format(device.device_id),
verify_ssl=False,
headers={"Content-Type": "application/json"},
cookies=cookies,
token=token,
)
helper_funcs.py の createDeviceApiChild は任意の device_id 文字列を受け取り、self.token をコピーします。example.js は newClient("device/pfsense/" + device.device_id + "/api") を実行した後、setBearer(client.getBearer()) を呼び出します。example-upgrade-devices.py の newDevApiClient も同じコピーです。GET /mim/devices は列挙のオラクルです(tags フィルタのみ)。
pfapi_openapi.yml および py/pfapi/models/login_response.py の LoginResponse は token / user / version / alerts です。LDAP の nexus_group_prefix はロール view, logs, usermgmt, services, firewall, interfaces, devices, system, superuser を、任意の read/modify/delete 付きでドキュメント化しています。これらの名前の中にデバイスリストを取るものはありません。
誤った方向性: これを SaaS のクロステナント IDOR として扱うこと。OpenAPI が securitySchemes について沈黙していることを未認証 RCE とみなすこと(モックは Bearer なしでは依然として 401 を返します)。Plus を実行せずに mim_view と mim_system の分割が実在すると仮定すること。POST /diag/cmd/php を未公開の Critical としてランク付けすること(Netgate は Diagnostics: Command を意図された管理者権限として扱っています)。茶番: リバースシェル。オラクルは 1 つのトークン、2 つのデバイス ID、両方 200、証拠は B のみ。
python3 pfsense-api-fleet-jwt-Abraxas-Labs.py
または
cd lab
./run.sh
Loopback 127.0.0.1:19161。Compose プロジェクト pfsense-api-fleet-jwt。公開されているログインおよびデバイスごとのプロキシコントラクトの Python モック。スタックは実行後に破棄される。Pin YAML の抜粋は poc.py にバンドルされている。リポジトリのローカルクローンは任意。Plus イメージなし。Witness PFSENSE-API-FLEET-JWT-WITNESS。最終行:
SUCCESS PFSENSE-API-FLEET-JWT one-token=yes device-a=200 device-b=200 schema-no-device-acl=yes plus-rbac=not-executed PFSENSE-API-FLEET-JWT-WITNESS
JWT をデバイスタグまたは許可リストにバインドし、ファイアウォール A 向けのログインが /api/device/pfsense/{B}/api を呼び出せないようにします。コントローラー管理者とインスタンスコンソールを分離し、ヘルプデスクロールが登録済みのすべてのボックスで POST /diag/cmd/php を実行できないようにします。LoginResponse でロールと許可されたデバイスを返します。GET /mim/devices を表示フィルターではなく認可境界として扱います。