Unauthenticated Command Injection
Corrija agora. Ela está sob exploração confirmada pelo CISA e tem exploit funcional público.
Apply updates per vendor instructions.
Resumo
Falha de authentication bypass encadeada com command injection no remote_agent.php do Cacti, atingindo instâncias até a versão 1.2.22. Qualquer atacante não autenticado com acesso de rede ao endpoint consegue executar comandos arbitrários no servidor, desde que exista pelo menos um poller_item com ação POLLER_ACTION_SCRIPT_PHP — condição comum porque templates padrão do Cacti (como Device - Uptime e Device - Polling Time) criam esse tipo de item. É por isso que o CVSS 9.8 não é exagero: a barreira de autenticação é falsa e o pré-requisito de configuração é praticamente universal em instalações reais.
Detalhamento técnico
O arquivo remote_agent.php normalmente serve apenas os pollers remotos do Cacti, mas não exige autenticação — em vez disso, chama remote_client_authorized() para decidir se o requisitante é um poller legítimo. Essa função pega o IP do cliente via get_client_addr() (lib/functions.php), resolve esse IP para hostname via gethostbyaddr() e compara contra os hostnames cadastrados na tabela poller. Se bater, o acesso é liberado.
O problema (CWE-290, authentication bypass by spoofing) está em get_client_addr(): a função itera por uma lista de cabeçalhos HTTP — X-Forwarded-For, X-Client-IP, X-Real-IP, CF-Connecting-IP, True-Client-IP, entre outros — e usa o primeiro valor válido como IP do cliente, sem verificar se a requisição de fato passou por um proxy confiável. Como esses cabeçalhos (HTTP_*) são inteiramente controlados pelo atacante via requisição HTTP, basta enviar um cabeçalho tipo Forwarded-For com o próprio IP do servidor Cacti. gethostbyaddr() resolve esse IP para o hostname do servidor, e como existe uma entrada padrão na tabela poller com esse hostname (o poller local), o cheque passa e remote_client_authorized() retorna true.
Com o bypass feito, o atacante aciona action=polldata, que chama poll_for_data(). Essa função lê os parâmetros host_id, local_data_id e poller_id da requisição e busca em poller_item a linha correspondente. Se a ação cadastrada for POLLER_ACTION_SCRIPT_PHP (execução de script PHP via script_server.php), o código monta uma string de comando e a passa para proc_open(), concatenando o valor de poller_id sem sanitização — esse parâmetro é obtido via get_nfilter_request_var(), que não filtra caracteres especiais de shell. Isso é injeção de comando clássica (CWE-78): qualquer metacaractere de shell no valor de poller_id é interpretado pelo interpretador de comandos do sistema.
O atacante controla, portanto, dois pontos: o cabeçalho HTTP usado para falsificar o IP (autenticação) e o valor de poller_id (execução). host_id e local_data_id são inteiros pequenos e sequenciais, facilmente bruteforçáveis até encontrar uma combinação com ação POLLER_ACTION_SCRIPT_PHP configurada.
Como é explorada
O vetor é HTTP direto contra remote_agent.php, sem necessidade de conta, sessão ou qualquer interação de usuário — só acesso de rede ao endpoint (geralmente exposto junto com a interface web do Cacti). O atacante envia uma requisição com um cabeçalho de IP forjado (por exemplo Forwarded-For apontando para o IP do próprio servidor Cacti) para passar o cheque de remote_client_authorized(), depois envia action=polldata com host_id e local_data_id — que podem exigir algumas tentativas de bruteforce até acertar um poller_item cuja ação seja script PHP — e injeta o payload de comando no parâmetro poller_id.
Complexidade de exploração é baixa: não há proteção CSRF relevante no endpoint (ele nem deveria precisar de sessão), a superfície de bruteforce é pequena (IDs numéricos baixos em instalações típicas) e o cabeçalho de spoofing de IP não exige nada além de escolher, entre a lista suportada, um que o servidor não filtre por vir de proxy confiável. O resultado final é execução de comando arbitrário com os privilégios do processo web/PHP do Cacti — RCE completo, não apenas leitura de dados.
A vulnerabilidade está no catálogo KEV da CISA, confirmando exploração ativa em produção, e existem módulo Metasploit, template Nuclei e PoCs públicas circulando — ou seja, o custo de exploração para um atacante oportunista é baixo e a varredura em massa de instâncias Cacti expostas é esperada.
Versões
Como se proteger
A correção do fornecedor está nas versões 1.2.23 e 1.3.0: o patch reescreve get_client_addr() para não confiar ciegamente em cabeçalhos HTTP_* como fonte de IP do cliente e introduz a opção de configuração $proxy_headers (em include/config.php.dist), que permite desabilitar totalmente a leitura de cabeçalhos de proxy (false, usando só REMOTE_ADDR) ou restringir a uma lista explícita de cabeçalhos confiáveis, em vez do comportamento anterior que aceitava qualquer um dos cabeçalhos listados. Atualizar para 1.2.23 ou 1.3.0 é a mitigação real; o patch por si só não neutraliza o command injection em poller_id — a correção completa depende dos três commits referenciados, que tratam tanto o bypass de autenticação quanto o saneamento da chamada a proc_open.
Se a atualização não for possível de imediato: restrinja o acesso de rede a remote_agent.php para apenas os pollers legítimos (via regra de firewall, controle de acesso no servidor web, ou bloqueio do path inteiro se pollers remotos não forem usados) — isso remove a exposição do endpoint a qualquer origem não confiável. Definir $proxy_headers = false manualmente em include/config.php em versões que já suportam a variável reduz a superfície de spoofing de IP, mas não corrige o command injection subjacente se o endpoint ainda for alcançável por um poller malicioso ou comprometido.
Não funciona como mitigação: confiar em WAF genérico para filtrar cabeçalhos X-Forwarded-For — a lista de cabeçalhos aceitos pela função vulnerável é ampla (inclui CF-Connecting-IP, True-Client-IP, X-Real-IP etc.), então um WAF que bloqueie só um ou dois cabeçalhos deixa outros vetores abertos. A única mitigação equivalente a corrigir é isolar o endpoint na rede até a atualização.
Como detectar
Em logs de aplicação do Cacti (tabela de log / cacti.log), procure por mensagens de erro do tipo "Unauthorized remote agent access attempt from ()" seguidas de tentativas repetidas — indicam bypasses que falharam e podem revelar tentativas anteriores de acerto. No tráfego HTTP, sinal forte é a combinação de requisições para /remote_agent.php?action=polldata com cabeçalhos como Forwarded-For, X-Forwarded-For, X-Client-IP, CF-Connecting-IP ou True-Client-IP contendo o próprio IP do servidor Cacti, e parâmetros host_id/local_data_id variando sequencialmente (padrão de bruteforce). Valores do parâmetro poller_id contendo metacaracteres de shell (;, |, `, $(), &&) são evidência direta de tentativa de injeção — não há uso legítimo desses caracteres nesse campo.
Como o endpoint normalmente não gera logs de autenticação de usuário (é tráfego de poller, não de sessão web), ambientes sem logging de aplicação detalhado ou sem captura de cabeçalhos HTTP completos no proxy/WAF podem não deixar rastro algum — a ausência de sinal não indica ausência de exploração.