持久化漏洞利用建议器技术解析

本文详细分析Metasploit框架中的持久化漏洞利用建议器模块,该模块基于会话类型、平台架构和可用漏洞自动推荐适合的持久化攻击模块,包含完整的技术实现逻辑和验证机制。

https://sploitus.com/exploit?id=MSF:POST-MULTI-RECON-PERSISTENCE_SUGGESTER-

此模块需要Metasploit:https://metasploit.com/download

当前源代码:https://github.com/rapid7/metasploit-framework

class MetasploitModule < Msf::Post

include Msf::Auxiliary::Report

def initialize(info = {}) super( update_info( info, ‘Name’ => ‘持久化漏洞利用建议器’, ‘Description’ => %q{ 此模块建议可用的持久化模块。 模块的建议基于用户打开的shell的架构和平台, 以及meterpreter中可用的漏洞利用。 需要注意的是,并非所有模块都会被检查。 漏洞利用的选择基于以下条件:会话类型、 平台、架构和必需的默认选项。 }, ‘License’ => MSF_LICENSE, ‘Author’ => [ ‘h00die’ ], ‘Platform’ => all_platforms, ‘SessionTypes’ => [ ‘meterpreter’, ‘shell’ ], ‘Notes’ => { ‘Stability’ => [], ‘Reliability’ => [], ‘SideEffects’ => [] } ) ) register_options [ Msf::OptInt.new(‘SESSION’, [ true, ‘运行此模块的会话’ ]), Msf::OptBool.new(‘SHOWDESCRIPTION’, [true, ‘显示可用漏洞利用的详细描述’, false]) ]

register_advanced_options(
  [
    # 大多数Linux持久化模块是arch-cmd,但仅用于payload目的
    # 但通常我们最终会获得meterpreter会话,从而使这些模块无效
    # 因此默认禁用此检查
    Msf::OptBool.new('ValidateArch', [true, '验证架构', false]),
    Msf::OptBool.new('ValidatePlatform', [true, '验证平台', true]),
    Msf::OptBool.new('ValidateMeterpreterCommands', [true, '验证Meterpreter命令', false]),
    # https://github.com/rapid7/rex-text/blob/a72151d409cd812978f63ad0c330efbc8f44b977/lib/rex/text/color.rb#L13
    Msf::OptString.new('Colors', [false, '模块检查的有效、无效和忽略颜色(取消设置以禁用)', 'grn/red/blu'])
  ]
)

end

def valid_colors?(color_str = datastore[‘Colors’]) tokens = color_str.split(’/’) tokens.each do |tok| print_warning “#{tok} 不太可能具有打印颜色的功能。” if tok == ‘clr’

  unless Rex::Text::Color::SUPPORTED_FORMAT_CODES.include?("%#{tok}")
    print_error "#{tok} 不是有效颜色。请参阅 https://github.com/rapid7/rex-text/blob/a72151d409cd812978f63ad0c330efbc8f44b977/lib/rex/text/color.rb#L13 获取有效颜色选项"
    return false
  end
end
true

end

def all_platforms Msf::Module::Platform.subclasses.collect { |c| c.realname.downcase } end

def session_arch # 在可用时优先调用原生架构,因为大多数LPE需要这个(例如x86,x64),而不是Java/Python Meterpreter的值(例如Java,Python) session.respond_to?(:native_arch) ? session.native_arch : session.arch end

def is_module_arch?(mod) mod_arch = mod.target.arch || mod.arch mod_arch.include?(session_arch) rescue StandardError => e print_error “检查 #{mod.fullname} 的模块架构失败 => #{e}” end

def is_module_wanted?(mod) mod[:result][:incompatibility_reasons].empty? end

def is_session_type?(mod) # 有一些模块没有定义任何兼容的会话类型。 # 我们可以假设这意味着模块可以在所有会话类型上运行, # 或者我们可以将其视为不正确的模块元数据。 mod.session_types.include?(session.type) end

