Autor: Rodrigo Tavares
Introdução
Afinal de contas, o que é essa vulnerabilidade do Spring Framework com Java, divulgada na última quarta-feira dia 30 de março de 2022?
Também chamada de SpringShell ou Spring4Shell, a maior dúvida nesse momento é:
- "Será que a minha aplicação está exposta e correndo risco de ataque"
- "Será que meus dados ou dos meus clientes estão seguros?"
Então, pra saber tudo sobre essa vulnerabilidade e principalmente, como corrigir o problema, vem comigo até o final desse artigo que eu vou te mostrar.
Como foi relatada
Na última quarta-feira, 30 de março, alguns blogs apresentaram alguns prints de tela relatando uma possível vulnerabilidade no Spring Framework, o que eles chamaram de SpringShell.
Em alguns lugares chamavam de Spring4Shell, fazendo uma relação com uma vulnerabilidade parecida encontrada no Log4J recentemente conhecida como Log4JShell.
Print de uma PoC, proof of concept, postada no Twitter no dia 30 de março de 2022 demonstrando a vulnerabilidade
Segunda vez, mesmo problema
Um ponto interessante, é que essa mesma vulnerabilidade já havia sido tratada e corrigida em 2010, mas devido a uma nova funcionalidade do Java 9, foi possível explorar a vulnerabilidade de novo, vamos entender isso mais à frente.
No que consiste o problema
Para permitir a troca de dados entre cliente e aplicação o Spring converte dados das requisições HTTP em objetos e vice-versa.
Com o intuito de justamente evitar que um possível invasor tenha controle indevido à aplicação, container ou servidor usando funcionalidades internas desses objetos, o Spring Framework faz um tratamento especial protegendo acessos à objetos como Class
, ClassLoader
e ProtectionDomain
, provavelmente ajustes feitos em 2010, na primeira ocorrência do problema.
Na atualização do Java 9, houve uma modificação do objeto Class, o que fez com que os tratamentos e validações implementadas pelo Spring Framework não fossem suficientes.
Classe CachedIntrospectionResults do Spring Framework, linha 288
PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
if (Class.class == beanClass &&
("classLoader".equals(pd.getName()) || "protectionDomain".equals(pd.getName()))) {
// Ignore Class.getClassLoader() and getProtectionDomain() methods - nobody needs to bind to those
continue;
}
...
}
Como é possível ver nesse trecho de código do Spring Framework, os métodos getClassLoader()
e getProtectionDomain()
não podem ser sobrescritos e ainda há um comentário que dizendo que eles foram ignorados por não haver necessidade de atribuir nada à neles.
// Ignore Class.getClassLoader() and getProtectionDomain() methods - nobody needs to bind to those
Resumindo, o objetivo da conversão das informações de uma requisição HTTP em objetos ou a serialização destes para texto é única e exclusivamente para troca de POJOs entre a aplicação e os clientes, controles à recursos do Java, servidores, bancos de dados e etc, devem ser explicitamente programados.
O que mudou no Java 9
O Java 9 introduziu o método getModule
no objeto Class
e isso permitiu a utilização do método getModule().getClassLoader()
.
Vale dizer que o simples acesso ao ClassLoader
não é por si só uma vulnerabilidade, isso depende da forma que as funcionalidades dele foram implementadas.
Nesse caso específico, a vulnerabilidade está explorando a implementação da classe, WebAppClassLoaderBase
do Tomcat 9.
Um ponto importante aqui é que o Java 9 foi lançado em 2017, ou seja, essa vulnerabilidade já vem sendo explorada por 5 anos até 2022, data de publicação desse artigo.
Passos para reprodução
- Primeiro o invasor usa o ClassLoader das implementações de log pra criar um arquivo JSP malicioso.
- Em seguida, o invasor escreve o código malicioso na página JSP.
- E Pronto, agora é só fazer uma requisição pra página.
Esse tipo de acesso é chamado de RCE, Remote Code Execution, podemos dizer que é uma forma mais sofisticada do conhecido SQL Injection.
Vulnerabilidades como essa são registradas com um CVE, Common Vulnerabilities and Exposures, e aqui falamos de dois:
- CVE-2022-22965: Justamente esse que foi exposto no dia 30 de março;
- CVE-2010-1622:, solução da exposição do ClassLoader em 2010, antes do lançamento do Java 9.
Quem está vulnerável?
Agora que já vimos como funciona a vulnerabilidade vamos ver quem tá exposto.
Com os detalhes que vimos até agora, acredito você já tenha algumas pistas, segue abaixo todos os itens que você precisa validar:
- Aplicação distribuída no formato war
- Lembrando que esse não é o formato de distribuição padrão
- Utilizar JDK 9 ou superior
- Ter no seu projeto a dependência spring-webmvc ou spring-webflux
Usar as versões do Spring Framework 5.3.0 até a 5.3.17, 5.2.0 até a 5.2.19 ou versões mais antigas
- Para Spring Boot são as versões 2.6.5 e 2.5.11 ou versões anteriores
- Usar o Apache Tomcat como container para servlets nas versões 10.0.19, 9.0.61 e 8.5.77 ou versões anteriores.
E lembrando que a vulnerabilidade não consiste em apenas um deles, você precisa atender todos para estar vulnerável.
Essa configuração são mais prováveis ao usar o Spring Framework para prover páginas JSP, não há dados da quantidade de sistemas e/ou aplicações afetadas, mas pelos padrões de programação atuais utilizando APIs Rest com páginas SPA ou aplicativos móveis, aparentemente vai afetar mais aplicações legadas.
A vulnerabilidade requer que haja um diretório para processar as páginas JSP ou para o deploy de arquivos War, pois é justamente onde serão criadas as páginas JSP para acesso como um backdoor.
O que fazer para resolver?
Abaixo estão opções para solução da vulnerabilidade, implementando apenas uma das opções abaixo sua aplicação estará segura.
Atualizar o Tomcat
Atualizar o Tomcat, com o patch de correção nas versões:
- 10.0.20;
- 9.0.62;
- 8.5.78.
ou qualquer versão superior à essas.
A vantagem de atualizar o Tomcat é justamente centralizar o ponto de correção, resolvendo diretamente o problema em todas as aplicações publicadas.
Atualizar o Spring Framework (ou Spring Boot)
Logo após a divulgação da vulnerabilidade, foi lançado um patch para correção do Spring Framework, sendo assim, basta atualizá-lo para a versão:
- 5.3.18;
- 5.2.20.
ou qualquer versão superior à essas.
Caso sua aplicação esteja usando Spring Boot atualize para as versões 2.6.6, 2.5.12 ou superior, que estão com suas referências atualizadas para uma das versões do Spring Framework citadas à cima.
Essa abordagem de atualização já é mais trabalhosa, uma vez que você precisará efetuá-la em todas as aplicações afetadas, então garanta que não esqueceu nenhuma.
De qualquer forma, mesmo que tenha atualizado o Tomcat, siga com a atualização das dependências do Spring Framework, evitando assim futuros problemas.
Downgrade do Java
Não sendo possível atualizar nem o Spring ou nem o Tomcat, outra possibilidade é fazer um downgrade do Java para a versão 8.
Realmente, pessoalmente é a que menos recomendo, principalmente se sua aplicação foi construída em versões posteriores, pois você pode ter comportamentos não esperados pela sua aplicação.
Desabilitar acesso à Class
Por fim, você pode desabilitar a atribuição de valores ao objeto Class
com a instrução disallowedFields
para o WebDataBinder
de forma global, como pode ser visto no código abaixo.
@ControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
public class BinderControllerAdvice {
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
String[] denylist = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"};
dataBinder.setDisallowedFields(denylist);
}
}
Aplicar globalmente vai funcionar, mas há uma brecha.
Se qualquer controller implementar seu próprio InitBinder
, ele vai sobrescrever as configurações globais.
Sendo assim, para seguir essa estratégia de maneira mais segura, você precisa sobrescrever a classe RequestMappingHandlerAdapter
na sua aplicação e atualizar WebDataBinder
ao final de todas as outras inicializações.
Após a sobrescrição, na sua aplicação Spring Boot você declara um WebMvcRegistrations
bean para Spring MVC ou um WebFluxRegistrations
bean pra Spring WebFlux.
package expertostech;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.annotation.InitBinderDataBinderFactory;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(CarApp.class, args);
}
@Bean
public WebMvcRegistrations mvcRegistrations() {
return new WebMvcRegistrations() {
@Override
public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
return new ExtendedRequestMappingHandlerAdapter();
}
};
}
private static class ExtendedRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {
@Override
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> methods) {
return new ServletRequestDataBinderFactory(methods, getWebBindingInitializer()) {
@Override
protected ServletRequestDataBinder createBinderInstance(
Object target, String name, NativeWebRequest request) throws Exception {
ServletRequestDataBinder binder = super.createBinderInstance(target, name, request);
String[] fields = binder.getDisallowedFields();
List<String> fieldList = new ArrayList<>(fields != null ? Arrays.asList(fields) : Collections.emptyList());
fieldList.addAll(Arrays.asList("class.*", "Class.*", "*.class.*", "*.Class.*"));
binder.setDisallowedFields(fieldList.toArray(new String[] {}));
return binder;
}
};
}
}
}
E para o Spring MVC sem o Spring Boot, a aplicação pode trocar de @EnableWebMvc
e estender a classe DelegatingWebMvcConfiguration
sobrescrevendo o método createRequestMappingHandlerAdapter
, veja mais detalhes nas referências, configurações avançadas.
Conclusão
Corri aqui pra trazer esse conteúdo pra vocês o mais rápido possível.
Como vocês podem observar a vulnerabilidade é algo bem específico, e muito improvável colocar em risco aplicações mais novas.
Como esse é um assunto crítico, importante e sensível, vamos fazer esse conteúdo chegar ao máximo de pessoas possível, então compartilha esse artigo em todas as suas redes sociais, na empresa, seus projetos, com seus clientes, na sua faculdade, com seus professores, e tudo mais.
Não esquece de seguir o perfil Expertos Tech em todas as redes sociais: https://linktr.ee/expertostech
Referências:
Top comments (0)