
pfSense Plus MIM API toolkit b4acd7ca — Средний — Удалённый — Остаточные привилегии — официальные примеры verify_ssl=False
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · pfsense-api-example-ssl-verify
Класс: Остаточные привилегии Достижимость: Удалённая
pfSense Plus MIM API (Netgate/pfsense-api b4acd7ca) - Netgate
Сгенерированные Client / AuthenticatedClient по умолчанию имеют verify_ssl=True и передают это в httpx. В документации verify_ssl=False уже называется риском безопасности, и сначала показан путь к PEM-бандлу. Все официальные примеры на Python и helper_funcs.py по-прежнему создают их с verify_ssl=False. В example.js устанавливается NODE_TLS_REJECT_UNAUTHORIZED=0, поэтому весь процесс Node пропускает проверку. JSON для входа помещает имя пользователя и пароль в base64. Это контракт OpenAPI, а не шифрование. TLS — это уровень конфиденциальности.
Узел на пути к :8443 предъявляет любой сертификат. Пример всё равно отправляет POST на /login. Подставной сервер перехватывает base64-логин администратора и выпускает JWT. Этот токен — вся цепочка: G4 export-key, G5 ACME dump, G6 fleet JWT, G7 ACB userkey, предполагаемый php/shell и JSON, который G1 интерполирует в SSH.
Это часть 9 из pfSense Plus MIM API toolkit. Сначала прочитайте эту остаточную уязвимость, даже несмотря на то, что она уровня Medium. Украденный JWT — это то, как остальное становится достижимым с рабочей станции, на которой скопировали example.py.
| ID | пока нет CVE |
| Класс | Остаточные привилегии (проверка TLS в примере отключена) |
| Достижимость | Удалённая (на пути к :8443) |
| CWE | CWE-295 |
| CVSS | Medium: 6.8 CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N |
| Продукт | Netgate/pfsense-api (клиент Plus Nexus MIM; не пакет CE REST) |
| Затронуто | публичный main b4acd7ca21dbca739a530403a1f62a7440f7726f (2026-05-25), официальные примеры и helper_funcs.py |
| Аутентификация | оператор копирует и запускает пример против контроллера; дополнительная роль контроллера не требуется |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1:19191. Не разворачивает pfSense Plus. |
AC:H — это узел на пути между рабочей станцией оператора и контроллером MIM. UI:R — это копирование/запуск примера.
Жертва: рабочая станция оператора, запускающая официальный пример или helper_funcs.py против https://controller:8443. Атакующий: на пути к этому HTTPS (LAN, подложный DHCP, скомпрометированный узел VPN, ARP). Не неаутентифицированный доступ из Интернета к межсетевому экрану.
| Метод | Sink | Привилегия |
|---|---|---|
Предъявить любой сертификат, пока образец отправляет POST на /login или /api/login с verify_ssl=False / NODE_TLS_REJECT_UNAUTHORIZED=0 | httpx verify=False (образцы на Python) или Node TLS reject-unauthorized 0 | Конфиденциальность (подтверждено в лаборатории): прочитать JSON логина. Лаборатория: подставной сервер перехватил base64 admin / lab-controller-secret (captured_b64_admin=yes). Декодирование — одна команда base64 -d. |
Тот же MITM, 200 LoginResponse | образец принимает любой сертификат | Конфиденциальность (подтверждено в лаборатории): украсть access JWT. Лаборатория: INJECT получил токен, содержащий PFSENSE-API-EXAMPLE-SSL-VERIFY-WITNESS. |
Тот же MITM, Set-Cookie при логине | образец копирует client.get_httpx_client().cookies в AuthenticatedClient | Конфиденциальность (cookie подтверждён в лаборатории, воспроизведение выведено): 24-часовой refresh JWT. Воспроизвести POST /login/refresh с этим cookie плюс base64-имя пользователя до истечения TTL refresh. Лаборатория перехватила Set-Cookie: refresh=.... Refresh против живого контроллера не выполнялся. |
| Воспроизвести украденный Bearer на реальном контроллере (если атакующий также может до него добраться) | Плюс MIM /api и /api/device/pfsense/{device_id}/api | Целостность (выведено): эквивалент администратора как залогиненный пользователь на этом API. Схема включает POST /diag/cmd/php, POST /diag/cmd/shell, POST /diag/editfile, POST /diag/backup/restore, POST /diag/control halt/reboot. Здесь не проверялось. Эта лаборатория — подставной контроллер, не Plus. |
| Воспроизвести украденный Bearer как leftover 6 (fleet-токен) | один JWT из POST /login переиспользуется для каждого device_id | Целостность (выведено): обратиться к firewall B после аутентификации как пользователь, предназначенный для firewall A. В LoginResponse нет allow-list по устройствам. Пакет: pfsense-api-fleet-jwt. Plus RBAC не выполнялся. |
| Воспроизвести украденный Bearer как leftover 4 | GET /system/certauth/{refid}/export-key, GET /system/certificates/{refid}/export-key, GET /system/certificates/{refid}/export-p12 | Конфиденциальность (выведено): извлечь приватные ключи CA и leaf, PKCS12 документирован без защиты паролем, затем выдать себя за VPN / WebGUI TLS / IPsec. Пакет: pfsense-api-export-key-get. |
| Воспроизвести украденный Bearer как leftover 5 | GET /services/acme | Конфиденциальность (выведено): украсть ключ аккаунта Let's Encrypt и выданные ключи сертификатов для каждого hostname, который регистрирует firewall. Пакет: pfsense-api-acme-key-dump. |
Воспроизвести украденный Bearer или утечь userkey из образца, который выполняет GET ACB, как leftover 7 | GET /services/acb?userkey= и GET /services/acb/bkp?userkey= | Конфиденциальность (выведено): перечислить и получить блобы Auto Config Backup (полный конфиг). Пакет: pfsense-api-acb-userkey. |
Сформировать JSON ControllerSummary под leftover 1 | MITM HTTPS с verify_ssl=False поставляет JSON, который example-add-devices.py экранирует кавычками, а затем подставляет через .format в SSH | Целостность (выведено): $() / обратные кавычки / переводы строк в поле контроллера выполняются как SSHUSER на каждом адресе в pfsense_addresses. Пакет: pfsense-api-enroll-json-ssh. |
| Инжектировать ответы в сторону клиента | skip-verify принимает любой сертификат | Целостность (токен подтверждён в лаборатории, fleet выведен): выпустить JWT, который образец сохранит. Лаборатория: INJECT-токен со свидетелем. Поддельные списки устройств / прогресс обновления / успешный логин против живого образца — те же ворота. |
POST /diag/control, POST прошивки, восстановление из сломанного бэкапа (после украденного Bearer) | предполагаемая поверхность администратора, когда TLS уже нет | Доступность (выведено): halt / reboot / сломанный образ. Тот же Bearer. В лаборатории не проверялось. |