Class: Msf::Encoder

Inherits:
Module show all
Defined in:
lib/msf/core/encoder.rb

Overview

This class is the base class that all encoders inherit from.

Direct Known Subclasses

Alphanum, NonAlpha, NonUpper, Xor, XorDynamic

Defined Under Namespace

Modules: Type Classes: Alphanum, NonAlpha, NonUpper, Xor, XorAdditiveFeedback, XorDynamic

Constant Summary collapse

MAX_KEY_SEARCH_ATTEMPTS =

Maximum number of randomized key candidates to try in #find_key before giving up. This prevents an unbounded loop when no verifying key exists for the supplied badchar set (for example sparc/longxor_tag against a highly restrictive badchar list), which would otherwise spin forever.

1024

Constants inherited from Module

Module::REPLICANT_EXTENSION_DS_KEY

Constants included from Module::ModuleInfo

Module::ModuleInfo::ReferencesWithOptionalThirdElement, Module::ModuleInfo::UpdateableOptions

Constants included from Module::Failure

Module::Failure::BadConfig, Module::Failure::Disconnected, Module::Failure::NoAccess, Module::Failure::NoTarget, Module::Failure::None, Module::Failure::NotFound, Module::Failure::NotVulnerable, Module::Failure::PayloadFailed, Module::Failure::TimeoutExpired, Module::Failure::UnexpectedReply, Module::Failure::Unknown, Module::Failure::Unreachable, Module::Failure::UserInterrupt

Instance Attribute Summary collapse

Attributes inherited from Module

#error, #job_id, #license, #platform, #privileged, #references, #run_uuid, #user_data

Attributes included from Framework::Offspring

#framework

Attributes included from Module::UUID

#uuid

Attributes included from Rex::Ui::Subscriber::Input

#user_input

Attributes included from Rex::Ui::Subscriber::Output

#user_output

Attributes included from Module::Privileged

#priveli, #privileged

Attributes included from Module::Options

#options

Attributes included from Module::ModuleStore

#module_store

Attributes included from Module::ModuleInfo

#module_info

Attributes included from Module::FullName

#aliased_as

Attributes included from Module::DataStore

#datastore

Attributes included from Module::Author

#author

Attributes included from Module::Arch

#arch

Attributes included from Module::Alert

#alerts, #you_have_been_warned

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Module

#adapted_refname, #adapter_refname, #black_listed_auth_filenames, cached?, #debugging?, #default_cred?, #default_options, #fail_with, #file_path, #framework, #has_check?, #merge_module_info_with_target_info, #orig_cls, #owner, #perform_extensions, #platform?, #platform_to_s, #post_auth?, #register_extensions, #register_parent, #replicant, #required_cred_options, #set_defaults, #stage_refname, #stager_refname, #workspace

Methods included from Module::Reliability

#reliability, #reliability_to_s

Methods included from Module::Stability

#stability, #stability_to_s

Methods included from Module::SideEffects

#side_effects, #side_effects_to_s

Methods included from Module::UI

#init_ui

Methods included from Module::UI::Message

#print_error, #print_good, #print_prefix, #print_status, #print_warning

Methods included from Module::UI::Message::Verbose

#vprint_error, #vprint_good, #vprint_status, #vprint_warning

Methods included from Module::UI::Line

#print_line, #print_line_prefix

Methods included from Module::UI::Line::Verbose

#vprint_line

Methods included from Rex::Ui::Subscriber

#copy_ui, #init_ui, #reset_ui

Methods included from Rex::Ui::Subscriber::Input

#gets

Methods included from Rex::Ui::Subscriber::Output

#flush, #print, #print_blank_line, #print_error, #print_good, #print_line, #print_status, #print_warning

Methods included from Module::Type

#auxiliary?, #encoder?, #evasion?, #exploit?, #nop?, #payload?, #post?

Methods included from Module::Ranking

#rank, #rank_to_h, #rank_to_s

Methods included from Module::Privileged

#privileged?

Methods included from Module::Options

#deregister_option_group, #deregister_options, #register_advanced_options, #register_evasion_options, #register_option_group, #register_options, #validate

Methods included from Module::Network

#comm, #support_ipv6?, #target_host, #target_port

Methods included from Module::ModuleStore

#[], #[]=

Methods included from Module::ModuleInfo

