Class: Rex::Post::Meterpreter::Ui::Console::CommandDispatcher::Bofloader

Inherits:
Object
  • Object
show all
Includes:
Rex::Post::Meterpreter::Ui::Console::CommandDispatcher
Defined in:
lib/rex/post/meterpreter/ui/console/command_dispatcher/bofloader.rb

Overview

Bofloader extension - load and execute bof files

Constant Summary collapse

Klass =
Console::CommandDispatcher::Bofloader
DEFAULT_ENTRY =
'go'.freeze
@@execute_bof_opts =
Rex::Parser::Arguments.new(
  ['-h', '--help'] => [ false, 'Help Banner' ],
  ['-c', '--compile'] => [ false, 'Compile the input file (requires mingw).' ],
  ['-e', '--entry'] => [ true, "The entry point (default: #{DEFAULT_ENTRY})." ],
  ['-f', '--format-string'] => [ true, 'Argument format-string. Choose combination of: b, i, s, z, Z' ]
)

Instance Attribute Summary

Attributes included from Ui::Text::DispatcherShell::CommandDispatcher

#shell, #tab_complete_items

Instance Method Summary collapse

Methods included from Rex::Post::Meterpreter::Ui::Console::CommandDispatcher

check_hash, #client, #docs_dir, #filter_commands, #log_error, #msf_loaded?, #session, set_hash, #unknown_command

Methods included from Msf::Ui::Console::CommandDispatcher::Session

#cmd_background, #cmd_background_help, #cmd_exit, #cmd_irb, #cmd_irb_help, #cmd_irb_tabs, #cmd_pry, #cmd_pry_help, #cmd_resource, #cmd_resource_help, #cmd_resource_tabs, #cmd_sessions, #cmd_sessions_help

Methods included from Ui::Text::DispatcherShell::CommandDispatcher

#cmd_help, #cmd_help_help, #cmd_help_tabs, #deprecated_cmd, #deprecated_commands, #deprecated_help, #docs_dir, #help_to_s, included, #print, #print_error, #print_good, #print_line, #print_status, #print_warning, #tab_complete_directory, #tab_complete_filenames, #tab_complete_generic, #tab_complete_source_address, #unknown_command, #update_prompt

Constructor Details

#initialize(shell) ⇒ Bofloader

Returns a new instance of Bofloader.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/bofloader.rb', line 27

def initialize(shell)
  super
  print_line
  print_line
  print_line('meterpreter                  ')
  print_line('   ▄▄▄▄    ▒█████    █████▒  ')
  print_line('  ▓█████▄ ▒██▒  ██▒▓██   ▒   ')
  print_line('  ▒██▒ ▄██▒██░  ██▒▒████ ░   ')
  print_line('  ▒██░█▀  ▒██   ██░░▓█▒  ░   ')
  print_line('  ░▓█  ▀█▓░ ████▓▒░░▒█░      ')
  print_line('  ░▒▓███▀▒░ ▒░▒░▒░  ▒ ░      ')
  print_line('  ▒░▒   ░   ░ ▒ ▒░  ░     ~ by @kev169, @GuhnooPluxLinux, @R0wdyJoe, @skylerknecht ~')
  print_line('   ░    ░ ░ ░ ░ ▒   ░ ░      ')
  print_line('   ░          ░ ░  loader    ')
  print_line('')
  print_line
end

Instance Method Details

#cmd_execute_bof(*args) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/bofloader.rb', line 105

