Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2020-5421 — Reproducción de la vulnerabilidad de seguridad Spring CVE-2020-5421 | Kitploit
Herramientas/GitHubGitHub/pandamingx/cve-2020-5421
Análisis de VulnerabilidadesAnálisis de CódigoExplotaciónExplotación de Aplicaciones WebPruebas de PenetraciónAprendizaje y Educación
GitHubpandamingx/cve-2020-5421

CVE-2020-5421

Reproducción de la vulnerabilidad de seguridad Spring CVE-2020-5421

Ver Repositorio
3hace 5 añosAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

Replicación de la vulnerabilidad de seguridad Spring CVE-2020-5421

Resumen de la vulnerabilidad

CVE-2020-5421 puede eludir la protección contra ataques RFD mediante el parámetro de ruta jsessionid. La protección anterior contra RFD se agregó para abordar CVE-2015-5211.
¿Qué es RFD?

La vulnerabilidad de descarga de archivos reflejada (RFD) es una técnica de ataque en la que, al simular la descarga de un archivo desde un dominio de confianza, el atacante puede obtener acceso completo a la computadora de la víctima.

Versiones afectadas

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

Replicación de la vulnerabilidad

Dirección de GitHub: https://github.com/pandaMingx/CVE-2020-5421

Versión

Probado con 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 replicación

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;
    }
}

Configuración adicional

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

Agregue ;jsessionid= en la URL, por ejemplo: http://localhost:8080/spring/;jsessionid=/input.bat?input=calc, se descargará un archivo ejecutable llamado input.bat.

Análisis de la vulnerabilidad

CVE-2020-5421 es una evasión de la corrección de CVE-2015-5211. Localice el código de corrección de 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");
		}
	}

Siga el método rawUrlPathHelper.getOriginatingRequestUri, hasta llegar a org.springframework.web.util.UrlPathHelper.removeJsessionid, donde la cadena ;jsessionid= en la URL de la solicitud se trunca (o antes del siguiente punto y coma).

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;
    }

Debido a este código que elimina ;jsessionid=, después de eliminar ;jsessionid=, el código de defensa posterior de CVE-2015-5211 no puede obtener la extensión real del archivo de la solicitud, evadiendo así la defensa RDF.

Recomendaciones de corrección

Durante la replicación de la vulnerabilidad, se agregaron dos parámetros en application.properties: spring.mvc.pathmatch.use-suffix-pattern=true, spring.mvc.contentnegotiation.favor-path-extension=true (por defecto en SpringBoot es false). Por lo tanto, la condición de explotación de CVE-2020-5421 es que deben estar habilitados el modo de coincidencia de sufijos y el mecanismo de negociación de contenido. Si el proyecto SpringBoot no tiene estos dos modos habilitados, no existe condición de explotación y no es necesario tratarlo.
Si existen condiciones de explotación, se proporcionan dos soluciones, de las cuales la solución dos es adecuada para proyectos con alto riesgo de actualizar la versión de Spring.

Solución 1: Actualizar la versión de Spring a una versión segura:

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

Solución 2: Agregar un filtro de seguridad

La solución dos verificará si la extensión de la URL que contiene ;jsessionid= es una extensión segura. Si no lo es, establecerá Content-Disposition=inline;filename=f.txt, forzando a que el contenido de la respuesta se descargue en un archivo llamado f.txt. (El método es consistente con el mecanismo de defensa RDF de 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 referencia

  • 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
Descargar herramienta