
pfSense Plus MIM API toolkit b4acd7ca - Medio - Remoto - Privilegio residual - muestras oficiales verify_ssl=False
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · pfsense-api-example-ssl-verify
Clase: Privilegio residual Alcance: Remoto
pfSense Plus MIM API (Netgate/pfsense-api b4acd7ca) - Netgate
El Client / AuthenticatedClient generado usa verify_ssl=True por defecto y lo pasa a httpx. La documentación ya califica verify_ssl=False como un riesgo de seguridad y muestra primero una ruta de bundle PEM. Todas las muestras oficiales de Python y helper_funcs.py siguen construyéndolos con verify_ssl=False. example.js establece NODE_TLS_REJECT_UNAUTHORIZED=0, por lo que todo el proceso Node omite la verificación. El JSON de inicio de sesión coloca el nombre de usuario y la contraseña en base64. Ese es el contrato OpenAPI, no cifrado. TLS es la capa de confidencialidad.
Un par en la ruta de :8443 presenta cualquier certificado. La muestra igualmente hace POST a /login. El sustituto captura el inicio de sesión de administrador en base64 y acuña un JWT. Ese token es la cadena: G4 export-key, G5 ACME dump, G6 fleet JWT, G7 ACB userkey, el php/shell previsto, y el JSON que G1 interpola en SSH.
Esta es la Parte 9 del pfSense Plus MIM API toolkit. Lee primero este residual aunque sea Medium. El JWT robado es la forma en que el resto se vuelve alcanzable desde una estación de trabajo que copió example.py.
| ID | sin CVE todavía |
| Clase | Privilegio residual (verificación TLS de la muestra desactivada) |
| Alcance | Remoto (en la ruta a :8443) |
| CWE | CWE-295 |
| CVSS | Medio: 6.8 CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N |
| Producto | Netgate/pfsense-api (cliente Plus Nexus MIM; no el paquete REST de CE) |
| Afectado | main público b4acd7ca21dbca739a530403a1f62a7440f7726f (2026-05-25) muestras oficiales y helper_funcs.py |
| Autenticación | el operador copia y ejecuta una muestra contra el controlador; sin rol adicional en el controlador |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1:19191. No compone pfSense Plus. |
AC:H está en la ruta entre la estación de trabajo del operador y el controlador MIM. UI:R es copiar/ejecutar la muestra.
Víctima: estación de trabajo del operador ejecutando una muestra oficial o helper_funcs.py contra https://controller:8443. Atacante: en la ruta a ese HTTPS (LAN, DHCP rogue, salto VPN comprometido, ARP). No es Internet sin autenticar hacia el firewall.
| Método | Sink | Privilegio |
|---|---|---|
Presentar cualquier certificado mientras la muestra hace POST a /login o /api/login con verify_ssl=False / NODE_TLS_REJECT_UNAUTHORIZED=0 | httpx verify=False (muestras de Python) o Node TLS reject-unauthorized 0 | Confidencialidad (probado en laboratorio): leer el JSON de login. Laboratorio: el stand-in capturó base64 admin / lab-controller-secret (captured_b64_admin=yes). La decodificación es un solo base64 -d. |
Mismo MITM, 200 LoginResponse | la muestra acepta cualquier certificado | Confidencialidad (probado en laboratorio): robar el JWT de acceso. Laboratorio: INJECT obtuvo un token que llevaba PFSENSE-API-EXAMPLE-SSL-VERIFY-WITNESS. |
Mismo MITM, Set-Cookie en el login | la muestra copia client.get_httpx_client().cookies en AuthenticatedClient | Confidencialidad (cookie probada en laboratorio, replay inferido): JWT de refresco de 24 horas. Replay POST /login/refresh con esa cookie más el nombre de usuario en base64 hasta el TTL del refresco. El laboratorio capturó Set-Cookie: refresh=.... El refresco contra un controlador en vivo no se ejecutó. |
| Replay del Bearer robado al controlador real (si el atacante también puede enrutar hacia él) | Plus MIM /api y /api/device/pfsense/{device_id}/api | Integridad (inferido): equivalente a admin como el usuario logueado en esta API. El esquema incluye POST /diag/cmd/php, POST /diag/cmd/shell, POST /diag/editfile, POST /diag/backup/restore, POST /diag/control halt/reboot. No ejercitado aquí. Este laboratorio es un stand-in del controlador, no Plus. |
| Replay del Bearer robado como leftover 6 (token de flota) | un POST /login JWT reutilizado para cada device_id | Integridad (inferido): hablar con el firewall B después de autenticarse como un usuario destinado al firewall A. LoginResponse no tiene lista de permitidos por dispositivo. Pack: pfsense-api-fleet-jwt. Plus RBAC no ejecutado. |
| Replay del Bearer robado como leftover 4 | GET /system/certauth/{refid}/export-key, GET /system/certificates/{refid}/export-key, GET /system/certificates/{refid}/export-p12 | Confidencialidad (inferido): extraer claves privadas de CA y hoja, PKCS12 documentado sin protección por contraseña, luego suplantar VPN / WebGUI TLS / IPsec. Pack: pfsense-api-export-key-get. |
| Replay del Bearer robado como leftover 5 | GET /services/acme | Confidencialidad (inferido): robar la clave de cuenta de Let's Encrypt y las claves de certificados emitidos para cada hostname que el firewall enrola. Pack: pfsense-api-acme-key-dump. |
Replay del Bearer robado, o filtrar userkey de una muestra que hace GET a ACB, como leftover 7 | GET /services/acb?userkey= y GET /services/acb/bkp?userkey= | Confidencialidad (inferido): listar y obtener blobs de Auto Config Backup (configuración completa). Pack: pfsense-api-acb-userkey. |
Dar forma al JSON de ControllerSummary hacia leftover 1 | MITM de verify_ssl=False HTTPS suministra el JSON que example-add-devices.py escapa entre comillas y luego .formatea en SSH | Integridad (inferido): $() / backticks / saltos de línea en un campo del controlador se ejecutan como SSHUSER en cada dirección en pfsense_addresses. Pack: pfsense-api-enroll-json-ssh. |
| Inyectar respuestas hacia el cliente | skip-verify acepta cualquier certificado | Integridad (token probado en laboratorio, flota inferida): acuñar un JWT que la muestra conservará. Laboratorio: token INJECT con el witness. Listas de dispositivos falsas / progreso de actualización / éxito de login contra una muestra en vivo son la misma puerta. |
POST /diag/control, POST de firmware, restauración de un backup roto (tras robar el Bearer) | superficie de admin prevista una vez que TLS desaparece | Disponibilidad (inferido): halt / reboot / imagen rota. Mismo Bearer. No probado en laboratorio. |