#alias, #description, #disclosure_date, #info_fixups, #merge_check_key, #merge_info, #merge_info_advanced_options, #merge_info_alias, #merge_info_description, #merge_info_evasion_options, #merge_info_name, #merge_info_options, #merge_info_string, #merge_info_version, #name, #notes, #update_info

Methods included from Module::FullName

#aliases, #fullname, #promptname, #realname, #refname, #shortname

Methods included from Module::Failure

fail_reason_from_check_code, #report_failure

Methods included from Module::DataStore

#import_defaults, #import_target_defaults, #share_datastore

Methods included from Module::Compatibility

#compat, #compatible?, #init_compat

Methods included from Module::Author

#author_to_s, #each_author

Methods included from Module::Auth

#store_valid_credential

Methods included from Module::Arch

#arch?, #arch_to_s, #each_arch

Methods included from Module::Alert

#add_alert, #add_error, #add_info, #add_warning, #alert_user, #errors, #get_alerts, #infos, #is_usable?, #warnings, #without_prompt

Constructor Details

#initialize(info) ⇒ Encoder

Initializes an encoder module instance using the supplied information hash.



163
164
165
166
167
# File 'lib/msf/core/encoder.rb', line 163

def initialize(info)
  super({
      'Platform' => '' # All platforms by default
    }.update(info))
end

Instance Attribute Details

#available_space ⇒ Object

The amount of space available to the encoder, which may be nil, indicating that the smallest possible encoding should be used.



456
457
458
# File 'lib/msf/core/encoder.rb', line 456

def available_space
  @available_space
end

Class Method Details

.type ⇒ Object

Returns MODULE_ENCODER to indicate that this is an encoder module.



179
180
181
# File 'lib/msf/core/encoder.rb', line 179

def self.type
  return Msf::MODULE_ENCODER
end

Instance Method Details

#can_preserve_registers? ⇒ Boolean

Determines whether the encoder can preserve registers at all

Returns:

  • (Boolean)


434
435
436
# File 'lib/msf/core/encoder.rb', line 434

def can_preserve_registers?
  false
end

#decoder_block_size ⇒ Object

Returns the size of each logical encoding block, in bytes. This is typically the same as decoder_key_size.



224
225
226
# File 'lib/msf/core/encoder.rb', line 224

def decoder_block_size
  return decoder_hash['BlockSize']
end

#decoder_hash ⇒ Object

Returns the module's decoder hash or an empty hash.



239
240
241
# File 'lib/msf/core/encoder.rb', line 239

def decoder_hash
  module_info['Decoder'] || {}
end

#decoder_key_offset ⇒ Object

Returns the offset to the key associated with the decoder stub.



209
210
211
# File 'lib/msf/core/encoder.rb', line 209

def decoder_key_offset
  return decoder_hash['KeyOffset']
end

#decoder_key_pack ⇒ Object

Returns the byte-packing character that should be used to encode the key.



232
233
234
# File 'lib/msf/core/encoder.rb', line 232

def decoder_key_pack
  return decoder_hash['KeyPack'] || 'V'
end

#decoder_key_size ⇒ Object

Returns the size of the key, in bytes.



216
217
218
# File 'lib/msf/core/encoder.rb', line 216

def decoder_key_size
  return decoder_hash['KeySize']
end

#decoder_stub(state) ⇒ Object

Returns the decoder stub to use based on the supplied state.



202
203
204
# File 'lib/msf/core/encoder.rb', line 202

def decoder_stub(state)
  return decoder_hash['Stub'] || ''
end

#do_encode(state) ⇒ Object

Performs the actual encoding operation after the encoder state has been initialized and is ready to go.



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/msf/core/encoder.rb', line 315

