#
# ronin-exploits - A Ruby library for ronin-rb that provides exploitation and
# payload crafting functionality.
#
# Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# ronin-exploits is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-exploits is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-exploits. If not, see .
#
require 'ronin/exploits/target'
require 'ronin/exploits/exceptions'
module Ronin
module Exploits
module Mixins
#
# Adds target information to an exploit class.
#
# @api public
#
# @since 1.0.0
#
module HasTargets
class TargetError < ExploitError
end
class NoMatchingTarget < TargetError
end
class NoTargetSelected < TargetError
end
#
# Adds {ClassMethods} to the including exploit class.
#
# @param [Class] exploit
# The exploit class including {HasTargets}.
#
# @api private
#
def self.included(exploit)
exploit.extend ClassMethods
end
#
# Class methods for defining targets.
#
module ClassMethods
#
# The targets for the exploit.
#
# @return [Array]
#
# @api semipublic
#
def targets
@targets ||= if superclass.kind_of?(ClassMethods)
superclass.targets.dup
else
[]
end
end
#
# Adds a target to the exploit.
#
# @param [Hash{Symbol => Object}] kwargs
# Additional keyword arguments for {Target#initialize}.
#
# @option kwargs [Symbol, nil] :arch
# The architecture of the target.
#
# @option kwargs [Symbol, nil] :os
# The Operating System (OS) of the target.
#
# @option kwargs [String, nil] :os_version
# The Operating System (OS) version of the target.
#
# @option kwargs [Symbol, nil] :software
# The software name of the target.
#
# @option kwargs [String, nil] :version
# The software version of the target.
#
# @example
# target arch: :x86_64, os: :linux, software: 'Apache' do |t|
# t.foo = 0x123456
# t.bar = '...'
# end
#
# @api public
#
def target(**kwargs,&block)
targets << Target.new(**kwargs,&block)
end
end
# Currently selected exploit target.
#
# @return [Target, nil]
#
# @api public
attr_reader :target
#
# Sets that {#target} if the `target:` keyword argument is giving.
#
# @param [Hash{Symbol => Object}, Integer, nil] target
# The optional target information to select.
#
# @param [Hash{Symbol => Object}] kwargs
# Additional keyword arguments.
#
# @api public
#
def initialize(target: nil, **kwargs)
super(**kwargs)
if target
case target
when Hash then select_target(**target)
when Integer then self.target = target
else
raise(ArgumentError,"target: must be either a Hash or an Integer")
end
end
end
#
# Validates that a target was selected and all required params are set.
#
# @raise [Ronin::Core::Params::RequiredParam]
# One of the required params was not set.
#
# @raise [NoTargetSelected]
# No target was selected.
#
# @api semipublic
#
def perform_validate
unless target
raise(NoTargetSelected,"no target was selected")
end
super()
end
#
# Selects the target to use in exploitation.
#
# @param [Symbol] arch
# The targeted Architecture.
#
# @param [Symbol] os
# The targeted Operating System (OS).
#
# @param [Symbol] os_version
# The targeted Operating System (OS) version.
#
# @param [Symbol] software
# The targeted software.
#
# @param [Symbol] version
# The targeted software version.
#
# @raise [NoMatchingTarget]
# No matching target could be found.
#
# @api semipublic
#
def select_target(arch: nil, os: nil, os_version: nil, software: nil, version: nil)
targets = self.class.targets.lazy
if arch
targets = targets.select { |target| target.arch == arch }
end
if os
targets = targets.select { |target| target.os == os }
end
if os_version
targets = targets.select { |target| target.os_version == os_version }
end
if software
targets = targets.select { |target| target.software == software }
end
if version
targets = targets.select do |target|
target.version == version
end
end
unless (@target = targets.first)
raise(NoMatchingTarget,"could not find any matching targets")
end
end
#
# Sets the target.
#
# @param [Target, Integer, nil] new_target
# The new target value or a target index.
#
# @return [Target, nil]
# The updated target value.
#
# @raise [ArgumentError]
# The new target value must be a {Target}, an Integer or nil.
#
# @raise [NoMatchingTarget]
# The target index was out of bounds.
#
def target=(new_target)
case new_target
when Integer
@target = self.class.targets.fetch(new_target) do
raise(NoMatchingTarget,"target index is out of bounds: #{new_target.inspect}")
end
when Target, nil
@target = new_target
else
raise(ArgumentError,"target value must be a #{Target}, Integer or nil")
end
end
#
# The current targeted architecture.
#
# @return [Symbol, nil]
#
# @api public
#
def arch
target.arch if target
end
#
# The current targeted OS.
#
# @return [Symbol, nil]
#
# @api public
#
def os
target.os if target
end
#
# The current targeted OS version.
#
# @return [String, nil]
#
# @api public
#
def os_version
target.os_version if target
end
#
# The current targeted software.
#
# @return [String, nil]
#
# @api public
#
def software
target.software if target
end
#
# The current targeted software version.
#
# @return [String, nil]
#
# @api public
#
def version
target.version if target
end
end
end
end
end