Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2020-5421 — Spring 安全漏洞 CVE-2020-5421复现 | Kitploit
Tools/GitHubGitHub/pandamingx/cve-2020-5421
Vulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & Education
GitHubpandamingx/cve-2020-5421

CVE-2020-5421

Spring 安全漏洞 CVE-2020-5421复现

Repository anzeigen
3vor 5 JahrenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

Wiederholung der Spring-Sicherheitslücke CVE-2020-5421

Übersicht der Sicherheitslücke

CVE-2020-5421 kann über den jsessionid-Pfadparameter den Schutz gegen RFD-Angriffe umgehen. Der vorherige Schutz gegen RFD wurde als Reaktion auf CVE-2015-5211 hinzugefügt.
Was ist RFD?

Reflective File Download (RFD) ist eine Angriffstechnik, bei der ein Angreifer durch das virtuelle Herunterladen einer Datei von einer vertrauenswürdigen Domäne vollständigen Zugriff auf den Computer des Opfers erlangen kann.

Betroffene Versionen

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

Reproduktion der Sicherheitslücke

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

Version

Getestet mit SpringBoot-2.1.7.RELEASE und 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>

Reproduktionscode

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

Zusätzliche Konfiguration

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

Fügen Sie ;jsessionid= zur URL hinzu, z.B. http://localhost:8080/spring/;jsessionid=/input.bat?input=calc, dann wird eine ausführbare Datei namens input.bat heruntergeladen.

Analyse der Sicherheitslücke

CVE-2020-5421 umgeht die Reparaturmethode von CVE-2015-5211. Die Reparatur von CVE-2015-5211 befindet sich in der Methode addContentDispositionHeader der Klasse AbstractMessageConverterMethodProcessor unter org.springframework.web.servlet.mvc.method.annotation.

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

Wenn man der Methode rawUrlPathHelper.getOriginatingRequestUri folgt, gelangt man zur Methode org.springframework.web.util.UrlPathHelper.removeJsessionid, die die Zeichenfolge ;jsessionid= in der Anfrage-URL abschneidet (oder vor dem nächsten ;).

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

Da dieser Code ;jsessionid= entfernt, kann der nachfolgende Verteidigungscode von CVE-2015-5211 nach der Entfernung von ;jsessionid= den tatsächlichen Dateinamensuffix der Anfrage nicht mehr abrufen, wodurch der RFD-Abwehrcode umgangen wird.

Empfehlungen zur Behebung

Während der Reproduktion der Sicherheitslücke wurden in der Datei application.properties zwei Parameter hinzugefügt: spring.mvc.pathmatch.use-suffix-pattern=true, spring.mvc.contentnegotiation.favor-path-extension=true (Standardmäßig false in SpringBoot). Es ist ersichtlich, dass die Ausnutzungsbedingung von CVE-2020-5421 das Aktivieren des Suffix-Matching-Modus und des Content-Negotiation-Mechanismus erfordert. Wenn in einem SpringBoot-Projekt diese beiden Modi nicht aktiviert sind, besteht keine Ausnutzungsbedingung, und es muss nicht behandelt werden.
Falls die Ausnutzungsbedingungen vorliegen, werden hier zwei Lösungen angeboten, wobei Lösung 2 für Projekte geeignet ist, bei denen ein Upgrade der Spring-Version ein hohes Risiko darstellt.

Lösung 1: Upgrade der Spring-Version auf eine sichere Version:

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

Lösung 2: Hinzufügen eines Sicherheitsfilters

Lösung 2 überprüft, ob der Suffix einer URL, die ;jsessionid= enthält, ein sicherer Suffix ist. Ist dies nicht der Fall, wird Content-Disposition=inline;filename=f.txt gesetzt, um den Inhalt der Antwort zwangsweise in eine Datei namens f.txt herunterzuladen. (Die Vorgehensweise entspricht dem RFD-Abwehrmechanismus von 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;
    }

}

Referenzen

  • 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
Tool herunterladen