
pfSense Plus MIM API toolkit b4acd7ca - High - Remote - IDOR - 컨트롤러 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
하나의 POST /login Bearer가 등록된 모든 device_id에 재사용됩니다. 공식 예제는 GET /mim/devices에서 가져온 모든 id에 대해 해당 토큰을 /api/device/pfsense/{device_id}/api로 복사합니다. LoginResponse 필드는 token, user, version, alerts뿐입니다. LDAP MIM 역할은 전역입니다. 이들은 디바이스에 바인딩되지 않습니다. 디바이스를 나열할 수 있는 컨트롤러 로그인이 방화벽 A를 위해 의도된 사용자에게 발급된 토큰으로 방화벽 B와 통신합니다.
이는 하나의 고객 테넌시이며, 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 | 정찰(실험실 입증). Mock이 site-a와 site-b를 모두 반환했습니다. 인증 없음은 401입니다. |
| 하나의 토큰으로 방화벽 A와 방화벽 B의 시스템 상태 읽기 | GET /api/device/pfsense/{device_id}/api/system | 정찰(실험실 입증). device-a=200, device-b=200. B에서만 Witness. |
| 두 ID 모두에 진단 PHP 래퍼 POST | POST /api/device/pfsense/{device_id}/api/diag/cmd/php | 무결성(Mock에서만 실험실). 둘 다 200. 또한 RBAC가 실행되지 않았으므로 실제 역할 게이트는 입증되지 않음. |
로그인에 console 또는 system이 포함된 경우 등록된 모든 박스에서 PHP / shell / 파일 편집 실행 | POST /diag/cmd/php, POST /diag/cmd/shell, GET/POST /diag/editfile, 다운로드/업로드 | 무결성(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 및 관련 계정/인증서 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 아님.
| 표면 | 이유 |
|---|---|
| 인증 없는 나열 / php / shell | Bearer 없이 및 잘못된 Bearer로 Mock 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는 목록 확인용 오라클이다(태그 필터만 해당).
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를 의도된 관리자 권한으로 취급한다). 극적인 요소: 리버스 셸. 오라클은 하나의 토큰, 두 개의 디바이스 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를 표시 필터가 아닌 인가 경계로 취급하십시오.