def do_encode(state)

  # Copy the decoder stub since we may need to modify it
  stub = decoder_stub(state).dup

  if (state.key != nil and state.decoder_key_offset)
    # Substitute the decoder key in the copy of the decoder stub with the
    # one that we found
    real_key = state.key

    # If we're using context encoding, the actual value we use for
    # substitution is the context address, not the key we use for
    # encoding
    real_key = state.context_address if (state.context_encoding)

    stub[state.decoder_key_offset,state.decoder_key_size] = [ real_key.to_i ].pack(state.decoder_key_pack)
  else
    stub = encode_finalize_stub(state, stub)
  end

  # Walk the buffer encoding each block along the way
  offset = 0

  if (decoder_block_size)
    while (offset < state.buf.length)
      block = state.buf[offset, decoder_block_size]

      # Append here (String#<<) instead of creating a new string with
      # String#+ because the allocations kill performance with large
      # buffers. This isn't usually noticeable on most shellcode, but
      # when doing stage encoding on meterpreter (~750k bytes) the
      # difference is 2 orders of magnitude.
      state.encoded << encode_block(state,
          block + ("\x00" * (decoder_block_size - block.length)))

      offset += decoder_block_size
    end
  else
    state.encoded = encode_block(state, state.buf)
  end

  # Prefix the decoder stub to the encoded buffer
  state.encoded = stub + state.encoded

  # Last but not least, do one last badchar pass to see if the stub +
  # encoded payload leads to any bad char issues...
  if ((badchar_idx = has_badchars?(state.encoded, state.badchars)) != nil)
    raise BadcharError.new(state.encoded, badchar_idx, stub.length, state.encoded[badchar_idx]),
        "The #{self.name} encoder failed to encode without bad characters.",
        caller
  end

  return true
end

#encode(buf, badchars = nil, state = nil, platform = nil) ⇒ Object

This method generates an encoded version of the supplied buffer in buf using the bad characters as guides. On success, an encoded and functional version of the supplied buffer will be returned. Otherwise, an exception will be thrown if an error is encountered during the encoding process.



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/msf/core/encoder.rb', line 256

def encode(buf, badchars = nil, state = nil, platform = nil)

  # Configure platform hints if necessary
  init_platform(platform) if platform

  # Initialize an empty set of bad characters
  badchars = '' if (!badchars)

  # Initialize the encoding state and key as necessary
  if (state == nil)
    state = EncoderState.new
  end

  # Prepend data to the buffer as necessary
  buf = prepend_buf + buf

  init_state(state)

  # Save the buffer in the encoding state
  state.badchars = badchars || ''
  state.buf      = buf

  # If this encoder is key-based and we don't already have a key, find one
  if ((decoder_key_size) and
      (state.key == nil))
    # Find a key that doesn't contain and won't generate any bad
    # characters
    state.init_key(obtain_key(buf, badchars, state))

    if (state.key == nil)
      raise NoKeyError, "A key could not be found for the #{self.name} encoder.", caller
    end
  end

  # Reset the encoded buffer at this point since it may have been changed
  # while finding a key.
  state.encoded = ''

  # Call encode_begin to do any encoder specific pre-processing
  encode_begin(state)

  # Perform the actual encoding operation with the determined state
  do_encode(state)

  # Call encoded_end to do any encoder specific post-processing
  encode_end(state)

  if arch?(ARCH_CMD)
    dlog("#{self.name} result: #{state.encoded}")
  end

  # Return the encoded buffer to the caller
  return state.encoded
end

#encode_begin(state) ⇒ Object

Called when encoding is about to start immediately after the encoding state has been initialized.



394
395
396
# File 'lib/msf/core/encoder.rb', line 394

def encode_begin(state)
  return nil
end

#encode_block(state, block) ⇒ Object

Called once for each block being encoded based on the attributes of the decoder.



417
418
419
# File 'lib/msf/core/encoder.rb', line 417

def encode_block(state, block)
  return block
end

#encode_end(state) ⇒ Object

Called after encoding has completed.



409
410
411
# File 'lib/msf/core/encoder.rb', line 409

def encode_end(state)
  return nil
end

#encode_finalize_stub(state, stub) ⇒ Object

This callback allows a derived class to finalize a stub after a key have been selected. The finalized stub should be returned.



402
403
404
# File 'lib/msf/core/encoder.rb', line 402

def encode_finalize_stub(state, stub)
  stub
end

#encoder_type ⇒ Object

Returns the type or types of encoders that this specific module classifies as. If there is more than one type, the values should be separated by whitespace.



195
196
197
# File 'lib/msf/core/encoder.rb', line 195

def encoder_type
  module_info['EncoderType'] || Type::Unspecified
end

#find_bad_keys(buf, badchars) ⇒ Object (protected)

Returns the list of bad keys associated with this encoder.



646
647
648
# File 'lib/msf/core/encoder.rb', line 646

def find_bad_keys(buf, badchars)
  return Array.new(decoder_key_size) { Hash.new }
