
pfSense Plus MIM API toolkit b4acd7ca - Alta - Local - Privilégio remanescente - unix sock enroll sem JWT
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · pfsense-api-unix-sock-noauth
Classe: Privilégio remanescente Alcance: Local
pfSense Plus MIM API b4acd7ca - Netgate
github.com/Netgate/pfsense-api é a OpenAPI pública mais o Python gerado para o pfSense Plus Nexus Multi-instance Management. Não é o pacote REST do CE. O exemplo oficial de enroll faz login via HTTPS :8443 com um Bearer JWT e, em seguida, envia por SSH um script POSIX para cada firewall. Esse script comunica com /var/run/pfnet-controller.sock sem Authorization.
curl --unix-socket /var/run/pfnet-controller.sock -H "Content-Type: application/json" -d "{controller JSON}" http://unix/api/mim/controllers
curl --unix-socket /var/run/pfnet-controller.sock http://unix/api/mim/device/identity
Fallbacks obsoletos /api/system/controller e /api/system/mim/devid são iguais: sem Bearer. O login HTTPS ainda emite um JWT. Esse token não é aplicado ao sock no dispositivo. Esta é uma fronteira de confiança separada de :8443 mais os grupos mim_*.
Parte 3 da série Nexus MIM. Depois que G1 / G2 chegam à máquina, este sock é o plano local.
Um processo local que consegue connect(2) ao sock pode registrar um controlador e extrair a identidade do dispositivo sem JWT. Além disso, não foi executado. O DAC real do sock é desconhecido.
| ID | sem CVE ainda |
| Classe | Privilégio remanescente |
| Alcance | Local (connect(2) para /var/run/pfnet-controller.sock) |
| CWE | CWE-306 |
| CVSS | Alto: 7.8 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H se o modo do sock permitir que outro uid local se conecte. Honestamente: DAC desconhecido, Plus não executado. |
| Produto | Netgate/pfsense-api (Plus Nexus MIM toolkit) |
| Afetado | main público b4acd7ca21dbca739a530403a1f62a7440f7726f (2026-05-25) |
| Autenticação | nenhuma nos curls oficiais de enroll via unix-socket |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1:19121. Mock HTTP Unix. Sem imagem Plus. |
O ator é qualquer processo no dispositivo pfSense que consiga connect(2) para /var/run/pfnet-controller.sock. Não é TCP remoto neste exemplo (apenas curl --unix-socket). O JWT HTTPS :8443 e os grupos mim_* não se aplicam neste fd.
| Método | Destino | Privilégio |
|---|---|---|
POST oficial do device_script sem Authorization | POST http://unix/api/mim/controllers | Integridade: o mock aceitou o JSON de enroll. Log de acerto authorization_present=false. |
GET oficial do device_script sem Authorization | GET http://unix/api/mim/device/identity | Recon: o mock retornou JSON com formato de DeviceIdentity, incluindo PFSENSE-API-UNIX-SOCK-NOAUTH-WITNESS. |
Mesmo GET com Authorization: Bearer lab-jwt | mesmo caminho de identidade | Controle de laboratório: 401, witness não está no corpo. Distingue presente vs ausente. Não é um resultado Plus. |
Estas são as mesmas operações OpenAPI que a amostra oficial e a tag mim colocam no dispositivo. Comprovado apenas como protocolo, não como um 200 do Plus.
| Método | Destino | Privilégio |
|---|---|---|
POST /mim/controllers (fallback POST /api/system/controller) com ControllerIdentity do atacante (key, vpn_pubkey, vpn_listenaddr, vpn_netkey, cert) | SetController | Integridade: registrar um controlador hostil. Especificação: o dispositivo "continuará a emitir uma solicitação de conexão a cada 10 segundos" (VPN Netgard para o atacante). |
GET /mim/device/identity (fallback GET /api/system/mim/devid) | GetDeviceIdentity | Recon: chaves públicas Noise/VPN do dispositivo, address, texto curl de controller_add. |
GET /mim/controllers | GetControllers | Recon: listar controladores mais informações do dispositivo (ControllerInfo.noise_secret, chaves). |
GET /mim/controllers/id/{key} | GetMIMController | Recon: um registro de controlador. |
DELETE /mim/controllers/id/{key} | DeleteMIMController | Integridade / disponibilidade: desregistrar o controlador real (key em hex ou base64-URL). |
POST /mim/device/pubkey com privkey / pubkey do atacante | SetDevicePublicKey (DevicePublicKeyOption inclui privkey) | Integridade: substituir a identidade ED25519 do dispositivo. O handshake do controlador então confia na chave do atacante. |
Não concede por si só um shell root no host. Concede o plano MIM: em quem o dispositivo confia como controlador, e a chave privada Noise do dispositivo se o POST de pubkey for honrado.
Requer: connect(2) local para /var/run/pfnet-controller.sock. DAC (mode, owner, group, SO_PEERCRED) não está na árvore pública do cliente. Se o daemon vincula 0600 root, isto já é local como root (confused deputy para um helper, pacote ou escape que consiga ver /var/run). Se o sock for acessível a outro uid, CVSS 7.8 conforme pontuado.
/mim/controllers no Nexus HTTPS :8443. Essa porta está documentada como User Manager JWT. Este leftover é o unix fd.POST /login ainda emite um Bearer JWT. AuthenticatedClient ainda o envia para CONTROLLER_URL+"/api".verify_ssl=True. Path params percent-encode ../.security: MISSING em todas as 755 ops é uma lacuna de schema, não um skip HTTPS não autenticado comprovado.Mesmo pin, plano de enrollment remoto: pfsense-api-enroll-json-ssh e pfsense-api-enroll-autoadd. Mapa da série: pfsense-api-nexus-mim.
Fiz pin de Netgate/pfsense-api b4acd7ca21dbca739a530403a1f62a7440f7726f e li o loop de enrollment oficial: login, GET /mim/controller/identity, stringify, SSH exec de unix-socket POST/GET, POST /mim/devices. HTTPS usa AuthenticatedClient(token=...). Os curls do sock não.
Falsos caminhos: tratar securitySchemes em falta como HTTPS não autenticado (o login também o tem em falta; os exemplos são sempre Bearer primeiro). Assumir que o sock é world-writable (o mode não está neste repo). Alegar um Plus 200. O leftover aqui provado é o protocolo de enroll publicado.
Teatro: uma reverse shell. O oráculo é a forma curl oficial mais uma testemunha no corpo da identity mais um controlo Bearer 401.
python3 pfsense-api-unix-sock-noauth-Abraxas-Labs.py
ou