Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2020-5421 — Reprodução da vulnerabilidade de segurança do Spring CVE-2020-5421 | Kitploit
Ferramentas/GitHubGitHub/pandamingx/cve-2020-5421
Análise de VulnerabilidadesAnálise de CódigoExploraçãoExploração de Aplicações WebTestes de PenetraçãoAprendizado e Educação
GitHubpandamingx/cve-2020-5421

CVE-2020-5421

Reprodução da vulnerabilidade de segurança do Spring CVE-2020-5421

Ver Repositório
35há 5 anosAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Replicação da Vulnerabilidade de Segurança Spring CVE-2020-5421

Visão Geral da Vulnerabilidade

CVE-2020-5421 pode contornar a proteção contra ataques RFD através do parâmetro de caminho jsessionid. A proteção anterior contra RFD foi adicionada em resposta ao CVE-2015-5211.
O que é RFD

A vulnerabilidade de download reflexivo de arquivos (RFD) é uma técnica de ataque que, ao fazer download virtual de um arquivo de um domínio confiável, permite que um atacante obtenha acesso total ao computador da vítima.

Versões Afetadas

Spring Framework 5.2.0 - 5.2.8
Spring Framework 5.1.0 - 5.1.17
Spring Framework 5.0.0 - 5.0.18
Spring Framework 4.3.0 - 4.3.28

Reprodução da Vulnerabilidade

github地址:https://github.com/pandaMingx/CVE-2020-5421

Versão

Testado com SpringBoot-2.1.7.RELEASE, Spring-xxx-5.1.9.RELEASE.

root@kitploit:~
   <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

Código de Reprodução

root@kitploit:~
@Controller
@RequestMapping(value = "spring")
public class cve20205421 {

    // localhost:8080/spring/input?input=hello
    @RequestMapping("input")
    @ResponseBody
    public String input(String input){
        return input;
    }
}

Configuração Adicional

root@kitploit:~
spring.mvc.pathmatch.use-suffix-pattern=true
spring.mvc.contentnegotiation.favor-path-extension=true

Adicionando ;jsessionid= na URL, como http://localhost:8080/spring/;jsessionid=/input.bat?input=calc, resultará no download de um arquivo executável chamado input.bat.

Análise da Vulnerabilidade

CVE-2020-5421 é uma forma de contornar a correção do CVE-2015-5211, localizando o código de correção do CVE-2015-5211. org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor. addContentDispositionHeader

root@kitploit:~
/**
	 * Check if the path has a file extension and whether the extension is
	 * either {@link #WHITELISTED_EXTENSIONS whitelisted} or explicitly
	 * {@link ContentNegotiationManager#getAllFileExtensions() registered}.
	 * If not, and the status is in the 2xx range, a 'Content-Disposition'
	 * header with a safe attachment file name ("f.txt") is added to prevent
	 * RFD exploits.
	 */
	private void addContentDispositionHeader(ServletServerHttpRequest request, ServletServerHttpResponse response) {
		HttpHeaders headers = response.getHeaders();
		if (headers.containsKey(HttpHeaders.CONTENT_DISPOSITION)) {
			return;
		}

		try {
			int status = response.getServletResponse().getStatus();
			if (status < 200 || status > 299) {
				return;
			}
		}
		catch (Throwable ex) {
			// ignore
		}

		HttpServletRequest servletRequest = request.getServletRequest();
		String requestUri = rawUrlPathHelper.getOriginatingRequestUri(servletRequest);

		int index = requestUri.lastIndexOf('/') + 1;
		String filename = requestUri.substring(index);
		String pathParams = "";

		index = filename.indexOf(';');
		if (index != -1) {
			pathParams = filename.substring(index);
			filename = filename.substring(0, index);
		}

		filename = decodingUrlPathHelper.decodeRequestString(servletRequest, filename);
		String ext = StringUtils.getFilenameExtension(filename);

		pathParams = decodingUrlPathHelper.decodeRequestString(servletRequest, pathParams);
		String extInPathParams = StringUtils.getFilenameExtension(pathParams);

		if (!safeExtension(servletRequest, ext) || !safeExtension(servletRequest, extInPathParams)) {
			headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline;filename=f.txt");
		}
	}

Seguindo o método rawUrlPathHelper.getOriginatingRequestUri, chegamos ao método org.springframework.web.util.UrlPathHelper.removeJsessionid, que irá truncar a string ;jsessionid= na URL da requisição (ou antes do próximo ;).

