CVE-2017-9791
Corrija agora. Ela está sob exploração confirmada pelo CISA e tem exploit funcional público.
Apply updates per vendor instructions.
Resumo
CVE-2017-9791 (S2-048) é uma injeção OGNL no Struts 1 plugin do Apache Struts 2, explorável quando uma Struts 1 Action passa um valor de campo não confiável diretamente para o construtor de ActionMessage, em vez de usar uma chave de recurso. O caso concreto documentado pelo fornecedor e usado nos PoCs públicos é a aplicação de demonstração Struts Showcase (endpoint integration/saveGangster.action), que contém exatamente esse antipadrão de código. É crítica onde existe, mas não é uma falha genérica do framework — depende de código de aplicação específico, não de uma rota built-in presente em qualquer deploy Struts.
Detalhamento técnico
O Struts 1 plugin permite migrar Actions legadas do Struts 1 para rodar dentro do Struts 2. Nesse modelo, mensagens de erro/feedback são representadas por org.apache.struts.action.ActionMessage. O problema (CWE-20, Improper Input Validation, segundo a CISA; na prática uma injeção de expressão OGNL) ocorre quando o código da aplicação constrói o ActionMessage concatenando um campo de entrada do usuário diretamente na string da mensagem, em vez de referenciar uma chave de recurso e passar o valor do usuário como parâmetro dela. O próprio advisory do Apache mostra o padrão inseguro: `new ActionMessage("Gangster " + gform.getName() + " was added")` versus o seguro `new ActionMessage("struts1.gangsterAdded", gform.getName())`.
Quando o valor bruto entra na cadeia de processamento da mensagem, ele é avaliado como expressão OGNL pelo motor do Struts/XWork antes de ser exibido. Isso dá ao atacante controle sobre uma expressão OGNL executada no contexto da aplicação. Os PoCs usam a técnica já conhecida de outras falhas OGNL do Struts (S2-045/S2-046/S2-057): acessar `ognl.OgnlContext@DEFAULT_MEMBER_ACCESS`, limpar `ExcludedPackageNames` e `ExcludedClasses` do `OgnlUtil`, e assim desabilitar o sandbox padrão que bloqueia chamadas a classes Java arbitrárias — abrindo caminho para `java.lang.Runtime.getRuntime().exec()` ou `ProcessBuilder`.
O atacante controla o conteúdo do campo de formulário que alimenta o ActionMessage (no exemplo do Showcase, o parâmetro `name` do formulário de cadastro de "gangster"). Não há necessidade de autenticação nem de条件 de rede além de acesso HTTP ao endpoint vulnerável, o que explica o CVSS 9.8 — mas o vetor só existe se a aplicação real reproduzir o padrão inseguro descrito, não é uma falha automática de qualquer app que use o Struts 1 plugin.
Como é explorada
A exploração é uma requisição HTTP POST para uma action Struts 1 executada via Struts 1 plugin, com um campo de formulário preenchido com uma expressão OGNL malformada envolta em `%{...}`. Não é necessária autenticação; a complexidade é baixa e o vetor é de rede — daí AV:N/AC:L/PR:N/UI:N no CVSS. O PoC público (exploit-db 42324) e o módulo Metasploit (exploit-db 44643, ranking Excellent) demonstram o ataque contra o endpoint `/struts2-showcase/integration/saveGangster.action` (ou `editGangster`), enviando o payload no parâmetro `name`.
O pré-requisito real que a manchete ignora: a aplicação alvo precisa usar o Struts 1 plugin com uma Struts 1 Action que repasse entrada de usuário sem sanitização para um ActionMessage — exatamente o padrão presente no app de demonstração Struts Showcase. Instâncias que expõem o Showcase publicamente, ou aplicações customizadas que copiaram esse padrão de código, são o universo real de exposição; não é qualquer aplicativo que usa o Struts 1 plugin.
O resultado final, quando a exploração funciona, é execução de comando arbitrário no sistema operacional com os privilégios do processo Java que roda o servidor de aplicação — controle total do processo, sem necessidade de escrita em disco prévia. A CVE está no catálogo KEV da CISA, confirmando exploração ativa observada, e possui módulo Metasploit e templates Nuclei prontos, o que a mantém como alvo corrente de varredura automatizada mesmo anos após a divulgação.
Versões
Como se proteger
O Apache não publicou uma versão de patch binária que elimine a classe de falha por si só — a orientação oficial (S2-048) é de correção de código: nunca construir ActionMessage concatenando entrada de usuário na string da mensagem; sempre usar uma chave de recurso (resource key) e passar o valor do usuário como parâmetro dela, como no exemplo `new ActionMessage("struts1.gangsterAdded", gform.getName())`. Isso significa auditar todo código de Actions Struts 1 executadas sob o plugin, procurando por concatenação direta de entrada do usuário em qualquer chamada a ActionMessage (ou classes análogas como ActionError).
Como paliativo imediato: remover ou desabilitar o aplicativo de demonstração Struts Showcase de ambientes de produção — é o vetor documentado nos PoCs e não deveria estar exposto em nenhum caso. Se o Struts 1 plugin não é usado pela aplicação, removê-lo das dependências elimina a superfície. Onde não for possível corrigir o código imediatamente, um WAF pode bloquear padrões característicos de payload OGNL (presença de `%{`, `ognl.OgnlContext`, `DEFAULT_MEMBER_ACCESS`, `_memberAccess`) como controle compensatório, mas isso não fecha a falha — apenas reduz a superfície de exploração trivial.
Atualizar a versão do framework Struts por si só não corrige a falha se a aplicação mantiver o padrão de código inseguro — isso é o mito a descartar aqui: diferente de outras CVEs do Struts (como as de OGNL no core), esta depende de uma decisão de codificação na camada de aplicação sobre o Struts 1 plugin, não de um bug isolado no parser do framework.
Como detectar
Em logs de acesso HTTP, procurar por requisições POST a endpoints de Struts 1 Actions executadas via plugin (no caso documentado, `/struts2-showcase/integration/saveGangster.action` ou `editGangster`) contendo, em qualquer parâmetro de formulário, sequências como `%{`, `ognl.OgnlContext`, `DEFAULT_MEMBER_ACCESS`, `_memberAccess`, `getExcludedPackageNames`, `Runtime.getRuntime().exec` ou `ProcessBuilder` — assinaturas usadas pelo PoC público e pelo módulo Metasploit. A ausência dessas strings não garante segurança, já que variações de payload OGNL para bypass de sandbox são conhecidas e mutáveis; times que dependem só de assinatura de string devem tratar isso como detecção parcial, não como confirmação de ausência de exploração.