def is_module_platform?(mod) return false if mod.target.nil?

platform_obj = Msf::Module::Platform.find_platform session.platform

module_platforms = mod.target.platform ? mod.target.platform.platforms : mod.platform.platforms
module_platforms.include? platform_obj

rescue ArgumentError => e # 当未找到时,find_platform会引发ArgumentError elog(‘无法找到平台’, error: e) return false end

def has_required_module_options?(mod) get_all_missing_module_options(mod).empty? end

def get_all_missing_module_options(mod) missing_options = [] mod.options.each_pair do |option_name, option| missing_options « option_name if option.required && option.default.nil? && mod.datastore[option_name].blank? end missing_options end

def valid_incompatibility_reasons(mod, verify_reasons) # 由于我们可能忽略一些原因(例如接受在纸面上不兼容的架构值), # 这会跟踪有效的原因,说明为什么我们认为正在评估的模块无效。 valid_reasons = [] valid_reasons « “缺少必需的模块选项 (#{get_all_missing_module_options(mod).join(’. ‘)})” unless verify_reasons[:has_required_module_options]

incompatible_opts = []
incompatible_opts << '架构' unless verify_reasons[:is_module_arch]
incompatible_opts << '平台' unless verify_reasons[:is_module_platform]
incompatible_opts << '会话类型' unless verify_reasons[:is_session_type]
valid_reasons << "不兼容 (#{incompatible_opts.join(', ')})" if incompatible_opts.any?

valid_reasons << '缺少/无法加载的Meterpreter命令' if verify_reasons[:missing_meterpreter_commands].any?
valid_reasons

end

def set_module_options(mod) ignore_list = [‘ACTION’, ‘TARGET’].freeze datastore.each_pair do |k, v| mod.datastore[k] = v unless ignore_list.include?(k.upcase) end if !mod.datastore[‘SESSION’] && session.present? mod.datastore[‘SESSION’] = session.sid end end

def set_module_target(mod) session_platform = Msf::Module::Platform.find_platform(session.platform) target_index = mod.targets.find_index do |target| # 如果目标没有定义自己的兼容平台或架构,则默认使用父级(模块)值。 target_platforms = target.platform&.platforms || mod.platform.platforms target_architectures = target.arch || mod.arch

  target_platforms.include?(session_platform) && target_architectures.include?(session_arch)
end
mod.datastore['Target'] = target_index if target_index

end

def setup return unless session

print_status "为 #{session.session_type} 收集持久化模块..."

setup_validation_options
if valid_colors?
  setup_color_options
else
  fail_with(Failure::BadConfig, '颜色选项设置不正确')
end

# 将持久化模块收集到数组中
@persistence_modules = []
exploit_refnames = framework.exploits.module_refnames
exploit_refnames.each_with_index do |name, index|
  print "%bld%blu[*]%clr 收集漏洞利用 #{index + 1} / #{exploit_refnames.count}\r"
  next unless name.include? '/persistence/'

  mod = framework.exploits.create name
  next unless mod

  set_module_options mod
  set_module_target mod
  verify_result = verify_mod(mod)
  @persistence_modules << { module: mod, result: verify_result } if verify_result[:has_check]
end

end

def verify_mod(mod) return { has_check: false } unless mod.is_a?(Msf::Exploit::Local) && mod.has_check?

result = {
  has_check: true,
  is_module_platform: (@validate_platform ? is_module_platform?(mod) : true),
  is_module_arch: (@validate_arch ? is_module_arch?(mod) : true),
  has_required_module_options: has_required_module_options?(mod),
  missing_meterpreter_commands: (@validate_meterpreter_commands && session.type == 'meterpreter') ? meterpreter_session_incompatibility_reasons(session) : [],
  is_session_type: is_session_type?(mod)
}
result[:incompatibility_reasons] = valid_incompatibility_reasons(mod, result)
result

end