def cmd_execute_bof(*args)
  if args.empty?
    cmd_execute_bof_help
    return false
  end

  bof_args_literal = []
  bof_args_nonliteral = []
  bof_args_format = nil
  entry = DEFAULT_ENTRY
  compile = false

  args, bof_args_literal = args.split('--') if args.include?('--')
  if args.include?('-h') || args.include?('--help')
    cmd_execute_bof_help
    return false
  end
  bof_filename = args.shift

  @@execute_bof_opts.parse(args) do |opt, _idx, val|
    case opt
    when '-c', '--compile'
      compile = true
    when '-f', '--format-string'
      bof_args_format = val
    when '-e', '--entry'
      entry = val
    when nil
      if val.start_with?('-')
        print_error("Unknown argument: #{val}")
        return false
      end
      bof_args_nonliteral << val
    end
  end

  bof_args = bof_args_nonliteral + bof_args_literal

  unless ::File.file?(bof_filename) && ::File.readable?(bof_filename)
    print_error("Unreadable file: #{bof_filename}")
    return
  end

  if bof_args_format
    if bof_args_format.length != bof_args.length
      print_error('Format string must be the same length as arguments.')
      return
    end

    bof_args_format.chars.each_with_index do |fmt, idx|
      bof_arg = bof_args[idx]
      case fmt
      when 'b'
        if bof_arg.start_with?('file:')
          local_filename = bof_arg.split('file:')[1]

          unless ::File.file?(local_filename) && ::File.readable?(local_filename)
            print_error("Argument ##{idx + 1} contains an unreadable file: #{local_filename}")
            return false
          end
          bof_arg = ::File.binread(local_filename)
        else
          unless bof_arg.length.even?
            print_error("Argument ##{idx + 1} was not appropriately padded to an even length string!")
            return false
          end
          bytes = bof_arg.scan(/(?:[a-fA-F0-9]{2})/).map { |v| v.to_i(16) }
          if (bof_arg.length / 2 != bytes.length)
            print_error("Argument ##{idx + 1} contains invalid characters!")
            return false
          end
          bof_arg = bytes.pack('C*')
        end
      when 'i', 's'
        if bof_arg =~ /^\d+$/
          bof_arg = bof_arg.to_i
        elsif bof_arg =~ /^0x[a-fA-F0-9]+$/
          bof_arg = bof_arg[2..].to_i(16)
        else
          print_error("Argument ##{idx + 1} must be a number!")
          return false
        end
      end
      bof_args[idx] = bof_arg
    end
  elsif bof_args.length > 1
    print_error('Arguments detected but no format string specified.')
    return
  else
    print_status('No arguments specified, executing bof with no arguments.')
  end

  if compile
    bof_data = compile_c(bof_filename)
    return unless bof_data
  else
    bof_data = ::File.binread(bof_filename)
  end

  # loading all data will hang on invalid files like DLLs, so only parse the 20-byte header at first
  parsed = Metasm::COFF.decode_header(bof_data[0...20])
  bof_arch = { # map of metasm to metasploit architectures
    'AMD64' => ARCH_X64,
    'I386' => ARCH_X86
  }.fetch(parsed.header.machine, nil)

  unless bof_arch
    print_error('Unable to determine the file architecture.')
    return
  end
  unless bof_arch == client.arch
    print_error("The file architecture is incompatible with the current session (file: #{bof_arch} session: #{client.arch})")
    return
  end

  parsed = Metasm::COFF.decode(bof_data)
  unless (executable_symbols = get_executable_symbols(parsed)).include?(entry)
    print_error("The specified entry point was not found: #{entry}")
    print_error("Available symbols: #{executable_symbols.join(', ')}")
    return
  end

  begin
    output = client.bofloader.execute(bof_data, args_format: bof_args_format, args: bof_args, entry: entry)
  rescue Rex::Post::Meterpreter::Extensions::Bofloader::BofPackingError => e
    print_error("Error processing the specified arguments: #{e.message}")
    return
  end

  if output.nil?
    print_status('No output returned from bof')
  else
    print_line(output)
  end
end

#cmd_execute_bof_helpObject



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/bofloader.rb', line 63

def cmd_execute_bof_help
  print_line('Usage:   execute_bof </path/to/bof_file> [bof_nonliteral_arguments] [--format-string] [-- bof_literal_arguments]')
  print_line(@@execute_bof_opts.usage)
  print_line(
    <<~HELP
      Examples:
        execute_bof /bofs/dir.x64.o -- --help
        execute_bof /bofs/dir.x64.o --format-string Zs C:\\\\ 0
        execute_bof /bofs/upload.x64.o --format-string bZ file:/local/file.txt C:\\remote\\file.txt#{' '}
        execute_bof /bofs/dir.x64.c --compile --format-string Zs -- C:\\\\ 0#{' '}
      #{'  '}

      Argument formats:
        b       binary data (e.g. 01020304)
        i       32-bit integer
        s       16-bit integer
        z       null-terminated utf-8 string
        Z       null-terminated utf-16 string
    HELP
  )
end

#cmd_execute_bof_tabs(str, words) ⇒ Object

Tab complete the first argument as a file on the local filesystem



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/bofloader.rb', line 86

def cmd_execute_bof_tabs(str, words)
  return if words.include?('--')
  return tab_complete_filenames(str, words) if words.length == 1

  if (str =~ /^file:(.*)/)
    files = tab_complete_filenames(Regexp.last_match(1), words)
    return files.map { |f| 'file:' + f }
  end
  fmt = {
    '-c' => [ nil ],
    '--compile' => [ nil ],
    '-e' => [ true ],
    '--entry' => [ true ],
    '-f' => [ true ],
    '--format-string' => [ true ]
  }
  tab_complete_generic(fmt, str, words)
end

#commandsObject

List of supported commands.



57
58
59
60
61
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/bofloader.rb', line 57

def commands
  {
    'execute_bof' => 'Execute an arbitrary BOF file'
  }
end

#nameObject

Name for this dispatcher



23
24
25
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/bofloader.rb', line 23

def name
  'Beacon Object File Loader'
end