pfSense Plus MIM API toolkit b4acd7ca - Alta - Remoto - Privilégio remanescente - example-add-devices.py JSON para SSH
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · pfsense-api-enroll-json-ssh
Classe: Privilégio remanescente Alcance: Remoto
Kit de ferramentas da API MIM do pfSense Plus (b4acd7ca) - Netgate
example-add-devices.py é a forma documentada de registrar o controlador Nexus em cada dispositivo pfSense Plus via SSH. Ele faz json.dumps de GET /mim/controller/identity, substitui apenas ", e então aplica .format nesse blob duas vezes em um script POSIX curl -d com aspas duplas. O exec_command do Paramiko executa o script como SSHUSER (padrão de exemplo admin) em cada endereço em pfsense_addresses.
json.dumps mais replace('"', '\\"') mantém as aspas fechadas. $(), crases e ${...} não são metacaracteres JSON. Eles permanecem ativos dentro da string -d com aspas duplas ainda abertas. O exec do OpenSSH é um shell. Esse é o resquício.
Este não é o pacote REST CE de terceiros. É o cliente OpenAPI oficial e os exemplos para o controlador pfSense Plus Nexus Multi-instance Management. Eu não compus o Plus. O laboratório é a interpolação fixada mais um mock de /var/run/pfnet-controller.sock.
| ID | sem CVE ainda |
| Classe | Privilégio remanescente (JSON do controlador para SSH de enrollment) |
| Alcance | Remoto (on-path para CONTROLLER_URL enquanto o exemplo define verify_ssl=False, ou um escritor de campos string de ControllerSummary) |
| CWE | CWE-78 |
| CVSS | Alto: 8.1 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Produto | Netgate/pfsense-api (kit MIM do Plus) |
| Afetado | main público b4acd7ca21dbca739a530403a1f62a7440f7726f example-add-devices.py |
| Autenticação | operador executa o exemplo documentado. O atacante está on-path, ou pode escrever strings de summary. O login no controlador ainda acontece primeiro. |
| Licença | GNU Affero GPL v3.0 |
| Laboratório | apenas 127.0.0.1:19101. Sem imagem Plus. |
Parte 1 da série de resquícios do Nexus MIM. Irmãos: enroll-autoadd, unix-sock-noauth, example-ssl-verify (G9 é como um par on-path molda este JSON), fleet-jwt, export-key-get.
O resquício é a substituição de comando dentro da string -d com aspas duplas ainda abertas. json.dumps mais a passagem de aspas mantêm a string fechada. $(), crases e ${...} sobrevivem e são executados como SSHUSER quando o OpenSSH faz exec do script.
| Método | Sink | Privilégio | Comprovado em laboratório ou inferido |
|---|---|---|---|
Colocar $() (ou backticks / ${...}) num campo string de ControllerSummary, depois deixar o operador executar example-add-devices.py | controller_info.replace('"', '\\"') depois device_script.format(...) depois sshclient.exec_command(device_script) (example-add-devices.py ~104-161) | Shell como SSHUSER (padrão da amostra admin; o SSH admin do pfSense é tipicamente equivalente a root) em cada endereço em pfsense_addresses | Comprovado em laboratório na interpolação: INJECT vpn_listenaddr=$(printf %s PFSENSE-API-ENROLL-JSON-SSH-WITNESS >/oracle/witness) sobreviveu ao escape apenas de aspas (dollar-paren-survived=yes) e escreveu o witness (inject-oracle=yes). O Paramiko/SSH em si não está neste compose. |
TLS MITM de GET /api/mim/controller/identity enquanto a amostra usa verify_ssl=False | Mesmo parse de ControllerSummary depois o mesmo script | Mesmo shell. Todos os campos de ControllerSummary são str ou Unset, portanto um corpo JSON MITM que faça parse de tipos é suficiente. | Inferido a partir da amostra (example-add-devices.py login Client e AuthenticatedClient ambos definem verify_ssl=False). O laboratório envia o objeto summary diretamente. Irmão: example-ssl-verify. |
Escrever caracteres de substituição em name ou vpn_listenaddr no controller, aguardar o próximo enroll | Mesmo sink | Mesmo shell quando um operador executa a amostra a seguir | Inferido. Requer uma escrita prévia desses campos (já controller-admin, ou o irmão unix-sock-noauth). |
Mesmo shell, chaves extra via ControllerSummary.additional_properties | O mesmo dump inclui chaves MITM extra (to_dict copia sobras após from_dict) | Campos extra seguem para -d e para o shell | Inferido a partir de to_dict. O laboratório injeta apenas através de vpn_listenaddr. |
| Método | Sink | Privilégio | Comprovado em laboratório ou inferido |
|---|---|---|---|
| Ler o stdout da estação de trabalho de enrollment | print("CONTROLLER_SUMMARY:", controller_info) | Identidade do controller key, vpn_pubkey / vpn_netkey / endereços de escuta do Netgard | Inferido a partir do print da amostra. O laboratório não imprime um blob de controller ao vivo. |
| Sniffar ou fazer MITM do canal exec SSH | O payload de exec_command(device_script) é o JSON -d do curl | Mesma identidade de controller no fio para cada dispositivo | Inferido. AutoAddPolicy nesse canal é um relatório irmão. |
| Caso CONTROL no gadget | POST /case name=control | vpn_listenaddr honesto chega ao unix socket inalterado | Comprovado em laboratório. 192.0.2.10:51820 enviado como está, sem oracle (control-clean=yes). |
| Método | Sink | Privilégio | Comprovado em laboratório ou inferido |
|---|---|---|---|
Comando injetado como SSHUSER | Mesmo exec_command | Parar o pfnet-controller, apagar o enrollment, reiniciar, ou de outra forma retirar o dispositivo do MIM | Inferido como efeito colateral do shell. O laboratório apenas escreve o ficheiro witness. |
Requer: o caminho da amostra oficial (enroll do operador). UI:N para o peer on-path assim que essa amostra estiver em execução. O login no controller continua a acontecer primeiro.