end

#find_context_key(buf, badchars, state) ⇒ Object (protected)

Parses a context information file in an effort to find a compatible key



559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
# File 'lib/msf/core/encoder.rb', line 559

def find_context_key(buf, badchars, state)
  # Make sure our context information file is sane
  if !File.exist?(datastore['ContextInformationFile'])
    raise NoKeyError, "A context information file must specified when using context encoding", caller
  end

  # Holds the address and key that we ultimately find
  address = nil
  key = nil

  # Now, parse records from the information file searching for entries
  # that are compatible with our bad character set
  File.open(datastore['ContextInformationFile']) { |f|
    begin
      # Keep looping until we hit an EOF error or we find
      # a compatible key
      while key.nil?
        # Read in the header
        type, chunk_base_address, size = f.read(9).unpack('CNN')
        offset = 0

        # Read in the blob of data that will act as our key state
        data = f.read(size)

        # If the address doesn't contain bad characters, check to see
        # the data itself will result in bad characters being generated
        while data.length > decoder_key_size
          # Extract the current set of key bytes
          key_bytes = []

          # My ruby is rusty
          data[0, decoder_key_size].each_byte { |b|
            key_bytes << b
          }

          # If the key verifies correctly, we need to check it's address
          if find_key_verify(buf, key_bytes, badchars)
            address = chunk_base_address + offset

            # Pack it to byte form so that we can check each byte for
            # bad characters
            address_bytes = integer_to_key_bytes(address)

            # Scan each byte and see what we've got going on to make sure
            # no funny business is happening with the address
            invalid_key = false
            address_bytes.each { |byte|
              if badchars.index(byte.chr)
                invalid_key = true
              end
            }

            if invalid_key == false
              key = key_bytes_to_integer(key_bytes)
              break
            end
          end

          # If it didn't verify, then we need to proceed
          data = data[1, data.length - 1]
          offset += 1
        end
      end
    rescue EOFError
    end
  }

  # If the key is nil after all is said and done, then we failed to locate
  # a compatible context-sensitive key
  if key.nil?
    raise NoKeyError, "No context key could be located in #{datastore['ContextInformationFile']}", caller
  # Otherwise, we successfully determined the key, now we need to update
  # the encoding state with our context address and set context encoding
  # to true so that the encoders know to use it
  else
    ilog("#{refname}: Successfully found context address @ #{"%.8x" % address} using key #{"%.8x" % key}")

    state.context_address  = address
    state.context_encoding = true
  end

  return key
end

#find_key(buf, badchars, state) ⇒ Object (protected)

This method finds a compatible key for the supplied buffer based also on the supplied bad characters list. This is meant to make encoders more reliable and less prone to bad character failure by doing a fairly complete key search before giving up on an encoder.



502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
# File 'lib/msf/core/encoder.rb', line 502

def find_key(buf, badchars, state)
  # Otherwise, we use the traditional method
  key_bytes = [ ]
  cur_key   = [ ]
  bad_keys  = find_bad_keys(buf, badchars)
  found     = false
  allset    = [*(0..255)]
  attempts  = 0

  # Keep chugging until we find something...right
  while (!found)
    # Give up if no verifying key has been found within a bounded number of
    # attempts. Returning nil signals the caller to move on to the next
    # encoder rather than spinning forever.
    return nil if (attempts += 1) > MAX_KEY_SEARCH_ATTEMPTS

    # Scan each byte position
    0.upto(decoder_key_size - 1) { |index|

      # Subtract the bad and leave the good
      good_keys = allset - bad_keys[index].keys

      # Was there anything left for this index?
      if (good_keys.length == 0)
        # Not much we can do about this :(
        return nil
      end

      # Set the appropriate key byte
      key_bytes[index] = good_keys[ rand(good_keys.length) ]
    }

    # Assume that we're going to rock this...
    found = true

    # Scan each byte and see what we've got going on to make sure
    # no funny business is happening
    key_bytes.each { |byte|
      if (badchars.index(byte.chr) != nil)
        found = false
      end
    }

    found = find_key_verify(buf, key_bytes, badchars) if found
  end

  # Do we have all the key bytes accounted for?
  if (key_bytes.length != decoder_key_size)
    return nil
  end

  return key_bytes_to_integer(key_bytes)
end