def setup_validation_options @validate_arch = datastore[‘ValidateArch’] @validate_platform = datastore[‘ValidatePlatform’] @validate_meterpreter_commands = datastore[‘ValidateMeterpreterCommands’] end

def setup_color_options @valid_color, @invalid_color, @ignored_color = (datastore[‘Colors’] || ‘’).split(’/’)

@valid_color = "%#{@valid_color}" unless @valid_color.blank?
@invalid_color = "%#{@invalid_color}" unless @invalid_color.blank?
@ignored_color = "%#{@ignored_color}" unless @ignored_color.blank?

end

def show_found_exploits unless datastore[‘VERBOSE’] print_status “正在尝试 #{@persistence_modules.length} 个漏洞利用检查…” return end

vprint_status "正在尝试以下 #{@persistence_modules.length} 个漏洞利用检查:"
@persistence_modules.each do |x|
  vprint_status x[:module].fullname
end

end

def run runnable_exploits = @persistence_modules.select { |mod| is_module_wanted?(mod) } if runnable_exploits.empty? print_error ‘无可用建议。’ vprint_line vprint_session_info vprint_status unwanted_modules_table(@persistence_modules.reject { |mod| is_module_wanted?(mod) }) return end

show_found_exploits
results = runnable_exploits.map.with_index do |mod, index|
  print "%bld%blu[*]%clr 为漏洞利用运行检查方法 #{index + 1} / #{runnable_exploits.count}\r"
  begin
    checkcode = mod[:module].check
  rescue StandardError => e
    elog("#本地持久化建议器失败:#{e.class},当使用 #{mod[:module].shortname} 时", error: e)
    vprint_error "使用模块 #{mod[:module].fullname} 的检查失败,错误为 #{e.class}"
    next { module: mod[:module], errors: ['检查引发异常。'] }
  end

  if checkcode.nil?
    vprint_error "使用 #{mod[:module].fullname} 的检查因未知原因失败"
    next { module: mod[:module], errors: ['检查因未知原因失败。'] }
  end

  # 参见 def is_check_interesting?
  unless is_check_interesting? checkcode
    vprint_status "#{mod[:module].fullname}: #{checkcode.message}"
    next { module: mod[:module], errors: [checkcode.message] }
  end

  # 打印漏洞利用的完整名称和检查代码消息
  print_good "#{mod[:module].fullname}: #{checkcode.message}"

  # 如果数据存储选项为true,将显示详细描述
  if datastore['SHOWDESCRIPTION']
    # 描述文本的格式化
    Rex::Text.wordwrap(Rex::Text.compress(mod[:module].description), 2, 70).split(/\n/).each do |line|
      print_line line
    end
  end

  next { module: mod[:module], checkcode: checkcode.message }
end

print_line
print_status valid_modules_table(results)

vprint_line
vprint_session_info
vprint_status unwanted_modules_table(@persistence_modules.reject { |mod| is_module_wanted?(mod) })

report_data = {}
results.each do |result|
  report_data[result[:module].fullname] = result[:checkcode] if result[:checkcode]
end

report_note({
  host: session.session_host,
  type: 'persistence.suggested_module',
  data: report_data
})

end

def valid_modules_table(results) name_styler = ::Msf::Ui::Console::TablePrint::CustomColorStyler.new check_styler = ::Msf::Ui::Console::TablePrint::CustomColorStyler.new

# 按检查代码拆分所有结果。
# 我们希望返回检查代码的模块位于顶部。
checkcode_rows, without_checkcode_rows = results.partition { |result| result[:checkcode] }
rows = (checkcode_rows + without_checkcode_rows).map.with_index do |result, index|
  color = result[:checkcode] ? @valid_color : @invalid_color
  check_res = result.fetch(:checkcode) { result[:errors].join('. ') }
  name_styler.merge!({ result[:module].fullname => color })
  check_styler.merge!({ check_res => color })

  [
    index + 1,
    result[:module].fullname,
    result[:checkcode] ? '是' : '否',
    check_res
  ]
