보안정보
전문화된 보안 관련 자료, 보안 트렌드를 엿볼 수 있는
차세대 통합보안관리 기업 이글루코퍼레이션 보안정보입니다.
Spring4Shell(CVE-2022-22965) 취약점 원인분석 및 대응방안
2022.05.04
35,667
01. Spring Framework의 개요
자바 엔터프라이즈 어플리케이션 개발의 일반적인 환경인 EJB(Enterprise Java Bean)가 갖는 단점으로 단위 테스트 및 예외처리의 복잡성, 불필요한 메서드 구현 등이 있다. 이를 보완하고자 아키텍처의 유연성(독립적인 모듈 구성, AOP를 활용한 Logging 및 Transaction 등)을 강화한 JAVA 기반의 Web Framework인 Spring Framework가 등장하게 되었다. Spring Framework는 Spring MVC 패턴, 모델(Model), 뷰(View), 컨트롤러(Controller)의 구성을 통해 동적 웹 사이트 구현을 위한 다양한 서비스를 제공함에 따라 개발자들의 각광을 받게 되었다. 이러한 변화는 개발자 포럼인 StackOverflow에서 발표한 ‘2021 Developer Survey’의 ‘Web frameworks’분야에서 Professional Developers 집단에서 16.48%의 높은 응답 수치를 통해 Spring Framework의 시장 점유율을 유추할 수 있다.
[그림 1] 2021년 사용 웹 프레임워크, 라이브러리 투표 (출처 : StackOverflow, ‘2021 Developer Survey’)
점유율이 높은 만큼 Spring Framework의 취약점을 이용한 웹 기반의 사이버 공격도 증가하고 있다. NIST의 NVD에서 관리되고 있는 CVE목록에 따르면 Spring Framework에서 발견되어 등록된 CVE는 △ 2019년 15개, △ 2020년 20개, △ 2021년 24개로 점차 증가하고 있다. 2022년 4월 7일 기준 6개의 CVE가 등록되어 있는데, 이에 대한 설명은 다음 [표 1]와 같다.
CVE Name |
CVSS |
Description |
CVE-2022-27772 |
7.8 |
** UNSUPPORTED WHEN ASSIGNED ** spring-boot versions prior to version v2.2.11.RELEASE was vulnerable to temporary directory hijacking. This vulnerability impacted the org.springframework.boot.web.server.AbstractConfigurableWebServerFactory.createTempDir method. NOTE: This vulnerability only affects products and/or versions that are no longer supported by the maintainer. |
CVE-2022-22965 |
9.8 |
A Spring MVC or Spring WebFlux application running on JDK 9+ may be vulnerable to remote code execution (RCE) via data binding. The specific exploit requires the application to run on Tomcat as a WAR deployment. If the application is deployed as a Spring Boot executable jar, i.e. the default, it is not vulnerable to the exploit. However, the nature of the vulnerability is more general, and there may be other ways to exploit it. |
CVE-2022-22963 |
9.8 |
In Spring Cloud Function versions 3.1.6, 3.2.2 and older unsupported versions, when using routing functionality it is possible for a user to provide a specially crafted SpEL as a routing-expression that may result in remote code execution and access to local resources. |
CVE-2022-22950 |
5.4 |
n Spring Framework versions 5.3.0 - 5.3.16 and older unsupported versions, it is possible for a user to provide a specially crafted SpEL expression that may cause a denial of service condition. |
CVE-2022-22947 |
10.0 |
In spring cloud gateway versions prior to 3.1.1+ and 3.0.7+ , applications are vulnerable to a code injection attack when the Gateway Actuator endpoint is enabled, exposed and unsecured. A remote attacker could make a maliciously crafted request that could allow arbitrary remote execution on the remote host. |
CVE-2022-22946 |
5.5 |
In spring cloud gateway versions prior to 3.1.1+ , applications that are configured to enable HTTP2 and no key store or trusted certificates are set will be configured to use an insecure TrustManager. This makes the gateway able to connect to remote services with invalid or custom certificates. |
[표 1] 2022년 등록된 Spring 제품군의 CVE 목록
2022년에 등록된 CVE 중 CVSS가 9.0 이상의 Critical 취약점은 [그림 2]와 같이 △ CVE-2022-22947, △ CVE-2022-22963, △ CVE-2022-22965로 3개가 존재하며 모두 2022년 3월에 공개된 취약점이다.
[그림 2] 2022년 3월 공개된 CVSS CRITICAL 수준의 Spring 관련 취약점 목록 (출처 : https://twitter.com/dev_Gejr/status/1509655264227733507/photo/1)
이번 호에서는 공격의 영향도나 추가 공격 가능성 등을 고려하여 Spring Core에서 발생한 취약점인 Spring4Shell(CVE-2022-22965)에 대해서 보다 상세하게 분석해보고자 한다.
02. Spring4Shell(CVE-2022-22965) 분석
1) Spring4Shell 취약점 소개
2022년 3월 29일, 사이버 보안 연구원으로 추측되는 중국인의 트위터에 “A Java Springcore [sic] RCE 0day exploit has been leaked”라는 글과 함께 JAVA library인 Spring Core를 익스플로잇 한 PoC(Proof of Concept) 스크린샷이 공개되었다. 공개된 트위터 내용은 얼마 뒤 바로 삭제가 되었으나, 이미 많은 보안 연구원들은 해당 트위터 내용을 토대로 PoC 테스트를 수행하여 해당 PoC 공격이 Spring Framework를 타깃으로 하는 제로데이 공격임을 인지하게 되었다.
Spring4Shell(CVE-2022-22965)취약점은 2021년 IT 환경의 퍼펙트스톰을 유발한 Log4Shell(CVE-2021-44228)과 유사한 명칭(4Shell = for Shell)을 사용하고 있다. 용어에서 알 수 있듯이 두 공격 모두 Shell을 이용해 RCE(Remote Command Execution)공격이 가능하며, JAVA 기반으로 동작하는 프레임워크를 활용하고 있다는 점에서 유사점을 보이고 있다. Spring4Shell 취약점은 CVSS3.0에서 9.8이라는 높은 수치를 보이고 있으며, RCE공격에 성공하기 위한 발현 조건은 [표 2]와 같다.
구분 |
취약점 발현조건 |
JAVA |
JDK (JAVA Development Kit) 9 버전 이상의 환경 |
Spring Framework |
5.3.17 및 5.2.19 이하 버전의 Spring Framework 환경 (배포 방식을 WAR로 하고 있을 경우) |
Apache Tomcat |
8.5.77 이하, 9.0.61 이하 및 10.0.19 이하 버전의 환경 |
매개변수 |
POJO(Plain Old Java Object로 환경과 기술에 종속되지 않고 재활용될 수 있도록 설계된 객체)방식의 처리 환경 |
[표 2] Spring4Shell(CVE-2022-22965) 취약점 발현 조건
Spring4Shell 취약점을 이해하기 위해서는 Attacker(공격자)와 Victim(피해시스템)간의 공격구성을 [그림 3]과 같이 도식화할 수 있다.
[그림 3] Spring4Shell(CVE-2022-22965) 공격 구성도
[그림 3]의 구성도에서 설명된 공격순서는 다음과 같다.
2) Spring4Shell 취약점 환경구성
Spirng4Shell 취약점의 상세 분석을 위해 취약점이 발현되는 환경을 구성하였다. 취약점이 발현되기 위한 조건으로 △ JDK 9+, △ Spring Framework 5.2.20 및 5.3.18 미만 버전, △ Apache Tomcat 8.5.78 이하, 9.0.62 이하 및 10.0.20 미만 버전, △ WAR 배포 방식의 환경이 필요하다. 발현 조건을 만족하기 위해 Windows Server 2012 가상 머신에 JDK 11 버전, Spring Framework 5.1.2 버전, Apache Tomcat 9.0.60 버전을 설치하였고 WAR 배포 방식을 선택하여 환경을 구성했다. 이후 PoC를 사용하여 공격을 수행하였을 때, 정상적으로 웹 쉘 파일 생성 여부를 확인하였다. 또 생성된 파일에 접근하여 명령어를 전달하였을 때, 정상적으로 수행되는지도 함께 확인하였다. Spring4Shell 취약점 분석을 위해 구성한 환경은 [표 3] Victim Server 환경과 [표 4] Attacker 환경과 같다.
구분 |
환경 정보 |
Server OS |
Windows Server 2012 |
JDK |
11 |
Spring Framework |
5.1.2 |
Apache Tomcat |
9.0.60 |
IP |
192.168.80.246 |
구분 |
환경 정보 |
Server OS |
Windows 10 |
IP |
192.168.0.21 |
[표 4] Attacker 환경
먼저 Spring4Shell 취약점은 자바 객체인 Bean의 정보를 가져올 때 사용되는 getBeanInfo 메소드의 기능을 통해 발현된다. 매개변수를 Bean을 통해 가져오기 위한 backend를 [그림 4]과 같은 환경으로 구현하였다.
[그림 4] POJO 방식의 매개변수 처리 (좌: Gretting Class, 우: Controller)
3) Spring4Shell 취약점 상세분석
Spring4Shell 취약점이 발현되는 근본적인 원인은 △ ClassLoader와 △ JDK 9+ 이다.
전체적인 공격 흐름은 다음과 같다. Spring Framework는 매개변수를 받아오는 과정에서 객체 값을 불러오기 위해 getBeanInfo 메소드를 사용하게 된다. 사용된 getBeanInfo 메소드는 인자를 지정해주지 않고 호출할 경우 불러오는 객체의 속성값 뿐만 아니라 상위클래스의 값이 함께 저장된다. 이러한 점은 결과적으로 ClassLoader를 호출할 수 있게 된다. 호출된 ClassLoader는 다른 클래스를 동적으로 호출하여 최종적으로 Apache Tomcat log를 관리하는 AccessLogValve 클래스에 접근할 수 있게 되고, log를 저장하는데 필요한 속성값(log 파일 이름, log 파일 내용, log 파일 확장자, log 파일 저장 디렉터리 등)을 변경하여 access_log 대신 WebShell이 저장되게 한다.
Spring Framework 환경에서 POJO 방식으로 매개변수 값을 가져올 때, 객체 정보를 가져오는 getBeanInfo 메소드를 호출한다. getBeanInfo 메소드는 Introspector 클래스 내에서 Property 값을 PropertyDescriptor 배열로 저장하며, getBeanInfo를 호출하는 방법은 여러 가지가 존재한다.
(출처 : https://docs.oracle.com/javase/9/docs/api/java/beans/Introspector.html)
NO |
Method |
Description |
1 |
getBeanInfo(Class beanClass) |
Introspect on a Java Bean and learn about all its properties, exposed methods, and events. |
2 |
getBeanInfo(Class beanClass, int flags) |
Introspect on a Java bean and learn about all its properties, exposed methods, and events, subject to some control flags. |
3 |
getBeanInfo(Class beanClass, Class stopClass) |
Introspect on a Java bean and learn all about its properties, exposed methods, below a given "stop" point. |
4 |
getBeanInfo(Class beanClass, Class stopClass, int flags) |
Introspect on a Java Bean and learn about all its properties, exposed methods and events, below a given stopClass point subject to some control flags. |
[표 5] getBeanInfo 메소드
인자 값으로 stopClass 인자를 지정해주지 않은 [표 5]에서 1번 방식으로 getBeanInfo 메소드를 호출할 경우 상위 클래스(Object class)에 대한 속성값도 함께 저장된다. 확인을 위해 실제 환경에서 stopClass 인자 없이 getBeanInfo 메소드를 호출하여 객체의 property 정보를 불러와 출력하도록 구현하였다. 사용한 객체는 ‘id’, ‘content’ 변수만 지정한 클래스의 객체를 사용하였고, 검증 결과 ‘id’, ‘content’ 값 외 ‘class’ 이름의 속성이 존재하는 것을 확인하였다. 이후 ‘class’의 속성도 확인해본 결과 여러 속성 중 ‘classLoader’가 존재하는 것을 확인하였다.
[그림 5] class의 Property에 classLoader가 존재하는 것을 확인
Spring4Shell 취약점은 getBeanInfo 메소드를 stopClass 인자 없이 호출하여 상위 객체의 정보를 함께 저장하고, 이 점을 이용해 classLoader를 호출하여 다른 클래스에 접근 후 악성 행위를 진행한다. stopClass 인자 없이 getBeanInfo 메소드를 호출하여 상위 객체의 Property를 이용한 취약점은 이번이 처음이 아니다. 2010년 초 CVE-2010-1622의 취약점이 발견되었는데, 이 취약점 역시 classLoader를 호출하여 원격 코드 실행을 진행한 취약점이다.
[그림 6] CVE-2010-1622 소개
[그림 7] Spring Framework 2.5.6 버전 내 CachedIntrospectionResults.java
CVE-2010-1162 취약점을 조치하기 위해 Spring에서는 [그림 7]과 같이 getBeanInfo를 통해 저장된 PropertyDescriptor의 값을 확인하는 로직을 추가하여 Spring Framework 2.5.6 버전을 배포하였다. [그림 7]에서 242번 라인을 보면 Property의 이름이 classLoader인 경우에는 해당 Property 값을 저장하지 않도록 구현하였고, 결과적으로 classLoader Property가 저장되지 않아 공격자의 페이로드 내부 classLoader가 호출되지 않도록 막는 로직이 추가되었다.
2010년 당시 보안 패치된 취약점이 2022년 다시 발현된 이유는 JDK에 있다. JDK는 9버전을 배포할 당시 △ module 의 기능을 함께 배포하였다. 모듈 시스템의 도입으로 기존의 CVE-2010-1162 취약점에서 사용된 class.classLoader 호출을 class.module.classLoader 호출로 우회하였고, 결과적으로 다시 classLoader가 호출되어 최종적으로 RCE가 가능한 취약점으로 발현된 것이다.
Spring4Shell 취약점은 JDK 9 버전 이상에서 추가된 module 기능을 통해 classLoader를 호출한다. 호출된 classLoader는 다른 클래스들을 연쇄적으로 호출하게 되고 최종적으로 Apache Tomcat의 Logging 관련 클래스인 AccessLogValve에 도달하여 Attribute 값을 변경한다. 기존 실제 로그가 저장되는 Attribute 설정을 WebShell이 저장되게 변경하는 것이다. 실제 PoC에 사용된 Payload를 확인해보면 [표 6]의 값을 사용하여 공격하는 것을 알 수 있다.
No |
Parameter |
Data |
1 |
class.module.classLoader.resources. context.parent.pipeline.first.pattern |
%25%7Bprefix%7Di%20java.io.InputStream%20in%20%3D%20%25 %7Bc%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).get InputStream()%3B%20int%20a%20%3D%20- 1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048% 5D%3B%20while((a%3Din.read(b))!%3D- 1)%7B%20out.println(new%20String(b))%3B%20%7D%20%25%7Bs uffix%7Di |
2 |
class.module.classLoader.resources. context.parent.pipeline.first.suffix |
.jsp |
3 |
class.module.classLoader.resources. context.parent.pipeline.first.directory |
webapps/ROOT |
4 |
class.module.classLoader. resources.context.parent.pipeline.first.prefix |
shell |
5 |
class.module.classLoader.resources. context.parent.pipeline.first.fileDateFormat |
|
[표 6] PoC payload (출처 : https://github.com/reznok/Spring4Shell-POC/blob/master/exploit.py)
[그림 8] Spring4Shell PoC payload 일부
공개된 PoC 의 Payload 일부를 확인해보면 [그림 8]과 같은 형태를 띠고 있는 것을 알 수 있다. JDK 9+ 이상에서 추가된 module을 사용하여 classLoader를 호출하고, 호출된 classLoader가 여러 메소드가 연쇄적으로 호출되는 것을 의미한다. 호출된 메소드들의 순서는 [그림 9]와 같다.
[그림 9] Spring4Shell payload 호출 순서
Spring4Shell 취약점에 사용된 Payload는 Module을 통해 ClassLoader를 호출하고 Log 저장 설정을 변경하기 위해 getFirst 메소드를 통하여 AccessLogValve에 접근한다. 이후 AccessLogValve 내에서 Logging을 위해 사용되는 Attribute 값을 악의적인 값으로 조작하여 WebShell을 생성한다. 실제 PoC 공격 구문에는 AccessLogValve의 5개의 Attribute의 값을 조작하여 공격을 수행, WebShell을 생성한다. 사용되는 5가지 Attribute에 대한 설명은 Apache Tomcat 공식 홈페이지에 존재하며, [표 7]과 같다.
Attribute |
Description |
directory |
Absolute or relative pathname of a directory in which log files created by this valve will be placed. If a relative path is specified, it is interpreted as relative to $CATALINA_BASE. If no directory attribute is specified, the default value is "logs" (relative to $CATALINA_BASE). |
prefix |
The prefix added to the start of each log file's name. If not specified, the default value is "access_log". |
suffix |
The suffix added to the end of each log file's name. If not specified, the default value is "" (a zero-length string), meaning that no suffix will be added. |
pattern |
A formatting layout identifying the various information fields from the request and response to be logged, or the word common or combined to select a standard format. See below for more information on configuring this attribute. |
fileDateFormat |
Allows a customized timestamp in the access log file name. The file is rotated whenever the formatted timestamp changes. The default value is .yyyy-MM-dd. If you wish to rotate every hour, then set this value to .yyyy-MM-dd.HH. The date format will always be localized using the locale en_US. |
[표 7] Spring4Shell 공격에 사용된 AccessLogValve의 Attribute 설명(출처 : https://tomcat.apache.org/tomcat-9.0-doc/config/valve.html#Access_Log_Valve/Attributes)
Spring4Shell 취약점 공격 구문으로 인한 AccessLogValve의 Attribute 값 변화를 확인하기 위하여 PoC 공격을 진행하기 전 과 후의 값을 확인하였다. 먼저 PoC 공격을 진행하기 전 정상적인 Attribute 값을 확인하였다. 이후 PoC 공격을 진행한 뒤 Attribute 값이 어떻게 변화되는지 확인하였다. 확인 방법으로는 AccessLogValve 내 Attribute 값을 설정하는 setter에 브레이크포인트를 걸고 디버깅을 진행하여 각각의 값 변화를 확인하였다.
[그림 10] PoC로 공격을 수행하기 이전 AccessLog Attribute (정상)
[그림 11] PoC로 공격을 수행한 이후 AccessLog Attribute (변조)
PoC 공격을 진행하기 이전의 AccessLogValve의 Attribute 값은 [그림 10]과 같고 PoC 공격 이후는 [그림 11]과 같다. 공격 구문에 사용된 Payload 값에 맞춰 Attribute가 변조된 것을 알 수 있다.
[그림 12] Spring4Shell 취약점 공격구문으로 인한 Attribute 값 변화
기존에 정상적인 log를 남기도록 설정된 AccessLogValve의 Attribute 값이 PoC 공격을 통해 classLoader가 호출, AccessLogValve 클래스에 접근하여 set[Attribute]를 호출하였고 기존의 값이 비정상적인 값들로 변경된 것을 확인할 수 있다.
변경된 Attribute의 다른 요소들도 중요하지만 △ directory, △ pattern, △ suffix 의 값이 중요하다. 원하는 경로에 파일을 생성할 수 있게 해주는 △ directory 값을 WEB-ROOT 하위로 설정하여 브라우저를 통해 접근할 수 있도록 조작한다. 또한, Log 파일의 내용을 의미하는 △ patter의 값을 WebShell의 내용으로 조작하였고, JAVA 기반의 JSP(Java Server Pages) 파일로 로그 파일을 저장할 수 있도록 △ suffix의 값을 조작하였다.
결론적으로 PoC에 포함된 Payload로 인해 logs 디렉터리 내에 “%h %l %u %t %r %s %b” 의 내용으로 저장되어야 할 ‘localhost_access_log.txt’ 파일이 Attribute 변조로 인해 ‘wtpwebapps/test123/resources’ 디렉터리에 WebShell의 내용을 가진 ‘test.jsp’ 파일로 저장되게 변조된 것이다.
조작된 Attribute 값에 맞춰 정상 로그파일 대신 실제 WebShell이 생성되는 것을 확인할 수 있었으며, 브라우저를 통해 WebShell 페이지에 공격 구문을 전달하였을 시, 공격 구문이 Spring Framework가 설치된 취약 환경에서 정상적으로 작동하는 것을 알 수 있다.
[그림 13] Log file 대신 생성된 WebShell 파일
[그림 14] 브라우저를 통해 접근 후 명령어 전달 결과
4) Spring4Shell PoC 분석
Spring4Shell을 공격하기 위해 공개된 PoC유형은 현재 2가지 버전이 존재한다. 두 버전은 PoC 공격을 진행한 이후 생성된 웹 쉘에 명령어 전달 과정에서 차이점이 있다. 하나의 버전에선 pwd인자에 특정 문자를 입력하여야 함께 전달된 명령어가 수행되도록 구현되어 있고, 다른 버전은 특별한 확인 절차 없이 바로 명령어를 전달할 수 있도록 구현되어 있다. pwd 인자에 특정 문자를 입력하여야 하는 PoC의 경우 WebShell에 접근하여 명령어를 전달할 때, 누구나 WebShell을 이용하는 것을 방지하고자 사용자를 확인하는 방법으로 일종의 Password를 요구 및 확인하는 것으로 추측된다.
공개된 PoC는 pwd 인자의 존재 여부를 제외하고는 두 버전의 PoC 모두 공통으로 Header에 문자열 치환을 위한 특수 데이터값을 넣어준 점과 Body에 Module을 이용한 ClassLoader를 호출, 이를 통해 AccessLogValve의 Attribute 값을 조작하는 구문이 존재한다. 이번 PoC 분석 부문에서는 공격의 페이로드에 집중하기 위해 pwd인자를 사용하지 않고 1개를 인자만을 사용하는 PoC를 분석하였다.
[그림 15] PoC Code 내 요청 Header 추가 영역
[그림 15]을 통해 요청 시 Header 영역에 문자 치환을 위한 헤더 값을 넣어준 것을 확인할 수 있다. 이 값은 아래 [그림 16]에서 다뤄지는 Body 영역에 추가되는 데이터에 영향을 끼친다.
[그림 16] PoC Code 내 요청 Body 추가 영역
PoC 코드 내에 Body 영역에 붙는 데이터를 확인해보면, Log 파일의 내용을 의미하는 Log_Pattern 값이 URL Encoding을 통해 변환되어 담겨있는 것을 알 수 있다. 가독성을 위해 URL Decoding을 진행하였다.
URL Decoding을 통해 확인된 Pattern 값은 JSP 기반의 one-line WebShell으로 보기에는 JSP File Format 형식이 맞지 않다는 것을 알 수 있다. 해당 값을 확인해보면 JSP 파일의 시작과 끝을 의미하는 ‘<%’, ‘%>’이 존재하지 않고 ‘%{ }I’로 감싸진 문자열 값을 확인할 수 있다. 이 값에 대한 정보는 Apache Tomcat AbstractAccessLogValve 클래스 사용 설명서를 통해 알 수 있는데, Header에서 데이터를 가져오는 것을 의미한다.
결과적으로 함께 전달된 Header 값[그림 17]에서 ‘prefix’, ‘suffix’, ‘c’에 매칭된 데이터를 가져와 완성된 pattern 데이터를 꾸리게 되고, 변화된 pattern 데이터는 JSP File Format 형식을 갖추게 되어 JSP 기반의 one-line WebShell이 완성된다.
[그림 17] 전달된 Header 값
No. |
설명 |
Code |
1 |
기본 공격 구문 |
%25%7Bprefix%7Di%20java.io.InputStream%20in%20%3D%20%25%7Bc%7Di.getRuntime() .exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20- 1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3 Din.read(b))!%3D- 1)%7B%20out.println(new%20String(b))%3B%20%7D%20%25%7Bsuffix%7Di |
2 |
URL Decoding |
%{prefix}i java.io.InputStream in = %{c}i.getRuntime().exec(request.getParameter("cmd")).getInputStreamint a = -1; byte[] b = new byte[2048]; while((a=in.read(b))!=-1){ out.println(new String(b)); } %{suffix}i |
3 |
Header 데이터 치환 |
<% java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter(“층”)).getInputStreamint a = -1; byte[] b = new byte[2048]; while((a=in.read(b))!=-1){ out.println(new String(b)); } %>// |
[표 8] 변환되는 Pattern 데이터 값
03. 대응방안
Spring4Shell 취약점을 대응하기에 앞서 취약한 환경 및 공격 수행 여부를 확인하는 방법은 △ 운영 시스템의 버전 확인, △ 로그 분석을 통한 공격 확인, △ 스캐너 도구를 이용하여 확인, △ PoC를 이용하여 직접 원격 점검, △ 사용 중인 S/W 영향도 확인 등이 존재한다.
No. | 설명 | Code | |
1 | 운영 시스템의 버전 확인 | JDK | java –version |
Spring Framework | 프로젝트의 pom.xml 파일 내 <org.springframework-version> 태그 확인 | ||
Apache Tomcat | Tomcat 설치 디렉터리 내 lib로 이동 후 java –cp catalina.jar org.apache.catalina.util.ServerInfo | ||
2 | 로그 분석을 통한 공격 확인 | 비정상적 페이지(생성된 웹 쉘 페이지) 요청 응답 값이 200 인 경우 Ex) 192.168.80.1 - - [07/Apr/2022:11:07:52 -0700] "POST /igloo/test HTTP/1.1" 200 386 192.168.80.1 - - [07/Apr/2022:11:07:55 -0700] "GET /igloo/test HTTP/1.1" 200 560 192.168.80.1 - - [07/Apr/2022:11:08:21 -0700] "GET /igloo/resources/test.jsp?cmd=whoami HTTP/1.1" 200 4102 | |
3 | 스캐너 도구를 이용하여 확인 | 1.
OWASP ZAP | |
4 | PoC 를 이용하여 직접 원격 점검 | 공개된 PoC를 이용하여 직접 원격에서 공격 진행 후 실제 증명이 되는지 확인 •https://github.com/reznok/Spring4Shell-POC/blob/master/exploit.py •https://github.com/BobTheShoplifter/Spring4Shell-POC/blob/main/poc.py | |
5 | 사용 중인 S/W 영향도 확인 | Spring Framework를 사용 중인 S/W의 Spring4Shell 영향도 분석 사이트 •https://github.com/NCSC-NL/spring4shell/tree/main/software |
[표 9] Spring4Shell 취약 환경 및 공격 수행 여부 확인 방법
[그림 18] OWASP ZAP 스캐너 사용 결과
Spring4Shell 취약점을 대응하는 공개된 방법으로는 △ Spring Framework 버전 업데이트(5.3.18 버전 및 5.2.20), △ Apache Tomcat 버전 업데이트(8.5.78, 9.0.62 및 10.0.20 버전), △ 보안 솔루션을 통한 차단 정책 설정, △ 전역 클래스 설정, △ JDK 다운그레이드(JDK 8 버전 이하), △ 서버 내 웹 쉘 생성 및 접근 이력 사후 점검 등의 방안이 존재한다.
1) Spring Framework 버전 업데이트(5.3.18 버전 및 5.2.20)
Spring4Shell 취약점을 대응하기 위해 Spring에서 2022년 4월 1일 5.3.18 버전 및 5.2.20 버전의 보안패치를 배포했다. 소스코드를 비교해본 결과 Spring Core에 존재했던 보안 로직에서 더욱 강화된 로직이 추가됐다. Spring4Shell 의 근본적인 원인인 ClassLoader를 사용하지 못하게 하기 위해서 Spring Framework을 5.3.18 버전 및 5.2.20 버전으로 업데이트 해야한다.
[그림 19] Spring Core의 5.3.17 버전과 5.3.18 버전 패치 비교
보안 패치가 적용된 Spring Framework 5.3.18 및 5.2.20 버전에선 기존의 방식과는 다른 방식으로 classLoader를 확인하고 있는 것을 알 수 있다. 빨간색 마킹된 영역(L289 ~ L291)은 취약점이 존재하는 5.3.17 버전의 코드이고, 파란색 영역(L290 ~ L296, L345 ~ 349)은 기존의 코드에서 수정되거나 추가된 5.3.18 버전의 코드이다.
5.3.18 버전에서 294번 ~ 296번 라인과 345번 ~ 349번 라인을 보면 보안성을 검증하는 로직이 추가된 것을 알 수 있다. PropertyDescriptor 배열 타입으로 저장된 값들의 Property Type을 동적으로 확인할 수 있는 isAssignableFrom 메소드를 사용하여 ClassLoader와 ProtectionDomain 여부를 확인하는 방안이 적용되었다.
2) Apache Tomcat 버전 업데이트(8.5.78, 9.0.62 및 10.0.20 버전)
이번 Spring4Shell 취약점에 영향을 미친 Apache Tomcat에서도 해당 취약점에 대한 조치가 이뤄졌다. 2022년 3월 31일 WebappClassLoaderBase 클래스에서 getResources 메소드 호출 시 resources를 반환하는 기존 코드가 NULL을 반환하는 코드로 변환된 8.5.78, 9.0.62 및 10.0.20 버전이 배포되었다. 다만, Apache Tomcat 업데이트를 적용하면 패치된 431번 라인을 통해 getResources 메소드가 NULL을 반환하므로 적절한 예외 처리가 필요하다.
[그림 20] Spring4Shell 취약점 보안패치 버전(Tomcat 9.0.62)의 getResources 변경 내역
3) 보안 솔루션을 통한 차단 정책 설정
네트워크 구간에서 차단을 위한 방법으로는 보안 솔루션 등을 통한 차단 정책이 존재한다. 현재 공개된 차단 정책 설정은 3가지가 존재하며, 이 설정을 통해 차단 정책을 설정할 수 있다. 차단 방법은 △ Snort 탐지 정책, △ YARA 탐지 정책, △ SIEM 를 통한 탐지 운영이 존재하며 시스템 운영 환경에 맞는 방법을 선택하여 설정할 것을 권고한다.
No. |
Code |
탐지 정책 |
1 |
IGRSS.1.0 5773 |
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"IGRSS.1.05773 Java, Classloader, CVE-2022-22965, Attempted Administrator Privilege Gain"; flow:to_server,establishedcontent:"class nocasehttp_client_bodycontent:"ClassLoadernocasehttp_client_body pcre:"/class(x2e|%2e)(module(x2e|%2e))?ClassLoader(x2e|%2e)/Pi"; sid:105773;) |
2 |
IGRSS.1.0 5772 |
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"IGRSS.1.05772 Java, Classloader, CVE-2022-22965, Attempted Administrator Privilege Gain"; flow:to_server,establishedcontent:"class nocasehttp_client_bodycontent:"ClassLoadernocasehttp_client_body pcre:"/class((x5b|%5b)s*?([x22x27]|%22|%27)module([x22x27]|%22|%27)s*?(x5d| %5d))?(x5b|%5b)s*?([x22x27]|%22|%27)ClassLoader([x22x27]|%22|%27)s*?(x5d|% 5d)/Pi"; sid:105772;) |
3 |
IGRSS.2.0 5771 |
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"IGRSS.2.05771 Java, getRuntime, CVE-2022-22965, Attempted User Privilege Gain"; flow:to_server,established:"<%"; http_headergetRuntimenocasehttp_headercontent:"exec nocasehttp_header |
4 |
IGRSS.1.0 5770 |
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"IGRSS.1.05770 Java, Classloader, CVE-2022-22965, Attempted Administrator Privilege Gain"; flow:to_server,established content:"classnocasehttp_uricontent:"ClassLoaderfast_patternnocasehttp_uri pcre:"/class(x5bs*?[x22x27]module[x22x27]s*?x5d)?x5bs*?[x22x27]ClassLoa der[x22x27]s*?x5d/Ui |
5 |
IGRSS.1.0 5769 |
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"IGRSS.1.05769 Java, Classloader, CVE-2022-22965, Attempted Administrator Privilege Gain"; flow:to_server,established content:"ClassLoaderfast_pattern:onlyhttp_uri pcre:"/classx2e(modulex2e)?ClassLoaderx2e[^&]+?=/Ui |
6 |
IGRSS.1.0 5761 |
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"IGRSS.1.05761 Java, ClassLoader, CVE-2014-0112, Attempted Administrator Privilege Gain"; flow:to_server,establishedcontent:"class nocasehttp_client_bodycontent:"ClassLoadernocasehttp_client_body pcre:"/class((x5b|%5b)s*?([x22x27]|%22|%27)module([x22x27]|%22|%27)s*?(x5d| %5d))?(x5b|%5b)s*?([x22x27]|%22|%27)ClassLoader([x22x27]|%22|%27)s*?(x5d|% 5d)/Pi"; sid:105761;) |
7 |
IGRSS.1.0 5762 |
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"IGRSS.1.05762 Java, ClassLoader, CVE-2014-0114, Attempted Administrator Privilege Gain"; flow:to_server,established content:"ClassLoaderfast_pattern:onlyhttp_uri pcre:"/classx2e(modulex2e)?ClassLoaderx2e[^&]+?=/Ui |
8 |
IGRSS.1.0 5763 |
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"IGRSS.1.05763 Java, ClassLoader, CVE-2014-0114, Attempted Administrator Privilege Gain"; flow:to_server,establishedcontent:"class nocasehttp_client_bodycontent:"ClassLoadernocasehttp_client_body pcre:"/class(.|%2e)(module(.|%2e))?ClassLoader(.|%2e)/Pi"; sid:105763;) |
9 |
IGRSS.1.0 5764 |
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"IGRSS.1.05764 Java, ClassLoader, CVE-2014-0112, Attempted Administrator Privilege Gain"; flow:to_server,establishedcontent:"class nocasehttp_uricontent:"ClassLoaderfast_patternnocasehttp_uri pcre:"/class(x5bs*?[x22x27]module[x22x27]s*?x5d)?x5bs*?[x22x27]ClassLoa der[x22x27]s*?x5d/Ui |
10 |
IGRSS.2.0 5759 |
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"IGRSS.2.05759 Java, getRuntime, Vul, Attempted User Privilege Gain"; flow:to_server,establishedhttp_header content:"getRuntimenocasehttp_headercontent:"execnocase http_header |
[표 10] Spring4Shell Snort 탐지 정책
[표 11] Spring4Shell YARA 탐지 정책
[표 12] SIEM 검색 쿼리
[그림 21] SIEM을 통한 공격 발생 여부 확인
4) 전역 클래스 설정
서버 단에서 차단을 위한 방법으로는 웹 데이터가 바인딩 되는 과정에서 특정 문자열을 차단하는 전역 클래스를 설정하는 방법이 있다. 차단하여야 하는 문자열은 △ class.*, △ Class.*, △ *.class.*, △ *.Class.* 이다. class로부터 메소드 호출을 방지하고자 해당 문자열을 차단하여야 한다.
[그림 22] 프로젝트 패키지 내 특정 문자열을 차단하는 전역 클래스 생성
5) JDK 다운그레이드 (JDK 8 버전 이하)
Spring4Shell 취약점이 발현되기 위한 조건으로 JDK 9 버전 이상이 필요하다. 취약점이 발현되지 않게 하려면 취약점이 발현되는 JDK 9 버전보다 아래 버전인 8 이하의 버전으로 다운그레이드를 진행하는 방법이 있다. 다만, 이 방법을 적용하여 조치하게 되면 서버 운영에 문제가 발생할 수 있으므로 서버 운영 담당자 혹은 개발자와 협의하여 영향도를 검토한 뒤 조치해야 한다.
6) 서버 내 웹 쉘 생성 및 접근이력 사후점검
Spring4Shell 취약점이 발현할 수 있는 △ JDK 9+, △ 5.3.18 버전 및 5.2.20보다 낮은 버전의 Spring Framework 환경, △ Apache-Tomcat(8.5.78 미만, 9.0.62 미만, 10.0.20 미만 버전)을 사용하고 있다면 외부로부터 Spring4Shell 공격이 들어와 시스템 운영 환경에 WebShell 파일 및 의도를 파악하기 어려운 파일이 존재할 수 있다. 보안 점검을 통해 시스템 내 사용자 의도와 상관없이 생성된 파일이 존재하는지를 파악하고 삭제할 것을 권고한다.
04. 참고 자료
[1] Spring4Shell 관련 글
https://www.lunasec.io/docs/blog/spring-rce-vulnerabilities/
[2] Spring4Shell 관련 분석 글
[3] PoC 코드 1
https://github.com/reznok/Spring4Shell-POC/blob/master/exploit.py
[4] PoC 코드 2
https://github.com/BobTheShoplifter/Spring4Shell-POC/blob/main/poc.py
[5] CVE-2010-1622 분석 글
http://blog.o0o.nu/2010/06/cve-2010-1622.html
[6] Spring 공식 블로그
https://spring.io/blog
[7] yara rule github
https://github.com/Neo23x0/signature-base/blob/master/yara/expl_spring4shell.yar
[8] Spring4Shell 관련 정리 github
https://github.com/NCSC-NL/spring4shell
[9] StackOverflow 2021 Survey
https://insights.stackoverflow.com/survey/2021
[10] Spring 환경 발생 CVE 등록 현황
https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=spring