metasploit-framework/lib/msf/core/exploit/brute.rb

182 lines
4.7 KiB
Ruby

module Msf
###
#
# This modules provides a target-aware brute-forcing wrapper. It implements
# the exploit method and calls exploit_brute with target supplied information.
# If the selected target is not a bruteforce target, then exploit_single is
# called.
#
###
module Exploit::Brute
#
# Initializes an instance of an exploit module that supports brute force
# targets.
#
def initialize(info = {})
super
#
# Register BruteWait and BruteStep as two advanced options for this
# exploit even though not all targets may be brute force targets.
#
register_advanced_options(
[
OptInt.new('BruteWait', [ false, "Delay between brute force attempts", 1 ]),
OptInt.new('BruteStep', [ false, "Step size between brute force attempts", 16 ])
], Msf::Exploit::Brute)
end
#
# Entry point for initiating an exploit. This module wrappers the exploit
# method and determines whether or not the selected target supports brute
# force. If it does, it does some special things and wraps the brute
# forcing logic.
#
def exploit
# Is the selected target a brute force target?
if (target.bruteforce?)
# The step direction is automatically calculated
direction = {}
bf = target.bruteforce
# Get the start and stop address hashes
start = bf.start_addresses ? bf.start_addresses.dup : {}
stop = bf.stop_addresses ? bf.stop_addresses.dup : {}
step = bf.step_size
delay = bf.delay
# Enumerate each start address and try to figure out the direction
start.each_pair { |name, addr|
# If there's a stop address, figure out if it's above or below
# the start address
if (stop[name])
if (stop[name] < addr)
direction[name] = -1
else
direction[name] = 1
end
# If there's no stop address, infer the direction based on
# the default
else
direction[name] = bf.default_direction
end
}
# Import start/stop address overrides from the datastore
import_from_datastore(start, 'Start')
import_from_datastore(stop, 'Stop')
# User-defined brute wait?
if (self.datastore['BruteWait'])
delay = self.datastore['BruteWait'].to_i
end
# User-defined brute step?
if (self.datastore['BruteStep'])
step = self.datastore['BruteStep'].to_i
end
# Okay, we've got all this crap out of the way, let's actually brute
# force
stopped = []
curr = start.dup
# Automatically determine the step size based off the nop sled length
if step == 0
step = payload.nop_sled_size
if step == 0
raise OptionValidateError.new(['BruteStep']), "The step size for this exploit is invalid"
end
end
# Keep going until we run out of options
while (curr.length != stopped.length)
# Stop brute forcing once a session is found
break if session_created?
# Fire off an exploit attempt with the supplied addresses
brute_exploit(curr)
# Give it time before we try again
brute_wait(delay)
# Scan each current key, increasing it or decreasing it by the
# step size according to its direction
curr.each_key { |k|
# Has movement been stopped on this address? If so, skip it.
next if (stopped.include?(k))
# Calculate the next address before we move it to see if
# we're going to go over
next_addr = step * direction[k]
# If this item has hit a stop address, add it to the stopped
# hash and move it no further
if (stop[k])
if ((direction[k] == 1 and curr[k] + next_addr >= stop[k]) or
(direction[k] == -1 and curr[k] + next_addr < stop[k]))
stopped << k
next
end
end
# If it's not time to stop, move it
curr[k] += next_addr
}
end
else
single_exploit
end
end
#
# This routine is called once per brute force iteration. The addresses
# parameter is a hash of addresses that are incremented each iteration and
# are derived from the target's bruteforce information or the module's
# datastore in case they are being overriden.
#
def brute_exploit(addrs)
end
#
# Call if the target is not a brute force target.
#
def single_exploit
end
#
# Waits for the provide delay.
#
def brute_wait(delay)
sleep(delay)
end
protected
#
# Imports information into the supplied hash from the datastore.
# This is a way of allowing the user to override values for a
# specific brute force target by name without them actually
# being conveyed in the options list. This is a bit of a change
# from 2.x, but 2.x didn't have per-target brute force
# addresses, which I think is more valuable.
#
def import_from_datastore(hash, prefix = '')
hash.each_key { |k|
if (self.datastore[prefix + k])
hash[k] = self.datastore[prefix + k]
end
}
end
end
end