root@kitploit:~
private String removeJsessionid(String requestUri) {
        int startIndex = requestUri.toLowerCase().indexOf(";jsessionid=");
        if (startIndex != -1) {
            int endIndex = requestUri.indexOf(59, startIndex + 12);
            String start = requestUri.substring(0, startIndex);
            requestUri = endIndex != -1 ? start + requestUri.substring(endIndex) : start;
        }

        return requestUri;
    }

Devido a este código que remove ;jsessionid=, após a remoção, o código de defesa subsequente do CVE-2015-5211 não conseguirá obter o nome real da extensão do arquivo da requisição, contornando assim a defesa contra RFD.

Recomendações de Correção

Durante a reprodução da vulnerabilidade, dois parâmetros foram adicionados em applcation.properties: spring.mvc.pathmatch.use-suffix-pattern=true, spring.mvc.contentnegotiation.favor-path-extension=true (padrão é false no SpringBoot).
Portanto, a condição de exploração do CVE-2020-5421 é que o modo de correspondência de sufixo e o mecanismo de negociação de conteúdo devem estar ativados. Se o projeto SpringBoot não tiver esses dois modos habilitados, não há condição de exploração, podendo ser ignorado.
Se existirem condições de exploração, aqui são fornecidas duas soluções, sendo a segunda adequada para projetos onde a atualização da versão do Spring apresenta riscos maiores.

Solução 1: Atualizar a versão do Spring para uma versão segura:

Spring Framework 5.2.9
Spring Framework 5.1.18
Spring Framework 5.0.19
Spring Framework 4.3.29

Solução 2: Adicionar um filtro de segurança

A segunda solução verificará se a extensão da URL que contém ;jsessionid= é uma extensão segura. Se não for, define Content-Disposition=inline;filename=f.txt, forçando o download do conteúdo da resposta para um arquivo chamado f.txt. (A abordagem é a mesma do mecanismo de defesa RFD do Spring).

root@kitploit:~
public class SpringJsessionidRdfFilter implements Filter {

    private final Set<String> safeExtensions = new HashSet<>();
    /* Extensions associated with the built-in message converters */
    private static final Set<String> WHITELISTED_EXTENSIONS = new HashSet<>(Arrays.asList(
            "txt", "text", "yml", "properties", "csv",
            "json", "xml", "atom", "rss",
            "png", "jpe", "jpeg", "jpg", "gif", "wbmp", "bmp"));

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;

        String contentDisposition = response.getHeader(HttpHeaders.CONTENT_DISPOSITION);
        if (!"".equals(contentDisposition)&&null != contentDisposition) {
            return;
        }

        try {
            int status = response.getStatus();
            if (status < 200 || status > 299) {
                return;
            }
        }
        catch (Throwable ex) {
            // ignore
        }

        String requestUri = request.getRequestURI();

        System.out.println(requestUri);

        if(requestUri.contains(";jsessionid=")){
            int index = requestUri.lastIndexOf('/') + 1;
            String filename = requestUri.substring(index);
            String pathParams = "";

            index = filename.indexOf(';');
            if (index != -1) {
                pathParams = filename.substring(index);
                filename = filename.substring(0, index);
            }

            UrlPathHelper decodingUrlPathHelper = new UrlPathHelper();
            filename = decodingUrlPathHelper.decodeRequestString(request, filename);
            String ext = StringUtils.getFilenameExtension(filename);

            pathParams = decodingUrlPathHelper.decodeRequestString(request, pathParams);
            String extInPathParams = StringUtils.getFilenameExtension(pathParams);

            if (!safeExtension(request, ext) || !safeExtension(request, extInPathParams)) {
                response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "inline;filename=f.txt");
            }
        }
        filterChain.doFilter(servletRequest,servletResponse);
    }

    private boolean safeExtension(HttpServletRequest request, @Nullable String extension) {
        if (!StringUtils.hasText(extension)) {
            return true;
        }
        extension = extension.toLowerCase(Locale.ENGLISH);
        this.safeExtensions.addAll(WHITELISTED_EXTENSIONS);
        if (this.safeExtensions.contains(extension)) {
            return true;
        }
        String pattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        if (pattern != null && pattern.endsWith("." + extension)) {
            return true;
        }
        if (extension.equals("html")) {
            String name = HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
            Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(name);
            if (!CollectionUtils.isEmpty(mediaTypes) && mediaTypes.contains(MediaType.TEXT_HTML)) {
                return true;
            }
        }
        return false;
    }

}

Documentos de Referência

  • https://www.xf1433.com/4595.html
  • https://www.nsfocus.com.cn/html/2020/39_0921/976.html
  • https://zhuanlan.zhihu.com/p/161166505
  • https://github.com/spring-projects/spring-framework/commit/2281e421915627792a88acb64d0fea51ad138092
Baixar ferramenta