#find_key_verify(buf, key_bytes, badchars) ⇒ Object (protected)

Determines if the key selected by find_key is usable



691
692
693
# File 'lib/msf/core/encoder.rb', line 691

def find_key_verify(buf, key_bytes, badchars)
  true
end

#has_badchars?(buf, badchars) ⇒ Boolean (protected)

Returns the index of any bad characters found in the supplied buffer.

Returns:

  • (Boolean)


653
654
655
656
657
658
659
660
661
662
663
# File 'lib/msf/core/encoder.rb', line 653

def has_badchars?(buf, badchars)
  badchars.each_byte { |badchar|
    idx = buf.index(badchar.chr)

    if (idx != nil)
      return idx
    end
  }

  return nil
end

#init_platform(platform) ⇒ Object (protected)

This provides a hook method for platform specific processing prior to the rest of encode() running



480
481
482
# File 'lib/msf/core/encoder.rb', line 480

def init_platform(platform)

end

#init_state(state) ⇒ Object (protected)

Initializes the encoding state supplied as an argument to the attributes that have been defined for this decoder stub, such as key offset, size, and pack.



465
466
467
468
469
470
471
472
473
474
# File 'lib/msf/core/encoder.rb', line 465

def init_state(state)
  # Update the state with default decoder information
  state.decoder_key_offset = decoder_key_offset
  state.decoder_key_size   = decoder_key_size
  state.decoder_key_pack   = decoder_key_pack
  state.decoder_stub       = nil

  # Restore the original buffer in case it was modified.
  state.buf                = state.orig_buf
end

#integer_to_key_bytes(integer) ⇒ Object (protected)

Convert an integer into the individual key bytes based on the decoder's key size and packing requirements



684
685
686
# File 'lib/msf/core/encoder.rb', line 684

def integer_to_key_bytes(integer)
  return [ integer.to_i ].pack(decoder_key_pack).unpack('C*')[0, decoder_key_size]
end

#key_bytes_to_buffer(key_bytes) ⇒ Object (protected)

Convert individual key bytes into a byte buffer



676
677
678
# File 'lib/msf/core/encoder.rb', line 676

def key_bytes_to_buffer(key_bytes)
  return key_bytes.pack('C*')[0, decoder_key_size]
end

#key_bytes_to_integer(key_bytes) ⇒ Object (protected)

Convert individual key bytes into a single integer based on the decoder's key size and packing requirements



669
670
671
# File 'lib/msf/core/encoder.rb', line 669

def key_bytes_to_integer(key_bytes)
  return key_bytes_to_buffer(key_bytes).unpack(decoder_key_pack)[0]
end

#modified_registers ⇒ Object

A list of registers always modified by the encoder



441
442
443
# File 'lib/msf/core/encoder.rb', line 441

def modified_registers
  []
end

#obtain_key(buf, badchars, state) ⇒ Object (protected)

Obtains the key to use during encoding. If context encoding is enabled, special steps are taken. Otherwise, the derived class is given an opportunity to find the key.



488
489
490
491
492
493
494
# File 'lib/msf/core/encoder.rb', line 488

def obtain_key(buf, badchars, state)
  if datastore['EnableContextEncoding']
    return find_context_key(buf, badchars, state)
  else
    return find_key(buf, badchars, state)
  end
end

#prepend_buf ⇒ Object

Returns a string that should be prepended to the encoded version of the buffer before returning it to callers.



380
381
382
# File 'lib/msf/core/encoder.rb', line 380

def prepend_buf
  return ''
end

#preserves_stack? ⇒ Boolean

Determines whether the encoder can preserve the stack frame

Returns:

  • (Boolean)


448
449
450
# File 'lib/msf/core/encoder.rb', line 448

def preserves_stack?
  false
end

#to_native(buf) ⇒ Object

Provides the encoder with an opportunity to return the native format (as in the format the code will be in when it executes on the target). In general, the same buffer is returned to the caller. However, for things like unicode, the buffer is unicod encoded and then returned.



427
428
429
# File 'lib/msf/core/encoder.rb', line 427

def to_native(buf)
  buf
end

#type ⇒ Object

Returns MODULE_ENCODER to indicate that this is an encoder module.



186
187
188
# File 'lib/msf/core/encoder.rb', line 186

def type
  return Msf::MODULE_ENCODER
end