1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-10-29 18:07:27 +01:00
This commit is contained in:
jvazquez-r7 2013-07-24 11:42:11 -05:00
commit 47c21dfe85
36 changed files with 772 additions and 100 deletions

View File

@ -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
#

View File

@ -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

View File

@ -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

View File

@ -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
#

View 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

View File

@ -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

View File

@ -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

View File

@ -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.
},

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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")

View File

@ -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

View File

@ -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" )

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -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.

View 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

View File

@ -0,0 +1,3 @@
foo
bar
baz

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View 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

View 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

View 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

View 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

View File

@ -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

View File

@ -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

View File

@ -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]