pfSense Plus MIM API toolkit b4acd7ca - Alta - Remota - Lectura de archivos - Reglas Snort {file} decode-then-join
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · pfsense-api-snort-file-join
Clase: Lectura de archivos Alcance: Remoto
pfSense Plus MIM API (Netgate/pfsense-api b4acd7ca) - Netgate
GET /services/snort/rules/{name}/{file} toma dos parámetros de ruta, ambos type: string, sin pattern. Descripción: inspeccionar un archivo en el conjunto de reglas. SnortRuleFileContents.filename es "ruta del archivo". El get_snort_ruleset_file._get_kwargs generado hace quote(str(...), safe=""), por lo que ../ es ..%2F en el cable. Un servidor que decodifica la URL y luego la une al directorio del conjunto de reglas todavía puede salir de él.
Un llamador que puede hacer GET a esa ruta, más un JWT de flota, lee archivos fuera del conjunto de reglas nombrado como el agente MIM en cada máquina inscrita. Además, el join no se ejecutó. El laboratorio es un gadget de decodificación-y-unión de la ruta pública.
Este es el sobrante 8 de 9. Mapa: pfsense-api-nexus-mim. Mismo pin sobrante: fleet JWT. Este no es jaredhendrickson/pfsense-api.
| ID | sin CVE todavía |
| Clase | Lectura de archivos (Snort {file} decodificación-y-unión) |
| Alcance | Remoto (GET autenticado; el JWT de flota reutiliza la misma ruta en cada device_id) |
| CWE | CWE-22 |
| CVSS | Alto: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N para un JWT que puede hacer GET al archivo del conjunto de reglas de Snort |
| Producto | Netgate/pfsense-api (controlador pfSense Plus Nexus MIM OpenAPI + cliente generado) |
| Afectado | main b4acd7ca21dbca739a530403a1f62a7440f7726f (2026-05-25, Add Snort-3 API definitions) |
| Autenticación | Los ejemplos envían un JWT de controlador después de POST /login. OpenAPI no tiene securitySchemes. No se ha demostrado sin autenticación. |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1:19181. Gadget de decodificación-y-unión. Además, el join no se ejecutó. |
Necesita un llamador que pueda hacer GET /services/snort/rules/{name}/{file}. Los ejemplos oficiales usan Authorization: Bearer después de POST /login, luego reproducen ese token como base_url=.../api/device/pfsense/{device_id}/api para cada id de GET /mim/devices. Las operaciones generadas todavía tipan AuthenticatedClient | Client. CSRF no está en juego en este GET.
| Método | Sink | Privilegio | Laboratorio vs inferido |
|---|---|---|---|
GET del cliente generado con {file}=../../oracle/WITNESS (wire ..%2F..%2Foracle%2FWITNESS) | decodificar unquote(file) y luego unir al directorio del ruleset nombrado y leer | Recon: HTTP 200, filename /oracle/WITNESS, cuerpo PFSENSE-API-SNORT-FILE-JOIN-WITNESS. Lo previsto es un archivo .rules dentro de ese ruleset. | Probado en laboratorio (gadget). Además, el join no se ejecutó. |
Mismo GET con {file}=../../../etc/passwd | mismo join si el controlador lo mapea sobre el sistema de archivos del dispositivo | Recon: leer archivos del host como el agente MIM (Plus WebGUI/PHP normalmente es root). El laboratorio usó /oracle/WITNESS, no un /etc/passwd real. | Inferido (mismo join, ruta del host). |
Mismo GET tras un fleet JWT (POST /login una vez, replay en /api/device/pfsense/{id}/api) | proxy del dispositivo del mismo {file} sin restricciones | Recon: un token de controlador con forma de helpdesk lee archivos en todas las máquinas enroladas, no solo en la que estaba destinada el login. | Inferido (contrato G6 + esta ruta). Además, RBAC no se ejecutó. |
Mismo remanente en {name} | GET|DELETE /services/snort/rules/{name} y los hermanos address/suppress {name} también son cadenas sin restricciones | Recon / integridad: renombrar o eliminar un ruleset cuyo nombre es una ruta. | Inferido de la especificación. El laboratorio solo accionó {file}. |
| Control confinado de la misma ruta de wire | relative_to(ruleset_dir.resolve()) tras decodificar | Cerrado. HTTP 400, sin testigo. | Probado en laboratorio |
{file}=local.rules benigno | lectura prevista del ruleset | Cerrado. 200, sid:1, sin testigo, tanto naive como control. | Probado en laboratorio |
Adyacente cerrado: codificación porcentual del cliente de ../ (encoding=%2F). Join de control tras decodificar (control-confined=yes). Además, join del controlador (plus-join-not-executed). Lectura prevista de local.rules dentro del ruleset foo. GET no autenticado (no probado; los ejemplos envían Bearer). POST /services/snort/rules archive / .so plantado (administrador de IDS previsto, no este remanente). Diagnósticos PHP/shell. Escritura de archivos. Shell del host. Este remanente no es RCE.
Fijé Netgate/pfsense-api b4acd7ca21dbca739a530403a1f62a7440f7726f porque HEAD el 2026-10-14 no tenía etiquetas y el último commit es "Add Snort-3 API definitions". Así es como existe esta ruta. El YAML no tiene ninguna clave pattern: en ningún sitio. {name} y {file} son cadenas sin restricciones. quote(..., safe="") es un codificador de cliente, no una comprobación de basedir del servidor.
/services/snort/rules/{name}/{file}:
parameters:
- in: path
name: name
required: true
schema:
type: string
- in: path
name: file
required: true
schema:
type: string
get:
description: Obtain the contents of the specified file in the ruleset. This is intended for inspecting scripts provided by it.
operationId: GetSnortRulesetFile
"url": "/services/snort/rules/{name}/{file}".format(
name=quote(str(name), safe=""),
file=quote(str(file), safe=""),
),
Giros incorrectos ya registrados: tratar la codificación del cliente como cierre de recorrido; componer pfSense Plus; clasificar el archivo .so de Snort como este remanente; afirmar GET no autenticado. Teatro: un reverse shell. El oráculo es el gadget 200 de /oracle/WITNESS más el control 400.
python3 pfsense-api-snort-file-join-Abraxas-Labs.py
o
cd lab
./run.sh
Atacar solo http://127.0.0.1:19181. Proyecto Compose pfsense-api-snort-file-join. Gadget de decodificar-y-unir de la ruta pública. Además, la unión no se ejecuta. La pila se desmonta después de la ejecución.
SUCCESS PFSENSE-API-SNORT-FILE-JOIN encoding=%2F decode-then-join=yes control-confined=yes plus-join-not-executed PFSENSE-API-SNORT-FILE-JOIN-WITNESS