Class: Rex::Post::Meterpreter::Extensions::Bofloader::CnaParser

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/post/meterpreter/extensions/bofloader/cna_parser.rb

Overview

Statically imports conventional BOF launchers from Aggressor scripts.

Defined Under Namespace

Classes: Error, Unsupported

Constant Summary collapse

FORMAT_TYPES =
{
  'b' => 'bytes',
  'i' => 'int32',
  's' => 'int16',
  'z' => 'string',
  'Z' => 'wstring'
}.freeze
INTEGER_RANGES =
{
  'i' => (-2**31)..(2**31 - 1),
  's' => (-2**15)..(2**15 - 1)
}.freeze
REQUIRED_INTRINSICS =
%w[
  barch beacon_command_register beacon_inline_execute bof_pack iff openf readb script_resource
].freeze
NULL =
Object.new.freeze
PARAMETER_PATTERN =
/\A\$(\d+)\z/
USAGE_MARKER_PATTERN =
/\A(?:(?<optional>optional|opt)|required)(?=\s|:|\z)\s*:?\s*/i
VARIABLE_PATTERN =
/\A\$[a-zA-Z_]\w*\z/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path:) ⇒ CnaParser

Creates a CNA parser.

Parameters:

  • path (String) —

    Aggressor script path.



43
44
45
46
# File 'lib/rex/post/meterpreter/extensions/bofloader/cna_parser.rb', line 43

def initialize(path:)
  @path = path
  @base_path = ::File.dirname(path)
end

Class Method Details

.parse(path:) ⇒ Hash

Parses an Aggressor script.

Parameters:

  • path (String) —

    Aggressor script path.

Returns:

  • (Hash) —

    Normalized BOF catalog.



36
37
38
# File 'lib/rex/post/meterpreter/extensions/bofloader/cna_parser.rb', line 36

def self.parse(path:)
  new(path: path).parse
end

Instance Method Details

#parse ⇒ Hash

Returns a normalized catalog of statically supported CNA aliases.

Returns:

  • (Hash) —

    Catalog and per-alias import warnings.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/rex/post/meterpreter/extensions/bofloader/cna_parser.rb', line 51

def parse
  source = read_source
  @code = remove_comments(source)
  @subroutines = named_blocks('sub')
  @overridden_intrinsics = (declared_block_names('sub') + declared_block_names('inline')).uniq & REQUIRED_INTRINSICS

  aliases = named_blocks('alias')
  registrations = command_registrations
  bofs = {}
  warnings = []
  duplicate_aliases = aliases.map(&:first).tally.select { |_name, count| count > 1 }.keys
  duplicate_aliases.each { |alias_name| warnings << "CNA alias '#{alias_name}' is declared more than once" }

  aliases.each do |alias_name, body|
    next if duplicate_aliases.include?(alias_name)
    next if function_calls(body, 'beacon_inline_execute').empty?

    begin
      definition = import_alias(alias_name, body, registrations[alias_name])
      bofs[alias_name] = definition
    rescue Unsupported => e
      warnings << "CNA alias '#{alias_name}' was skipped: #{e.message}"
    end
  end

  if bofs.empty?
    reason = warnings.first || 'the script does not contain a beacon_inline_execute alias'
    raise Error, "No compatible BOFs found in CNA script #{@path}: #{reason}"
  end

  { 'path' => @path, 'bofs' => bofs, 'warnings' => warnings }
end