Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2020-5421 — إعادة إنتاج ثغرة أمنية في Spring CVE-2020-5421 | Kitploit
أدوات/GitHubGitHub/pandamingx/cve-2020-5421
تحليل الثغرات الأمنيةتحليل الكودالاستغلالاستغلال تطبيقات الويباختبار الاختراقالتعلم والتعليم
GitHubpandamingx/cve-2020-5421

CVE-2020-5421

إعادة إنتاج ثغرة أمنية في Spring CVE-2020-5421

عرض المستودع
3منذ 5 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

إعادة إنتاج الثغرة الأمنية CVE-2020-5421 في Spring

نظرة عامة على الثغرة

تتيح CVE-2020-5421 تجاوز الحماية ضد هجمات RFD من خلال معامل المسار jsessionid. وقد أُضيفت الحماية السابقة ضد RFD لمواجهة CVE-2015-5211.
ما هي RFD

ثغرة التنزيل الانعكاسي للملفات (RFD) هي أسلوب هجوم يتم من خلاله تنزيل ملف افتراضيًا من نطاق موثوق، مما يمنح المهاجم وصولًا كاملًا إلى جهاز الضحية.

الإصدارات المتأثرة

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

إعادة إنتاج الثغرة

رابط GitHub: https://github.com/pandaMingx/CVE-2020-5421

الإصدار

تم إجراء الاختبار بناءً على 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>

كود إعادة الإنتاج

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

إعدادات إضافية

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

أضف ;jsessionid= إلى عنوان URL، مثل http://localhost:8080/spring/;jsessionid=/input.bat?input=calc، وسيتم تنزيل ملف قابل للتنفيذ باسم input.bat.

تحليل الثغرة

CVE-2020-5421 هي تجاوز لطريقة إصلاح CVE-2015-5211. يقع كود الإصلاح الخاص بـ 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");
		}
	}

بمتابعة طريقة rawUrlPathHelper.getOriginatingRequestUri، نصل بالتدريج إلى طريقة org.springframework.web.util.UrlPathHelper.removeJsessionid، والتي تقوم بقطع السلسلة التي تبدأ بـ ;jsessionid= من عنوان URL للطلب (أو قبل الفاصلة المنقوطة التالية).

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

بسبب كود حذف ;jsessionid= هذا، فإن كود الدفاع اللاحق الخاص بـ CVE-2015-5211 لن يتمكن بعد حذف ;jsessionid= من الحصول على اسم الامتداد الحقيقي للطلب، وبالتالي يتم تجاوز كود الدفاع ضد RFD.

توصيات الإصلاح

أثناء إعادة إنتاج الثغرة، تمت إضافة معاملين في ملف applcation.properties: spring.mvc.pathmatch.use-suffix-pattern=true و spring.mvc.contentnegotiation.favor-path-extension=true (القيمة الافتراضية في SpringBoot هي false). يتضح أن شرط استغلال CVE-2020-5421 هو تفعيل نمط مطابقة الامتداد وآلية التفاوض على المحتوى (Content Negotiation). إذا لم يتم تمكين هذين النمطين في مشروع SpringBoot، فلا توجد شروط لاستغلال الثغرة ولا حاجة لاتخاذ إجراء. إذا توافرت شروط الاستغلال، نقدم هنا حلّين، حيث يناسب الحل الثاني المشاريع التي قد يشكّل ترقية إصدار Spring فيها مخاطرة كبيرة.

الحل الأول: ترقية إصدار Spring إلى إصدار آمن:

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

الحل الثاني: إضافة مرشح أمان

يتحقق الحل الثاني مما إذا كان امتداد عنوان URL الذي يحتوي على ;jsessionid= امتدادًا آمنًا، وإذا لم يكن كذلك، يتم تعيين Content-Disposition=inline;filename=f.txt لفرض تنزيل محتوى الاستجابة في ملف باسم f.txt. (النهج مطابق لآلية دفاع RFD في 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;
    }

}

المراجع

  • 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
تنزيل الأداة