end

Rex::Text::Table.new(
  'Header' => "会话 #{session.sid} 的有效模块:",
  'Indent' => 1,
  'Columns' => [ '#', '名称', '可能易受攻击?', '检查结果' ],
  'SortIndex' => -1,
  'WordWrap' => false, # 不进行自动换行,因为当彩色输出被分成多行时会搞乱
  'ColProps' => {
    '名称' => {
      'Stylers' => [name_styler]
    },
    '可能易受攻击?' => {
      'Stylers' => [::Msf::Ui::Console::TablePrint::CustomColorStyler.new({ '是' => @valid_color, '否' => @invalid_color })]
    },
    '检查结果' => {
      'Stylers' => [check_styler]
    }
  },
  'Rows' => rows
)

end

def unwanted_modules_table(unwanted_modules) arch_styler = ::Msf::Ui::Console::TablePrint::CustomColorStyler.new platform_styler = ::Msf::Ui::Console::TablePrint::CustomColorStyler.new session_type_styler = ::Msf::Ui::Console::TablePrint::CustomColorStyler.new

rows = unwanted_modules.map.with_index do |mod, index|
  begin
    platforms = mod[:module].target.platform&.platforms&.any? ? mod[:module].target.platform.platforms : mod[:module].platform.platforms
  rescue NoMethodError
    platforms = nil
  end
  platforms ||= []
  begin
    arch = mod[:module].target.arch&.any? ? mod[:module].target.arch : mod[:module].arch
  rescue NoMethodError
    arch = nil
  end
  arch ||= []

  arch.each do |a|
    if a != session_arch
      if @validate_arch
        color = @invalid_color
      else
        color = @ignored_color
      end
    else
      color = @valid_color
    end

    arch_styler.merge!({ a.to_s => color })
  end

  platforms.each do |module_platform|
    if module_platform != ::Msf::Module::Platform.find_platform(session.platform)
      if @validate_platform
        color = @invalid_color
      else
        color = @ignored_color
      end
    else
      color = @valid_color
    end

    platform_styler.merge!({ module_platform.realname => color })
  end

  mod[:module].session_types.each do |session_type|
    color = session_type == session.type ? @valid_color : @invalid_color
    session_type_styler.merge!(session_type.to_s => color)
  end

  [
    index + 1,
    mod[:module].fullname,
    mod[:result][:incompatibility_reasons].join('. '),
    platforms.any? ? platforms.map(&:realname).sort.join(', ') : '未定义平台',
    arch.any? ? arch.sort.join(', ') : '未定义架构',
    mod[:module].session_types.any? ? mod[:module].session_types.sort.join(', ') : '未定义会话类型'
  ]
end

Rex::Text::Table.new(
  'Header' => "会话 #{session.sid} 的不兼容模块:",
  'Indent' => 1,
  'Columns' => [ '#', '名称', '原因', '平台', '架构', '会话类型' ],
  'WordWrap' => false,
  'ColProps' => {
    '架构' => {
      'Stylers' => [arch_styler]
    },
    '平台' => {
      'Stylers' => [platform_styler]
    },
    '会话类型' => {
      'Stylers' => [session_type_styler]
    }
  },
  'Rows' => rows
)

end

def vprint_session_info vprint_status ‘当前会话信息:’ vprint_status " 会话类型:#{session.type}" vprint_status " 架构:#{session_arch}" vprint_status " 平台:#{session.platform}" end

def is_check_interesting?(checkcode) [ Msf::Exploit::CheckCode::Vulnerable, Msf::Exploit::CheckCode::Appears, Msf::Exploit::CheckCode::Detected ].include? checkcode end

def print_status(msg = ‘’) super(session ? “#{session.session_host} - #{msg}” : msg) end

def print_good(msg = ‘’) super(session ? “#{session.session_host} - #{msg}” : msg) end

def print_error(msg = ‘’) super(session ? “#{session.session_host} - #{msg}” : msg) end end

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计